mirror of
https://github.com/Xevion/easy7zip.git
synced 2025-12-11 23:09:54 -06:00
4.63
This commit is contained in:
committed by
Kornel Lesiński
parent
c1f1243a70
commit
3a524e5ba2
180
CPP/7zip/Crypto/ZipStrong.cpp
Executable file
180
CPP/7zip/Crypto/ZipStrong.cpp
Executable file
@@ -0,0 +1,180 @@
|
||||
// Crypto/ZipStrong.cpp
|
||||
|
||||
#include "StdAfx.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "../../../C/7zCrc.h"
|
||||
#include "../../../C/CpuArch.h"
|
||||
}
|
||||
|
||||
#include "../Common/StreamUtils.h"
|
||||
|
||||
#include "MyAES.h"
|
||||
#include "Sha1.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)
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
/*
|
||||
SetUi32(_iv, crc);
|
||||
for (int i = 0; i < 8; i++)
|
||||
_iv[4 + i] = (Byte)(unpackSize >> (8 * i));
|
||||
SetUi32(_iv + 12, 0);
|
||||
*/
|
||||
}
|
||||
else if (_ivSize == 16)
|
||||
{
|
||||
RINOK(ReadStream_FALSE(inStream, _iv, _ivSize));
|
||||
}
|
||||
else
|
||||
return E_NOTIMPL;
|
||||
RINOK(ReadStream_FALSE(inStream, temp, 4));
|
||||
_remSize = GetUi32(temp);
|
||||
if (_remSize > _buf.GetCapacity())
|
||||
{
|
||||
_buf.Free();
|
||||
_buf.SetCapacity(_remSize);
|
||||
}
|
||||
return ReadStream_FALSE(inStream, _buf, _remSize);
|
||||
}
|
||||
|
||||
HRESULT CDecoder::CheckPassword(bool &passwOK)
|
||||
{
|
||||
passwOK = false;
|
||||
if (_remSize < 10)
|
||||
return E_NOTIMPL;
|
||||
Byte *p = _buf;
|
||||
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;
|
||||
if ((flags & 1) == 0)
|
||||
return E_NOTIMPL;
|
||||
UInt32 rdSize = GetUi16(p + 8);
|
||||
UInt32 pos = 10;
|
||||
Byte *rd = p + pos;
|
||||
pos += rdSize;
|
||||
if (pos + 4 > _remSize)
|
||||
return E_NOTIMPL;
|
||||
UInt32 reserved = GetUi32(p + pos);
|
||||
pos += 4;
|
||||
if (reserved != 0)
|
||||
return E_NOTIMPL;
|
||||
if (pos + 2 > _remSize)
|
||||
return E_NOTIMPL;
|
||||
UInt32 validSize = GetUi16(p + pos);
|
||||
pos += 2;
|
||||
Byte *validData = p + pos;
|
||||
if (pos + validSize != _remSize)
|
||||
return E_NOTIMPL;
|
||||
|
||||
if (!_aesFilter)
|
||||
_aesFilter = new CAesCbcDecoder;
|
||||
|
||||
CMyComPtr<ICryptoProperties> cp;
|
||||
RINOK(_aesFilter.QueryInterface(IID_ICryptoProperties, &cp));
|
||||
{
|
||||
RINOK(cp->SetKey(_key.MasterKey, _key.KeySize));
|
||||
RINOK(cp->SetInitVector(_iv, 16));
|
||||
_aesFilter->Init();
|
||||
if (_aesFilter->Filter(rd, rdSize) != rdSize)
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
Byte fileKey[32];
|
||||
NSha1::CContext sha;
|
||||
sha.Init();
|
||||
sha.Update(_iv, 16);
|
||||
sha.Update(rd, rdSize - 16); // we don't use last 16 bytes (PAD bytes)
|
||||
DeriveKey(sha, fileKey);
|
||||
|
||||
RINOK(cp->SetKey(fileKey, _key.KeySize));
|
||||
RINOK(cp->SetInitVector(_iv, 16));
|
||||
_aesFilter->Init();
|
||||
if (_aesFilter->Filter(validData, validSize) != validSize)
|
||||
return E_NOTIMPL;
|
||||
|
||||
if (validSize < 4)
|
||||
return E_NOTIMPL;
|
||||
validSize -= 4;
|
||||
if (GetUi32(validData + validSize) != CrcCalc(validData, validSize))
|
||||
return S_OK;
|
||||
passwOK = true;
|
||||
_aesFilter->Init();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP_(UInt32) CDecoder::Filter(Byte *data, UInt32 size)
|
||||
{
|
||||
return _aesFilter->Filter(data, size);
|
||||
}
|
||||
|
||||
}}
|
||||
Reference in New Issue
Block a user