diff --git a/cryptlib.cpp b/cryptlib.cpp index 2c67cd74..d7fa4df0 100644 --- a/cryptlib.cpp +++ b/cryptlib.cpp @@ -333,11 +333,6 @@ void RandomNumberGenerator::GenerateIntoBufferedTransformation(BufferedTransform } } -const Algorithm & KeyDerivationFunction::GetAlgorithm() const -{ - return *this; -} - size_t KeyDerivationFunction::MinDerivedLength() const { return 0; diff --git a/cryptlib.h b/cryptlib.h index 35952ef9..3b143799 100644 --- a/cryptlib.h +++ b/cryptlib.h @@ -1419,8 +1419,6 @@ class CRYPTOPP_DLL CRYPTOPP_NO_VTABLE KeyDerivationFunction : public Algorithm public: virtual ~KeyDerivationFunction() {} - virtual const Algorithm & GetAlgorithm() const =0; - /// \brief Provides the name of this algorithm /// \return the standard algorithm name virtual std::string AlgorithmName() const =0; @@ -1452,15 +1450,14 @@ public: /// \param secret the seed input buffer /// \param secretLen the size of the secret buffer, in bytes /// \param params additional initialization parameters to configure this object - /// \returns the number of bytes derived + /// \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 secret seed and other parameters. Each class that derives from KeyDerivationFunction /// provides an overload that accepts most parameters used by the derivation function. - /// \details the number of bytes derived by DeriveKey() may be less than the number - /// requested in derivedLen. For example, a scheme may be limited to a - /// certain amount of time for derivation. - virtual size_t DeriveKey(byte *derived, size_t derivedLen, const byte *secret, size_t secretLen, const NameValuePairs& params) const =0; + /// \details the number of iterations performed by DeriveKey() may be 1. For example, a + // scheme like HKDF does not use the iteration count so it returns 1. + virtual size_t DeriveKey(byte *derived, size_t derivedLen, const byte *secret, size_t secretLen, const NameValuePairs& params = g_nullNameValuePairs) const =0; /// \brief Set or change parameters /// \param params additional initialization parameters to configure this object @@ -1469,6 +1466,9 @@ public: virtual void SetParameters(const NameValuePairs& params); protected: + /// \brief Returns the base class Algorithm + /// \return the base class Algorithm + virtual const Algorithm & GetAlgorithm() const =0; /// \brief Validates the derived key length /// \param length the size of the derived key material, in bytes diff --git a/hkdf.h b/hkdf.h index 0a09f2fa..97a70b7f 100644 --- a/hkdf.h +++ b/hkdf.h @@ -30,10 +30,6 @@ public: return name; } - const Algorithm & GetAlgorithm() const { - return *this; - } - std::string AlgorithmName() const { return StaticAlgorithmName(); } @@ -57,18 +53,26 @@ public: /// \param saltLen the size of the salt buffer, in bytes /// \param info the additional input buffer /// \param infoLen the size of the info buffer, in bytes + /// \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 salt and info can be nullptr with 0 length. - /// HDF is unusual in that a non-NULL salt with length 0 is different than a - /// NULL salt. A NULL salt causes HDF to use a string of 0's + /// HKDF is unusual in that a non-NULL salt with length 0 is different than a + /// NULL salt. A NULL salt causes HKDF to use a string of 0's /// of length T::DIGESTSIZE for the salt. + /// \details HKDF always returns 1 because it only performs 1 iteration. Other + /// derivation functions, like PBKDF's, will return more interesting values. size_t DeriveKey(byte *derived, size_t derivedLen, const byte *secret, size_t secretLen, const byte *salt, size_t saltLen, const byte* info, size_t infoLen) const; protected: + // KeyDerivationFunction interface + const Algorithm & GetAlgorithm() const { + return *this; + } + // If salt is absent (NULL), then use the NULL vector. Missing is different than // EMPTY (Non-NULL, 0 length). The length of s_NullVector used depends on the Hash // function. SHA-256 will use 32 bytes of s_NullVector. @@ -110,56 +114,53 @@ size_t HKDF::DeriveKey(byte *derived, size_t derivedLen, p = ConstByteArrayParameter(GetNullVector(), 0); SecByteBlock info(p.begin(), p.size()); - // key is PRK from the RFC, salt is IKM from the RFC - HMAC hmac; - SecByteBlock key(T::DIGESTSIZE), buffer(T::DIGESTSIZE); - - // Extract - hmac.SetKey(salt.begin(), salt.size()); - hmac.CalculateDigest(key, secret, secretLen); - - // Key - hmac.SetKey(key.begin(), key.size()); - byte block = 0; - - size_t bytesRemaining = derivedLen; - size_t digestSize = static_cast(T::DIGESTSIZE); - - // Expand - while (bytesRemaining > 0) - { - if (block++) {hmac.Update(buffer, buffer.size());} - if (info.size()) {hmac.Update(info.begin(), info.size());} - hmac.CalculateDigest(buffer, &block, 1); - -#if CRYPTOPP_MSC_VERSION - const size_t segmentLen = STDMIN(bytesRemaining, digestSize); - memcpy_s(derived, segmentLen, buffer, segmentLen); -#else - const size_t segmentLen = STDMIN(bytesRemaining, digestSize); - std::memcpy(derived, buffer, segmentLen); -#endif - - derived += segmentLen; - bytesRemaining -= segmentLen; - } - - return derivedLen; + return DeriveKey(derived, derivedLen, secret, secretLen, salt.begin(), salt.size(), info.begin(), info.size()); } template size_t HKDF::DeriveKey(byte *derived, size_t derivedLen, const byte *secret, size_t secretLen, const byte *salt, size_t saltLen, const byte* info, size_t infoLen) const { - AlgorithmParameters params; + CRYPTOPP_ASSERT(secret && secretLen); + CRYPTOPP_ASSERT(derived && derivedLen); + CRYPTOPP_ASSERT(derivedLen <= MaxDerivedLength()); - if (salt != NULLPTR) // Non-NULL and 0 length is valid for HKDF salt - params.operator()(Name::Salt(), ConstByteArrayParameter(salt, saltLen)); + ThrowIfInvalidDerivedLength(derivedLen); - if (info != NULLPTR) // Non-NULL and 0 length is valid for HKDF salt - params.operator()("Info", ConstByteArrayParameter(info, infoLen)); + // key is PRK from the RFC, salt is IKM from the RFC + HMAC hmac; + SecByteBlock key(T::DIGESTSIZE), buffer(T::DIGESTSIZE); - return DeriveKey(derived, derivedLen, secret, secretLen, params); + // Extract + hmac.SetKey(salt, saltLen); + hmac.CalculateDigest(key, secret, secretLen); + + // Key + hmac.SetKey(key.begin(), key.size()); + byte block = 0; + + // Expand + while (derivedLen > 0) + { + if (block++) {hmac.Update(buffer, buffer.size());} + if (infoLen) {hmac.Update(info, infoLen);} + hmac.CalculateDigest(buffer, &block, 1); + +#if CRYPTOPP_MSC_VERSION + const size_t digestSize = static_cast(T::DIGESTSIZE); + const size_t segmentLen = STDMIN(derivedLen, digestSize); + memcpy_s(derived, segmentLen, buffer, segmentLen); +#else + const size_t digestSize = static_cast(T::DIGESTSIZE); + const size_t segmentLen = STDMIN(derivedLen, digestSize); + std::memcpy(derived, buffer, segmentLen); +#endif + + derived += segmentLen; + derivedLen -= segmentLen; + } + + return 1; } NAMESPACE_END 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() {} -/// \brief PBKDF2 from PKCS #5 -/// \tparam T a HashTransformation class -template -class PKCS5_PBKDF2_HMAC : public PasswordBasedKeyDerivationFunction -{ -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; -}; + static std::string StaticAlgorithmName () { + const std::string name(std::string("PBKDF1(") + + std::string(T::StaticAlgorithmName()) + std::string(")")); + return name; + } -/* -class PBKDF2Params -{ -public: - SecByteBlock m_salt; - unsigned int m_interationCount; - ASNOptional > m_keyLength; + // 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; + } }; -*/ 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::GetValidDerivedLength(size_t keylength) const { - CRYPTOPP_UNUSED(purpose); + if (keylength > MaxDerivedLength()) + return MaxDerivedLength(); + return keylength; +} + +template +size_t PKCS5_PBKDF1::DeriveKey(byte *derived, size_t derivedLen, + const byte *secret, size_t secretLen, const NameValuePairs& params) const +{ + return derivedLen; +} + +template +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_ASSERT(derived && derivedLen); + CRYPTOPP_ASSERT(secret && secretLen); CRYPTOPP_ASSERT(derivedLen <= MaxDerivedKeyLength()); CRYPTOPP_ASSERT(iterations > 0 || timeInSeconds > 0); + CRYPTOPP_UNUSED(purpose); - if (!iterations) - iterations = 1; + ThrowIfInvalidDerivedLength(derivedLen); + + // 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