1 // Crypto/ZipStrong.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../C/7zCrc.h"
6 #include "../../../C/CpuArch.h"
7
8 #include "../Common/StreamUtils.h"
9
10 #include "Sha1Cls.h"
11 #include "ZipStrong.h"
12
13 namespace NCrypto {
14 namespace NZipStrong {
15
16 static const UInt16 kAES128 = 0x660E;
17
18 /*
19 DeriveKey() function is similar to CryptDeriveKey() from Windows.
20 New version of MSDN contains the following condition in CryptDeriveKey() description:
21 "If the hash is not a member of the SHA-2 family and the required key is for either 3DES or AES".
22 Now we support ZipStrong for AES only. And it uses SHA1.
23 Our DeriveKey() code is equal to CryptDeriveKey() in Windows for such conditions: (SHA1 + AES).
24 if (method != AES && method != 3DES), probably we need another code.
25 */
26
DeriveKey2(const UInt32 * digest32,Byte c,UInt32 * dest32)27 static void DeriveKey2(const UInt32 *digest32, Byte c, UInt32 *dest32)
28 {
29 const unsigned kBufSize = 64;
30 MY_ALIGN (16)
31 UInt32 buf32[kBufSize / 4];
32 memset(buf32, c, kBufSize);
33 for (unsigned i = 0; i < NSha1::kNumDigestWords; i++)
34 buf32[i] ^= digest32[i];
35 MY_ALIGN (16)
36 NSha1::CContext sha;
37 sha.Init();
38 sha.Update((const Byte *)buf32, kBufSize);
39 sha.Final((Byte *)dest32);
40 }
41
DeriveKey(NSha1::CContext & sha,Byte * key)42 static void DeriveKey(NSha1::CContext &sha, Byte *key)
43 {
44 MY_ALIGN (16)
45 UInt32 digest32[NSha1::kNumDigestWords];
46 sha.Final((Byte *)digest32);
47 MY_ALIGN (16)
48 UInt32 temp32[NSha1::kNumDigestWords * 2];
49 DeriveKey2(digest32, 0x36, temp32);
50 DeriveKey2(digest32, 0x5C, temp32 + NSha1::kNumDigestWords);
51 memcpy(key, temp32, 32);
52 }
53
SetPassword(const Byte * data,UInt32 size)54 void CKeyInfo::SetPassword(const Byte *data, UInt32 size)
55 {
56 MY_ALIGN (16)
57 NSha1::CContext sha;
58 sha.Init();
59 sha.Update(data, size);
60 DeriveKey(sha, MasterKey);
61 }
62
63
64
CDecoder()65 CDecoder::CDecoder()
66 {
67 CAesCbcDecoder *d = new CAesCbcDecoder();
68 _cbcDecoder = d;
69 _aesFilter = d;
70 }
71
Z7_COM7F_IMF(CDecoder::CryptoSetPassword (const Byte * data,UInt32 size))72 Z7_COM7F_IMF(CDecoder::CryptoSetPassword(const Byte *data, UInt32 size))
73 {
74 _key.SetPassword(data, size);
75 return S_OK;
76 }
77
Z7_COM7F_IMF(CDecoder::Init ())78 Z7_COM7F_IMF(CDecoder::Init())
79 {
80 return S_OK;
81 }
82
Z7_COM7F_IMF2(UInt32,CDecoder::Filter (Byte * data,UInt32 size))83 Z7_COM7F_IMF2(UInt32, CDecoder::Filter(Byte *data, UInt32 size))
84 {
85 return _aesFilter->Filter(data, size);
86 }
87
88
ReadHeader(ISequentialInStream * inStream,UInt32 crc,UInt64 unpackSize)89 HRESULT CDecoder::ReadHeader(ISequentialInStream *inStream, UInt32 crc, UInt64 unpackSize)
90 {
91 Byte temp[4];
92 RINOK(ReadStream_FALSE(inStream, temp, 2))
93 _ivSize = GetUi16(temp);
94 if (_ivSize == 0)
95 {
96 memset(_iv, 0, 16);
97 SetUi32(_iv + 0, crc)
98 SetUi64(_iv + 4, unpackSize)
99 _ivSize = 12;
100 }
101 else if (_ivSize == 16)
102 {
103 RINOK(ReadStream_FALSE(inStream, _iv, _ivSize))
104 }
105 else
106 return E_NOTIMPL;
107 RINOK(ReadStream_FALSE(inStream, temp, 4))
108 _remSize = GetUi32(temp);
109 // const UInt32 kAlign = 16;
110 if (_remSize < 16 || _remSize > (1 << 18))
111 return E_NOTIMPL;
112 if (_remSize > _bufAligned.Size())
113 {
114 _bufAligned.AllocAtLeast(_remSize);
115 if (!(Byte *)_bufAligned)
116 return E_OUTOFMEMORY;
117 }
118 return ReadStream_FALSE(inStream, _bufAligned, _remSize);
119 }
120
Init_and_CheckPassword(bool & passwOK)121 HRESULT CDecoder::Init_and_CheckPassword(bool &passwOK)
122 {
123 passwOK = false;
124 if (_remSize < 16)
125 return E_NOTIMPL;
126 Byte * const p = _bufAligned;
127 const unsigned format = GetUi16a(p);
128 if (format != 3)
129 return E_NOTIMPL;
130 unsigned algId = GetUi16a(p + 2);
131 if (algId < kAES128)
132 return E_NOTIMPL;
133 algId -= kAES128;
134 if (algId > 2)
135 return E_NOTIMPL;
136 const unsigned bitLen = GetUi16a(p + 4);
137 const unsigned flags = GetUi16a(p + 6);
138 if (algId * 64 + 128 != bitLen)
139 return E_NOTIMPL;
140 _key.KeySize = 16 + algId * 8;
141 const bool cert = ((flags & 2) != 0);
142
143 if (flags & 0x4000)
144 {
145 // Use 3DES for rd data
146 return E_NOTIMPL;
147 }
148
149 if (cert)
150 {
151 return E_NOTIMPL;
152 }
153 else
154 {
155 if ((flags & 1) == 0)
156 return E_NOTIMPL;
157 }
158
159 UInt32 rdSize = GetUi16a(p + 8);
160
161 if (rdSize + 16 > _remSize)
162 return E_NOTIMPL;
163
164 const unsigned kPadSize = kAesPadAllign; // is equal to blockSize of cipher for rd
165
166 /*
167 if (cert)
168 {
169 if ((rdSize & 0x7) != 0)
170 return E_NOTIMPL;
171 }
172 else
173 */
174 {
175 // PKCS7 padding
176 if (rdSize < kPadSize)
177 return E_NOTIMPL;
178 if (rdSize & (kPadSize - 1))
179 return E_NOTIMPL;
180 }
181
182 memmove(p, p + 10, rdSize);
183 const Byte *p2 = p + rdSize + 10;
184 UInt32 reserved = GetUi32(p2);
185 p2 += 4;
186
187 /*
188 if (cert)
189 {
190 UInt32 numRecipients = reserved;
191
192 if (numRecipients == 0)
193 return E_NOTIMPL;
194
195 {
196 UInt32 hashAlg = GetUi16(p2);
197 hashAlg = hashAlg;
198 UInt32 hashSize = GetUi16(p2 + 2);
199 hashSize = hashSize;
200 p2 += 4;
201
202 reserved = reserved;
203 // return E_NOTIMPL;
204
205 for (unsigned r = 0; r < numRecipients; r++)
206 {
207 UInt32 specSize = GetUi16(p2);
208 p2 += 2;
209 p2 += specSize;
210 }
211 }
212 }
213 else
214 */
215 {
216 if (reserved != 0)
217 return E_NOTIMPL;
218 }
219
220 UInt32 validSize = GetUi16(p2);
221 p2 += 2;
222 const size_t validOffset = (size_t)(p2 - p);
223 if ((validSize & 0xF) != 0 || validOffset + validSize != _remSize)
224 return E_NOTIMPL;
225
226 {
227 RINOK(_cbcDecoder->SetKey(_key.MasterKey, _key.KeySize))
228 RINOK(_cbcDecoder->SetInitVector(_iv, 16))
229 // SetInitVector() calls also Init()
230 RINOK(_cbcDecoder->Init()) // it's optional
231 Filter(p, rdSize);
232
233 rdSize -= kPadSize;
234 for (unsigned i = 0; i < kPadSize; i++)
235 if (p[(size_t)rdSize + i] != kPadSize)
236 return S_OK; // passwOK = false;
237 }
238
239 MY_ALIGN (16)
240 Byte fileKey[32];
241 MY_ALIGN (16)
242 NSha1::CContext sha;
243 sha.Init();
244 sha.Update(_iv, _ivSize);
245 sha.Update(p, rdSize);
246 DeriveKey(sha, fileKey);
247
248 RINOK(_cbcDecoder->SetKey(fileKey, _key.KeySize))
249 RINOK(_cbcDecoder->SetInitVector(_iv, 16))
250 // SetInitVector() calls also Init()
251 RINOK(_cbcDecoder->Init()) // it's optional
252
253 memmove(p, p + validOffset, validSize);
254 Filter(p, validSize);
255
256 if (validSize < 4)
257 return E_NOTIMPL;
258 validSize -= 4;
259 if (GetUi32(p + validSize) != CrcCalc(p, validSize))
260 return S_OK;
261 passwOK = true;
262 return S_OK;
263 }
264
265 }}
266