1 // 7zAes.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../C/CpuArch.h"
6 #include "../../../C/Sha256.h"
7
8 #include "../../Common/ComTry.h"
9 #include "../../Common/MyBuffer2.h"
10
11 #ifndef _7ZIP_ST
12 #include "../../Windows/Synchronization.h"
13 #endif
14
15 #include "../Common/StreamUtils.h"
16
17 #include "7zAes.h"
18 #include "MyAes.h"
19
20 #ifndef EXTRACT_ONLY
21 #include "RandGen.h"
22 #endif
23
24 namespace NCrypto {
25 namespace N7z {
26
27 static const unsigned k_NumCyclesPower_Supported_MAX = 24;
28
IsEqualTo(const CKeyInfo & a) const29 bool CKeyInfo::IsEqualTo(const CKeyInfo &a) const
30 {
31 if (SaltSize != a.SaltSize || NumCyclesPower != a.NumCyclesPower)
32 return false;
33 for (unsigned i = 0; i < SaltSize; i++)
34 if (Salt[i] != a.Salt[i])
35 return false;
36 return (Password == a.Password);
37 }
38
CalcKey()39 void CKeyInfo::CalcKey()
40 {
41 if (NumCyclesPower == 0x3F)
42 {
43 unsigned pos;
44 for (pos = 0; pos < SaltSize; pos++)
45 Key[pos] = Salt[pos];
46 for (unsigned i = 0; i < Password.Size() && pos < kKeySize; i++)
47 Key[pos++] = Password[i];
48 for (; pos < kKeySize; pos++)
49 Key[pos] = 0;
50 }
51 else
52 {
53 const unsigned kUnrPow = 6;
54 const UInt32 numUnroll = (UInt32)1 << (NumCyclesPower <= kUnrPow ? (unsigned)NumCyclesPower : kUnrPow);
55
56 const size_t bufSize = 8 + SaltSize + Password.Size();
57 const size_t unrollSize = bufSize * numUnroll;
58
59 // MY_ALIGN (16)
60 // CSha256 sha;
61 CAlignedBuffer sha(sizeof(CSha256) + unrollSize + bufSize * 2);
62 Byte *buf = sha + sizeof(CSha256);
63
64 memcpy(buf, Salt, SaltSize);
65 memcpy(buf + SaltSize, Password, Password.Size());
66 memset(buf + bufSize - 8, 0, 8);
67
68 Sha256_Init((CSha256 *)(void *)(Byte *)sha);
69
70 {
71 {
72 Byte *dest = buf;
73 for (UInt32 i = 1; i < numUnroll; i++)
74 {
75 dest += bufSize;
76 memcpy(dest, buf, bufSize);
77 }
78 }
79
80 const UInt32 numRounds = (UInt32)1 << NumCyclesPower;
81 UInt32 r = 0;
82 do
83 {
84 Byte *dest = buf + bufSize - 8;
85 UInt32 i = r;
86 r += numUnroll;
87 do
88 {
89 SetUi32(dest, i); i++; dest += bufSize;
90 // SetUi32(dest, i); i++; dest += bufSize;
91 }
92 while (i < r);
93 Sha256_Update((CSha256 *)(void *)(Byte *)sha, buf, unrollSize);
94 }
95 while (r < numRounds);
96 }
97 /*
98 UInt64 numRounds = (UInt64)1 << NumCyclesPower;
99
100 do
101 {
102 Sha256_Update((CSha256 *)(Byte *)sha, buf, bufSize);
103 for (unsigned i = 0; i < 8; i++)
104 if (++(ctr[i]) != 0)
105 break;
106 }
107 while (--numRounds != 0);
108 */
109
110 Sha256_Final((CSha256 *)(void *)(Byte *)sha, Key);
111 memset(sha, 0, sha.Size());
112 }
113 }
114
GetKey(CKeyInfo & key)115 bool CKeyInfoCache::GetKey(CKeyInfo &key)
116 {
117 FOR_VECTOR (i, Keys)
118 {
119 const CKeyInfo &cached = Keys[i];
120 if (key.IsEqualTo(cached))
121 {
122 for (unsigned j = 0; j < kKeySize; j++)
123 key.Key[j] = cached.Key[j];
124 if (i != 0)
125 Keys.MoveToFront(i);
126 return true;
127 }
128 }
129 return false;
130 }
131
FindAndAdd(const CKeyInfo & key)132 void CKeyInfoCache::FindAndAdd(const CKeyInfo &key)
133 {
134 FOR_VECTOR (i, Keys)
135 {
136 const CKeyInfo &cached = Keys[i];
137 if (key.IsEqualTo(cached))
138 {
139 if (i != 0)
140 Keys.MoveToFront(i);
141 return;
142 }
143 }
144 Add(key);
145 }
146
Add(const CKeyInfo & key)147 void CKeyInfoCache::Add(const CKeyInfo &key)
148 {
149 if (Keys.Size() >= Size)
150 Keys.DeleteBack();
151 Keys.Insert(0, key);
152 }
153
154 static CKeyInfoCache g_GlobalKeyCache(32);
155
156 #ifndef _7ZIP_ST
157 static NWindows::NSynchronization::CCriticalSection g_GlobalKeyCacheCriticalSection;
158 #define MT_LOCK NWindows::NSynchronization::CCriticalSectionLock lock(g_GlobalKeyCacheCriticalSection);
159 #else
160 #define MT_LOCK
161 #endif
162
CBase()163 CBase::CBase():
164 _cachedKeys(16),
165 _ivSize(0)
166 {
167 for (unsigned i = 0; i < sizeof(_iv); i++)
168 _iv[i] = 0;
169 }
170
PrepareKey()171 void CBase::PrepareKey()
172 {
173 // BCJ2 threads use same password. So we use long lock.
174 MT_LOCK
175
176 bool finded = false;
177 if (!_cachedKeys.GetKey(_key))
178 {
179 finded = g_GlobalKeyCache.GetKey(_key);
180 if (!finded)
181 _key.CalcKey();
182 _cachedKeys.Add(_key);
183 }
184 if (!finded)
185 g_GlobalKeyCache.FindAndAdd(_key);
186 }
187
188 #ifndef EXTRACT_ONLY
189
190 /*
191 STDMETHODIMP CEncoder::ResetSalt()
192 {
193 _key.SaltSize = 4;
194 g_RandomGenerator.Generate(_key.Salt, _key.SaltSize);
195 return S_OK;
196 }
197 */
198
ResetInitVector()199 STDMETHODIMP CEncoder::ResetInitVector()
200 {
201 for (unsigned i = 0; i < sizeof(_iv); i++)
202 _iv[i] = 0;
203 _ivSize = 16;
204 MY_RAND_GEN(_iv, _ivSize);
205 return S_OK;
206 }
207
WriteCoderProperties(ISequentialOutStream * outStream)208 STDMETHODIMP CEncoder::WriteCoderProperties(ISequentialOutStream *outStream)
209 {
210 Byte props[2 + sizeof(_key.Salt) + sizeof(_iv)];
211 unsigned propsSize = 1;
212
213 props[0] = (Byte)(_key.NumCyclesPower
214 | (_key.SaltSize == 0 ? 0 : (1 << 7))
215 | (_ivSize == 0 ? 0 : (1 << 6)));
216
217 if (_key.SaltSize != 0 || _ivSize != 0)
218 {
219 props[1] = (Byte)(
220 ((_key.SaltSize == 0 ? 0 : _key.SaltSize - 1) << 4)
221 | (_ivSize == 0 ? 0 : _ivSize - 1));
222 memcpy(props + 2, _key.Salt, _key.SaltSize);
223 propsSize = 2 + _key.SaltSize;
224 memcpy(props + propsSize, _iv, _ivSize);
225 propsSize += _ivSize;
226 }
227
228 return WriteStream(outStream, props, propsSize);
229 }
230
CEncoder()231 CEncoder::CEncoder()
232 {
233 // _key.SaltSize = 4; g_RandomGenerator.Generate(_key.Salt, _key.SaltSize);
234 // _key.NumCyclesPower = 0x3F;
235 _key.NumCyclesPower = 19;
236 _aesFilter = new CAesCbcEncoder(kKeySize);
237 }
238
239 #endif
240
CDecoder()241 CDecoder::CDecoder()
242 {
243 _aesFilter = new CAesCbcDecoder(kKeySize);
244 }
245
SetDecoderProperties2(const Byte * data,UInt32 size)246 STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *data, UInt32 size)
247 {
248 _key.ClearProps();
249
250 _ivSize = 0;
251 unsigned i;
252 for (i = 0; i < sizeof(_iv); i++)
253 _iv[i] = 0;
254
255 if (size == 0)
256 return S_OK;
257
258 Byte b0 = data[0];
259
260 _key.NumCyclesPower = b0 & 0x3F;
261 if ((b0 & 0xC0) == 0)
262 return size == 1 ? S_OK : E_INVALIDARG;
263
264 if (size <= 1)
265 return E_INVALIDARG;
266
267 Byte b1 = data[1];
268
269 unsigned saltSize = ((b0 >> 7) & 1) + (b1 >> 4);
270 unsigned ivSize = ((b0 >> 6) & 1) + (b1 & 0x0F);
271
272 if (size != 2 + saltSize + ivSize)
273 return E_INVALIDARG;
274 _key.SaltSize = saltSize;
275 data += 2;
276 for (i = 0; i < saltSize; i++)
277 _key.Salt[i] = *data++;
278 for (i = 0; i < ivSize; i++)
279 _iv[i] = *data++;
280 return (_key.NumCyclesPower <= k_NumCyclesPower_Supported_MAX
281 || _key.NumCyclesPower == 0x3F) ? S_OK : E_NOTIMPL;
282 }
283
284
CryptoSetPassword(const Byte * data,UInt32 size)285 STDMETHODIMP CBaseCoder::CryptoSetPassword(const Byte *data, UInt32 size)
286 {
287 COM_TRY_BEGIN
288
289 _key.Password.Wipe();
290 _key.Password.CopyFrom(data, (size_t)size);
291 return S_OK;
292
293 COM_TRY_END
294 }
295
Init()296 STDMETHODIMP CBaseCoder::Init()
297 {
298 COM_TRY_BEGIN
299
300 PrepareKey();
301 CMyComPtr<ICryptoProperties> cp;
302 RINOK(_aesFilter.QueryInterface(IID_ICryptoProperties, &cp));
303 if (!cp)
304 return E_FAIL;
305 RINOK(cp->SetKey(_key.Key, kKeySize));
306 RINOK(cp->SetInitVector(_iv, sizeof(_iv)));
307 return _aesFilter->Init();
308
309 COM_TRY_END
310 }
311
STDMETHODIMP_(UInt32)312 STDMETHODIMP_(UInt32) CBaseCoder::Filter(Byte *data, UInt32 size)
313 {
314 return _aesFilter->Filter(data, size);
315 }
316
317 }}
318