view src/win32/7zip/7z/CPP/7zip/Crypto/ZipStrong.cpp @ 1:f9f4f1b99eed

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