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