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