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