• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Crypto/Rar5Aes.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../C/CpuArch.h"
6 
7 #ifndef Z7_ST
8 #include "../../Windows/Synchronization.h"
9 #endif
10 
11 #include "Rar5Aes.h"
12 #include "HmacSha256.h"
13 
14 namespace NCrypto {
15 namespace NRar5 {
16 
17 static const unsigned kNumIterationsLog_Max = 24;
18 
19 static const unsigned kPswCheckCsumSize = 4;
20 static const unsigned kCheckSize = kPswCheckSize + kPswCheckCsumSize;
21 
CKey()22 CKey::CKey():
23     _needCalc(true),
24     _numIterationsLog(0)
25 {
26   for (unsigned i = 0; i < sizeof(_salt); i++)
27     _salt[i] = 0;
28 }
29 
CDecoder()30 CDecoder::CDecoder(): CAesCbcDecoder(kAesKeySize) {}
31 
ReadVarInt(const Byte * p,unsigned maxSize,UInt64 * val)32 static unsigned ReadVarInt(const Byte *p, unsigned maxSize, UInt64 *val)
33 {
34   *val = 0;
35 
36   for (unsigned i = 0; i < maxSize && i < 10;)
37   {
38     Byte b = p[i];
39     *val |= (UInt64)(b & 0x7F) << (7 * i);
40     i++;
41     if ((b & 0x80) == 0)
42       return i;
43   }
44   return 0;
45 }
46 
SetDecoderProps(const Byte * p,unsigned size,bool includeIV,bool isService)47 HRESULT CDecoder::SetDecoderProps(const Byte *p, unsigned size, bool includeIV, bool isService)
48 {
49   UInt64 Version;
50 
51   unsigned num = ReadVarInt(p, size, &Version);
52   if (num == 0)
53     return E_NOTIMPL;
54   p += num;
55   size -= num;
56 
57   if (Version != 0)
58     return E_NOTIMPL;
59 
60   num = ReadVarInt(p, size, &Flags);
61   if (num == 0)
62     return E_NOTIMPL;
63   p += num;
64   size -= num;
65 
66   bool isCheck = IsThereCheck();
67   if (size != 1 + kSaltSize + (includeIV ? AES_BLOCK_SIZE : 0) + (unsigned)(isCheck ? kCheckSize : 0))
68     return E_NOTIMPL;
69 
70   if (_numIterationsLog != p[0])
71   {
72     _numIterationsLog = p[0];
73     _needCalc = true;
74   }
75 
76   p++;
77 
78   if (memcmp(_salt, p, kSaltSize) != 0)
79   {
80     memcpy(_salt, p, kSaltSize);
81     _needCalc = true;
82   }
83 
84   p += kSaltSize;
85 
86   if (includeIV)
87   {
88     memcpy(_iv, p, AES_BLOCK_SIZE);
89     p += AES_BLOCK_SIZE;
90   }
91 
92   _canCheck = true;
93 
94   if (isCheck)
95   {
96     memcpy(_check, p, kPswCheckSize);
97     CSha256 sha;
98     Byte digest[SHA256_DIGEST_SIZE];
99     Sha256_Init(&sha);
100     Sha256_Update(&sha, _check, kPswCheckSize);
101     Sha256_Final(&sha, digest);
102     _canCheck = (memcmp(digest, p + kPswCheckSize, kPswCheckCsumSize) == 0);
103     if (_canCheck && isService)
104     {
105       // There was bug in RAR 5.21- : PswCheck field in service records ("QO") contained zeros.
106       // so we disable password checking for such bad records.
107       _canCheck = false;
108       for (unsigned i = 0; i < kPswCheckSize; i++)
109         if (p[i] != 0)
110         {
111           _canCheck = true;
112           break;
113         }
114     }
115   }
116 
117   return (_numIterationsLog <= kNumIterationsLog_Max ? S_OK : E_NOTIMPL);
118 }
119 
120 
SetPassword(const Byte * data,size_t size)121 void CDecoder::SetPassword(const Byte *data, size_t size)
122 {
123   if (size != _password.Size() || memcmp(data, _password, size) != 0)
124   {
125     _needCalc = true;
126     _password.Wipe();
127     _password.CopyFrom(data, size);
128   }
129 }
130 
131 
Z7_COM7F_IMF(CDecoder::Init ())132 Z7_COM7F_IMF(CDecoder::Init())
133 {
134   CalcKey_and_CheckPassword();
135   RINOK(SetKey(_key, kAesKeySize))
136   RINOK(SetInitVector(_iv, AES_BLOCK_SIZE))
137   return CAesCoder::Init();
138 }
139 
140 
Hmac_Convert_Crc32(UInt32 crc) const141 UInt32 CDecoder::Hmac_Convert_Crc32(UInt32 crc) const
142 {
143   MY_ALIGN (16)
144   NSha256::CHmac ctx;
145   ctx.SetKey(_hashKey, NSha256::kDigestSize);
146   UInt32 v;
147   SetUi32(&v, crc)
148   ctx.Update((const Byte *)&v, 4);
149   MY_ALIGN (16)
150   UInt32 h[SHA256_NUM_DIGEST_WORDS];
151   ctx.Final((Byte *)h);
152   crc = 0;
153   for (unsigned i = 0; i < SHA256_NUM_DIGEST_WORDS; i++)
154     crc ^= (UInt32)GetUi32(h + i);
155   return crc;
156 }
157 
158 
Hmac_Convert_32Bytes(Byte * data) const159 void CDecoder::Hmac_Convert_32Bytes(Byte *data) const
160 {
161   MY_ALIGN (16)
162   NSha256::CHmac ctx;
163   ctx.SetKey(_hashKey, NSha256::kDigestSize);
164   ctx.Update(data, NSha256::kDigestSize);
165   ctx.Final(data);
166 }
167 
168 
169 static CKey g_Key;
170 
171 #ifndef Z7_ST
172   static NWindows::NSynchronization::CCriticalSection g_GlobalKeyCacheCriticalSection;
173   #define MT_LOCK NWindows::NSynchronization::CCriticalSectionLock lock(g_GlobalKeyCacheCriticalSection);
174 #else
175   #define MT_LOCK
176 #endif
177 
CalcKey_and_CheckPassword()178 bool CDecoder::CalcKey_and_CheckPassword()
179 {
180   if (_needCalc)
181   {
182     {
183       MT_LOCK
184       if (!g_Key._needCalc && IsKeyEqualTo(g_Key))
185       {
186         CopyCalcedKeysFrom(g_Key);
187         _needCalc = false;
188       }
189     }
190 
191     if (_needCalc)
192     {
193       Byte pswCheck[SHA256_DIGEST_SIZE];
194 
195       {
196         // Pbkdf HMAC-SHA-256
197 
198         MY_ALIGN (16)
199         NSha256::CHmac baseCtx;
200         baseCtx.SetKey(_password, _password.Size());
201 
202         NSha256::CHmac ctx = baseCtx;
203         ctx.Update(_salt, sizeof(_salt));
204 
205         MY_ALIGN (16)
206         Byte u[NSha256::kDigestSize];
207         MY_ALIGN (16)
208         Byte key[NSha256::kDigestSize];
209 
210         u[0] = 0;
211         u[1] = 0;
212         u[2] = 0;
213         u[3] = 1;
214 
215         ctx.Update(u, 4);
216         ctx.Final(u);
217 
218         memcpy(key, u, NSha256::kDigestSize);
219 
220         UInt32 numIterations = ((UInt32)1 << _numIterationsLog) - 1;
221 
222         for (unsigned i = 0; i < 3; i++)
223         {
224           UInt32 j = numIterations;
225 
226           for (; j != 0; j--)
227           {
228             ctx = baseCtx;
229             ctx.Update(u, NSha256::kDigestSize);
230             ctx.Final(u);
231             for (unsigned s = 0; s < NSha256::kDigestSize; s++)
232               key[s] ^= u[s];
233           }
234 
235           // RAR uses additional iterations for additional keys
236           memcpy((i == 0 ? _key : (i == 1 ? _hashKey : pswCheck)), key, NSha256::kDigestSize);
237           numIterations = 16;
238         }
239       }
240 
241       {
242         unsigned i;
243 
244         for (i = 0; i < kPswCheckSize; i++)
245           _check_Calced[i] = pswCheck[i];
246 
247         for (i = kPswCheckSize; i < SHA256_DIGEST_SIZE; i++)
248           _check_Calced[i & (kPswCheckSize - 1)] ^= pswCheck[i];
249       }
250 
251       _needCalc = false;
252 
253       {
254         MT_LOCK
255         g_Key = *this;
256       }
257     }
258   }
259 
260   if (IsThereCheck() && _canCheck)
261     return (memcmp(_check_Calced, _check, kPswCheckSize) == 0);
262   return true;
263 }
264 
265 }}
266