From bd8edfa87b579073ead850f6ed19973381620cd3 Mon Sep 17 00:00:00 2001 From: Jeffrey Walton Date: Mon, 1 May 2017 16:23:57 -0400 Subject: [PATCH] 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::Encryption enc1; enc1.SetKey(key, 16); CBC_Mode::Encryption enc2; enc2.SetKeyWithIV(key, 16, iv); AlgorithmParameters params = MakeParameters (Name::BlockSize(), 32) (Name::IV(), ConstByteArrayParameter(iv, 32)); CTR_Mode::Encryption enc3; enc3.SetKey(key, 16, params); CBC_Mode::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); --- cryptlib.cpp | 4 +- modes.cpp | 6 +-- modes.h | 9 ++-- seckey.h | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++- simple.h | 8 +++ 5 files changed, 163 insertions(+), 9 deletions(-) diff --git a/cryptlib.cpp b/cryptlib.cpp index 19ea1c6a..8920d356 100644 --- a/cryptlib.cpp +++ b/cryptlib.cpp @@ -71,7 +71,7 @@ Algorithm::Algorithm(bool checkSelfTestStatus) void SimpleKeyingInterface::SetKey(const byte *key, size_t length, const NameValuePairs ¶ms) { this->ThrowIfInvalidKeyLength(length); - this->UncheckedSetKey(key, (unsigned int)length, params); + this->UncheckedSetKey(key, static_cast(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(ivWithLength.size())); return iv; } else if (params.GetValue(Name::IV(), iv)) diff --git a/modes.cpp b/modes.cpp index 6b8e81ae..0d9849ce 100644 --- a/modes.cpp +++ b/modes.cpp @@ -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; } diff --git a/modes.h b/modes.h index 78f2cd97..0955c3bd 100644 --- a/modes.h +++ b/modes.h @@ -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 diff --git a/seckey.h b/seckey.h index 0cba30e3..c8bbfc90 100644 --- a/seckey.h +++ b/seckey.h @@ -111,10 +111,121 @@ protected: { int rounds = param.GetIntValueWithDefault("Rounds", DEFAULT_ROUNDS); ThrowIfInvalidRounds(rounds, alg); - return (unsigned int)rounds; + return static_cast(rounds); } }; +//! \class VariableBlockSize +//! \brief Inherited by algorithms with variable blocksize +//! \tparam D Default blocksize +//! \tparam N Minimum blocksize +//! \tparam M Maximum blocksize +template // 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 ¶m, 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(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 CRYPTOPP_NO_VTABLE VariableBlockCipherImpl : public AlgorithmImpl > > +{ +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 diff --git a/simple.h b/simple.h index 2b92d343..8be4d422 100644 --- a/simple.h +++ b/simple.h @@ -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