annotate 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
rev   line source
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 }}