// 7zAes.cpp | |
#include "StdAfx.h" | |
#include "../../../C/Sha256.h" | |
#include "../../Common/ComTry.h" | |
#ifndef _7ZIP_ST | |
#include "../../Windows/Synchronization.h" | |
#endif | |
#include "../Common/StreamUtils.h" | |
#include "7zAes.h" | |
#include "MyAes.h" | |
#ifndef EXTRACT_ONLY | |
#include "RandGen.h" | |
#endif | |
namespace NCrypto { | |
namespace N7z { | |
static const unsigned k_NumCyclesPower_Supported_MAX = 24; | |
bool CKeyInfo::IsEqualTo(const CKeyInfo &a) const | |
{ | |
if (SaltSize != a.SaltSize || NumCyclesPower != a.NumCyclesPower) | |
return false; | |
for (unsigned i = 0; i < SaltSize; i++) | |
if (Salt[i] != a.Salt[i]) | |
return false; | |
return (Password == a.Password); | |
} | |
void CKeyInfo::CalcKey() | |
{ | |
if (NumCyclesPower == 0x3F) | |
{ | |
unsigned pos; | |
for (pos = 0; pos < SaltSize; pos++) | |
Key[pos] = Salt[pos]; | |
for (unsigned i = 0; i < Password.Size() && pos < kKeySize; i++) | |
Key[pos++] = Password[i]; | |
for (; pos < kKeySize; pos++) | |
Key[pos] = 0; | |
} | |
else | |
{ | |
size_t bufSize = 8 + SaltSize + Password.Size(); | |
CObjArray<Byte> buf(bufSize); | |
memcpy(buf, Salt, SaltSize); | |
memcpy(buf + SaltSize, Password, Password.Size()); | |
CSha256 sha; | |
Sha256_Init(&sha); | |
Byte *ctr = buf + SaltSize + Password.Size(); | |
for (unsigned i = 0; i < 8; i++) | |
ctr[i] = 0; | |
UInt64 numRounds = (UInt64)1 << NumCyclesPower; | |
do | |
{ | |
Sha256_Update(&sha, buf, bufSize); | |
for (unsigned i = 0; i < 8; i++) | |
if (++(ctr[i]) != 0) | |
break; | |
} | |
while (--numRounds != 0); | |
Sha256_Final(&sha, Key); | |
} | |
} | |
bool CKeyInfoCache::GetKey(CKeyInfo &key) | |
{ | |
FOR_VECTOR (i, Keys) | |
{ | |
const CKeyInfo &cached = Keys[i]; | |
if (key.IsEqualTo(cached)) | |
{ | |
for (unsigned j = 0; j < kKeySize; j++) | |
key.Key[j] = cached.Key[j]; | |
if (i != 0) | |
Keys.MoveToFront(i); | |
return true; | |
} | |
} | |
return false; | |
} | |
void CKeyInfoCache::FindAndAdd(const CKeyInfo &key) | |
{ | |
FOR_VECTOR (i, Keys) | |
{ | |
const CKeyInfo &cached = Keys[i]; | |
if (key.IsEqualTo(cached)) | |
{ | |
if (i != 0) | |
Keys.MoveToFront(i); | |
return; | |
} | |
} | |
Add(key); | |
} | |
void CKeyInfoCache::Add(const CKeyInfo &key) | |
{ | |
if (Keys.Size() >= Size) | |
Keys.DeleteBack(); | |
Keys.Insert(0, key); | |
} | |
static CKeyInfoCache g_GlobalKeyCache(32); | |
#ifndef _7ZIP_ST | |
static NWindows::NSynchronization::CCriticalSection g_GlobalKeyCacheCriticalSection; | |
#define MT_LOCK NWindows::NSynchronization::CCriticalSectionLock lock(g_GlobalKeyCacheCriticalSection); | |
#else | |
#define MT_LOCK | |
#endif | |
CBase::CBase(): | |
_cachedKeys(16), | |
_ivSize(0) | |
{ | |
for (unsigned i = 0; i < sizeof(_iv); i++) | |
_iv[i] = 0; | |
} | |
void CBase::PrepareKey() | |
{ | |
// BCJ2 threads use same password. So we use long lock. | |
MT_LOCK | |
bool finded = false; | |
if (!_cachedKeys.GetKey(_key)) | |
{ | |
finded = g_GlobalKeyCache.GetKey(_key); | |
if (!finded) | |
_key.CalcKey(); | |
_cachedKeys.Add(_key); | |
} | |
if (!finded) | |
g_GlobalKeyCache.FindAndAdd(_key); | |
} | |
#ifndef EXTRACT_ONLY | |
/* | |
STDMETHODIMP CEncoder::ResetSalt() | |
{ | |
_key.SaltSize = 4; | |
g_RandomGenerator.Generate(_key.Salt, _key.SaltSize); | |
return S_OK; | |
} | |
*/ | |
STDMETHODIMP CEncoder::ResetInitVector() | |
{ | |
for (unsigned i = 0; i < sizeof(_iv); i++) | |
_iv[i] = 0; | |
_ivSize = 8; | |
g_RandomGenerator.Generate(_iv, _ivSize); | |
return S_OK; | |
} | |
STDMETHODIMP CEncoder::WriteCoderProperties(ISequentialOutStream *outStream) | |
{ | |
Byte props[2 + sizeof(_key.Salt) + sizeof(_iv)]; | |
unsigned propsSize = 1; | |
props[0] = (Byte)(_key.NumCyclesPower | |
| (_key.SaltSize == 0 ? 0 : (1 << 7)) | |
| (_ivSize == 0 ? 0 : (1 << 6))); | |
if (_key.SaltSize != 0 || _ivSize != 0) | |
{ | |
props[1] = (Byte)( | |
((_key.SaltSize == 0 ? 0 : _key.SaltSize - 1) << 4) | |
| (_ivSize == 0 ? 0 : _ivSize - 1)); | |
memcpy(props + 2, _key.Salt, _key.SaltSize); | |
propsSize = 2 + _key.SaltSize; | |
memcpy(props + propsSize, _iv, _ivSize); | |
propsSize += _ivSize; | |
} | |
return WriteStream(outStream, props, propsSize); | |
} | |
CEncoder::CEncoder() | |
{ | |
// _key.SaltSize = 4; g_RandomGenerator.Generate(_key.Salt, _key.SaltSize); | |
// _key.NumCyclesPower = 0x3F; | |
_key.NumCyclesPower = 19; | |
_aesFilter = new CAesCbcEncoder(kKeySize); | |
} | |
#endif | |
CDecoder::CDecoder() | |
{ | |
_aesFilter = new CAesCbcDecoder(kKeySize); | |
} | |
STDMETHODIMP CDecoder::SetDecoderProperties2(const Byte *data, UInt32 size) | |
{ | |
_key.ClearProps(); | |
_ivSize = 0; | |
unsigned i; | |
for (i = 0; i < sizeof(_iv); i++) | |
_iv[i] = 0; | |
if (size == 0) | |
return S_OK; | |
Byte b0 = data[0]; | |
_key.NumCyclesPower = b0 & 0x3F; | |
if ((b0 & 0xC0) == 0) | |
return size == 1 ? S_OK : E_INVALIDARG; | |
if (size <= 1) | |
return E_INVALIDARG; | |
Byte b1 = data[1]; | |
unsigned saltSize = ((b0 >> 7) & 1) + (b1 >> 4); | |
unsigned ivSize = ((b0 >> 6) & 1) + (b1 & 0x0F); | |
if (size != 2 + saltSize + ivSize) | |
return E_INVALIDARG; | |
_key.SaltSize = saltSize; | |
data += 2; | |
for (i = 0; i < saltSize; i++) | |
_key.Salt[i] = *data++; | |
for (i = 0; i < ivSize; i++) | |
_iv[i] = *data++; | |
return (_key.NumCyclesPower <= k_NumCyclesPower_Supported_MAX | |
|| _key.NumCyclesPower == 0x3F) ? S_OK : E_NOTIMPL; | |
} | |
STDMETHODIMP CBaseCoder::CryptoSetPassword(const Byte *data, UInt32 size) | |
{ | |
COM_TRY_BEGIN | |
_key.Password.CopyFrom(data, (size_t)size); | |
return S_OK; | |
COM_TRY_END | |
} | |
STDMETHODIMP CBaseCoder::Init() | |
{ | |
COM_TRY_BEGIN | |
PrepareKey(); | |
CMyComPtr<ICryptoProperties> cp; | |
RINOK(_aesFilter.QueryInterface(IID_ICryptoProperties, &cp)); | |
if (!cp) | |
return E_FAIL; | |
RINOK(cp->SetKey(_key.Key, kKeySize)); | |
RINOK(cp->SetInitVector(_iv, sizeof(_iv))); | |
return _aesFilter->Init(); | |
COM_TRY_END | |
} | |
STDMETHODIMP_(UInt32) CBaseCoder::Filter(Byte *data, UInt32 size) | |
{ | |
return _aesFilter->Filter(data, size); | |
} | |
}} |