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