// Crypto/RarAes.cpp #include "StdAfx.h" #include "../../../C/CpuArch.h" #include "../../../C/RotateDefs.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.Wipe(); _password.CopyFrom(data, (size_t)size); } Z7_COM7F_IMF(CDecoder::Init()) { CalcKey(); RINOK(SetKey(_key, kAesKeySize)) RINOK(SetInitVector(_iv, AES_BLOCK_SIZE)) return CAesCoder::Init(); } // if (password_size_in_bytes + SaltSize > 64), // the original rar code updates password_with_salt buffer // with some generated data from SHA1 code. // #define RAR_SHA1_REDUCE #ifdef RAR_SHA1_REDUCE #define kNumW 16 #define WW(i) W[(i)&15] #else #define kNumW 80 #define WW(i) W[i] #endif static void UpdatePswDataSha1(Byte *data) { UInt32 W[kNumW]; size_t i; for (i = 0; i < SHA1_NUM_BLOCK_WORDS; i++) W[i] = GetBe32(data + i * 4); for (i = 16; i < 80; i++) { const UInt32 t = WW((i)-3) ^ WW((i)-8) ^ WW((i)-14) ^ WW((i)-16); WW(i) = rotlFixed(t, 1); } for (i = 0; i < SHA1_NUM_BLOCK_WORDS; i++) { SetUi32(data + i * 4, W[kNumW - SHA1_NUM_BLOCK_WORDS + i]) } } void CDecoder::CalcKey() { if (!_needCalc) return; const unsigned kSaltSize = 8; MY_ALIGN (16) 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; } MY_ALIGN (16) NSha1::CContext sha; sha.Init(); MY_ALIGN (16) Byte digest[NSha1::kDigestSize]; // rar reverts hash for sha. const UInt32 kNumRounds = (UInt32)1 << 18; UInt32 pos = 0; UInt32 i; for (i = 0; i < kNumRounds; i++) { sha.Update(buf, rawSize); // if (_rar350Mode) { const UInt32 kBlockSize = 64; const UInt32 endPos = (pos + (UInt32)rawSize) & ~(kBlockSize - 1); if (endPos > pos + kBlockSize) { UInt32 curPos = pos & ~(kBlockSize - 1); curPos += kBlockSize; do { UpdatePswDataSha1(buf + (curPos - pos)); curPos += kBlockSize; } while (curPos != endPos); } } pos += (UInt32)rawSize; #if 1 UInt32 pswNum; SetUi32a(&pswNum, i) sha.Update((const Byte *)&pswNum, 3); #else Byte pswNum[3] = { (Byte)i, (Byte)(i >> 8), (Byte)(i >> 16) }; sha.Update(pswNum, 3); #endif pos += 3; if (i % (kNumRounds / 16) == 0) { MY_ALIGN (16) 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; } }}