Files
easy7zip/CPP/7zip/Crypto/WzAES/WzAES.cpp
Igor Pavlov 980e181dcc 4.50 beta
2016-05-28 00:15:52 +01:00

224 lines
5.6 KiB
C++
Executable File

// WzAES.cpp
/*
This code implements Brian Gladman's scheme
specified in password Based File Encryption Utility.
Note: you must include Crypto/AES/MyAES.cpp to project to initialize AES tables
*/
#include "StdAfx.h"
#include "Windows/Defs.h"
#include "../../Common/StreamObjects.h"
#include "../../Common/StreamUtils.h"
#include "../Hash/Pbkdf2HmacSha1.h"
#include "../Hash/RandGen.h"
#include "WzAES.h"
// define it if you don't want to use speed-optimized version of Pbkdf2HmacSha1
// #define _NO_WZAES_OPTIMIZATIONS
namespace NCrypto {
namespace NWzAES {
const unsigned int kAesKeySizeMax = 32;
static const UInt32 kNumKeyGenIterations = 1000;
STDMETHODIMP CBaseCoder::CryptoSetPassword(const Byte *data, UInt32 size)
{
if(size > kPasswordSizeMax)
return E_INVALIDARG;
_key.Password.SetCapacity(size);
memcpy(_key.Password, data, size);
return S_OK;
}
#define SetUi32(p, d) { UInt32 x = (d); (p)[0] = (Byte)x; (p)[1] = (Byte)(x >> 8); \
(p)[2] = (Byte)(x >> 16); (p)[3] = (Byte)(x >> 24); }
void CBaseCoder::EncryptData(Byte *data, UInt32 size)
{
unsigned int pos = _blockPos;
for (; size > 0; size--)
{
if (pos == AES_BLOCK_SIZE)
{
if (++_counter[0] == 0)
_counter[1]++;
UInt32 outBuf[4];
AesEncode32(_counter, outBuf, Aes.rkey, Aes.numRounds2);
SetUi32(_buffer, outBuf[0]);
SetUi32(_buffer + 4, outBuf[1]);
SetUi32(_buffer + 8, outBuf[2]);
SetUi32(_buffer + 12, outBuf[3]);
pos = 0;
}
*data++ ^= _buffer[pos++];
}
_blockPos = pos;
}
#ifndef _NO_WZAES_OPTIMIZATIONS
static void BytesToBeUInt32s(const Byte *src, UInt32 *dest, int destSize)
{
for (int i = 0 ; i < destSize; i++)
dest[i] =
((UInt32)(src[i * 4 + 0]) << 24) |
((UInt32)(src[i * 4 + 1]) << 16) |
((UInt32)(src[i * 4 + 2]) << 8) |
((UInt32)(src[i * 4 + 3]));
}
#endif
STDMETHODIMP CBaseCoder::Init()
{
UInt32 keySize = _key.GetKeySize();
UInt32 keysTotalSize = 2 * keySize + kPwdVerifCodeSize;
Byte buf[2 * kAesKeySizeMax + kPwdVerifCodeSize];
// for (int ii = 0; ii < 1000; ii++)
{
#ifdef _NO_WZAES_OPTIMIZATIONS
NSha1::Pbkdf2Hmac(
_key.Password, _key.Password.GetCapacity(),
_key.Salt, _key.GetSaltSize(),
kNumKeyGenIterations,
buf, keysTotalSize);
#else
UInt32 buf32[(2 * kAesKeySizeMax + kPwdVerifCodeSize + 3) / 4];
UInt32 key32SizeTotal = (keysTotalSize + 3) / 4;
UInt32 salt[kSaltSizeMax * 4];
UInt32 saltSizeInWords = _key.GetSaltSize() / 4;
BytesToBeUInt32s(_key.Salt, salt, saltSizeInWords);
NSha1::Pbkdf2Hmac32(
_key.Password, _key.Password.GetCapacity(),
salt, saltSizeInWords,
kNumKeyGenIterations,
buf32, key32SizeTotal);
for (UInt32 j = 0; j < keysTotalSize; j++)
buf[j] = (Byte)(buf32[j / 4] >> (24 - 8 * (j & 3)));
#endif
}
_hmac.SetKey(buf + keySize, keySize);
memcpy(_key.PwdVerifComputed, buf + 2 * keySize, kPwdVerifCodeSize);
_blockPos = AES_BLOCK_SIZE;
for (int i = 0; i < 4; i++)
_counter[i] = 0;
AesSetKeyEncode(&Aes, buf, keySize);
return S_OK;
}
static HRESULT SafeWrite(ISequentialOutStream *outStream, const Byte *data, UInt32 size)
{
UInt32 processedSize;
RINOK(WriteStream(outStream, data, size, &processedSize));
return ((processedSize == size) ? S_OK : E_FAIL);
}
/*
STDMETHODIMP CEncoder::WriteCoderProperties(ISequentialOutStream *outStream)
{
Byte keySizeMode = 3;
return outStream->Write(&keySizeMode, 1, NULL);
}
*/
HRESULT CEncoder::WriteHeader(ISequentialOutStream *outStream)
{
UInt32 saltSize = _key.GetSaltSize();
g_RandomGenerator.Generate(_key.Salt, saltSize);
Init();
RINOK(SafeWrite(outStream, _key.Salt, saltSize));
return SafeWrite(outStream, _key.PwdVerifComputed, kPwdVerifCodeSize);
}
HRESULT CEncoder::WriteFooter(ISequentialOutStream *outStream)
{
Byte mac[kMacSize];
_hmac.Final(mac, kMacSize);
return SafeWrite(outStream, mac, kMacSize);
}
STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *data, UInt32 size)
{
if (size != 1)
return E_INVALIDARG;
_key.Init();
Byte keySizeMode = data[0];
if (keySizeMode < 1 || keySizeMode > 3)
return E_INVALIDARG;
_key.KeySizeMode = keySizeMode;
return S_OK;
}
HRESULT CDecoder::ReadHeader(ISequentialInStream *inStream)
{
UInt32 saltSize = _key.GetSaltSize();
UInt32 extraSize = saltSize + kPwdVerifCodeSize;
Byte temp[kSaltSizeMax + kPwdVerifCodeSize];
UInt32 processedSize;
RINOK(ReadStream(inStream, temp, extraSize, &processedSize));
if (processedSize != extraSize)
return E_FAIL;
UInt32 i;
for (i = 0; i < saltSize; i++)
_key.Salt[i] = temp[i];
for (i = 0; i < kPwdVerifCodeSize; i++)
_pwdVerifFromArchive[i] = temp[saltSize + i];
return S_OK;
}
static bool CompareArrays(const Byte *p1, const Byte *p2, UInt32 size)
{
for (UInt32 i = 0; i < size; i++)
if (p1[i] != p2[i])
return false;
return true;
}
bool CDecoder::CheckPasswordVerifyCode()
{
return CompareArrays(_key.PwdVerifComputed, _pwdVerifFromArchive, kPwdVerifCodeSize);
}
HRESULT CDecoder::CheckMac(ISequentialInStream *inStream, bool &isOK)
{
isOK = false;
UInt32 processedSize;
Byte mac1[kMacSize];
RINOK(ReadStream(inStream, mac1, kMacSize, &processedSize));
if (processedSize != kMacSize)
return E_FAIL;
Byte mac2[kMacSize];
_hmac.Final(mac2, kMacSize);
isOK = CompareArrays(mac1, mac2, kMacSize);
return S_OK;
}
STDMETHODIMP_(UInt32) CEncoder::Filter(Byte *data, UInt32 size)
{
EncryptData(data, size);
_hmac.Update(data, size);
return size;
}
STDMETHODIMP_(UInt32) CDecoder::Filter(Byte *data, UInt32 size)
{
_hmac.Update(data, size);
EncryptData(data, size);
return size;
}
}}