• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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