Imported Upstream version 9.20
[platform/upstream/7zip.git] / CPP / 7zip / Crypto / WzAes.cpp
1 // Crypto/WzAes.cpp\r
2 /*\r
3 This code implements Brian Gladman's scheme\r
4 specified in password Based File Encryption Utility.\r
5 \r
6 Note: you must include MyAes.cpp to project to initialize AES tables\r
7 */\r
8 \r
9 #include "StdAfx.h"\r
10 \r
11 #include "../Common/StreamObjects.h"\r
12 #include "../Common/StreamUtils.h"\r
13 \r
14 #include "Pbkdf2HmacSha1.h"\r
15 #include "RandGen.h"\r
16 #include "WzAes.h"\r
17 \r
18 // define it if you don't want to use speed-optimized version of Pbkdf2HmacSha1\r
19 // #define _NO_WZAES_OPTIMIZATIONS\r
20 \r
21 namespace NCrypto {\r
22 namespace NWzAes {\r
23 \r
24 const unsigned kAesKeySizeMax = 32;\r
25 \r
26 static const UInt32 kNumKeyGenIterations = 1000;\r
27 \r
28 STDMETHODIMP CBaseCoder::CryptoSetPassword(const Byte *data, UInt32 size)\r
29 {\r
30   if(size > kPasswordSizeMax)\r
31     return E_INVALIDARG;\r
32   _key.Password.SetCapacity(size);\r
33   memcpy(_key.Password, data, size);\r
34   return S_OK;\r
35 }\r
36 \r
37 #ifndef _NO_WZAES_OPTIMIZATIONS\r
38 \r
39 static void BytesToBeUInt32s(const Byte *src, UInt32 *dest, unsigned destSize)\r
40 {\r
41   for (unsigned i = 0; i < destSize; i++)\r
42       dest[i] =\r
43           ((UInt32)(src[i * 4 + 0]) << 24) |\r
44           ((UInt32)(src[i * 4 + 1]) << 16) |\r
45           ((UInt32)(src[i * 4 + 2]) <<  8) |\r
46           ((UInt32)(src[i * 4 + 3]));\r
47 }\r
48 \r
49 #endif\r
50 \r
51 STDMETHODIMP CBaseCoder::Init()\r
52 {\r
53   UInt32 keySize = _key.GetKeySize();\r
54   UInt32 keysTotalSize = 2 * keySize + kPwdVerifCodeSize;\r
55   Byte buf[2 * kAesKeySizeMax + kPwdVerifCodeSize];\r
56   \r
57   // for (unsigned ii = 0; ii < 1000; ii++)\r
58   {\r
59     #ifdef _NO_WZAES_OPTIMIZATIONS\r
60 \r
61     NSha1::Pbkdf2Hmac(\r
62       _key.Password, _key.Password.GetCapacity(),\r
63       _key.Salt, _key.GetSaltSize(),\r
64       kNumKeyGenIterations,\r
65       buf, keysTotalSize);\r
66 \r
67     #else\r
68 \r
69     UInt32 buf32[(2 * kAesKeySizeMax + kPwdVerifCodeSize + 3) / 4];\r
70     UInt32 key32SizeTotal = (keysTotalSize + 3) / 4;\r
71     UInt32 salt[kSaltSizeMax * 4];\r
72     UInt32 saltSizeInWords = _key.GetSaltSize() / 4;\r
73     BytesToBeUInt32s(_key.Salt, salt, saltSizeInWords);\r
74     NSha1::Pbkdf2Hmac32(\r
75       _key.Password, _key.Password.GetCapacity(),\r
76       salt, saltSizeInWords,\r
77       kNumKeyGenIterations,\r
78       buf32, key32SizeTotal);\r
79     for (UInt32 j = 0; j < keysTotalSize; j++)\r
80       buf[j] = (Byte)(buf32[j / 4] >> (24 - 8 * (j & 3)));\r
81     \r
82     #endif\r
83   }\r
84 \r
85   _hmac.SetKey(buf + keySize, keySize);\r
86   memcpy(_key.PwdVerifComputed, buf + 2 * keySize, kPwdVerifCodeSize);\r
87 \r
88   AesCtr2_Init(&_aes);\r
89   Aes_SetKey_Enc(_aes.aes + _aes.offset + 8, buf, keySize);\r
90   return S_OK;\r
91 }\r
92 \r
93 HRESULT CEncoder::WriteHeader(ISequentialOutStream *outStream)\r
94 {\r
95   UInt32 saltSize = _key.GetSaltSize();\r
96   g_RandomGenerator.Generate(_key.Salt, saltSize);\r
97   Init();\r
98   RINOK(WriteStream(outStream, _key.Salt, saltSize));\r
99   return WriteStream(outStream, _key.PwdVerifComputed, kPwdVerifCodeSize);\r
100 }\r
101 \r
102 HRESULT CEncoder::WriteFooter(ISequentialOutStream *outStream)\r
103 {\r
104   Byte mac[kMacSize];\r
105   _hmac.Final(mac, kMacSize);\r
106   return WriteStream(outStream, mac, kMacSize);\r
107 }\r
108 \r
109 STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *data, UInt32 size)\r
110 {\r
111   if (size != 1)\r
112     return E_INVALIDARG;\r
113   _key.Init();\r
114   return SetKeyMode(data[0]) ? S_OK : E_INVALIDARG;\r
115 }\r
116 \r
117 HRESULT CDecoder::ReadHeader(ISequentialInStream *inStream)\r
118 {\r
119   UInt32 saltSize = _key.GetSaltSize();\r
120   UInt32 extraSize = saltSize + kPwdVerifCodeSize;\r
121   Byte temp[kSaltSizeMax + kPwdVerifCodeSize];\r
122   RINOK(ReadStream_FAIL(inStream, temp, extraSize));\r
123   UInt32 i;\r
124   for (i = 0; i < saltSize; i++)\r
125     _key.Salt[i] = temp[i];\r
126   for (i = 0; i < kPwdVerifCodeSize; i++)\r
127     _pwdVerifFromArchive[i] = temp[saltSize + i];\r
128   return S_OK;\r
129 }\r
130 \r
131 static bool CompareArrays(const Byte *p1, const Byte *p2, UInt32 size)\r
132 {\r
133   for (UInt32 i = 0; i < size; i++)\r
134     if (p1[i] != p2[i])\r
135       return false;\r
136   return true;\r
137 }\r
138 \r
139 bool CDecoder::CheckPasswordVerifyCode()\r
140 {\r
141   return CompareArrays(_key.PwdVerifComputed, _pwdVerifFromArchive, kPwdVerifCodeSize);\r
142 }\r
143 \r
144 HRESULT CDecoder::CheckMac(ISequentialInStream *inStream, bool &isOK)\r
145 {\r
146   isOK = false;\r
147   Byte mac1[kMacSize];\r
148   RINOK(ReadStream_FAIL(inStream, mac1, kMacSize));\r
149   Byte mac2[kMacSize];\r
150   _hmac.Final(mac2, kMacSize);\r
151   isOK = CompareArrays(mac1, mac2, kMacSize);\r
152   return S_OK;\r
153 }\r
154 \r
155 CAesCtr2::CAesCtr2()\r
156 {\r
157   offset = ((0 - (unsigned)(ptrdiff_t)aes) & 0xF) / sizeof(UInt32);\r
158 }\r
159 \r
160 void AesCtr2_Init(CAesCtr2 *p)\r
161 {\r
162   UInt32 *ctr = p->aes + p->offset + 4;\r
163   unsigned i;\r
164   for (i = 0; i < 4; i++)\r
165     ctr[i] = 0;\r
166   p->pos = AES_BLOCK_SIZE;\r
167 }\r
168 \r
169 void AesCtr2_Code(CAesCtr2 *p, Byte *data, SizeT size)\r
170 {\r
171   unsigned pos = p->pos;\r
172   UInt32 *buf32 = p->aes + p->offset;\r
173   if (size == 0)\r
174     return;\r
175   if (pos != AES_BLOCK_SIZE)\r
176   {\r
177     const Byte *buf = (const Byte *)buf32;\r
178     do\r
179       *data++ ^= buf[pos++];\r
180     while (--size != 0 && pos != AES_BLOCK_SIZE);\r
181   }\r
182   if (size >= 16)\r
183   {\r
184     SizeT size2 = size >> 4;\r
185     g_AesCtr_Code(buf32 + 4, data, size2);\r
186     size2 <<= 4;\r
187     data += size2;\r
188     size -= size2;\r
189     pos = AES_BLOCK_SIZE;\r
190   }\r
191   if (size != 0)\r
192   {\r
193     unsigned j;\r
194     const Byte *buf;\r
195     for (j = 0; j < 4; j++)\r
196       buf32[j] = 0;\r
197     g_AesCtr_Code(buf32 + 4, (Byte *)buf32, 1);\r
198     buf = (const Byte *)buf32;\r
199     pos = 0;\r
200     do\r
201       *data++ ^= buf[pos++];\r
202     while (--size != 0 && pos != AES_BLOCK_SIZE);\r
203   }\r
204   p->pos = pos;\r
205 }\r
206 \r
207 STDMETHODIMP_(UInt32) CEncoder::Filter(Byte *data, UInt32 size)\r
208 {\r
209   AesCtr2_Code(&_aes, data, size);\r
210   _hmac.Update(data, size);\r
211   return size;\r
212 }\r
213 \r
214 STDMETHODIMP_(UInt32) CDecoder::Filter(Byte *data, UInt32 size)\r
215 {\r
216   _hmac.Update(data, size);\r
217   AesCtr2_Code(&_aes, data, size);\r
218   return size;\r
219 }\r
220 \r
221 }}\r