227 lines
4.6 KiB
C++
227 lines
4.6 KiB
C++
// Crypto/ZipStrong.cpp
|
|
|
|
#include "StdAfx.h"
|
|
|
|
#include "../../../C/7zCrc.h"
|
|
#include "../../../C/CpuArch.h"
|
|
|
|
#include "../Common/StreamUtils.h"
|
|
|
|
#include "Sha1Cls.h"
|
|
#include "ZipStrong.h"
|
|
|
|
namespace NCrypto {
|
|
namespace NZipStrong {
|
|
|
|
static const UInt16 kAES128 = 0x660E;
|
|
|
|
// DeriveKey* function is similar to CryptDeriveKey() from Windows.
|
|
// But MSDN tells that we need such scheme only if
|
|
// "the required key length is longer than the hash value"
|
|
// but ZipStrong uses it always.
|
|
|
|
static void DeriveKey2(const Byte *digest, Byte c, Byte *dest)
|
|
{
|
|
Byte buf[64];
|
|
memset(buf, c, 64);
|
|
for (unsigned i = 0; i < NSha1::kDigestSize; i++)
|
|
buf[i] ^= digest[i];
|
|
NSha1::CContext sha;
|
|
sha.Init();
|
|
sha.Update(buf, 64);
|
|
sha.Final(dest);
|
|
}
|
|
|
|
static void DeriveKey(NSha1::CContext &sha, Byte *key)
|
|
{
|
|
Byte digest[NSha1::kDigestSize];
|
|
sha.Final(digest);
|
|
Byte temp[NSha1::kDigestSize * 2];
|
|
DeriveKey2(digest, 0x36, temp);
|
|
DeriveKey2(digest, 0x5C, temp + NSha1::kDigestSize);
|
|
memcpy(key, temp, 32);
|
|
}
|
|
|
|
void CKeyInfo::SetPassword(const Byte *data, UInt32 size)
|
|
{
|
|
NSha1::CContext sha;
|
|
sha.Init();
|
|
sha.Update(data, size);
|
|
DeriveKey(sha, MasterKey);
|
|
}
|
|
|
|
STDMETHODIMP CBaseCoder::CryptoSetPassword(const Byte *data, UInt32 size)
|
|
{
|
|
_key.SetPassword(data, size);
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CBaseCoder::Init()
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDecoder::ReadHeader(ISequentialInStream *inStream, UInt32 crc, UInt64 unpackSize)
|
|
{
|
|
Byte temp[4];
|
|
RINOK(ReadStream_FALSE(inStream, temp, 2));
|
|
_ivSize = GetUi16(temp);
|
|
if (_ivSize == 0)
|
|
{
|
|
memset(_iv, 0, 16);
|
|
SetUi32(_iv + 0, crc);
|
|
SetUi64(_iv + 4, unpackSize);
|
|
_ivSize = 12;
|
|
}
|
|
else if (_ivSize == 16)
|
|
{
|
|
RINOK(ReadStream_FALSE(inStream, _iv, _ivSize));
|
|
}
|
|
else
|
|
return E_NOTIMPL;
|
|
RINOK(ReadStream_FALSE(inStream, temp, 4));
|
|
_remSize = GetUi32(temp);
|
|
const UInt32 kAlign = 16;
|
|
if (_remSize < 16 || _remSize > (1 << 18))
|
|
return E_NOTIMPL;
|
|
if (_remSize + kAlign > _buf.Size())
|
|
{
|
|
_buf.Alloc(_remSize + kAlign);
|
|
_bufAligned = (Byte *)((ptrdiff_t)((Byte *)_buf + kAlign - 1) & ~(ptrdiff_t)(kAlign - 1));
|
|
}
|
|
return ReadStream_FALSE(inStream, _bufAligned, _remSize);
|
|
}
|
|
|
|
HRESULT CDecoder::Init_and_CheckPassword(bool &passwOK)
|
|
{
|
|
passwOK = false;
|
|
if (_remSize < 16)
|
|
return E_NOTIMPL;
|
|
Byte *p = _bufAligned;
|
|
UInt16 format = GetUi16(p);
|
|
if (format != 3)
|
|
return E_NOTIMPL;
|
|
UInt16 algId = GetUi16(p + 2);
|
|
if (algId < kAES128)
|
|
return E_NOTIMPL;
|
|
algId -= kAES128;
|
|
if (algId > 2)
|
|
return E_NOTIMPL;
|
|
UInt16 bitLen = GetUi16(p + 4);
|
|
UInt16 flags = GetUi16(p + 6);
|
|
if (algId * 64 + 128 != bitLen)
|
|
return E_NOTIMPL;
|
|
_key.KeySize = 16 + algId * 8;
|
|
bool cert = ((flags & 2) != 0);
|
|
|
|
if ((flags & 0x4000) != 0)
|
|
{
|
|
// Use 3DES
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
if (cert)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
else
|
|
{
|
|
if ((flags & 1) == 0)
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
UInt32 rdSize = GetUi16(p + 8);
|
|
|
|
if (rdSize + 16 > _remSize)
|
|
return E_NOTIMPL;
|
|
|
|
/*
|
|
if (cert)
|
|
{
|
|
// how to filter rd, if ((rdSize & 0xF) != 0) ?
|
|
if ((rdSize & 0x7) != 0)
|
|
return E_NOTIMPL;
|
|
}
|
|
else
|
|
*/
|
|
{
|
|
if ((rdSize & 0xF) != 0)
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
memmove(p, p + 10, rdSize);
|
|
const Byte *p2 = p + rdSize + 10;
|
|
UInt32 reserved = GetUi32(p2);
|
|
p2 += 4;
|
|
|
|
/*
|
|
if (cert)
|
|
{
|
|
UInt32 numRecipients = reserved;
|
|
|
|
if (numRecipients == 0)
|
|
return E_NOTIMPL;
|
|
|
|
{
|
|
UInt32 hashAlg = GetUi16(p2);
|
|
hashAlg = hashAlg;
|
|
UInt32 hashSize = GetUi16(p2 + 2);
|
|
hashSize = hashSize;
|
|
p2 += 4;
|
|
|
|
reserved = reserved;
|
|
// return E_NOTIMPL;
|
|
|
|
for (unsigned r = 0; r < numRecipients; r++)
|
|
{
|
|
UInt32 specSize = GetUi16(p2);
|
|
p2 += 2;
|
|
p2 += specSize;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
*/
|
|
{
|
|
if (reserved != 0)
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
UInt32 validSize = GetUi16(p2);
|
|
p2 += 2;
|
|
const size_t validOffset = p2 - p;
|
|
if ((validSize & 0xF) != 0 || validOffset + validSize != _remSize)
|
|
return E_NOTIMPL;
|
|
|
|
{
|
|
RINOK(SetKey(_key.MasterKey, _key.KeySize));
|
|
RINOK(SetInitVector(_iv, 16));
|
|
RINOK(Init());
|
|
Filter(p, rdSize);
|
|
}
|
|
|
|
Byte fileKey[32];
|
|
NSha1::CContext sha;
|
|
sha.Init();
|
|
sha.Update(_iv, _ivSize);
|
|
sha.Update(p, rdSize - 16); // we don't use last 16 bytes (PAD bytes)
|
|
DeriveKey(sha, fileKey);
|
|
|
|
RINOK(SetKey(fileKey, _key.KeySize));
|
|
RINOK(SetInitVector(_iv, 16));
|
|
Init();
|
|
|
|
memmove(p, p + validOffset, validSize);
|
|
Filter(p, validSize);
|
|
|
|
if (validSize < 4)
|
|
return E_NOTIMPL;
|
|
validSize -= 4;
|
|
if (GetUi32(p + validSize) != CrcCalc(p, validSize))
|
|
return S_OK;
|
|
passwOK = true;
|
|
return S_OK;
|
|
}
|
|
|
|
}}
|