xref: /aosp_15_r20/external/lzma/CPP/7zip/Crypto/ZipStrong.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
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