Imported Upstream version 9.20
[platform/upstream/7zip.git] / CPP / 7zip / Crypto / 7zAes.cpp
1 // 7zAes.cpp\r
2 \r
3 #include "StdAfx.h"\r
4 \r
5 #include "../../../C/Sha256.h"\r
6 \r
7 #include "Windows/Synchronization.h"\r
8 \r
9 #include "../Common/StreamObjects.h"\r
10 #include "../Common/StreamUtils.h"\r
11 \r
12 #include "7zAes.h"\r
13 #include "MyAes.h"\r
14 \r
15 #ifndef EXTRACT_ONLY\r
16 #include "RandGen.h"\r
17 #endif\r
18 \r
19 using namespace NWindows;\r
20 \r
21 namespace NCrypto {\r
22 namespace NSevenZ {\r
23 \r
24 bool CKeyInfo::IsEqualTo(const CKeyInfo &a) const\r
25 {\r
26   if (SaltSize != a.SaltSize || NumCyclesPower != a.NumCyclesPower)\r
27     return false;\r
28   for (UInt32 i = 0; i < SaltSize; i++)\r
29     if (Salt[i] != a.Salt[i])\r
30       return false;\r
31   return (Password == a.Password);\r
32 }\r
33 \r
34 void CKeyInfo::CalculateDigest()\r
35 {\r
36   if (NumCyclesPower == 0x3F)\r
37   {\r
38     UInt32 pos;\r
39     for (pos = 0; pos < SaltSize; pos++)\r
40       Key[pos] = Salt[pos];\r
41     for (UInt32 i = 0; i < Password.GetCapacity() && pos < kKeySize; i++)\r
42       Key[pos++] = Password[i];\r
43     for (; pos < kKeySize; pos++)\r
44       Key[pos] = 0;\r
45   }\r
46   else\r
47   {\r
48     CSha256 sha;\r
49     Sha256_Init(&sha);\r
50     const UInt64 numRounds = (UInt64)1 << NumCyclesPower;\r
51     Byte temp[8] = { 0,0,0,0,0,0,0,0 };\r
52     for (UInt64 round = 0; round < numRounds; round++)\r
53     {\r
54       Sha256_Update(&sha, Salt, (size_t)SaltSize);\r
55       Sha256_Update(&sha, Password, Password.GetCapacity());\r
56       Sha256_Update(&sha, temp, 8);\r
57       for (int i = 0; i < 8; i++)\r
58         if (++(temp[i]) != 0)\r
59           break;\r
60     }\r
61     Sha256_Final(&sha, Key);\r
62   }\r
63 }\r
64 \r
65 bool CKeyInfoCache::Find(CKeyInfo &key)\r
66 {\r
67   for (int i = 0; i < Keys.Size(); i++)\r
68   {\r
69     const CKeyInfo &cached = Keys[i];\r
70     if (key.IsEqualTo(cached))\r
71     {\r
72       for (int j = 0; j < kKeySize; j++)\r
73         key.Key[j] = cached.Key[j];\r
74       if (i != 0)\r
75       {\r
76         Keys.Insert(0, cached);\r
77         Keys.Delete(i+1);\r
78       }\r
79       return true;\r
80     }\r
81   }\r
82   return false;\r
83 }\r
84 \r
85 void CKeyInfoCache::Add(CKeyInfo &key)\r
86 {\r
87   if (Find(key))\r
88     return;\r
89   if (Keys.Size() >= Size)\r
90     Keys.DeleteBack();\r
91   Keys.Insert(0, key);\r
92 }\r
93 \r
94 static CKeyInfoCache g_GlobalKeyCache(32);\r
95 static NSynchronization::CCriticalSection g_GlobalKeyCacheCriticalSection;\r
96 \r
97 CBase::CBase():\r
98   _cachedKeys(16),\r
99   _ivSize(0)\r
100 {\r
101   for (int i = 0; i < sizeof(_iv); i++)\r
102     _iv[i] = 0;\r
103 }\r
104 \r
105 void CBase::CalculateDigest()\r
106 {\r
107   NSynchronization::CCriticalSectionLock lock(g_GlobalKeyCacheCriticalSection);\r
108   if (_cachedKeys.Find(_key))\r
109     g_GlobalKeyCache.Add(_key);\r
110   else\r
111   {\r
112     if (!g_GlobalKeyCache.Find(_key))\r
113     {\r
114       _key.CalculateDigest();\r
115       g_GlobalKeyCache.Add(_key);\r
116     }\r
117     _cachedKeys.Add(_key);\r
118   }\r
119 }\r
120 \r
121 #ifndef EXTRACT_ONLY\r
122 \r
123 /*\r
124 STDMETHODIMP CEncoder::ResetSalt()\r
125 {\r
126   _key.SaltSize = 4;\r
127   g_RandomGenerator.Generate(_key.Salt, _key.SaltSize);\r
128   return S_OK;\r
129 }\r
130 */\r
131 \r
132 STDMETHODIMP CEncoder::ResetInitVector()\r
133 {\r
134   _ivSize = 8;\r
135   g_RandomGenerator.Generate(_iv, (unsigned)_ivSize);\r
136   return S_OK;\r
137 }\r
138 \r
139 STDMETHODIMP CEncoder::WriteCoderProperties(ISequentialOutStream *outStream)\r
140 {\r
141    // _key.Init();\r
142    for (UInt32 i = _ivSize; i < sizeof(_iv); i++)\r
143     _iv[i] = 0;\r
144 \r
145   UInt32 ivSize = _ivSize;\r
146   \r
147   // _key.NumCyclesPower = 0x3F;\r
148   _key.NumCyclesPower = 19;\r
149 \r
150   Byte firstByte = (Byte)(_key.NumCyclesPower |\r
151     (((_key.SaltSize == 0) ? 0 : 1) << 7) |\r
152     (((ivSize == 0) ? 0 : 1) << 6));\r
153   RINOK(outStream->Write(&firstByte, 1, NULL));\r
154   if (_key.SaltSize == 0 && ivSize == 0)\r
155     return S_OK;\r
156   Byte saltSizeSpec = (Byte)((_key.SaltSize == 0) ? 0 : (_key.SaltSize - 1));\r
157   Byte ivSizeSpec = (Byte)((ivSize == 0) ? 0 : (ivSize - 1));\r
158   Byte secondByte = (Byte)(((saltSizeSpec) << 4) | ivSizeSpec);\r
159   RINOK(outStream->Write(&secondByte, 1, NULL));\r
160   if (_key.SaltSize > 0)\r
161   {\r
162     RINOK(WriteStream(outStream, _key.Salt, _key.SaltSize));\r
163   }\r
164   if (ivSize > 0)\r
165   {\r
166     RINOK(WriteStream(outStream, _iv, ivSize));\r
167   }\r
168   return S_OK;\r
169 }\r
170 \r
171 HRESULT CEncoder::CreateFilter()\r
172 {\r
173   _aesFilter = new CAesCbcEncoder;\r
174   return S_OK;\r
175 }\r
176 \r
177 #endif\r
178 \r
179 STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *data, UInt32 size)\r
180 {\r
181   _key.Init();\r
182   UInt32 i;\r
183   for (i = 0; i < sizeof(_iv); i++)\r
184     _iv[i] = 0;\r
185   if (size == 0)\r
186     return S_OK;\r
187   UInt32 pos = 0;\r
188   Byte firstByte = data[pos++];\r
189 \r
190   _key.NumCyclesPower = firstByte & 0x3F;\r
191   if ((firstByte & 0xC0) == 0)\r
192     return S_OK;\r
193   _key.SaltSize = (firstByte >> 7) & 1;\r
194   UInt32 ivSize = (firstByte >> 6) & 1;\r
195 \r
196   if (pos >= size)\r
197     return E_INVALIDARG;\r
198   Byte secondByte = data[pos++];\r
199   \r
200   _key.SaltSize += (secondByte >> 4);\r
201   ivSize += (secondByte & 0x0F);\r
202   \r
203   if (pos + _key.SaltSize + ivSize > size)\r
204     return E_INVALIDARG;\r
205   for (i = 0; i < _key.SaltSize; i++)\r
206     _key.Salt[i] = data[pos++];\r
207   for (i = 0; i < ivSize; i++)\r
208     _iv[i] = data[pos++];\r
209   return (_key.NumCyclesPower <= 24) ? S_OK :  E_NOTIMPL;\r
210 }\r
211 \r
212 STDMETHODIMP CBaseCoder::CryptoSetPassword(const Byte *data, UInt32 size)\r
213 {\r
214   _key.Password.SetCapacity((size_t)size);\r
215   memcpy(_key.Password, data, (size_t)size);\r
216   return S_OK;\r
217 }\r
218 \r
219 STDMETHODIMP CBaseCoder::Init()\r
220 {\r
221   CalculateDigest();\r
222   if (_aesFilter == 0)\r
223   {\r
224     RINOK(CreateFilter());\r
225   }\r
226   CMyComPtr<ICryptoProperties> cp;\r
227   RINOK(_aesFilter.QueryInterface(IID_ICryptoProperties, &cp));\r
228   RINOK(cp->SetKey(_key.Key, sizeof(_key.Key)));\r
229   RINOK(cp->SetInitVector(_iv, sizeof(_iv)));\r
230   return S_OK;\r
231 }\r
232 \r
233 STDMETHODIMP_(UInt32) CBaseCoder::Filter(Byte *data, UInt32 size)\r
234 {\r
235   return _aesFilter->Filter(data, size);\r
236 }\r
237 \r
238 HRESULT CDecoder::CreateFilter()\r
239 {\r
240   _aesFilter = new CAesCbcDecoder;\r
241   return S_OK;\r
242 }\r
243 \r
244 }}\r