1 // Crypto/ZipStrong.cpp
\r
5 #include "../../../C/7zCrc.h"
\r
6 #include "../../../C/CpuArch.h"
\r
8 #include "../Common/StreamUtils.h"
\r
12 #include "ZipStrong.h"
\r
15 namespace NZipStrong {
\r
17 static const UInt16 kAES128 = 0x660E;
\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
24 static void DeriveKey2(const Byte *digest, Byte c, Byte *dest)
\r
28 for (unsigned i = 0; i < NSha1::kDigestSize; i++)
\r
29 buf[i] ^= digest[i];
\r
30 NSha1::CContext sha;
\r
32 sha.Update(buf, 64);
\r
36 static void DeriveKey(NSha1::CContext &sha, Byte *key)
\r
38 Byte digest[NSha1::kDigestSize];
\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
46 void CKeyInfo::SetPassword(const Byte *data, UInt32 size)
\r
48 NSha1::CContext sha;
\r
50 sha.Update(data, size);
\r
51 DeriveKey(sha, MasterKey);
\r
54 STDMETHODIMP CBaseCoder::CryptoSetPassword(const Byte *data, UInt32 size)
\r
56 _key.SetPassword(data, size);
\r
60 HRESULT CDecoder::ReadHeader(ISequentialInStream *inStream, UInt32 /* crc */, UInt64 /* unpackSize */)
\r
63 RINOK(ReadStream_FALSE(inStream, temp, 2));
\r
64 _ivSize = GetUi16(temp);
\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
75 else if (_ivSize == 16)
\r
77 RINOK(ReadStream_FALSE(inStream, _iv, _ivSize));
\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
86 if (_remSize + kAlign > _buf.GetCapacity())
\r
89 _buf.SetCapacity(_remSize + kAlign);
\r
90 _bufAligned = (Byte *)((ptrdiff_t)((Byte *)_buf + kAlign - 1) & ~(ptrdiff_t)(kAlign - 1));
\r
92 return ReadStream_FALSE(inStream, _bufAligned, _remSize);
\r
95 HRESULT CDecoder::CheckPassword(bool &passwOK)
\r
100 Byte *p = _bufAligned;
\r
101 UInt16 format = GetUi16(p);
\r
104 UInt16 algId = GetUi16(p + 2);
\r
105 if (algId < kAES128)
\r
110 UInt16 bitLen = GetUi16(p + 4);
\r
111 UInt16 flags = GetUi16(p + 6);
\r
112 if (algId * 64 + 128 != bitLen)
\r
114 _key.KeySize = 16 + algId * 8;
\r
115 if ((flags & 1) == 0)
\r
117 if ((flags & 0x4000) != 0)
\r
123 UInt32 rdSize = GetUi16(p + 8);
\r
124 if ((rdSize & 0xF) != 0 || rdSize + 16 > _remSize)
\r
126 memmove(p, p + 10, rdSize);
\r
127 Byte *validData = p + rdSize + 16;
\r
128 if (GetUi32(validData - 6) != 0) // reserved
\r
130 UInt32 validSize = GetUi16(validData - 2);
\r
131 if ((validSize & 0xF) != 0 || 16 + rdSize + validSize != _remSize)
\r
136 RINOK(SetKey(_key.MasterKey, _key.KeySize));
\r
137 RINOK(SetInitVector(_iv, 16));
\r
143 NSha1::CContext sha;
\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
149 RINOK(SetKey(fileKey, _key.KeySize));
\r
150 RINOK(SetInitVector(_iv, 16));
\r
152 Filter(validData, validSize);
\r
157 if (GetUi32(validData + validSize) != CrcCalc(validData, validSize))
\r