Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/kornelski/7z.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'CPP/7zip/Crypto/Rar5Aes.cpp')
-rw-r--r--CPP/7zip/Crypto/Rar5Aes.cpp257
1 files changed, 257 insertions, 0 deletions
diff --git a/CPP/7zip/Crypto/Rar5Aes.cpp b/CPP/7zip/Crypto/Rar5Aes.cpp
new file mode 100644
index 00000000..648a3d4d
--- /dev/null
+++ b/CPP/7zip/Crypto/Rar5Aes.cpp
@@ -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)
+{
+ unsigned i;
+ *val = 0;
+
+ for (i = 0; i < maxSize;)
+ {
+ Byte b = p[i];
+ if (i < 10)
+ *val |= (UInt64)(b & 0x7F) << (7 * 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);
+};
+
+
+#ifndef _7ZIP_ST
+ static CKey g_Key;
+ 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;
+}
+
+}}