// 7zAes.cpp #include "StdAfx.h" #include "../../../C/CpuArch.h" #include "../../../C/Sha256.h" #include "../../Common/ComTry.h" #include "../../Common/MyBuffer2.h" #ifndef Z7_ST #include "../../Windows/Synchronization.h" #endif #include "../Common/StreamUtils.h" #include "7zAes.h" #include "MyAes.h" #ifndef Z7_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 { const unsigned kUnrPow = 6; const UInt32 numUnroll = (UInt32)1 << (NumCyclesPower <= kUnrPow ? (unsigned)NumCyclesPower : kUnrPow); const size_t bufSize = 8 + SaltSize + Password.Size(); const size_t unrollSize = bufSize * numUnroll; // MY_ALIGN (16) // CSha256 sha; CAlignedBuffer sha(sizeof(CSha256) + unrollSize + bufSize * 2); Byte *buf = sha + sizeof(CSha256); memcpy(buf, Salt, SaltSize); memcpy(buf + SaltSize, Password, Password.Size()); memset(buf + bufSize - 8, 0, 8); Sha256_Init((CSha256 *)(void *)(Byte *)sha); { { Byte *dest = buf; for (UInt32 i = 1; i < numUnroll; i++) { dest += bufSize; memcpy(dest, buf, bufSize); } } const UInt32 numRounds = (UInt32)1 << NumCyclesPower; UInt32 r = 0; do { Byte *dest = buf + bufSize - 8; UInt32 i = r; r += numUnroll; do { SetUi32(dest, i) i++; dest += bufSize; // SetUi32(dest, i) i++; dest += bufSize; } while (i < r); Sha256_Update((CSha256 *)(void *)(Byte *)sha, buf, unrollSize); } while (r < numRounds); } /* UInt64 numRounds = (UInt64)1 << NumCyclesPower; do { Sha256_Update((CSha256 *)(Byte *)sha, buf, bufSize); for (unsigned i = 0; i < 8; i++) if (++(ctr[i]) != 0) break; } while (--numRounds != 0); */ Sha256_Final((CSha256 *)(void *)(Byte *)sha, Key); memset(sha, 0, sha.Size()); } } 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 Z7_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 Z7_EXTRACT_ONLY /* Z7_COM7F_IMF(CEncoder::ResetSalt()) { _key.SaltSize = 4; g_RandomGenerator.Generate(_key.Salt, _key.SaltSize); return S_OK; } */ Z7_COM7F_IMF(CEncoder::ResetInitVector()) { for (unsigned i = 0; i < sizeof(_iv); i++) _iv[i] = 0; _ivSize = 16; MY_RAND_GEN(_iv, _ivSize); return S_OK; } Z7_COM7F_IMF(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); } Z7_COM7F_IMF(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; } Z7_COM7F_IMF(CBaseCoder::CryptoSetPassword(const Byte *data, UInt32 size)) { COM_TRY_BEGIN _key.Password.Wipe(); _key.Password.CopyFrom(data, (size_t)size); return S_OK; COM_TRY_END } Z7_COM7F_IMF(CBaseCoder::Init()) { COM_TRY_BEGIN PrepareKey(); CMyComPtr 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 } Z7_COM7F_IMF2(UInt32, CBaseCoder::Filter(Byte *data, UInt32 size)) { return _aesFilter->Filter(data, size); } }}