chore: initial commit (extracted from Launchers monorepo)

Plugin: ns7zip v2.0.0
Architectures: x86-ansi, x86-unicode, amd64-unicode
License: LGPL-2.1-or-later
This commit is contained in:
Simone
2026-04-29 14:07:51 +02:00
commit d074cc7c07
3848 changed files with 1076682 additions and 0 deletions
+280
View File
@@ -0,0 +1,280 @@
// 7zAes.cpp
#include "StdAfx.h"
#include "../../../C/Sha256.h"
#include "../../Common/ComTry.h"
#ifndef _7ZIP_ST
#include "../../Windows/Synchronization.h"
#endif
#include "../Common/StreamUtils.h"
#include "7zAes.h"
#include "MyAes.h"
#ifndef EXTRACT_ONLY
#include "RandGen.h"
#endif
namespace NCrypto {
namespace N7z {
static const unsigned k_NumCyclesPower_Supported_MAX = 24;
bool CKeyInfo::IsEqualTo(const CKeyInfo &a) const
{
if (SaltSize != a.SaltSize || NumCyclesPower != a.NumCyclesPower)
return false;
for (unsigned i = 0; i < SaltSize; i++)
if (Salt[i] != a.Salt[i])
return false;
return (Password == a.Password);
}
void CKeyInfo::CalcKey()
{
if (NumCyclesPower == 0x3F)
{
unsigned pos;
for (pos = 0; pos < SaltSize; pos++)
Key[pos] = Salt[pos];
for (unsigned i = 0; i < Password.Size() && pos < kKeySize; i++)
Key[pos++] = Password[i];
for (; pos < kKeySize; pos++)
Key[pos] = 0;
}
else
{
size_t bufSize = 8 + SaltSize + Password.Size();
CObjArray<Byte> buf(bufSize);
memcpy(buf, Salt, SaltSize);
memcpy(buf + SaltSize, Password, Password.Size());
CSha256 sha;
Sha256_Init(&sha);
Byte *ctr = buf + SaltSize + Password.Size();
for (unsigned i = 0; i < 8; i++)
ctr[i] = 0;
UInt64 numRounds = (UInt64)1 << NumCyclesPower;
do
{
Sha256_Update(&sha, buf, bufSize);
for (unsigned i = 0; i < 8; i++)
if (++(ctr[i]) != 0)
break;
}
while (--numRounds != 0);
Sha256_Final(&sha, Key);
}
}
bool CKeyInfoCache::GetKey(CKeyInfo &key)
{
FOR_VECTOR (i, Keys)
{
const CKeyInfo &cached = Keys[i];
if (key.IsEqualTo(cached))
{
for (unsigned j = 0; j < kKeySize; j++)
key.Key[j] = cached.Key[j];
if (i != 0)
Keys.MoveToFront(i);
return true;
}
}
return false;
}
void CKeyInfoCache::FindAndAdd(const CKeyInfo &key)
{
FOR_VECTOR (i, Keys)
{
const CKeyInfo &cached = Keys[i];
if (key.IsEqualTo(cached))
{
if (i != 0)
Keys.MoveToFront(i);
return;
}
}
Add(key);
}
void CKeyInfoCache::Add(const CKeyInfo &key)
{
if (Keys.Size() >= Size)
Keys.DeleteBack();
Keys.Insert(0, key);
}
static CKeyInfoCache g_GlobalKeyCache(32);
#ifndef _7ZIP_ST
static NWindows::NSynchronization::CCriticalSection g_GlobalKeyCacheCriticalSection;
#define MT_LOCK NWindows::NSynchronization::CCriticalSectionLock lock(g_GlobalKeyCacheCriticalSection);
#else
#define MT_LOCK
#endif
CBase::CBase():
_cachedKeys(16),
_ivSize(0)
{
for (unsigned i = 0; i < sizeof(_iv); i++)
_iv[i] = 0;
}
void CBase::PrepareKey()
{
// BCJ2 threads use same password. So we use long lock.
MT_LOCK
bool finded = false;
if (!_cachedKeys.GetKey(_key))
{
finded = g_GlobalKeyCache.GetKey(_key);
if (!finded)
_key.CalcKey();
_cachedKeys.Add(_key);
}
if (!finded)
g_GlobalKeyCache.FindAndAdd(_key);
}
#ifndef EXTRACT_ONLY
/*
STDMETHODIMP CEncoder::ResetSalt()
{
_key.SaltSize = 4;
g_RandomGenerator.Generate(_key.Salt, _key.SaltSize);
return S_OK;
}
*/
STDMETHODIMP CEncoder::ResetInitVector()
{
for (unsigned i = 0; i < sizeof(_iv); i++)
_iv[i] = 0;
_ivSize = 16;
MY_RAND_GEN(_iv, _ivSize);
return S_OK;
}
STDMETHODIMP CEncoder::WriteCoderProperties(ISequentialOutStream *outStream)
{
Byte props[2 + sizeof(_key.Salt) + sizeof(_iv)];
unsigned propsSize = 1;
props[0] = (Byte)(_key.NumCyclesPower
| (_key.SaltSize == 0 ? 0 : (1 << 7))
| (_ivSize == 0 ? 0 : (1 << 6)));
if (_key.SaltSize != 0 || _ivSize != 0)
{
props[1] = (Byte)(
((_key.SaltSize == 0 ? 0 : _key.SaltSize - 1) << 4)
| (_ivSize == 0 ? 0 : _ivSize - 1));
memcpy(props + 2, _key.Salt, _key.SaltSize);
propsSize = 2 + _key.SaltSize;
memcpy(props + propsSize, _iv, _ivSize);
propsSize += _ivSize;
}
return WriteStream(outStream, props, propsSize);
}
CEncoder::CEncoder()
{
// _key.SaltSize = 4; g_RandomGenerator.Generate(_key.Salt, _key.SaltSize);
// _key.NumCyclesPower = 0x3F;
_key.NumCyclesPower = 19;
_aesFilter = new CAesCbcEncoder(kKeySize);
}
#endif
CDecoder::CDecoder()
{
_aesFilter = new CAesCbcDecoder(kKeySize);
}
STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *data, UInt32 size)
{
_key.ClearProps();
_ivSize = 0;
unsigned i;
for (i = 0; i < sizeof(_iv); i++)
_iv[i] = 0;
if (size == 0)
return S_OK;
Byte b0 = data[0];
_key.NumCyclesPower = b0 & 0x3F;
if ((b0 & 0xC0) == 0)
return size == 1 ? S_OK : E_INVALIDARG;
if (size <= 1)
return E_INVALIDARG;
Byte b1 = data[1];
unsigned saltSize = ((b0 >> 7) & 1) + (b1 >> 4);
unsigned ivSize = ((b0 >> 6) & 1) + (b1 & 0x0F);
if (size != 2 + saltSize + ivSize)
return E_INVALIDARG;
_key.SaltSize = saltSize;
data += 2;
for (i = 0; i < saltSize; i++)
_key.Salt[i] = *data++;
for (i = 0; i < ivSize; i++)
_iv[i] = *data++;
return (_key.NumCyclesPower <= k_NumCyclesPower_Supported_MAX
|| _key.NumCyclesPower == 0x3F) ? S_OK : E_NOTIMPL;
}
STDMETHODIMP CBaseCoder::CryptoSetPassword(const Byte *data, UInt32 size)
{
COM_TRY_BEGIN
_key.Password.CopyFrom(data, (size_t)size);
return S_OK;
COM_TRY_END
}
STDMETHODIMP CBaseCoder::Init()
{
COM_TRY_BEGIN
PrepareKey();
CMyComPtr<ICryptoProperties> cp;
RINOK(_aesFilter.QueryInterface(IID_ICryptoProperties, &cp));
if (!cp)
return E_FAIL;
RINOK(cp->SetKey(_key.Key, kKeySize));
RINOK(cp->SetInitVector(_iv, sizeof(_iv)));
return _aesFilter->Init();
COM_TRY_END
}
STDMETHODIMP_(UInt32) CBaseCoder::Filter(Byte *data, UInt32 size)
{
return _aesFilter->Filter(data, size);
}
}}
+118
View File
@@ -0,0 +1,118 @@
// 7zAes.h
#ifndef __CRYPTO_7Z_AES_H
#define __CRYPTO_7Z_AES_H
#include "../../Common/MyBuffer.h"
#include "../../Common/MyCom.h"
#include "../../Common/MyVector.h"
#include "../ICoder.h"
#include "../IPassword.h"
namespace NCrypto {
namespace N7z {
const unsigned kKeySize = 32;
const unsigned kSaltSizeMax = 16;
const unsigned kIvSizeMax = 16; // AES_BLOCK_SIZE;
class CKeyInfo
{
public:
unsigned NumCyclesPower;
unsigned SaltSize;
Byte Salt[kSaltSizeMax];
CByteBuffer Password;
Byte Key[kKeySize];
bool IsEqualTo(const CKeyInfo &a) const;
void CalcKey();
CKeyInfo() { ClearProps(); }
void ClearProps()
{
NumCyclesPower = 0;
SaltSize = 0;
for (unsigned i = 0; i < sizeof(Salt); i++)
Salt[i] = 0;
}
};
class CKeyInfoCache
{
unsigned Size;
CObjectVector<CKeyInfo> Keys;
public:
CKeyInfoCache(unsigned size): Size(size) {}
bool GetKey(CKeyInfo &key);
void Add(const CKeyInfo &key);
void FindAndAdd(const CKeyInfo &key);
};
class CBase
{
CKeyInfoCache _cachedKeys;
protected:
CKeyInfo _key;
Byte _iv[kIvSizeMax];
unsigned _ivSize;
void PrepareKey();
CBase();
};
class CBaseCoder:
public ICompressFilter,
public ICryptoSetPassword,
public CMyUnknownImp,
public CBase
{
protected:
CMyComPtr<ICompressFilter> _aesFilter;
public:
INTERFACE_ICompressFilter(;)
STDMETHOD(CryptoSetPassword)(const Byte *data, UInt32 size);
};
#ifndef EXTRACT_ONLY
class CEncoder:
public CBaseCoder,
public ICompressWriteCoderProperties,
// public ICryptoResetSalt,
public ICryptoResetInitVector
{
public:
MY_UNKNOWN_IMP4(
ICompressFilter,
ICryptoSetPassword,
ICompressWriteCoderProperties,
// ICryptoResetSalt,
ICryptoResetInitVector)
STDMETHOD(WriteCoderProperties)(ISequentialOutStream *outStream);
// STDMETHOD(ResetSalt)();
STDMETHOD(ResetInitVector)();
CEncoder();
};
#endif
class CDecoder:
public CBaseCoder,
public ICompressSetDecoderProperties2
{
public:
MY_UNKNOWN_IMP3(
ICompressFilter,
ICryptoSetPassword,
ICompressSetDecoderProperties2)
STDMETHOD(SetDecoderProperties2)(const Byte *data, UInt32 size);
CDecoder();
};
}}
#endif
@@ -0,0 +1,17 @@
// 7zAesRegister.cpp
#include "StdAfx.h"
#include "../Common/RegisterCodec.h"
#include "7zAes.h"
namespace NCrypto {
namespace N7z {
REGISTER_FILTER_E(7zAES,
CDecoder(),
CEncoder(),
0x6F10701, "7zAES")
}}
+4
View File
@@ -0,0 +1,4 @@
EXPORTS
CreateObject PRIVATE
GetNumberOfMethods PRIVATE
GetMethodProperty PRIVATE
+120
View File
@@ -0,0 +1,120 @@
// HmacSha1.cpp
#include "StdAfx.h"
#include "../../../C/CpuArch.h"
#include "HmacSha1.h"
namespace NCrypto {
namespace NSha1 {
void CHmac::SetKey(const Byte *key, size_t keySize)
{
Byte keyTemp[kBlockSize];
size_t i;
for (i = 0; i < kBlockSize; i++)
keyTemp[i] = 0;
if (keySize > kBlockSize)
{
_sha.Init();
_sha.Update(key, keySize);
_sha.Final(keyTemp);
}
else
for (i = 0; i < keySize; i++)
keyTemp[i] = key[i];
for (i = 0; i < kBlockSize; i++)
keyTemp[i] ^= 0x36;
_sha.Init();
_sha.Update(keyTemp, kBlockSize);
for (i = 0; i < kBlockSize; i++)
keyTemp[i] ^= 0x36 ^ 0x5C;
_sha2.Init();
_sha2.Update(keyTemp, kBlockSize);
}
void CHmac::Final(Byte *mac, size_t macSize)
{
Byte digest[kDigestSize];
_sha.Final(digest);
_sha2.Update(digest, kDigestSize);
_sha2.Final(digest);
for (size_t i = 0; i < macSize; i++)
mac[i] = digest[i];
}
void CHmac32::SetKey(const Byte *key, size_t keySize)
{
UInt32 keyTemp[kNumBlockWords];
size_t i;
for (i = 0; i < kNumBlockWords; i++)
keyTemp[i] = 0;
if (keySize > kBlockSize)
{
CContext sha;
sha.Init();
sha.Update(key, keySize);
Byte digest[kDigestSize];
sha.Final(digest);
for (i = 0 ; i < kNumDigestWords; i++)
keyTemp[i] = GetBe32(digest + i * 4 + 0);
}
else
for (i = 0; i < keySize; i++)
keyTemp[i / 4] |= (key[i] << (24 - 8 * (i & 3)));
for (i = 0; i < kNumBlockWords; i++)
keyTemp[i] ^= 0x36363636;
_sha.Init();
_sha.Update(keyTemp, kNumBlockWords);
for (i = 0; i < kNumBlockWords; i++)
keyTemp[i] ^= 0x36363636 ^ 0x5C5C5C5C;
_sha2.Init();
_sha2.Update(keyTemp, kNumBlockWords);
}
void CHmac32::Final(UInt32 *mac, size_t macSize)
{
UInt32 digest[kNumDigestWords];
_sha.Final(digest);
_sha2.Update(digest, kNumDigestWords);
_sha2.Final(digest);
for (size_t i = 0; i < macSize; i++)
mac[i] = digest[i];
}
void CHmac32::GetLoopXorDigest(UInt32 *mac, UInt32 numIteration)
{
UInt32 block[kNumBlockWords];
UInt32 block2[kNumBlockWords];
_sha.PrepareBlock(block, kNumDigestWords);
_sha2.PrepareBlock(block2, kNumDigestWords);
for (unsigned s = 0; s < kNumDigestWords; s++)
block[s] = mac[s];
for (UInt32 i = 0; i < numIteration; i++)
{
_sha.GetBlockDigest(block, block2);
_sha2.GetBlockDigest(block2, block);
for (unsigned s = 0; s < kNumDigestWords; s++)
mac[s] ^= block[s];
}
}
}}
+39
View File
@@ -0,0 +1,39 @@
// HmacSha1.h
// Implements HMAC-SHA-1 (RFC2104, FIPS-198)
#ifndef __CRYPTO_HMAC_SHA1_H
#define __CRYPTO_HMAC_SHA1_H
#include "Sha1Cls.h"
namespace NCrypto {
namespace NSha1 {
// Use: SetKey(key, keySize); for () Update(data, size); Final(mac, macSize);
class CHmac
{
CContext _sha;
CContext _sha2;
public:
void SetKey(const Byte *key, size_t keySize);
void Update(const Byte *data, size_t dataSize) { _sha.Update(data, dataSize); }
void Final(Byte *mac, size_t macSize = kDigestSize);
};
class CHmac32
{
CContext32 _sha;
CContext32 _sha2;
public:
void SetKey(const Byte *key, size_t keySize);
void Update(const UInt32 *data, size_t dataSize) { _sha.Update(data, dataSize); }
void Final(UInt32 *mac, size_t macSize = kNumDigestWords);
// It'sa for hmac function. in,out: mac[kNumDigestWords].
void GetLoopXorDigest(UInt32 *mac, UInt32 numIteration);
};
}}
#endif
@@ -0,0 +1,62 @@
// HmacSha256.cpp
#include "StdAfx.h"
#include "../../../C/CpuArch.h"
#include "HmacSha256.h"
namespace NCrypto {
namespace NSha256 {
static const unsigned kBlockSize = 64;
void CHmac::SetKey(const Byte *key, size_t keySize)
{
Byte temp[kBlockSize];
size_t i;
for (i = 0; i < kBlockSize; i++)
temp[i] = 0;
if (keySize > kBlockSize)
{
Sha256_Init(&_sha);
Sha256_Update(&_sha, key, keySize);
Sha256_Final(&_sha, temp);
}
else
for (i = 0; i < keySize; i++)
temp[i] = key[i];
for (i = 0; i < kBlockSize; i++)
temp[i] ^= 0x36;
Sha256_Init(&_sha);
Sha256_Update(&_sha, temp, kBlockSize);
for (i = 0; i < kBlockSize; i++)
temp[i] ^= 0x36 ^ 0x5C;
Sha256_Init(&_sha2);
Sha256_Update(&_sha2, temp, kBlockSize);
}
void CHmac::Final(Byte *mac)
{
Sha256_Final(&_sha, mac);
Sha256_Update(&_sha2, mac, SHA256_DIGEST_SIZE);
Sha256_Final(&_sha2, mac);
}
/*
void CHmac::Final(Byte *mac, size_t macSize)
{
Byte digest[SHA256_DIGEST_SIZE];
Final(digest);
for (size_t i = 0; i < macSize; i++)
mac[i] = digest[i];
}
*/
}}
@@ -0,0 +1,27 @@
// HmacSha256.h
// Implements HMAC-SHA-256 (RFC2104, FIPS-198)
#ifndef __CRYPTO_HMAC_SHA256_H
#define __CRYPTO_HMAC_SHA256_H
#include "../../../C/Sha256.h"
namespace NCrypto {
namespace NSha256 {
const unsigned kDigestSize = SHA256_DIGEST_SIZE;
class CHmac
{
CSha256 _sha;
CSha256 _sha2;
public:
void SetKey(const Byte *key, size_t keySize);
void Update(const Byte *data, size_t dataSize) { Sha256_Update(&_sha, data, dataSize); }
void Final(Byte *mac);
// void Final(Byte *mac, size_t macSize);
};
}}
#endif
+112
View File
@@ -0,0 +1,112 @@
// Crypto/MyAes.cpp
#include "StdAfx.h"
#include "../../../C/CpuArch.h"
#include "MyAes.h"
namespace NCrypto {
static struct CAesTabInit { CAesTabInit() { AesGenTables();} } g_AesTabInit;
CAesCbcCoder::CAesCbcCoder(bool encodeMode, unsigned keySize):
_keySize(keySize),
_keyIsSet(false),
_encodeMode(encodeMode)
{
_offset = ((0 - (unsigned)(ptrdiff_t)_aes) & 0xF) / sizeof(UInt32);
memset(_iv, 0, AES_BLOCK_SIZE);
SetFunctions(0);
}
STDMETHODIMP CAesCbcCoder::Init()
{
AesCbc_Init(_aes + _offset, _iv);
return _keyIsSet ? S_OK : E_FAIL;
}
STDMETHODIMP_(UInt32) CAesCbcCoder::Filter(Byte *data, UInt32 size)
{
if (!_keyIsSet)
return 0;
if (size == 0)
return 0;
if (size < AES_BLOCK_SIZE)
return AES_BLOCK_SIZE;
size >>= 4;
_codeFunc(_aes + _offset, data, size);
return size << 4;
}
STDMETHODIMP CAesCbcCoder::SetKey(const Byte *data, UInt32 size)
{
if ((size & 0x7) != 0 || size < 16 || size > 32)
return E_INVALIDARG;
if (_keySize != 0 && size != _keySize)
return E_INVALIDARG;
AES_SET_KEY_FUNC setKeyFunc = _encodeMode ? Aes_SetKey_Enc : Aes_SetKey_Dec;
setKeyFunc(_aes + _offset + 4, data, size);
_keyIsSet = true;
return S_OK;
}
STDMETHODIMP CAesCbcCoder::SetInitVector(const Byte *data, UInt32 size)
{
if (size != AES_BLOCK_SIZE)
return E_INVALIDARG;
memcpy(_iv, data, size);
CAesCbcCoder::Init(); // don't call virtual function here !!!
return S_OK;
}
EXTERN_C_BEGIN
void MY_FAST_CALL AesCbc_Encode(UInt32 *ivAes, Byte *data, size_t numBlocks);
void MY_FAST_CALL AesCbc_Decode(UInt32 *ivAes, Byte *data, size_t numBlocks);
void MY_FAST_CALL AesCtr_Code(UInt32 *ivAes, Byte *data, size_t numBlocks);
void MY_FAST_CALL AesCbc_Encode_Intel(UInt32 *ivAes, Byte *data, size_t numBlocks);
void MY_FAST_CALL AesCbc_Decode_Intel(UInt32 *ivAes, Byte *data, size_t numBlocks);
void MY_FAST_CALL AesCtr_Code_Intel(UInt32 *ivAes, Byte *data, size_t numBlocks);
EXTERN_C_END
bool CAesCbcCoder::SetFunctions(UInt32 algo)
{
_codeFunc = _encodeMode ?
g_AesCbc_Encode :
g_AesCbc_Decode;
if (algo == 1)
{
_codeFunc = _encodeMode ?
AesCbc_Encode:
AesCbc_Decode;
}
if (algo == 2)
{
#ifdef MY_CPU_X86_OR_AMD64
if (g_AesCbc_Encode != AesCbc_Encode_Intel)
#endif
return false;
}
return true;
}
STDMETHODIMP CAesCbcCoder::SetCoderProperties(const PROPID *propIDs, const PROPVARIANT *coderProps, UInt32 numProps)
{
for (UInt32 i = 0; i < numProps; i++)
{
const PROPVARIANT &prop = coderProps[i];
if (propIDs[i] == NCoderPropID::kDefaultProp)
{
if (prop.vt != VT_UI4)
return E_INVALIDARG;
if (!SetFunctions(prop.ulVal))
return E_NOTIMPL;
}
}
return S_OK;
}
}
+57
View File
@@ -0,0 +1,57 @@
// Crypto/MyAes.h
#ifndef __CRYPTO_MY_AES_H
#define __CRYPTO_MY_AES_H
#include "../../../C/Aes.h"
#include "../../Common/MyCom.h"
#include "../ICoder.h"
namespace NCrypto {
class CAesCbcCoder:
public ICompressFilter,
public ICryptoProperties,
public ICompressSetCoderProperties,
public CMyUnknownImp
{
AES_CODE_FUNC _codeFunc;
unsigned _offset;
unsigned _keySize;
bool _keyIsSet;
bool _encodeMode;
UInt32 _aes[AES_NUM_IVMRK_WORDS + 3];
Byte _iv[AES_BLOCK_SIZE];
bool SetFunctions(UInt32 algo);
public:
CAesCbcCoder(bool encodeMode, unsigned keySize);
virtual ~CAesCbcCoder() {}; // we need virtual destructor for derived classes
MY_UNKNOWN_IMP3(ICompressFilter, ICryptoProperties, ICompressSetCoderProperties)
INTERFACE_ICompressFilter(;)
STDMETHOD(SetKey)(const Byte *data, UInt32 size);
STDMETHOD(SetInitVector)(const Byte *data, UInt32 size);
STDMETHOD(SetCoderProperties)(const PROPID *propIDs, const PROPVARIANT *props, UInt32 numProps);
};
struct CAesCbcEncoder: public CAesCbcCoder
{
CAesCbcEncoder(unsigned keySize = 0): CAesCbcCoder(true, keySize) {}
};
struct CAesCbcDecoder: public CAesCbcCoder
{
CAesCbcDecoder(unsigned keySize = 0): CAesCbcCoder(false, keySize) {}
};
}
#endif
@@ -0,0 +1,16 @@
// MyAesReg.cpp
#include "StdAfx.h"
#include "../Common/RegisterCodec.h"
#include "MyAes.h"
namespace NCrypto {
REGISTER_FILTER_E(AES256CBC,
CAesCbcDecoder(32),
CAesCbcEncoder(32),
0x6F00181, "AES256CBC")
}
@@ -0,0 +1,97 @@
// Pbkdf2HmacSha1.cpp
#include "StdAfx.h"
#include "../../../C/CpuArch.h"
#include "HmacSha1.h"
namespace NCrypto {
namespace NSha1 {
void Pbkdf2Hmac(const Byte *pwd, size_t pwdSize,
const Byte *salt, size_t saltSize,
UInt32 numIterations,
Byte *key, size_t keySize)
{
CHmac baseCtx;
baseCtx.SetKey(pwd, pwdSize);
for (UInt32 i = 1; keySize != 0; i++)
{
CHmac ctx = baseCtx;
ctx.Update(salt, saltSize);
Byte u[kDigestSize];
SetBe32(u, i);
ctx.Update(u, 4);
ctx.Final(u, kDigestSize);
const unsigned curSize = (keySize < kDigestSize) ? (unsigned)keySize : kDigestSize;
unsigned s;
for (s = 0; s < curSize; s++)
key[s] = u[s];
for (UInt32 j = numIterations; j > 1; j--)
{
ctx = baseCtx;
ctx.Update(u, kDigestSize);
ctx.Final(u, kDigestSize);
for (s = 0; s < curSize; s++)
key[s] ^= u[s];
}
key += curSize;
keySize -= curSize;
}
}
void Pbkdf2Hmac32(const Byte *pwd, size_t pwdSize,
const UInt32 *salt, size_t saltSize,
UInt32 numIterations,
UInt32 *key, size_t keySize)
{
CHmac32 baseCtx;
baseCtx.SetKey(pwd, pwdSize);
for (UInt32 i = 1; keySize != 0; i++)
{
CHmac32 ctx = baseCtx;
ctx.Update(salt, saltSize);
UInt32 u[kNumDigestWords];
u[0] = i;
ctx.Update(u, 1);
ctx.Final(u, kNumDigestWords);
// Speed-optimized code start
ctx = baseCtx;
ctx.GetLoopXorDigest(u, numIterations - 1);
// Speed-optimized code end
const unsigned curSize = (keySize < kNumDigestWords) ? (unsigned)keySize : kNumDigestWords;
unsigned s;
for (s = 0; s < curSize; s++)
key[s] = u[s];
/*
// Default code start
for (UInt32 j = numIterations; j > 1; j--)
{
ctx = baseCtx;
ctx.Update(u, kNumDigestWords);
ctx.Final(u, kNumDigestWords);
for (s = 0; s < curSize; s++)
key[s] ^= u[s];
}
// Default code end
*/
key += curSize;
keySize -= curSize;
}
}
}}
@@ -0,0 +1,22 @@
// Pbkdf2HmacSha1.h
// Password-Based Key Derivation Function (RFC 2898, PKCS #5) based on HMAC-SHA-1
#ifndef __CRYPTO_PBKDF2_HMAC_SHA1_H
#define __CRYPTO_PBKDF2_HMAC_SHA1_H
#include <stddef.h>
#include "../../Common/MyTypes.h"
namespace NCrypto {
namespace NSha1 {
void Pbkdf2Hmac(const Byte *pwd, size_t pwdSize, const Byte *salt, size_t saltSize,
UInt32 numIterations, Byte *key, size_t keySize);
void Pbkdf2Hmac32(const Byte *pwd, size_t pwdSize, const UInt32 *salt, size_t saltSize,
UInt32 numIterations, UInt32 *key, size_t keySize);
}}
#endif
+233
View File
@@ -0,0 +1,233 @@
// RandGen.cpp
#include "StdAfx.h"
#include "RandGen.h"
#ifndef USE_STATIC_SYSTEM_RAND
#ifndef _7ZIP_ST
#include "../../Windows/Synchronization.h"
#endif
#ifdef _WIN32
#ifdef _WIN64
#define USE_STATIC_RtlGenRandom
#endif
#ifdef USE_STATIC_RtlGenRandom
#include <ntsecapi.h>
EXTERN_C_BEGIN
#ifndef RtlGenRandom
#define RtlGenRandom SystemFunction036
BOOLEAN WINAPI RtlGenRandom(PVOID RandomBuffer, ULONG RandomBufferLength);
#endif
EXTERN_C_END
#else
EXTERN_C_BEGIN
typedef BOOLEAN (WINAPI * Func_RtlGenRandom)(PVOID RandomBuffer, ULONG RandomBufferLength);
EXTERN_C_END
#endif
#else
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define USE_POSIX_TIME
#define USE_POSIX_TIME2
#endif
#ifdef USE_POSIX_TIME
#include <time.h>
#ifdef USE_POSIX_TIME2
#include <sys/time.h>
#endif
#endif
// The seed and first generated data block depend from processID,
// theadID, timer and system random generator, if available.
// Other generated data blocks depend from previous state
#define HASH_UPD(x) Sha256_Update(&hash, (const Byte *)&x, sizeof(x));
void CRandomGenerator::Init()
{
CSha256 hash;
Sha256_Init(&hash);
unsigned numIterations = 1000;
{
#ifndef UNDER_CE
const unsigned kNumIterations_Small = 100;
const unsigned kBufSize = 32;
Byte buf[kBufSize];
#endif
#ifdef _WIN32
DWORD w = ::GetCurrentProcessId();
HASH_UPD(w);
w = ::GetCurrentThreadId();
HASH_UPD(w);
#ifdef UNDER_CE
/*
if (CeGenRandom(kBufSize, buf))
{
numIterations = kNumIterations_Small;
Sha256_Update(&hash, buf, kBufSize);
}
*/
#elif defined(USE_STATIC_RtlGenRandom)
if (RtlGenRandom(buf, kBufSize))
{
numIterations = kNumIterations_Small;
Sha256_Update(&hash, buf, kBufSize);
}
#else
{
HMODULE hModule = ::LoadLibrary(TEXT("Advapi32.dll"));
if (hModule)
{
// SystemFunction036() is real name of RtlGenRandom() function
Func_RtlGenRandom my_RtlGenRandom = (Func_RtlGenRandom)GetProcAddress(hModule, "SystemFunction036");
if (my_RtlGenRandom)
{
if (my_RtlGenRandom(buf, kBufSize))
{
numIterations = kNumIterations_Small;
Sha256_Update(&hash, buf, kBufSize);
}
}
::FreeLibrary(hModule);
}
}
#endif
#else
pid_t pid = getpid();
HASH_UPD(pid);
pid = getppid();
HASH_UPD(pid);
{
int f = open("/dev/urandom", O_RDONLY);
unsigned numBytes = kBufSize;
if (f >= 0)
{
do
{
int n = read(f, buf, numBytes);
if (n <= 0)
break;
Sha256_Update(&hash, buf, n);
numBytes -= n;
}
while (numBytes);
close(f);
if (numBytes == 0)
numIterations = kNumIterations_Small;
}
}
/*
{
int n = getrandom(buf, kBufSize, 0);
if (n > 0)
{
Sha256_Update(&hash, buf, n);
if (n == kBufSize)
numIterations = kNumIterations_Small;
}
}
*/
#endif
}
#ifdef _DEBUG
numIterations = 2;
#endif
do
{
#ifdef _WIN32
LARGE_INTEGER v;
if (::QueryPerformanceCounter(&v))
HASH_UPD(v.QuadPart);
#endif
#ifdef USE_POSIX_TIME
#ifdef USE_POSIX_TIME2
timeval v;
if (gettimeofday(&v, 0) == 0)
{
HASH_UPD(v.tv_sec);
HASH_UPD(v.tv_usec);
}
#endif
time_t v2 = time(NULL);
HASH_UPD(v2);
#endif
#ifdef _WIN32
DWORD tickCount = ::GetTickCount();
HASH_UPD(tickCount);
#endif
for (unsigned j = 0; j < 100; j++)
{
Sha256_Final(&hash, _buff);
Sha256_Init(&hash);
Sha256_Update(&hash, _buff, SHA256_DIGEST_SIZE);
}
}
while (--numIterations);
Sha256_Final(&hash, _buff);
_needInit = false;
}
#ifndef _7ZIP_ST
static NWindows::NSynchronization::CCriticalSection g_CriticalSection;
#define MT_LOCK NWindows::NSynchronization::CCriticalSectionLock lock(g_CriticalSection);
#else
#define MT_LOCK
#endif
void CRandomGenerator::Generate(Byte *data, unsigned size)
{
MT_LOCK
if (_needInit)
Init();
while (size != 0)
{
CSha256 hash;
Sha256_Init(&hash);
Sha256_Update(&hash, _buff, SHA256_DIGEST_SIZE);
Sha256_Final(&hash, _buff);
Sha256_Init(&hash);
UInt32 salt = 0xF672ABD1;
HASH_UPD(salt);
Sha256_Update(&hash, _buff, SHA256_DIGEST_SIZE);
Byte buff[SHA256_DIGEST_SIZE];
Sha256_Final(&hash, buff);
for (unsigned i = 0; i < SHA256_DIGEST_SIZE && size != 0; i++, size--)
*data++ = buff[i];
}
}
CRandomGenerator g_RandomGenerator;
#endif
+40
View File
@@ -0,0 +1,40 @@
// RandGen.h
#ifndef __CRYPTO_RAND_GEN_H
#define __CRYPTO_RAND_GEN_H
#include "../../../C/Sha256.h"
#ifdef _WIN64
// #define USE_STATIC_SYSTEM_RAND
#endif
#ifdef USE_STATIC_SYSTEM_RAND
#ifdef _WIN32
#include <ntsecapi.h>
#define MY_RAND_GEN(data, size) RtlGenRandom(data, size)
#else
#define MY_RAND_GEN(data, size) getrandom(data, size, 0)
#endif
#else
class CRandomGenerator
{
Byte _buff[SHA256_DIGEST_SIZE];
bool _needInit;
void Init();
public:
CRandomGenerator(): _needInit(true) {};
void Generate(Byte *data, unsigned size);
};
extern CRandomGenerator g_RandomGenerator;
#define MY_RAND_GEN(data, size) g_RandomGenerator.Generate(data, size)
#endif
#endif
@@ -0,0 +1,130 @@
// Crypto/Rar20Crypto.cpp
#include "StdAfx.h"
#include "../../../C/7zCrc.h"
#include "../../../C/CpuArch.h"
#include "../../../C/RotateDefs.h"
#include "Rar20Crypto.h"
namespace NCrypto {
namespace NRar2 {
static const unsigned kNumRounds = 32;
static const Byte g_InitSubstTable[256] = {
215, 19,149, 35, 73,197,192,205,249, 28, 16,119, 48,221, 2, 42,
232, 1,177,233, 14, 88,219, 25,223,195,244, 90, 87,239,153,137,
255,199,147, 70, 92, 66,246, 13,216, 40, 62, 29,217,230, 86, 6,
71, 24,171,196,101,113,218,123, 93, 91,163,178,202, 67, 44,235,
107,250, 75,234, 49,167,125,211, 83,114,157,144, 32,193,143, 36,
158,124,247,187, 89,214,141, 47,121,228, 61,130,213,194,174,251,
97,110, 54,229,115, 57,152, 94,105,243,212, 55,209,245, 63, 11,
164,200, 31,156, 81,176,227, 21, 76, 99,139,188,127, 17,248, 51,
207,120,189,210, 8,226, 41, 72,183,203,135,165,166, 60, 98, 7,
122, 38,155,170, 69,172,252,238, 39,134, 59,128,236, 27,240, 80,
131, 3, 85,206,145, 79,154,142,159,220,201,133, 74, 64, 20,129,
224,185,138,103,173,182, 43, 34,254, 82,198,151,231,180, 58, 10,
118, 26,102, 12, 50,132, 22,191,136,111,162,179, 45, 4,148,108,
161, 56, 78,126,242,222, 15,175,146, 23, 33,241,181,190, 77,225,
0, 46,169,186, 68, 95,237, 65, 53,208,253,168, 9, 18,100, 52,
116,184,160, 96,109, 37, 30,106,140,104,150, 5,204,117,112, 84
};
void CData::UpdateKeys(const Byte *data)
{
for (unsigned i = 0; i < 16; i += 4)
for (unsigned j = 0; j < 4; j++)
Keys[j] ^= g_CrcTable[data[i + j]];
}
static inline void Swap(Byte &b1, Byte &b2)
{
Byte b = b1;
b1 = b2;
b2 = b;
}
void CData::SetPassword(const Byte *data, unsigned size)
{
Keys[0] = 0xD3A3B879L;
Keys[1] = 0x3F6D12F7L;
Keys[2] = 0x7515A235L;
Keys[3] = 0xA4E7F123L;
Byte psw[128];
memset(psw, 0, sizeof(psw));
if (size != 0)
{
if (size >= sizeof(psw))
size = sizeof(psw) - 1;
memcpy(psw, data, size);
}
memcpy(SubstTable, g_InitSubstTable, sizeof(SubstTable));
for (unsigned j = 0; j < 256; j++)
for (unsigned i = 0; i < size; i += 2)
{
unsigned n1 = (Byte)g_CrcTable[(psw[i] - j) & 0xFF];
unsigned n2 = (Byte)g_CrcTable[(psw[(size_t)i + 1] + j) & 0xFF];
for (unsigned k = 1; (n1 & 0xFF) != n2; n1++, k++)
Swap(SubstTable[n1 & 0xFF], SubstTable[(n1 + i + k) & 0xFF]);
}
for (unsigned i = 0; i < size; i += 16)
EncryptBlock(psw + i);
}
void CData::CryptBlock(Byte *buf, bool encrypt)
{
Byte inBuf[16];
UInt32 A, B, C, D;
A = GetUi32(buf + 0) ^ Keys[0];
B = GetUi32(buf + 4) ^ Keys[1];
C = GetUi32(buf + 8) ^ Keys[2];
D = GetUi32(buf + 12) ^ Keys[3];
if (!encrypt)
memcpy(inBuf, buf, sizeof(inBuf));
for (unsigned i = 0; i < kNumRounds; i++)
{
UInt32 key = Keys[(encrypt ? i : (kNumRounds - 1 - i)) & 3];
UInt32 TA = A ^ SubstLong((C + rotlFixed(D, 11)) ^ key);
UInt32 TB = B ^ SubstLong((D ^ rotlFixed(C, 17)) + key);
A = C; C = TA;
B = D; D = TB;
}
SetUi32(buf + 0, C ^ Keys[0]);
SetUi32(buf + 4, D ^ Keys[1]);
SetUi32(buf + 8, A ^ Keys[2]);
SetUi32(buf + 12, B ^ Keys[3]);
UpdateKeys(encrypt ? buf : inBuf);
}
STDMETHODIMP CDecoder::Init()
{
return S_OK;
}
static const UInt32 kBlockSize = 16;
STDMETHODIMP_(UInt32) CDecoder::Filter(Byte *data, UInt32 size)
{
if (size == 0)
return 0;
if (size < kBlockSize)
return kBlockSize;
size -= kBlockSize;
UInt32 i;
for (i = 0; i <= size; i += kBlockSize)
DecryptBlock(data + i);
return i;
}
}}
@@ -0,0 +1,48 @@
// Crypto/Rar20Crypto.h
#ifndef __CRYPTO_RAR20_CRYPTO_H
#define __CRYPTO_RAR20_CRYPTO_H
#include "../../Common/MyCom.h"
#include "../ICoder.h"
namespace NCrypto {
namespace NRar2 {
/* ICompressFilter::Init() does nothing for this filter.
Call SetPassword() to initialize filter. */
class CData
{
Byte SubstTable[256];
UInt32 Keys[4];
UInt32 SubstLong(UInt32 t) const
{
return (UInt32)SubstTable[(unsigned)t & 255]
| ((UInt32)SubstTable[(unsigned)(t >> 8) & 255] << 8)
| ((UInt32)SubstTable[(unsigned)(t >> 16) & 255] << 16)
| ((UInt32)SubstTable[(unsigned)(t >> 24) ] << 24);
}
void UpdateKeys(const Byte *data);
void CryptBlock(Byte *buf, bool encrypt);
public:
void EncryptBlock(Byte *buf) { CryptBlock(buf, true); }
void DecryptBlock(Byte *buf) { CryptBlock(buf, false); }
void SetPassword(const Byte *password, unsigned passwordLen);
};
class CDecoder:
public ICompressFilter,
public CMyUnknownImp,
public CData
{
public:
MY_UNKNOWN_IMP
INTERFACE_ICompressFilter(;)
};
}}
#endif
+257
View File
@@ -0,0 +1,257 @@
// Crypto/Rar5Aes.cpp
#include "StdAfx.h"
#include "../../../C/CpuArch.h"
#ifndef _7ZIP_ST
#include "../../Windows/Synchronization.h"
#endif
#include "Rar5Aes.h"
namespace NCrypto {
namespace NRar5 {
static const unsigned kNumIterationsLog_Max = 24;
static const unsigned kPswCheckCsumSize = 4;
static const unsigned kCheckSize = kPswCheckSize + kPswCheckCsumSize;
CKey::CKey():
_needCalc(true),
_numIterationsLog(0)
{
for (unsigned i = 0; i < sizeof(_salt); i++)
_salt[i] = 0;
}
CDecoder::CDecoder(): CAesCbcDecoder(kAesKeySize) {}
static unsigned ReadVarInt(const Byte *p, unsigned maxSize, UInt64 *val)
{
*val = 0;
for (unsigned i = 0; i < maxSize && i < 10;)
{
Byte b = p[i];
*val |= (UInt64)(b & 0x7F) << (7 * i);
i++;
if ((b & 0x80) == 0)
return i;
}
return 0;
}
HRESULT CDecoder::SetDecoderProps(const Byte *p, unsigned size, bool includeIV, bool isService)
{
UInt64 Version;
unsigned num = ReadVarInt(p, size, &Version);
if (num == 0)
return E_NOTIMPL;
p += num;
size -= num;
if (Version != 0)
return E_NOTIMPL;
num = ReadVarInt(p, size, &Flags);
if (num == 0)
return E_NOTIMPL;
p += num;
size -= num;
bool isCheck = IsThereCheck();
if (size != 1 + kSaltSize + (includeIV ? AES_BLOCK_SIZE : 0) + (unsigned)(isCheck ? kCheckSize : 0))
return E_NOTIMPL;
if (_numIterationsLog != p[0])
{
_numIterationsLog = p[0];
_needCalc = true;
}
p++;
if (memcmp(_salt, p, kSaltSize) != 0)
{
memcpy(_salt, p, kSaltSize);
_needCalc = true;
}
p += kSaltSize;
if (includeIV)
{
memcpy(_iv, p, AES_BLOCK_SIZE);
p += AES_BLOCK_SIZE;
}
_canCheck = true;
if (isCheck)
{
memcpy(_check, p, kPswCheckSize);
CSha256 sha;
Byte digest[SHA256_DIGEST_SIZE];
Sha256_Init(&sha);
Sha256_Update(&sha, _check, kPswCheckSize);
Sha256_Final(&sha, digest);
_canCheck = (memcmp(digest, p + kPswCheckSize, kPswCheckCsumSize) == 0);
if (_canCheck && isService)
{
// There was bug in RAR 5.21- : PswCheck field in service records ("QO") contained zeros.
// so we disable password checking for such bad records.
_canCheck = false;
for (unsigned i = 0; i < kPswCheckSize; i++)
if (p[i] != 0)
{
_canCheck = true;
break;
}
}
}
return (_numIterationsLog <= kNumIterationsLog_Max ? S_OK : E_NOTIMPL);
}
void CDecoder::SetPassword(const Byte *data, size_t size)
{
if (size != _password.Size() || memcmp(data, _password, size) != 0)
{
_needCalc = true;
_password.CopyFrom(data, size);
}
}
STDMETHODIMP CDecoder::Init()
{
CalcKey_and_CheckPassword();
RINOK(SetKey(_key, kAesKeySize));
RINOK(SetInitVector(_iv, AES_BLOCK_SIZE));
return CAesCbcCoder::Init();
}
UInt32 CDecoder::Hmac_Convert_Crc32(UInt32 crc) const
{
NSha256::CHmac ctx;
ctx.SetKey(_hashKey, NSha256::kDigestSize);
Byte v[4];
SetUi32(v, crc);
ctx.Update(v, 4);
Byte h[NSha256::kDigestSize];
ctx.Final(h);
crc = 0;
for (unsigned i = 0; i < NSha256::kDigestSize; i++)
crc ^= (UInt32)h[i] << ((i & 3) * 8);
return crc;
};
void CDecoder::Hmac_Convert_32Bytes(Byte *data) const
{
NSha256::CHmac ctx;
ctx.SetKey(_hashKey, NSha256::kDigestSize);
ctx.Update(data, NSha256::kDigestSize);
ctx.Final(data);
};
static CKey g_Key;
#ifndef _7ZIP_ST
static NWindows::NSynchronization::CCriticalSection g_GlobalKeyCacheCriticalSection;
#define MT_LOCK NWindows::NSynchronization::CCriticalSectionLock lock(g_GlobalKeyCacheCriticalSection);
#else
#define MT_LOCK
#endif
bool CDecoder::CalcKey_and_CheckPassword()
{
if (_needCalc)
{
{
MT_LOCK
if (!g_Key._needCalc && IsKeyEqualTo(g_Key))
{
CopyCalcedKeysFrom(g_Key);
_needCalc = false;
}
}
if (_needCalc)
{
Byte pswCheck[SHA256_DIGEST_SIZE];
{
// Pbkdf HMAC-SHA-256
NSha256::CHmac baseCtx;
baseCtx.SetKey(_password, _password.Size());
NSha256::CHmac ctx = baseCtx;
ctx.Update(_salt, sizeof(_salt));
Byte u[NSha256::kDigestSize];
Byte key[NSha256::kDigestSize];
u[0] = 0;
u[1] = 0;
u[2] = 0;
u[3] = 1;
ctx.Update(u, 4);
ctx.Final(u);
memcpy(key, u, NSha256::kDigestSize);
UInt32 numIterations = ((UInt32)1 << _numIterationsLog) - 1;
for (unsigned i = 0; i < 3; i++)
{
UInt32 j = numIterations;
for (; j != 0; j--)
{
ctx = baseCtx;
ctx.Update(u, NSha256::kDigestSize);
ctx.Final(u);
for (unsigned s = 0; s < NSha256::kDigestSize; s++)
key[s] ^= u[s];
}
// RAR uses additional iterations for additional keys
memcpy((i == 0 ? _key : (i == 1 ? _hashKey : pswCheck)), key, NSha256::kDigestSize);
numIterations = 16;
}
}
{
unsigned i;
for (i = 0; i < kPswCheckSize; i++)
_check_Calced[i] = pswCheck[i];
for (i = kPswCheckSize; i < SHA256_DIGEST_SIZE; i++)
_check_Calced[i & (kPswCheckSize - 1)] ^= pswCheck[i];
}
_needCalc = false;
{
MT_LOCK
g_Key = *this;
}
}
}
if (IsThereCheck() && _canCheck)
return (memcmp(_check_Calced, _check, kPswCheckSize) == 0);
return true;
}
}}
+84
View File
@@ -0,0 +1,84 @@
// Crypto/Rar5Aes.h
#ifndef __CRYPTO_RAR5_AES_H
#define __CRYPTO_RAR5_AES_H
#include "../../../C/Aes.h"
#include "../../Common/MyBuffer.h"
#include "HmacSha256.h"
#include "MyAes.h"
namespace NCrypto {
namespace NRar5 {
const unsigned kSaltSize = 16;
const unsigned kPswCheckSize = 8;
const unsigned kAesKeySize = 32;
namespace NCryptoFlags
{
const unsigned kPswCheck = 1 << 0;
const unsigned kUseMAC = 1 << 1;
}
struct CKey
{
bool _needCalc;
unsigned _numIterationsLog;
Byte _salt[kSaltSize];
CByteBuffer _password;
Byte _key[kAesKeySize];
Byte _check_Calced[kPswCheckSize];
Byte _hashKey[SHA256_DIGEST_SIZE];
void CopyCalcedKeysFrom(const CKey &k)
{
memcpy(_key, k._key, sizeof(_key));
memcpy(_check_Calced, k._check_Calced, sizeof(_check_Calced));
memcpy(_hashKey, k._hashKey, sizeof(_hashKey));
}
bool IsKeyEqualTo(const CKey &key)
{
return (_numIterationsLog == key._numIterationsLog
&& memcmp(_salt, key._salt, sizeof(_salt)) == 0
&& _password == key._password);
}
CKey();
};
class CDecoder:
public CAesCbcDecoder,
public CKey
{
Byte _check[kPswCheckSize];
bool _canCheck;
UInt64 Flags;
bool IsThereCheck() const { return ((Flags & NCryptoFlags::kPswCheck) != 0); }
public:
Byte _iv[AES_BLOCK_SIZE];
CDecoder();
STDMETHOD(Init)();
void SetPassword(const Byte *data, size_t size);
HRESULT SetDecoderProps(const Byte *data, unsigned size, bool includeIV, bool isService);
bool CalcKey_and_CheckPassword();
bool UseMAC() const { return (Flags & NCryptoFlags::kUseMAC) != 0; }
UInt32 Hmac_Convert_Crc32(UInt32 crc) const;
void Hmac_Convert_32Bytes(Byte *data) const;
};
}}
#endif
+133
View File
@@ -0,0 +1,133 @@
// Crypto/RarAes.cpp
#include "StdAfx.h"
#include "RarAes.h"
#include "Sha1Cls.h"
namespace NCrypto {
namespace NRar3 {
CDecoder::CDecoder():
CAesCbcDecoder(kAesKeySize),
_thereIsSalt(false),
_needCalc(true)
// _rar350Mode(false)
{
for (unsigned i = 0; i < sizeof(_salt); i++)
_salt[i] = 0;
}
HRESULT CDecoder::SetDecoderProperties2(const Byte *data, UInt32 size)
{
bool prev = _thereIsSalt;
_thereIsSalt = false;
if (size == 0)
{
if (!_needCalc && prev)
_needCalc = true;
return S_OK;
}
if (size < 8)
return E_INVALIDARG;
_thereIsSalt = true;
bool same = false;
if (_thereIsSalt == prev)
{
same = true;
if (_thereIsSalt)
{
for (unsigned i = 0; i < sizeof(_salt); i++)
if (_salt[i] != data[i])
{
same = false;
break;
}
}
}
for (unsigned i = 0; i < sizeof(_salt); i++)
_salt[i] = data[i];
if (!_needCalc && !same)
_needCalc = true;
return S_OK;
}
static const unsigned kPasswordLen_Bytes_MAX = 127 * 2;
void CDecoder::SetPassword(const Byte *data, unsigned size)
{
if (size > kPasswordLen_Bytes_MAX)
size = kPasswordLen_Bytes_MAX;
bool same = false;
if (size == _password.Size())
{
same = true;
for (UInt32 i = 0; i < size; i++)
if (data[i] != _password[i])
{
same = false;
break;
}
}
if (!_needCalc && !same)
_needCalc = true;
_password.CopyFrom(data, (size_t)size);
}
STDMETHODIMP CDecoder::Init()
{
CalcKey();
RINOK(SetKey(_key, kAesKeySize));
RINOK(SetInitVector(_iv, AES_BLOCK_SIZE));
return CAesCbcCoder::Init();
}
void CDecoder::CalcKey()
{
if (!_needCalc)
return;
const unsigned kSaltSize = 8;
Byte buf[kPasswordLen_Bytes_MAX + kSaltSize];
if (_password.Size() != 0)
memcpy(buf, _password, _password.Size());
size_t rawSize = _password.Size();
if (_thereIsSalt)
{
memcpy(buf + rawSize, _salt, kSaltSize);
rawSize += kSaltSize;
}
NSha1::CContext sha;
sha.Init();
Byte digest[NSha1::kDigestSize];
// rar reverts hash for sha.
const UInt32 kNumRounds = ((UInt32)1 << 18);
UInt32 i;
for (i = 0; i < kNumRounds; i++)
{
sha.UpdateRar(buf, rawSize /* , _rar350Mode */);
Byte pswNum[3] = { (Byte)i, (Byte)(i >> 8), (Byte)(i >> 16) };
sha.UpdateRar(pswNum, 3 /* , _rar350Mode */);
if (i % (kNumRounds / 16) == 0)
{
NSha1::CContext shaTemp = sha;
shaTemp.Final(digest);
_iv[i / (kNumRounds / 16)] = (Byte)digest[4 * 4 + 3];
}
}
sha.Final(digest);
for (i = 0; i < 4; i++)
for (unsigned j = 0; j < 4; j++)
_key[i * 4 + j] = (digest[i * 4 + 3 - j]);
_needCalc = false;
}
}}
+52
View File
@@ -0,0 +1,52 @@
// Crypto/RarAes.h
#ifndef __CRYPTO_RAR_AES_H
#define __CRYPTO_RAR_AES_H
#include "../../../C/Aes.h"
#include "../../Common/MyBuffer.h"
#include "../IPassword.h"
#include "MyAes.h"
namespace NCrypto {
namespace NRar3 {
const unsigned kAesKeySize = 16;
class CDecoder:
public CAesCbcDecoder
// public ICompressSetDecoderProperties2,
// public ICryptoSetPassword
{
Byte _salt[8];
bool _thereIsSalt;
bool _needCalc;
// bool _rar350Mode;
CByteBuffer _password;
Byte _key[kAesKeySize];
Byte _iv[AES_BLOCK_SIZE];
void CalcKey();
public:
/*
MY_UNKNOWN_IMP1(
ICryptoSetPassword
// ICompressSetDecoderProperties2
*/
STDMETHOD(Init)();
void SetPassword(const Byte *data, unsigned size);
HRESULT SetDecoderProperties2(const Byte *data, UInt32 size);
CDecoder();
// void SetRar350Mode(bool rar350Mode) { _rar350Mode = rar350Mode; }
};
}}
#endif
+51
View File
@@ -0,0 +1,51 @@
// Crypto/Sha1.h
#ifndef __CRYPTO_SHA1_H
#define __CRYPTO_SHA1_H
#include "../../../C/Sha1.h"
namespace NCrypto {
namespace NSha1 {
const unsigned kNumBlockWords = SHA1_NUM_BLOCK_WORDS;
const unsigned kNumDigestWords = SHA1_NUM_DIGEST_WORDS;
const unsigned kBlockSize = SHA1_BLOCK_SIZE;
const unsigned kDigestSize = SHA1_DIGEST_SIZE;
class CContextBase
{
protected:
CSha1 _s;
public:
void Init() throw() { Sha1_Init(&_s); }
void GetBlockDigest(const UInt32 *blockData, UInt32 *destDigest) throw() { Sha1_GetBlockDigest(&_s, blockData, destDigest); }
};
class CContext: public CContextBase
{
public:
void Update(const Byte *data, size_t size) throw() { Sha1_Update(&_s, data, size); }
void UpdateRar(Byte *data, size_t size /* , bool rar350Mode */) throw() { Sha1_Update_Rar(&_s, data, size /* , rar350Mode ? 1 : 0 */); }
void Final(Byte *digest) throw() { Sha1_Final(&_s, digest); }
};
class CContext32: public CContextBase
{
public:
void Update(const UInt32 *data, size_t size) throw() { Sha1_32_Update(&_s, data, size); }
void Final(UInt32 *digest) throw() { Sha1_32_Final(&_s, digest); }
/* PrepareBlock can be used only when size <= 13. size in Words
_buffer must be empty (_count & 0xF) == 0) */
void PrepareBlock(UInt32 *block, unsigned size) const throw()
{
Sha1_32_PrepareBlock(&_s, block, size);
}
};
}}
#endif
+8
View File
@@ -0,0 +1,8 @@
// StdAfx.h
#ifndef __STDAFX_H
#define __STDAFX_H
#include "../../Common/Common.h"
#endif
+235
View File
@@ -0,0 +1,235 @@
// Crypto/WzAes.cpp
/*
This code implements Brian Gladman's scheme
specified in "A Password Based File Encryption Utility".
Note: you must include MyAes.cpp to project to initialize AES tables
*/
#include "StdAfx.h"
#include "../../../C/CpuArch.h"
#include "../Common/StreamUtils.h"
#include "Pbkdf2HmacSha1.h"
#include "RandGen.h"
#include "WzAes.h"
// define it if you don't want to use speed-optimized version of NSha1::Pbkdf2Hmac
// #define _NO_WZAES_OPTIMIZATIONS
namespace NCrypto {
namespace NWzAes {
const unsigned kAesKeySizeMax = 32;
static const UInt32 kNumKeyGenIterations = 1000;
STDMETHODIMP CBaseCoder::CryptoSetPassword(const Byte *data, UInt32 size)
{
if (size > kPasswordSizeMax)
return E_INVALIDARG;
_key.Password.CopyFrom(data, (size_t)size);
return S_OK;
}
void CBaseCoder::Init2()
{
const unsigned dkSizeMax32 = (2 * kAesKeySizeMax + kPwdVerifSize + 3) / 4;
Byte dk[dkSizeMax32 * 4];
const unsigned keySize = _key.GetKeySize();
const unsigned dkSize = 2 * keySize + kPwdVerifSize;
// for (unsigned ii = 0; ii < 1000; ii++)
{
#ifdef _NO_WZAES_OPTIMIZATIONS
NSha1::Pbkdf2Hmac(
_key.Password, _key.Password.Size(),
_key.Salt, _key.GetSaltSize(),
kNumKeyGenIterations,
dk, dkSize);
#else
UInt32 dk32[dkSizeMax32];
const unsigned dkSize32 = (dkSize + 3) / 4;
UInt32 salt[kSaltSizeMax / 4];
unsigned numSaltWords = _key.GetNumSaltWords();
for (unsigned i = 0; i < numSaltWords; i++)
{
const Byte *src = _key.Salt + i * 4;
salt[i] = GetBe32(src);
}
NSha1::Pbkdf2Hmac32(
_key.Password, _key.Password.Size(),
salt, numSaltWords,
kNumKeyGenIterations,
dk32, dkSize32);
/*
for (unsigned j = 0; j < dkSize; j++)
dk[j] = (Byte)(dk32[j / 4] >> (24 - 8 * (j & 3)));
*/
for (unsigned j = 0; j < dkSize32; j++)
SetBe32(dk + j * 4, dk32[j]);
#endif
}
_hmac.SetKey(dk + keySize, keySize);
memcpy(_key.PwdVerifComputed, dk + 2 * keySize, kPwdVerifSize);
Aes_SetKey_Enc(_aes.aes + _aes.offset + 8, dk, keySize);
AesCtr2_Init(&_aes);
}
STDMETHODIMP CBaseCoder::Init()
{
return S_OK;
}
HRESULT CEncoder::WriteHeader(ISequentialOutStream *outStream)
{
unsigned saltSize = _key.GetSaltSize();
MY_RAND_GEN(_key.Salt, saltSize);
Init2();
RINOK(WriteStream(outStream, _key.Salt, saltSize));
return WriteStream(outStream, _key.PwdVerifComputed, kPwdVerifSize);
}
HRESULT CEncoder::WriteFooter(ISequentialOutStream *outStream)
{
Byte mac[kMacSize];
_hmac.Final(mac, kMacSize);
return WriteStream(outStream, mac, kMacSize);
}
/*
STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *data, UInt32 size)
{
if (size != 1)
return E_INVALIDARG;
_key.Init();
return SetKeyMode(data[0]) ? S_OK : E_INVALIDARG;
}
*/
HRESULT CDecoder::ReadHeader(ISequentialInStream *inStream)
{
unsigned saltSize = _key.GetSaltSize();
unsigned extraSize = saltSize + kPwdVerifSize;
Byte temp[kSaltSizeMax + kPwdVerifSize];
RINOK(ReadStream_FAIL(inStream, temp, extraSize));
unsigned i;
for (i = 0; i < saltSize; i++)
_key.Salt[i] = temp[i];
for (i = 0; i < kPwdVerifSize; i++)
_pwdVerifFromArchive[i] = temp[saltSize + i];
return S_OK;
}
static inline bool CompareArrays(const Byte *p1, const Byte *p2, unsigned size)
{
for (unsigned i = 0; i < size; i++)
if (p1[i] != p2[i])
return false;
return true;
}
bool CDecoder::Init_and_CheckPassword()
{
Init2();
return CompareArrays(_key.PwdVerifComputed, _pwdVerifFromArchive, kPwdVerifSize);
}
HRESULT CDecoder::CheckMac(ISequentialInStream *inStream, bool &isOK)
{
isOK = false;
Byte mac1[kMacSize];
RINOK(ReadStream_FAIL(inStream, mac1, kMacSize));
Byte mac2[kMacSize];
_hmac.Final(mac2, kMacSize);
isOK = CompareArrays(mac1, mac2, kMacSize);
return S_OK;
}
CAesCtr2::CAesCtr2()
{
offset = ((0 - (unsigned)(ptrdiff_t)aes) & 0xF) / sizeof(UInt32);
}
void AesCtr2_Init(CAesCtr2 *p)
{
UInt32 *ctr = p->aes + p->offset + 4;
unsigned i;
for (i = 0; i < 4; i++)
ctr[i] = 0;
p->pos = AES_BLOCK_SIZE;
}
/* (size != 16 * N) is allowed only for last call */
void AesCtr2_Code(CAesCtr2 *p, Byte *data, SizeT size)
{
unsigned pos = p->pos;
UInt32 *buf32 = p->aes + p->offset;
if (size == 0)
return;
if (pos != AES_BLOCK_SIZE)
{
const Byte *buf = (const Byte *)buf32;
do
*data++ ^= buf[pos++];
while (--size != 0 && pos != AES_BLOCK_SIZE);
}
if (size >= 16)
{
SizeT size2 = size >> 4;
g_AesCtr_Code(buf32 + 4, data, size2);
size2 <<= 4;
data += size2;
size -= size2;
pos = AES_BLOCK_SIZE;
}
if (size != 0)
{
unsigned j;
const Byte *buf;
for (j = 0; j < 4; j++)
buf32[j] = 0;
g_AesCtr_Code(buf32 + 4, (Byte *)buf32, 1);
buf = (const Byte *)buf32;
pos = 0;
do
*data++ ^= buf[pos++];
while (--size != 0);
}
p->pos = pos;
}
/* (size != 16 * N) is allowed only for last Filter() call */
STDMETHODIMP_(UInt32) CEncoder::Filter(Byte *data, UInt32 size)
{
AesCtr2_Code(&_aes, data, size);
_hmac.Update(data, size);
return size;
}
STDMETHODIMP_(UInt32) CDecoder::Filter(Byte *data, UInt32 size)
{
_hmac.Update(data, size);
AesCtr2_Code(&_aes, data, size);
return size;
}
}}
+137
View File
@@ -0,0 +1,137 @@
// Crypto/WzAes.h
/*
This code implements Brian Gladman's scheme
specified in "A Password Based File Encryption Utility":
- AES encryption (128,192,256-bit) in Counter (CTR) mode.
- HMAC-SHA1 authentication for encrypted data (10 bytes)
- Keys are derived by PPKDF2(RFC2898)-HMAC-SHA1 from ASCII password and
Salt (saltSize = aesKeySize / 2).
- 2 bytes contain Password Verifier's Code
*/
#ifndef __CRYPTO_WZ_AES_H
#define __CRYPTO_WZ_AES_H
#include "../../../C/Aes.h"
#include "../../Common/MyBuffer.h"
#include "../../Common/MyCom.h"
#include "../ICoder.h"
#include "../IPassword.h"
#include "HmacSha1.h"
namespace NCrypto {
namespace NWzAes {
/* ICompressFilter::Init() does nothing for this filter.
Call to init:
Encoder:
CryptoSetPassword();
WriteHeader();
Decoder:
[CryptoSetPassword();]
ReadHeader();
[CryptoSetPassword();] Init_and_CheckPassword();
[CryptoSetPassword();] Init_and_CheckPassword();
*/
const UInt32 kPasswordSizeMax = 99; // 128;
const unsigned kSaltSizeMax = 16;
const unsigned kPwdVerifSize = 2;
const unsigned kMacSize = 10;
enum EKeySizeMode
{
kKeySizeMode_AES128 = 1,
kKeySizeMode_AES192 = 2,
kKeySizeMode_AES256 = 3
};
struct CKeyInfo
{
EKeySizeMode KeySizeMode;
Byte Salt[kSaltSizeMax];
Byte PwdVerifComputed[kPwdVerifSize];
CByteBuffer Password;
unsigned GetKeySize() const { return (8 * KeySizeMode + 8); }
unsigned GetSaltSize() const { return (4 * KeySizeMode + 4); }
unsigned GetNumSaltWords() const { return (KeySizeMode + 1); }
CKeyInfo(): KeySizeMode(kKeySizeMode_AES256) {}
};
struct CAesCtr2
{
unsigned pos;
unsigned offset;
UInt32 aes[4 + AES_NUM_IVMRK_WORDS + 3];
CAesCtr2();
};
void AesCtr2_Init(CAesCtr2 *p);
void AesCtr2_Code(CAesCtr2 *p, Byte *data, SizeT size);
class CBaseCoder:
public ICompressFilter,
public ICryptoSetPassword,
public CMyUnknownImp
{
protected:
CKeyInfo _key;
NSha1::CHmac _hmac;
CAesCtr2 _aes;
void Init2();
public:
MY_UNKNOWN_IMP1(ICryptoSetPassword)
STDMETHOD(CryptoSetPassword)(const Byte *data, UInt32 size);
STDMETHOD(Init)();
unsigned GetHeaderSize() const { return _key.GetSaltSize() + kPwdVerifSize; }
unsigned GetAddPackSize() const { return GetHeaderSize() + kMacSize; }
bool SetKeyMode(unsigned mode)
{
if (mode < kKeySizeMode_AES128 || mode > kKeySizeMode_AES256)
return false;
_key.KeySizeMode = (EKeySizeMode)mode;
return true;
}
virtual ~CBaseCoder() {}
};
class CEncoder:
public CBaseCoder
{
public:
STDMETHOD_(UInt32, Filter)(Byte *data, UInt32 size);
HRESULT WriteHeader(ISequentialOutStream *outStream);
HRESULT WriteFooter(ISequentialOutStream *outStream);
};
class CDecoder:
public CBaseCoder
// public ICompressSetDecoderProperties2
{
Byte _pwdVerifFromArchive[kPwdVerifSize];
public:
// ICompressSetDecoderProperties2
// STDMETHOD(SetDecoderProperties2)(const Byte *data, UInt32 size);
STDMETHOD_(UInt32, Filter)(Byte *data, UInt32 size);
HRESULT ReadHeader(ISequentialInStream *inStream);
bool Init_and_CheckPassword();
HRESULT CheckMac(ISequentialInStream *inStream, bool &isOK);
};
}}
#endif
@@ -0,0 +1,114 @@
// Crypto/ZipCrypto.cpp
#include "StdAfx.h"
#include "../../../C/7zCrc.h"
#include "../Common/StreamUtils.h"
#include "RandGen.h"
#include "ZipCrypto.h"
namespace NCrypto {
namespace NZip {
#define UPDATE_KEYS(b) { \
key0 = CRC_UPDATE_BYTE(key0, b); \
key1 = (key1 + (key0 & 0xFF)) * 0x8088405 + 1; \
key2 = CRC_UPDATE_BYTE(key2, (Byte)(key1 >> 24)); } \
#define DECRYPT_BYTE_1 UInt32 temp = key2 | 2;
#define DECRYPT_BYTE_2 ((Byte)((temp * (temp ^ 1)) >> 8))
STDMETHODIMP CCipher::CryptoSetPassword(const Byte *data, UInt32 size)
{
UInt32 key0 = 0x12345678;
UInt32 key1 = 0x23456789;
UInt32 key2 = 0x34567890;
for (UInt32 i = 0; i < size; i++)
UPDATE_KEYS(data[i]);
KeyMem0 = key0;
KeyMem1 = key1;
KeyMem2 = key2;
return S_OK;
}
STDMETHODIMP CCipher::Init()
{
return S_OK;
}
HRESULT CEncoder::WriteHeader_Check16(ISequentialOutStream *outStream, UInt16 crc)
{
Byte h[kHeaderSize];
/* PKZIP before 2.0 used 2 byte CRC check.
PKZIP 2.0+ used 1 byte CRC check. It's more secure.
We also use 1 byte CRC. */
MY_RAND_GEN(h, kHeaderSize - 1);
// h[kHeaderSize - 2] = (Byte)(crc);
h[kHeaderSize - 1] = (Byte)(crc >> 8);
RestoreKeys();
Filter(h, kHeaderSize);
return WriteStream(outStream, h, kHeaderSize);
}
STDMETHODIMP_(UInt32) CEncoder::Filter(Byte *data, UInt32 size)
{
UInt32 key0 = this->Key0;
UInt32 key1 = this->Key1;
UInt32 key2 = this->Key2;
for (UInt32 i = 0; i < size; i++)
{
Byte b = data[i];
DECRYPT_BYTE_1
data[i] = (Byte)(b ^ DECRYPT_BYTE_2);
UPDATE_KEYS(b);
}
this->Key0 = key0;
this->Key1 = key1;
this->Key2 = key2;
return size;
}
HRESULT CDecoder::ReadHeader(ISequentialInStream *inStream)
{
return ReadStream_FAIL(inStream, _header, kHeaderSize);
}
void CDecoder::Init_BeforeDecode()
{
RestoreKeys();
Filter(_header, kHeaderSize);
}
STDMETHODIMP_(UInt32) CDecoder::Filter(Byte *data, UInt32 size)
{
UInt32 key0 = this->Key0;
UInt32 key1 = this->Key1;
UInt32 key2 = this->Key2;
for (UInt32 i = 0; i < size; i++)
{
DECRYPT_BYTE_1
Byte b = (Byte)(data[i] ^ DECRYPT_BYTE_2);
UPDATE_KEYS(b);
data[i] = b;
}
this->Key0 = key0;
this->Key1 = key1;
this->Key2 = key2;
return size;
}
}}
@@ -0,0 +1,75 @@
// Crypto/ZipCrypto.h
#ifndef __CRYPTO_ZIP_CRYPTO_H
#define __CRYPTO_ZIP_CRYPTO_H
#include "../../Common/MyCom.h"
#include "../ICoder.h"
#include "../IPassword.h"
namespace NCrypto {
namespace NZip {
const unsigned kHeaderSize = 12;
/* ICompressFilter::Init() does nothing for this filter.
Call to init:
Encoder:
CryptoSetPassword();
WriteHeader();
Decoder:
[CryptoSetPassword();]
ReadHeader();
[CryptoSetPassword();] Init_and_GetCrcByte();
[CryptoSetPassword();] Init_and_GetCrcByte();
*/
class CCipher:
public ICompressFilter,
public ICryptoSetPassword,
public CMyUnknownImp
{
protected:
UInt32 Key0;
UInt32 Key1;
UInt32 Key2;
UInt32 KeyMem0;
UInt32 KeyMem1;
UInt32 KeyMem2;
void RestoreKeys()
{
Key0 = KeyMem0;
Key1 = KeyMem1;
Key2 = KeyMem2;
}
public:
MY_UNKNOWN_IMP1(ICryptoSetPassword)
STDMETHOD(Init)();
STDMETHOD(CryptoSetPassword)(const Byte *data, UInt32 size);
virtual ~CCipher() {}
};
class CEncoder: public CCipher
{
public:
STDMETHOD_(UInt32, Filter)(Byte *data, UInt32 size);
HRESULT WriteHeader_Check16(ISequentialOutStream *outStream, UInt16 crc);
};
class CDecoder: public CCipher
{
public:
Byte _header[kHeaderSize];
STDMETHOD_(UInt32, Filter)(Byte *data, UInt32 size);
HRESULT ReadHeader(ISequentialInStream *inStream);
void Init_BeforeDecode();
};
}}
#endif
@@ -0,0 +1,240 @@
// 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.
New version of MSDN contains the following condition in CryptDeriveKey() description:
"If the hash is not a member of the SHA-2 family and the required key is for either 3DES or AES".
Now we support ZipStrong for AES only. And it uses SHA1.
Our DeriveKey() code is equal to CryptDeriveKey() in Windows for such conditions: (SHA1 + AES).
if (method != AES && method != 3DES), probably we need another code.
*/
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 > _bufAligned.Size())
{
_bufAligned.AllocAtLeast(_remSize);
if (!(Byte *)_bufAligned)
return E_OUTOFMEMORY;
}
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 for rd data
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;
const unsigned kPadSize = kAesPadAllign; // is equal to blockSize of cipher for rd
/*
if (cert)
{
if ((rdSize & 0x7) != 0)
return E_NOTIMPL;
}
else
*/
{
// PKCS7 padding
if (rdSize < kPadSize)
return E_NOTIMPL;
if ((rdSize & (kPadSize - 1)) != 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);
rdSize -= kPadSize;
for (unsigned i = 0; i < kPadSize; i++)
if (p[(size_t)rdSize + i] != kPadSize)
return S_OK; // passwOK = false;
}
Byte fileKey[32];
NSha1::CContext sha;
sha.Init();
sha.Update(_iv, _ivSize);
sha.Update(p, rdSize);
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;
}
}}
@@ -0,0 +1,65 @@
// Crypto/ZipStrong.h
#ifndef __CRYPTO_ZIP_STRONG_H
#define __CRYPTO_ZIP_STRONG_H
#include "../../Common/MyBuffer2.h"
#include "../IPassword.h"
#include "MyAes.h"
namespace NCrypto {
namespace NZipStrong {
/* ICompressFilter::Init() does nothing for this filter.
Call to init:
Decoder:
[CryptoSetPassword();]
ReadHeader();
[CryptoSetPassword();] Init_and_CheckPassword();
[CryptoSetPassword();] Init_and_CheckPassword();
*/
struct CKeyInfo
{
Byte MasterKey[32];
UInt32 KeySize;
void SetPassword(const Byte *data, UInt32 size);
};
class CBaseCoder:
public CAesCbcDecoder,
public ICryptoSetPassword
{
protected:
CKeyInfo _key;
CAlignedBuffer _bufAligned;
public:
STDMETHOD(Init)();
STDMETHOD(CryptoSetPassword)(const Byte *data, UInt32 size);
};
const unsigned kAesPadAllign = AES_BLOCK_SIZE;
class CDecoder: public CBaseCoder
{
UInt32 _ivSize;
Byte _iv[16];
UInt32 _remSize;
public:
MY_UNKNOWN_IMP1(ICryptoSetPassword)
HRESULT ReadHeader(ISequentialInStream *inStream, UInt32 crc, UInt64 unpackSize);
HRESULT Init_and_CheckPassword(bool &passwOK);
UInt32 GetPadSize(UInt32 packSize32) const
{
// Padding is to align to blockSize of cipher.
// Change it, if is not AES
return kAesPadAllign - (packSize32 & (kAesPadAllign - 1));
}
};
}}
#endif