Add variable block size support for block ciphers

This should lead the way for more modern block ciphers like Threefish and Kalyna. It tested well with both regular cipher modes (the mode has an instance of the cipher) and external cipher modes (the cipher and mode are distinct objects, and the mode holds a reference to the cipher).

We still have to work out the details of naming a cipher. For example, Kalyna with a 128-bit key can use a 128-bit or 256-bit block size. Kalyna-128 is not enough to describe the algorithm and locate it in the object registry. Kalyna-128-128 looks kind of weird; maybe Kalyna-128(128) or Kalyna-128(256) would be better.

Here are the initial test cases to verify functionality:

byte key[64] = {}, iv[32] = {};

ECB_Mode<Kalyna>::Encryption enc1;
enc1.SetKey(key, 16);

CBC_Mode<Kalyna>::Encryption enc2;
enc2.SetKeyWithIV(key, 16, iv);

AlgorithmParameters params = MakeParameters
                        (Name::BlockSize(), 32)
                        (Name::IV(), ConstByteArrayParameter(iv, 32));

CTR_Mode<Kalyna>::Encryption enc3;
enc3.SetKey(key, 16, params);

CBC_Mode<Kalyna>::Encryption enc4;
enc4.SetKey(key, 32, params);

Kalyna::Encryption enc5;
ECB_Mode_ExternalCipher::Encryption ecb(enc5);
ecb.SetKey(key, 16, params);

Kalyna::Encryption enc6;
ECB_Mode_ExternalCipher::Encryption cbc(enc6);
cbc.SetKey(key, 32, params);
This commit is contained in:
Jeffrey Walton 2017-05-01 16:23:57 -04:00
parent 1543649ead
commit bd8edfa87b
No known key found for this signature in database
GPG Key ID: B36AB348921B1838
5 changed files with 163 additions and 9 deletions

View File

@ -71,7 +71,7 @@ Algorithm::Algorithm(bool checkSelfTestStatus)
void SimpleKeyingInterface::SetKey(const byte *key, size_t length, const NameValuePairs &params)
{
this->ThrowIfInvalidKeyLength(length);
this->UncheckedSetKey(key, (unsigned int)length, params);
this->UncheckedSetKey(key, static_cast<unsigned int>(length), params);
}
void SimpleKeyingInterface::SetKeyWithRounds(const byte *key, size_t length, int rounds)
@ -127,7 +127,7 @@ const byte * SimpleKeyingInterface::GetIVAndThrowIfInvalid(const NameValuePairs
{
iv = ivWithLength.begin();
ThrowIfInvalidIV(iv);
size = ThrowIfInvalidIVLength((int)ivWithLength.size());
size = ThrowIfInvalidIVLength(static_cast<int>(ivWithLength.size()));
return iv;
}
else if (params.GetValue(Name::IV(), iv))

View File

@ -67,7 +67,7 @@ void CFB_ModePolicy::TransformRegister()
void CFB_ModePolicy::CipherResynchronize(const byte *iv, size_t length)
{
CRYPTOPP_ASSERT(length == BlockSize());
CopyOrZero(m_register, iv, length);
CopyOrZero(m_register, m_register.size(), iv, length);
TransformRegister();
}
@ -99,7 +99,7 @@ void OFB_ModePolicy::CipherResynchronize(byte *keystreamBuffer, const byte *iv,
CRYPTOPP_UNUSED(keystreamBuffer), CRYPTOPP_UNUSED(length);
CRYPTOPP_ASSERT(length == BlockSize());
CopyOrZero(m_register, iv, length);
CopyOrZero(m_register, m_register.size(), iv, length);
}
void CTR_ModePolicy::SeekToIteration(lword iterationCount)
@ -144,7 +144,7 @@ void CTR_ModePolicy::CipherResynchronize(byte *keystreamBuffer, const byte *iv,
CRYPTOPP_UNUSED(keystreamBuffer), CRYPTOPP_UNUSED(length);
CRYPTOPP_ASSERT(length == BlockSize());
CopyOrZero(m_register, iv, length);
CopyOrZero(m_register, m_register.size(), iv, length);
m_counterArray = m_register;
}

View File

@ -130,12 +130,15 @@ protected:
unsigned int m_feedbackSize;
};
inline void CopyOrZero(void *dest, const void *src, size_t s)
inline void CopyOrZero(void *dest, size_t d, const void *src, size_t s)
{
CRYPTOPP_ASSERT(dest);
CRYPTOPP_ASSERT(d >= s);
if (src)
memcpy_s(dest, s, src, s);
memcpy_s(dest, d, src, s);
else
memset(dest, 0, s);
memset(dest, 0, d);
}
//! \class OFB_ModePolicy

