Imported Upstream version 9.20
[platform/upstream/7zip.git] / CPP / 7zip / Crypto / ZipStrong.cpp
1 // Crypto/ZipStrong.cpp\r
2 \r
3 #include "StdAfx.h"\r
4 \r
5 #include "../../../C/7zCrc.h"\r
6 #include "../../../C/CpuArch.h"\r
7 \r
8 #include "../Common/StreamUtils.h"\r
9 \r
10 #include "MyAes.h"\r
11 #include "Sha1.h"\r
12 #include "ZipStrong.h"\r
13 \r
14 namespace NCrypto {\r
15 namespace NZipStrong {\r
16 \r
17 static const UInt16 kAES128 = 0x660E;\r
18 \r
19 // DeriveKey* function is similar to CryptDeriveKey() from Windows.\r
20 // But MSDN tells that we need such scheme only if\r
21 // "the required key length is longer than the hash value"\r
22 // but ZipStrong uses it always.\r
23 \r
24 static void DeriveKey2(const Byte *digest, Byte c, Byte *dest)\r
25 {\r
26   Byte buf[64];\r
27   memset(buf, c, 64);\r
28   for (unsigned i = 0; i < NSha1::kDigestSize; i++)\r
29     buf[i] ^= digest[i];\r
30   NSha1::CContext sha;\r
31   sha.Init();\r
32   sha.Update(buf, 64);\r
33   sha.Final(dest);\r
34 }\r
35  \r
36 static void DeriveKey(NSha1::CContext &sha, Byte *key)\r
37 {\r
38   Byte digest[NSha1::kDigestSize];\r
39   sha.Final(digest);\r
40   Byte temp[NSha1::kDigestSize * 2];\r
41   DeriveKey2(digest, 0x36, temp);\r
42   DeriveKey2(digest, 0x5C, temp + NSha1::kDigestSize);\r
43   memcpy(key, temp, 32);\r
44 }\r
45 \r
46 void CKeyInfo::SetPassword(const Byte *data, UInt32 size)\r
47 {\r
48   NSha1::CContext sha;\r
49   sha.Init();\r
50   sha.Update(data, size);\r
51   DeriveKey(sha, MasterKey);\r
52 }\r
53 \r
54 STDMETHODIMP CBaseCoder::CryptoSetPassword(const Byte *data, UInt32 size)\r
55 {\r
56   _key.SetPassword(data, size);\r
57   return S_OK;\r
58 }\r
59 \r
60 HRESULT CDecoder::ReadHeader(ISequentialInStream *inStream, UInt32 /* crc */, UInt64 /* unpackSize */)\r
61 {\r
62   Byte temp[4];\r
63   RINOK(ReadStream_FALSE(inStream, temp, 2));\r
64   _ivSize = GetUi16(temp);\r
65   if (_ivSize == 0)\r
66   {\r
67     return E_NOTIMPL;\r
68     /*\r
69     SetUi32(_iv, crc);\r
70     for (int i = 0; i < 8; i++)\r
71      _iv[4 + i] = (Byte)(unpackSize >> (8 * i));\r
72     SetUi32(_iv + 12, 0);\r
73     */\r
74   }\r
75   else if (_ivSize == 16)\r
76   {\r
77     RINOK(ReadStream_FALSE(inStream, _iv, _ivSize));\r
78   }\r
79   else\r
80     return E_NOTIMPL;\r
81   RINOK(ReadStream_FALSE(inStream, temp, 4));\r
82   _remSize = GetUi32(temp);\r
83   const UInt32 kAlign = 16;\r
84   if (_remSize < 16 || _remSize > (1 << 18))\r
85     return E_NOTIMPL;\r
86   if (_remSize + kAlign > _buf.GetCapacity())\r
87   {\r
88     _buf.Free();\r
89     _buf.SetCapacity(_remSize + kAlign);\r
90     _bufAligned = (Byte *)((ptrdiff_t)((Byte *)_buf + kAlign - 1) & ~(ptrdiff_t)(kAlign - 1));\r
91   }\r
92   return ReadStream_FALSE(inStream, _bufAligned, _remSize);\r
93 }\r
94 \r
95 HRESULT CDecoder::CheckPassword(bool &passwOK)\r
96 {\r
97   passwOK = false;\r
98   if (_remSize < 16)\r
99     return E_NOTIMPL;\r
100   Byte *p = _bufAligned;\r
101   UInt16 format = GetUi16(p);\r
102   if (format != 3)\r
103     return E_NOTIMPL;\r
104   UInt16 algId = GetUi16(p + 2);\r
105   if (algId < kAES128)\r
106     return E_NOTIMPL;\r
107   algId -= kAES128;\r
108   if (algId > 2)\r
109     return E_NOTIMPL;\r
110   UInt16 bitLen = GetUi16(p + 4);\r
111   UInt16 flags = GetUi16(p + 6);\r
112   if (algId * 64 + 128 != bitLen)\r
113     return E_NOTIMPL;\r
114   _key.KeySize = 16 + algId * 8;\r
115   if ((flags & 1) == 0)\r
116     return E_NOTIMPL;\r
117   if ((flags & 0x4000) != 0)\r
118   {\r
119     // Use 3DES\r
120     return E_NOTIMPL;\r
121   }\r
122 \r
123   UInt32 rdSize = GetUi16(p + 8);\r
124   if ((rdSize & 0xF) != 0 || rdSize + 16 > _remSize)\r
125     return E_NOTIMPL;\r
126   memmove(p, p + 10, rdSize);\r
127   Byte *validData = p + rdSize + 16;\r
128   if (GetUi32(validData - 6) != 0) // reserved\r
129     return E_NOTIMPL;\r
130   UInt32 validSize = GetUi16(validData - 2);\r
131   if ((validSize & 0xF) != 0 || 16 + rdSize + validSize != _remSize)\r
132     return E_NOTIMPL;\r
133 \r
134 \r
135   {\r
136     RINOK(SetKey(_key.MasterKey, _key.KeySize));\r
137     RINOK(SetInitVector(_iv, 16));\r
138     Init();\r
139     Filter(p, rdSize);\r
140   }\r
141 \r
142   Byte fileKey[32];\r
143   NSha1::CContext sha;\r
144   sha.Init();\r
145   sha.Update(_iv, 16);\r
146   sha.Update(p, rdSize - 16); // we don't use last 16 bytes (PAD bytes)\r
147   DeriveKey(sha, fileKey);\r
148   \r
149   RINOK(SetKey(fileKey, _key.KeySize));\r
150   RINOK(SetInitVector(_iv, 16));\r
151   Init();\r
152   Filter(validData, validSize);\r
153 \r
154   if (validSize < 4)\r
155     return E_NOTIMPL;\r
156   validSize -= 4;\r
157   if (GetUi32(validData + validSize) != CrcCalc(validData, validSize))\r
158     return S_OK;\r
159   passwOK = true;\r
160   Init();\r
161   return S_OK;\r
162 }\r
163 \r
164 }}\r