ext-cryptopp/default.cpp
Jeffrey Walton 6b1a56cf72
Fixup under-aligned buffers for DefaultEncryptors and DefaultDecryptors on AltiVec and Power8
This commit supports the upcoming AltiVec and Power8 processor support for DefaultEncryptors and DefaultDecryptors. The commit favors AlignedSecByteBlock over SecByteBlock in places where messages are handled on the AltiVec and Power8 processor data paths. The data paths include all block cipher modes of operation, and some filters like FilterWithBufferedInput.

Intel and ARM processors are tolerant of under-aligned buffers when using crypto intstructions. AltiVec and Power8 are less tolerant, and they simply ignore the three low-order bits to ensure an address is aligned. The AltiVec and Power8 have caused a fair number of wild writes on the stack and in the heap.

Testing on a 64-bit Intel Skylake show a marked improvement in performance. We suspect GCC is generating better code since it knows the alignment of the pointers, and does not have to emit fixup code for under-aligned and mis-aligned data. Testing on an mid-2000's 32-bit VIA C7-D with SSE2+SSSE3 showed no improvement, and no performance was lost.
2017-09-04 10:47:55 -04:00

308 lines
11 KiB
C++

// default.cpp - originally written and placed in the public domain by Wei Dai
#include "pch.h"
#include "config.h"
#if CRYPTOPP_MSC_VERSION
# pragma warning(disable: 4127 4189)
#endif
#include "cryptlib.h"
#include "filters.h"
#include "smartptr.h"
#include "default.h"
#include "queue.h"
#include <time.h>
#include <memory>
NAMESPACE_BEGIN(CryptoPP)
// The purpose of this function Mash() is to take an arbitrary length input
// string and *deterministicly* produce an arbitrary length output string such
// that (1) it looks random, (2) no information about the input is
// deducible from it, and (3) it contains as much entropy as it can hold, or
// the amount of entropy in the input string, whichever is smaller.
template <class H>
static void Mash(const byte *in, size_t inLen, byte *out, size_t outLen, int iterations)
{
if (BytePrecision(outLen) > 2)
throw InvalidArgument("Mash: output legnth too large");
size_t bufSize = RoundUpToMultipleOf(outLen, (size_t)H::DIGESTSIZE);
byte b[2];
AlignedSecByteBlock buf(bufSize);
AlignedSecByteBlock outBuf(bufSize);
H hash;
unsigned int i;
for(i=0; i<outLen; i+=H::DIGESTSIZE)
{
b[0] = (byte) (i >> 8);
b[1] = (byte) i;
hash.Update(b, 2);
hash.Update(in, inLen);
hash.Final(outBuf+i);
}
while (iterations-- > 1)
{
memcpy(buf, outBuf, bufSize);
for (i=0; i<bufSize; i+=H::DIGESTSIZE)
{
b[0] = (byte) (i >> 8);
b[1] = (byte) i;
hash.Update(b, 2);
hash.Update(buf, bufSize);
hash.Final(outBuf+i);
}
}
memcpy(out, outBuf, outLen);
}
template <class BC, class H, class Info>
static void GenerateKeyIV(const byte *passphrase, size_t passphraseLength, const byte *salt, size_t saltLength, unsigned int iterations, byte *key, byte *IV)
{
// UBsan. User supplied params, may be NULL
AlignedSecByteBlock temp(passphraseLength+saltLength);
if (passphrase != NULLPTR)
memcpy(temp, passphrase, passphraseLength);
if (salt != NULLPTR)
memcpy(temp+passphraseLength, salt, saltLength);
// OK. Derived params, cannot be NULL
AlignedSecByteBlock keyIV(Info::KEYLENGTH+Info::BLOCKSIZE);
Mash<H>(temp, passphraseLength + saltLength, keyIV, Info::KEYLENGTH+Info::BLOCKSIZE, iterations);
memcpy(key, keyIV, Info::KEYLENGTH);
memcpy(IV, keyIV+Info::KEYLENGTH, Info::BLOCKSIZE);
}
// ********************************************************
template <class BC, class H, class Info>
DataEncryptor<BC,H,Info>::DataEncryptor(const char *passphrase, BufferedTransformation *attachment)
: ProxyFilter(NULLPTR, 0, 0, attachment), m_passphrase((const byte *)passphrase, strlen(passphrase))
{
CRYPTOPP_COMPILE_ASSERT((int)SALTLENGTH <= DIGESTSIZE);
CRYPTOPP_COMPILE_ASSERT((int)BLOCKSIZE <= (int)DIGESTSIZE);
}
template <class BC, class H, class Info>
DataEncryptor<BC,H,Info>::DataEncryptor(const byte *passphrase, size_t passphraseLength, BufferedTransformation *attachment)
: ProxyFilter(NULLPTR, 0, 0, attachment), m_passphrase(passphrase, passphraseLength)
{
CRYPTOPP_COMPILE_ASSERT((int)SALTLENGTH <= (int)DIGESTSIZE);
CRYPTOPP_COMPILE_ASSERT((int)BLOCKSIZE <= (int)DIGESTSIZE);
}
template <class BC, class H, class Info>
void DataEncryptor<BC,H,Info>::FirstPut(const byte *)
{
AlignedSecByteBlock salt(DIGESTSIZE), keyCheck(DIGESTSIZE);
H hash;
// use hash(passphrase | time | clock) as salt
hash.Update(m_passphrase, m_passphrase.size());
time_t t=time(NULLPTR);
hash.Update((byte *)&t, sizeof(t));
clock_t c=clock();
hash.Update((byte *)&c, sizeof(c));
hash.Final(salt);
// use hash(passphrase | salt) as key check
hash.Update(m_passphrase, m_passphrase.size());
hash.Update(salt, SALTLENGTH);
hash.Final(keyCheck);
AttachedTransformation()->Put(salt, SALTLENGTH);
// mash passphrase and salt together into key and IV
AlignedSecByteBlock key(KEYLENGTH);
AlignedSecByteBlock IV(BLOCKSIZE);
GenerateKeyIV<BC,H,Info>(m_passphrase, m_passphrase.size(), salt, SALTLENGTH, ITERATIONS, key, IV);
m_cipher.SetKeyWithIV(key, key.size(), IV);
SetFilter(new StreamTransformationFilter(m_cipher));
m_filter->Put(keyCheck, BLOCKSIZE);
}
template <class BC, class H, class Info>
void DataEncryptor<BC,H,Info>::LastPut(const byte *inString, size_t length)
{
CRYPTOPP_UNUSED(inString); CRYPTOPP_UNUSED(length);
m_filter->MessageEnd();
}
// ********************************************************
template <class BC, class H, class Info>
DataDecryptor<BC,H,Info>::DataDecryptor(const char *p, BufferedTransformation *attachment, bool throwException)
: ProxyFilter(NULLPTR, SALTLENGTH+BLOCKSIZE, 0, attachment)
, m_state(WAITING_FOR_KEYCHECK)
, m_passphrase((const byte *)p, strlen(p))
, m_throwException(throwException)
{
CRYPTOPP_COMPILE_ASSERT((int)SALTLENGTH <= (int)DIGESTSIZE);
CRYPTOPP_COMPILE_ASSERT((int)BLOCKSIZE <= (int)DIGESTSIZE);
}
template <class BC, class H, class Info>
DataDecryptor<BC,H,Info>::DataDecryptor(const byte *passphrase, size_t passphraseLength, BufferedTransformation *attachment, bool throwException)
: ProxyFilter(NULLPTR, SALTLENGTH+BLOCKSIZE, 0, attachment)
, m_state(WAITING_FOR_KEYCHECK)
, m_passphrase(passphrase, passphraseLength)
, m_throwException(throwException)
{
CRYPTOPP_COMPILE_ASSERT((int)SALTLENGTH <= (int)DIGESTSIZE);
CRYPTOPP_COMPILE_ASSERT((int)BLOCKSIZE <= (int)DIGESTSIZE);
}
template <class BC, class H, class Info>
void DataDecryptor<BC,H,Info>::FirstPut(const byte *inString)
{
CheckKey(inString, inString+SALTLENGTH);
}
template <class BC, class H, class Info>
void DataDecryptor<BC,H,Info>::LastPut(const byte *inString, size_t length)
{
CRYPTOPP_UNUSED(inString); CRYPTOPP_UNUSED(length);
if (m_filter.get() == NULLPTR)
{
m_state = KEY_BAD;
if (m_throwException)
throw KeyBadErr();
}
else
{
m_filter->MessageEnd();
m_state = WAITING_FOR_KEYCHECK;
}
}
template <class BC, class H, class Info>
void DataDecryptor<BC,H,Info>::CheckKey(const byte *salt, const byte *keyCheck)
{
AlignedSecByteBlock check(STDMAX((unsigned int)2*BLOCKSIZE, (unsigned int)DIGESTSIZE));
H hash;
hash.Update(m_passphrase, m_passphrase.size());
hash.Update(salt, SALTLENGTH);
hash.Final(check);
AlignedSecByteBlock key(KEYLENGTH);
AlignedSecByteBlock IV(BLOCKSIZE);
GenerateKeyIV<BC,H,Info>(m_passphrase, m_passphrase.size(), salt, SALTLENGTH, ITERATIONS, key, IV);
m_cipher.SetKeyWithIV(key, key.size(), IV);
member_ptr<StreamTransformationFilter> decryptor(new StreamTransformationFilter(m_cipher));
decryptor->Put(keyCheck, BLOCKSIZE);
decryptor->ForceNextPut();
decryptor->Get(check+BLOCKSIZE, BLOCKSIZE);
SetFilter(decryptor.release());
if (!VerifyBufsEqual(check, check+BLOCKSIZE, BLOCKSIZE))
{
m_state = KEY_BAD;
if (m_throwException)
throw KeyBadErr();
}
else
m_state = KEY_GOOD;
}
// ********************************************************
template <class H, class MAC>
static MAC* NewDataEncryptorMAC(const byte *passphrase, size_t passphraseLength)
{
size_t macKeyLength = MAC::StaticGetValidKeyLength(16);
AlignedSecByteBlock macKey(macKeyLength);
// since the MAC is encrypted there is no reason to mash the passphrase for many iterations
Mash<H>(passphrase, passphraseLength, macKey, macKeyLength, 1);
return new MAC(macKey, macKeyLength);
}
template <class BC, class H, class MAC, class Info>
DataEncryptorWithMAC<BC,H,MAC,Info>::DataEncryptorWithMAC(const char *passphrase, BufferedTransformation *attachment)
: ProxyFilter(NULLPTR, 0, 0, attachment)
, m_mac(NewDataEncryptorMAC<H,MAC>((const byte *)passphrase, strlen(passphrase)))
{
SetFilter(new HashFilter(*m_mac, new DataEncryptor<BC,H,Info>(passphrase), true));
}
template <class BC, class H, class MAC, class Info>
DataEncryptorWithMAC<BC,H,MAC,Info>::DataEncryptorWithMAC(const byte *passphrase, size_t passphraseLength, BufferedTransformation *attachment)
: ProxyFilter(NULLPTR, 0, 0, attachment)
, m_mac(NewDataEncryptorMAC<H,MAC>(passphrase, passphraseLength))
{
SetFilter(new HashFilter(*m_mac, new DataEncryptor<BC,H,Info>(passphrase, passphraseLength), true));
}
template <class BC, class H, class MAC, class Info>
void DataEncryptorWithMAC<BC,H,MAC,Info>::LastPut(const byte *inString, size_t length)
{
CRYPTOPP_UNUSED(inString); CRYPTOPP_UNUSED(length);
m_filter->MessageEnd();
}
// ********************************************************
template <class BC, class H, class MAC, class Info>
DataDecryptorWithMAC<BC,H,MAC,Info>::DataDecryptorWithMAC(const char *passphrase, BufferedTransformation *attachment, bool throwException)
: ProxyFilter(NULLPTR, 0, 0, attachment)
, m_mac(NewDataEncryptorMAC<H,MAC>((const byte *)passphrase, strlen(passphrase)))
, m_throwException(throwException)
{
SetFilter(new DataDecryptor<BC,H,Info>(passphrase, m_hashVerifier=new HashVerificationFilter(*m_mac, NULLPTR, HashVerificationFilter::PUT_MESSAGE), throwException));
}
template <class BC, class H, class MAC, class Info>
DataDecryptorWithMAC<BC,H,MAC,Info>::DataDecryptorWithMAC(const byte *passphrase, size_t passphraseLength, BufferedTransformation *attachment, bool throwException)
: ProxyFilter(NULLPTR, 0, 0, attachment)
, m_mac(NewDataEncryptorMAC<H,MAC>(passphrase, passphraseLength))
, m_throwException(throwException)
{
SetFilter(new DataDecryptor<BC,H,Info>(passphrase, passphraseLength, m_hashVerifier=new HashVerificationFilter(*m_mac, NULLPTR, HashVerificationFilter::PUT_MESSAGE), throwException));
}
template <class BC, class H, class MAC, class Info>
typename DataDecryptor<BC,H,Info>::State DataDecryptorWithMAC<BC,H,MAC,Info>::CurrentState() const
{
return static_cast<const DataDecryptor<BC,H,Info> *>(m_filter.get())->CurrentState();
}
template <class BC, class H, class MAC, class Info>
bool DataDecryptorWithMAC<BC,H,MAC,Info>::CheckLastMAC() const
{
return m_hashVerifier->GetLastResult();
}
template <class BC, class H, class MAC, class Info>
void DataDecryptorWithMAC<BC,H,MAC,Info>::LastPut(const byte *inString, size_t length)
{
CRYPTOPP_UNUSED(inString); CRYPTOPP_UNUSED(length);
m_filter->MessageEnd();
if (m_throwException && !CheckLastMAC())
throw MACBadErr();
}
template struct DataParametersInfo<LegacyBlockCipher::BLOCKSIZE, LegacyBlockCipher::DEFAULT_KEYLENGTH, LegacyHashModule::DIGESTSIZE, 8, 200>;
template struct DataParametersInfo<DefaultBlockCipher::BLOCKSIZE, DefaultBlockCipher::DEFAULT_KEYLENGTH, DefaultHashModule::DIGESTSIZE, 8, 2500>;
template class DataEncryptor<LegacyBlockCipher,LegacyHashModule,LegacyParametersInfo>;
template class DataDecryptor<LegacyBlockCipher,LegacyHashModule,LegacyParametersInfo>;
template class DataEncryptor<DefaultBlockCipher,DefaultHashModule,DefaultParametersInfo>;
template class DataDecryptor<DefaultBlockCipher,DefaultHashModule,DefaultParametersInfo>;
template class DataEncryptorWithMAC<LegacyBlockCipher,LegacyHashModule,DefaultMAC,LegacyParametersInfo>;
template class DataDecryptorWithMAC<LegacyBlockCipher,LegacyHashModule,DefaultMAC,LegacyParametersInfo>;
template class DataEncryptorWithMAC<DefaultBlockCipher,DefaultHashModule,DefaultMAC,DefaultParametersInfo>;
template class DataDecryptorWithMAC<DefaultBlockCipher,DefaultHashModule,DefaultMAC,DefaultParametersInfo>;
NAMESPACE_END