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