145
seckey.h
View File

@ -111,10 +111,121 @@ protected:
{
int rounds = param.GetIntValueWithDefault("Rounds", DEFAULT_ROUNDS);
ThrowIfInvalidRounds(rounds, alg);
return (unsigned int)rounds;
return static_cast<unsigned int>(rounds);
}
};
//! \class VariableBlockSize
//! \brief Inherited by algorithms with variable blocksize
//! \tparam D Default blocksize
//! \tparam N Minimum blocksize
//! \tparam M Maximum blocksize
template <unsigned int D, unsigned int N=1, unsigned int M=INT_MAX> // use INT_MAX here because enums are treated as signed ints
class VariableBlockSize
{
public:
//! \brief The default blocksize for the algorithm provided as a constant.
CRYPTOPP_CONSTANT(BLOCKSIZE = D)
//! \brief The default blocksize for the algorithm provided as a constant.
CRYPTOPP_CONSTANT(DEFAULT_BLOCKSIZE = D)
//! \brief The minimum blocksize for the algorithm provided as a constant.
CRYPTOPP_CONSTANT(MIN_BLOCKSIZE = N)
//! \brief The maximum blocksize for the algorithm provided as a constant.
CRYPTOPP_CONSTANT(MAX_BLOCKSIZE = M)
//! \brief The default blocksize for the algorithm based on key length
//! provided by a static function.
//! \param keylength the size of the key, in bytes
//! \details keylength is unused in the default implementation.
CRYPTOPP_STATIC_CONSTEXPR unsigned int StaticGetDefaultBlockSize(size_t keylength)
{
return (keylength >= 64) ? 64 :
(keylength >= 32) ? 32 : 16;
}
protected:
//! \brief Validates the blocksize for an algorithm.
//! \param blocksize the candidate blocksize
//! \param alg an Algorithm object used if the blocksize is invalid
//! \throws InvalidBlockSize if the blocksize is invalid
//! \details ThrowIfInvalidBlockSize() validates the blocksize and throws if invalid.
inline void ThrowIfInvalidBlockSize(int blocksize, const Algorithm *alg)
{
if (M == INT_MAX) // Coverity and result_independent_of_operands
{
if (blocksize < MIN_BLOCKSIZE)
throw InvalidBlockSize(alg ? alg->AlgorithmName() : std::string("VariableBlockSize"), blocksize);
}
else
{
if (blocksize < MIN_BLOCKSIZE || blocksize > MAX_BLOCKSIZE)
throw InvalidBlockSize(alg ? alg->AlgorithmName() : std::string("VariableBlockSize"), blocksize);
}
}
//! \brief Validates the blocksize for an algorithm
//! \param param the candidate blocksize
//! \param alg an Algorithm object used if the blocksize is invalid
//! \returns the blocksize for the algorithm
//! \throws InvalidBlockSize if the blocksize is invalid
//! \details GetBlockSizeAndThrowIfInvalid() validates the blocksize and throws if invalid.
inline unsigned int GetBlockSizeAndThrowIfInvalid(const NameValuePairs &param, const Algorithm *alg)
{
int keylength = param.GetIntValueWithDefault("KeySize", 0);
int blocksize = param.GetIntValueWithDefault("BlockSize", DEFAULT_BLOCKSIZE);
if (keylength > 0)
ThrowIfInvalidBlockSize(keylength, blocksize, alg);
else
ThrowIfInvalidBlockSize(blocksize, alg);
return static_cast<unsigned int>(blocksize);
}
//! Provides the block size of the cipher
//! \return the block size, in bytes
//! \details The sematics of BlockSize() is return DEFAULT_BLOCKSIZE if the default blocksize
//! is in effect. If the blocksize has changed, then the value returned is the BlockSize()
//! parameter used during SetKey().
//! \details DEFAULT_BLOCKSIZE should be paired with DEFAULT_KEYLENGTH, and it is the same as
//! BLOCKSIZE in a FixedBlockSize cipher.
virtual unsigned int BlockSize() const =0;
//! Provides the minimum block size of the cipher
//! \return the minimum block size, in bytes
//! \details MinBlockSize() returns the smallest blocksize a cipher can use. The size can
//! be affected by the key length. For example, Threefish has key sizes of 256, 512 and 1024 bits,
//! and the blocksize follows the key length. If a 512-bit key is used, then the block size is 512
//! bits. Once keyed, the minimum block size of 256 is not accurate, nor is a block size of 1024 bit.
virtual unsigned int MinBlockSize() const
{ return MIN_BLOCKSIZE; }
//! Provides the maximum block size of the cipher
//! \return the maximum block size, in bytes
//! \details MaxBlockSize() returns the largest blocksize a cipher can use. The size can
//! be affected by the key length. For example, Threefish has key sizes of 256, 512 and 1024 bits,
//! and the blocksize follows the key length. If a 512-bit key is used, then the block size is 512
//! bits. Once keyed, the minimum block size of 256 is not accurate, nor is a block size of 1024 bit.
virtual unsigned int MaxBlockSize() const
{ return MAX_BLOCKSIZE; }
//! Provides the initialization vector length of the cipher
//! \return the initialization vector length, in bytes
//! \details The sematics of IVSize() is return IV_LENGTH if the default blocksize is
//! in effect. If the blocksize has changed, then the default implentation returns the value of
//! the BlockSize() parameter used during SetKey().
//! \details Derived classes may override the behavior such that a different value is returned.
//! This may happen with a cipher that requires an IV that is twice the block size.
virtual unsigned int IVSize() const =0;
//! \brief Provides the minimum size of an IV
//! \return minimal length of IVs accepted by this cipher, in bytes
virtual unsigned int MinIVLength() const
{ return MIN_BLOCKSIZE; }
//! \brief Provides the maximum size of an IV
//! \return maximal length of IVs accepted by this cipher, in bytes
virtual unsigned int MaxIVLength() const
{ return MAX_BLOCKSIZE; }
};
// ************** key length ***************
//! \class FixedKeyLength
@ -316,6 +427,38 @@ public:
unsigned int BlockSize() const {return this->BLOCKSIZE;}
};
//! \class VariableBlockCipherImpl
//! \brief Provides a base implementation of Algorithm and SimpleKeyingInterface for block ciphers with varibale block sizes
//! \tparam INFO a SimpleKeyingInterface derived class
//! \tparam BASE a SimpleKeyingInterface derived class
//! \details VariableBlockCipherImpl() provides a default implementation for block ciphers with varibale block sizes using AlgorithmImpl()
//! and SimpleKeyingInterfaceImpl().
//! \sa Algorithm(), SimpleKeyingInterface(), AlgorithmImpl(), SimpleKeyingInterfaceImpl()
template <class INFO, class BASE = BlockCipher>
class CRYPTOPP_NO_VTABLE VariableBlockCipherImpl : public AlgorithmImpl<SimpleKeyingInterfaceImpl<TwoBases<BASE, INFO> > >
{
public:
VariableBlockCipherImpl() : m_blocksize(0) {}
VariableBlockCipherImpl(unsigned int blocksize) : m_blocksize(blocksize) {}
//! Provides the block size of the algorithm
//! \returns the block size, in bytes
unsigned int BlockSize() const {
return m_blocksize ? m_blocksize : this->BLOCKSIZE;
}
//! Provides the initialization vector length of the algorithm
//! \returns the initialization vector length, in bytes
unsigned int IVSize() const {
if (!this->IsResynchronizable())
throw NotImplemented(GetAlgorithm().AlgorithmName() + ": this object doesn't support resynchronization");
return m_blocksize ? m_blocksize : this->IV_LENGTH;
}
protected:
unsigned int m_blocksize;
};
//! \class BlockCipherFinal
//! \brief Provides class member functions to key a block cipher
//! \tparam DIR a CipherDir

View File

@ -58,6 +58,14 @@ public:
explicit InvalidRounds(const std::string &algorithm, unsigned int rounds) : InvalidArgument(algorithm + ": " + IntToString(rounds) + " is not a valid number of rounds") {}
};
//! \class InvalidBlockSize
//! \brief Exception thrown when an invalid block size is encountered
class CRYPTOPP_DLL InvalidBlockSize : public InvalidArgument
{
public:
explicit InvalidBlockSize(const std::string &algorithm, size_t length) : InvalidArgument(algorithm + ": " + IntToString(length) + " is not a valid block size") {}
};
//! \class InvalidPersonalizationLength
//! \brief Exception thrown when an invalid personalization string length is encountered
class CRYPTOPP_DLL InvalidPersonalizationLength : public InvalidArgument