From 7b33bc5e04ae58ff4f82a5fbc810e8c42856791b Mon Sep 17 00:00:00 2001 From: Jeffrey Walton Date: Thu, 29 Mar 2018 23:13:56 -0400 Subject: Cutover PBKDF to KeyDerivationFunction interface (GH #610, PR #612) --- pwdbased.h | 326 ++++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 258 insertions(+), 68 deletions(-) (limited to 'pwdbased.h') diff --git a/pwdbased.h b/pwdbased.h index 7d5f79e0..c4307257 100644 --- a/pwdbased.h +++ b/pwdbased.h @@ -1,4 +1,6 @@ // pwdbased.h - originally written and placed in the public domain by Wei Dai +// Cutover to KeyDerivationFunction interface by Uri Blumenthal +// Marcel Raad and Jeffrey Walton in March 2018. /// \file pwdbased.h /// \brief Password based key derivation functions @@ -13,82 +15,103 @@ NAMESPACE_BEGIN(CryptoPP) -/// \brief Abstract base class for password based key derivation function -class PasswordBasedKeyDerivationFunction +struct PasswordBasedKeyDerivationFunction : public KeyDerivationFunction { -public: - virtual ~PasswordBasedKeyDerivationFunction() {} - - /// \brief Provides the maximum derived key length - /// \returns maximum derived key length, in bytes - virtual size_t MaxDerivedKeyLength() const =0; - - /// \brief Determines if the derivation function uses the purpose byte - /// \returns true if the derivation function uses the purpose byte, false otherwise - virtual bool UsesPurposeByte() const =0; - - /// \brief Derive key from the password - /// \param derived the byte buffer to receive the derived password - /// \param derivedLen the size of the byte buffer to receive the derived password - /// \param purpose an octet indicating the purpose of the derivation - /// \param password the byte buffer with the password - /// \param passwordLen the size of the password, in bytes - /// \param salt the byte buffer with the salt - /// \param saltLen the size of the salt, in bytes - /// \param iterations the number of iterations to attempt - /// \param timeInSeconds the length of time the derivation function should execute - /// \returns iteration count achieved - /// \details DeriveKey returns the actual iteration count achieved. If timeInSeconds == 0, then the complete number - /// of iterations will be obtained. If timeInSeconds != 0, then DeriveKey will iterate until time elapsed, as - /// measured by ThreadUserTimer. - virtual unsigned int DeriveKey(byte *derived, size_t derivedLen, byte purpose, const byte *password, size_t passwordLen, const byte *salt, size_t saltLen, unsigned int iterations, double timeInSeconds=0) const =0; }; +// ******************** PBKDF1 ******************** + /// \brief PBKDF1 from PKCS #5 /// \tparam T a HashTransformation class template class PKCS5_PBKDF1 : public PasswordBasedKeyDerivationFunction { public: - size_t MaxDerivedKeyLength() const {return T::DIGESTSIZE;} - bool UsesPurposeByte() const {return false;} - // PKCS #5 says PBKDF1 should only take 8-byte salts. This implementation allows salts of any length. - unsigned int DeriveKey(byte *derived, size_t derivedLen, byte purpose, const byte *password, size_t passwordLen, const byte *salt, size_t saltLen, unsigned int iterations, double timeInSeconds=0) const; + virtual ~PKCS5_PBKDF1() {} + + static std::string StaticAlgorithmName () { + const std::string name(std::string("PBKDF1(") + + std::string(T::StaticAlgorithmName()) + std::string(")")); + return name; + } + + // KeyDerivationFunction interface + std::string AlgorithmName() const { + return StaticAlgorithmName(); + } + + // KeyDerivationFunction interface + size_t MaxDerivedKeyLength() const { + return static_cast(T::DIGESTSIZE); + } + + // KeyDerivationFunction interface + size_t GetValidDerivedLength(size_t keylength) const; + + // KeyDerivationFunction interface + virtual size_t DeriveKey(byte *derived, size_t derivedLen, const byte *secret, size_t secretLen, + const NameValuePairs& params = g_nullNameValuePairs) const; + + /// \brief Derive a key from a secret seed + /// \param derived the derived output buffer + /// \param derivedLen the size of the derived buffer, in bytes + /// \param purpose a purpose byte + /// \param secret the seed input buffer + /// \param secretLen the size of the secret buffer, in bytes + /// \param salt the salt input buffer + /// \param saltLen the size of the salt buffer, in bytes + /// \param iterations the number of iterations + /// \param timeInSeconds the in seconds + /// \returns the number of iterations performed + /// \throws InvalidDerivedLength if derivedLen is invalid for the scheme + /// \details DeriveKey() provides a standard interface to derive a key from + /// a seed and other parameters. Each class that derives from KeyDerivationFunction + /// provides an overload that accepts most parameters used by the derivation function. + /// \details If timeInSeconds is > 0.0 then DeriveKey will run for + /// that amount of time. If timeInSeconds is 0.0 then DeriveKey will + /// run for the specified number of iterations. + /// \details PKCS #5 says PBKDF1 should only take 8-byte salts. This implementation + /// allows salts of any length. + size_t DeriveKey(byte *derived, size_t derivedLen, byte purpose, const byte *secret, size_t secretLen, const byte *salt, size_t saltLen, unsigned int iterations, double timeInSeconds=0) const; + +protected: + // KeyDerivationFunction interface + const Algorithm & GetAlgorithm() const { + return *this; + } }; -/// \brief PBKDF2 from PKCS #5 -/// \tparam T a HashTransformation class template -class PKCS5_PBKDF2_HMAC : public PasswordBasedKeyDerivationFunction +size_t PKCS5_PBKDF1::GetValidDerivedLength(size_t keylength) const { -public: - size_t MaxDerivedKeyLength() const {return 0xffffffffU;} // should multiply by T::DIGESTSIZE, but gets overflow that way - bool UsesPurposeByte() const {return false;} - unsigned int DeriveKey(byte *derived, size_t derivedLen, byte purpose, const byte *password, size_t passwordLen, const byte *salt, size_t saltLen, unsigned int iterations, double timeInSeconds=0) const; -}; + if (keylength > MaxDerivedLength()) + return MaxDerivedLength(); + return keylength; +} -/* -class PBKDF2Params +template +size_t PKCS5_PBKDF1::DeriveKey(byte *derived, size_t derivedLen, + const byte *secret, size_t secretLen, const NameValuePairs& params) const { -public: - SecByteBlock m_salt; - unsigned int m_interationCount; - ASNOptional > m_keyLength; -}; -*/ + return derivedLen; +} template -unsigned int PKCS5_PBKDF1::DeriveKey(byte *derived, size_t derivedLen, byte purpose, const byte *password, size_t passwordLen, const byte *salt, size_t saltLen, unsigned int iterations, double timeInSeconds) const +size_t PKCS5_PBKDF1::DeriveKey(byte *derived, size_t derivedLen, byte purpose, const byte *secret, size_t secretLen, const byte *salt, size_t saltLen, unsigned int iterations, double timeInSeconds) const { - CRYPTOPP_UNUSED(purpose); + CRYPTOPP_ASSERT(derived && derivedLen); + CRYPTOPP_ASSERT(secret && secretLen); CRYPTOPP_ASSERT(derivedLen <= MaxDerivedKeyLength()); CRYPTOPP_ASSERT(iterations > 0 || timeInSeconds > 0); + CRYPTOPP_UNUSED(purpose); + + ThrowIfInvalidDerivedLength(derivedLen); - if (!iterations) - iterations = 1; + // Business logic + if (!iterations) { iterations = 1; } T hash; - hash.Update(password, passwordLen); + hash.Update(secret, secretLen); hash.Update(salt, saltLen); SecByteBlock buffer(hash.DigestSize()); @@ -107,17 +130,107 @@ unsigned int PKCS5_PBKDF1::DeriveKey(byte *derived, size_t derivedLen, byte p return i; } +// ******************** PKCS5_PBKDF2_HMAC ******************** + +/// \brief PBKDF2 from PKCS #5 +/// \tparam T a HashTransformation class template -unsigned int PKCS5_PBKDF2_HMAC::DeriveKey(byte *derived, size_t derivedLen, byte purpose, const byte *password, size_t passwordLen, const byte *salt, size_t saltLen, unsigned int iterations, double timeInSeconds) const +class PKCS5_PBKDF2_HMAC : public PasswordBasedKeyDerivationFunction { - CRYPTOPP_UNUSED(purpose); - CRYPTOPP_ASSERT(derivedLen <= MaxDerivedKeyLength()); +public: + virtual ~PKCS5_PBKDF2_HMAC() {} + + static std::string StaticAlgorithmName () { + const std::string name(std::string("PBKDF2_HMAC(") + + std::string(T::StaticAlgorithmName()) + std::string(")")); + return name; + } + + // KeyDerivationFunction interface + std::string AlgorithmName() const { + return StaticAlgorithmName(); + } + + // KeyDerivationFunction interface + // should multiply by T::DIGESTSIZE, but gets overflow that way + size_t MaxDerivedKeyLength() const { + return 0xffffffffU; + } + + // KeyDerivationFunction interface + size_t GetValidDerivedLength(size_t keylength) const; + + // KeyDerivationFunction interface + size_t DeriveKey(byte *derived, size_t derivedLen, const byte *secret, size_t secretLen, + const NameValuePairs& params = g_nullNameValuePairs) const; + + /// \brief Derive a key from a secret seed + /// \param derived the derived output buffer + /// \param derivedLen the size of the derived buffer, in bytes + /// \param purpose a purpose byte + /// \param secret the seed input buffer + /// \param secretLen the size of the secret buffer, in bytes + /// \param salt the salt input buffer + /// \param saltLen the size of the salt buffer, in bytes + /// \param iterations the number of iterations + /// \param timeInSeconds the in seconds + /// \returns the number of iterations performed + /// \throws InvalidDerivedLength if derivedLen is invalid for the scheme + /// \details DeriveKey() provides a standard interface to derive a key from + /// a seed and other parameters. Each class that derives from KeyDerivationFunction + /// provides an overload that accepts most parameters used by the derivation function. + /// \details If timeInSeconds is > 0.0 then DeriveKey will run for + /// that amount of time. If timeInSeconds is 0.0 then DeriveKey will + /// run for the specified number of iterations. + size_t DeriveKey(byte *derived, size_t derivedLen, byte purpose, const byte *secret, size_t secretLen, + const byte *salt, size_t saltLen, unsigned int iterations, double timeInSeconds=0) const; + +protected: + // KeyDerivationFunction interface + const Algorithm & GetAlgorithm() const { + return *this; + } +}; + +template +size_t PKCS5_PBKDF2_HMAC::GetValidDerivedLength(size_t keylength) const +{ + if (keylength > MaxDerivedLength()) + return MaxDerivedLength(); + return keylength; +} + +template +size_t PKCS5_PBKDF2_HMAC::DeriveKey(byte *derived, size_t derivedLen, + const byte *secret, size_t secretLen, const NameValuePairs& params) const +{ + CRYPTOPP_ASSERT(derived && derivedLen); + CRYPTOPP_ASSERT(secret && secretLen); + + byte purpose = (byte)params.GetIntValueWithDefault("Purpose", 0); + unsigned int iterations = (unsigned int)params.GetIntValueWithDefault("Iterations", 1); + + ConstByteArrayParameter salt; + (void)params.GetValue(Name::Salt(), salt); + + return DeriveKey(derived, derivedLen, purpose, secret, secretLen, salt.begin(), salt.size(), iterations, 0.0f); +} + +template +size_t PKCS5_PBKDF2_HMAC::DeriveKey(byte *derived, size_t derivedLen, byte purpose, const byte *secret, size_t secretLen, const byte *salt, size_t saltLen, unsigned int iterations, double timeInSeconds) const +{ + CRYPTOPP_ASSERT(derived && derivedLen); + CRYPTOPP_ASSERT(secret && secretLen); + CRYPTOPP_ASSERT(derivedLen <= MaxDerivedLength()); CRYPTOPP_ASSERT(iterations > 0 || timeInSeconds > 0); + CRYPTOPP_UNUSED(purpose); - if (!iterations) - iterations = 1; + ThrowIfInvalidDerivedLength(derivedLen); - HMAC hmac(password, passwordLen); + // Business logic + if (!iterations) { iterations = 1; } + + HMAC hmac(secret, secretLen); SecByteBlock buffer(hmac.DigestSize()); ThreadUserTimer timer; @@ -167,29 +280,107 @@ unsigned int PKCS5_PBKDF2_HMAC::DeriveKey(byte *derived, size_t derivedLen, b return iterations; } +// ******************** PKCS12_PBKDF ******************** + /// \brief PBKDF from PKCS #12, appendix B /// \tparam T a HashTransformation class template class PKCS12_PBKDF : public PasswordBasedKeyDerivationFunction { public: - size_t MaxDerivedKeyLength() const {return size_t(0)-1;} - bool UsesPurposeByte() const {return true;} - unsigned int DeriveKey(byte *derived, size_t derivedLen, byte purpose, const byte *password, size_t passwordLen, const byte *salt, size_t saltLen, unsigned int iterations, double timeInSeconds) const; + virtual ~PKCS12_PBKDF() {} + + static std::string StaticAlgorithmName () { + const std::string name(std::string("PBKDF_PKCS12(") + + std::string(T::StaticAlgorithmName()) + std::string(")")); + return name; + } + + // KeyDerivationFunction interface + std::string AlgorithmName() const { + return StaticAlgorithmName(); + } + + // TODO - check this + size_t MaxDerivedKeyLength() const { + return static_cast(-1); + } + + // KeyDerivationFunction interface + size_t GetValidDerivedLength(size_t keylength) const; + + // KeyDerivationFunction interface + size_t DeriveKey(byte *derived, size_t derivedLen, const byte *secret, size_t secretLen, + const NameValuePairs& params = g_nullNameValuePairs) const; + + /// \brief Derive a key from a secret seed + /// \param derived the derived output buffer + /// \param derivedLen the size of the derived buffer, in bytes + /// \param purpose a purpose byte + /// \param secret the seed input buffer + /// \param secretLen the size of the secret buffer, in bytes + /// \param salt the salt input buffer + /// \param saltLen the size of the salt buffer, in bytes + /// \param iterations the number of iterations + /// \param timeInSeconds the in seconds + /// \returns the number of iterations performed + /// \throws InvalidDerivedLength if derivedLen is invalid for the scheme + /// \details DeriveKey() provides a standard interface to derive a key from + /// a seed and other parameters. Each class that derives from KeyDerivationFunction + /// provides an overload that accepts most parameters used by the derivation function. + /// \details If timeInSeconds is > 0.0 then DeriveKey will run for + /// that amount of time. If timeInSeconds is 0.0 then DeriveKey will + /// run for the specified number of iterations. + size_t DeriveKey(byte *derived, size_t derivedLen, byte purpose, const byte *secret, size_t secretLen, + const byte *salt, size_t saltLen, unsigned int iterations, double timeInSeconds) const; + +protected: + // KeyDerivationFunction interface + const Algorithm & GetAlgorithm() const { + return *this; + } }; template -unsigned int PKCS12_PBKDF::DeriveKey(byte *derived, size_t derivedLen, byte purpose, const byte *password, size_t passwordLen, const byte *salt, size_t saltLen, unsigned int iterations, double timeInSeconds) const +size_t PKCS12_PBKDF::GetValidDerivedLength(size_t keylength) const +{ + if (keylength > MaxDerivedLength()) + return MaxDerivedLength(); + return keylength; +} + +template +size_t PKCS12_PBKDF::DeriveKey(byte *derived, size_t derivedLen, + const byte *secret, size_t secretLen, const NameValuePairs& params) const +{ + CRYPTOPP_ASSERT(derived && derivedLen); + CRYPTOPP_ASSERT(secret && secretLen); + CRYPTOPP_ASSERT(derivedLen <= MaxDerivedLength()); + + byte purpose = (byte)params.GetIntValueWithDefault("Purpose", 0); + unsigned int iterations = (unsigned int)params.GetIntValueWithDefault("Iterations", 1); + + // NULL or 0 length salt OK + ConstByteArrayParameter salt; + (void)params.GetValue(Name::Salt(), salt); + + return DeriveKey(derived, derivedLen, purpose, secret, secretLen, salt.begin(), salt.size(), iterations, 0.0f); +} + +template +size_t PKCS12_PBKDF::DeriveKey(byte *derived, size_t derivedLen, byte purpose, const byte *secret, size_t secretLen, const byte *salt, size_t saltLen, unsigned int iterations, double timeInSeconds) const { CRYPTOPP_ASSERT(derivedLen <= MaxDerivedKeyLength()); CRYPTOPP_ASSERT(iterations > 0 || timeInSeconds > 0); - if (!iterations) - iterations = 1; + ThrowIfInvalidDerivedLength(derivedLen); + + // Business logic + if (!iterations) { iterations = 1; } const size_t v = T::BLOCKSIZE; // v is in bytes rather than bits as in PKCS #12 const size_t DLen = v, SLen = RoundUpToMultipleOf(saltLen, v); - const size_t PLen = RoundUpToMultipleOf(passwordLen, v), ILen = SLen + PLen; + const size_t PLen = RoundUpToMultipleOf(secretLen, v), ILen = SLen + PLen; SecByteBlock buffer(DLen + SLen + PLen); byte *D = buffer, *S = buffer+DLen, *P = buffer+DLen+SLen, *I = S; @@ -198,8 +389,7 @@ unsigned int PKCS12_PBKDF::DeriveKey(byte *derived, size_t derivedLen, byte p for (i=0; i