diff options
Diffstat (limited to 'CryptoPP/pwdbased.h')
-rw-r--r-- | CryptoPP/pwdbased.h | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/CryptoPP/pwdbased.h b/CryptoPP/pwdbased.h new file mode 100644 index 000000000..f755724b1 --- /dev/null +++ b/CryptoPP/pwdbased.h @@ -0,0 +1,214 @@ +// pwdbased.h - written and placed in the public domain by Wei Dai + +#ifndef CRYPTOPP_PWDBASED_H +#define CRYPTOPP_PWDBASED_H + +#include "cryptlib.h" +#include "hmac.h" +#include "hrtimer.h" +#include "integer.h" + +NAMESPACE_BEGIN(CryptoPP) + +//! abstract base class for password based key derivation function +class PasswordBasedKeyDerivationFunction +{ +public: + virtual size_t MaxDerivedKeyLength() const =0; + virtual bool UsesPurposeByte() const =0; + //! derive key from password + /*! If timeInSeconds != 0, will iterate until time elapsed, as measured by ThreadUserTimer + Returns actual iteration count, which is equal to iterations if timeInSeconds == 0, and not less than iterations otherwise. */ + 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 from PKCS #5, T should be a HashTransformation class +template <class T> +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; +}; + +//! PBKDF2 from PKCS #5, T should be a HashTransformation class +template <class T> +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; +}; + +/* +class PBKDF2Params +{ +public: + SecByteBlock m_salt; + unsigned int m_interationCount; + ASNOptional<ASNUnsignedWrapper<word32> > m_keyLength; +}; +*/ + +template <class T> +unsigned int PKCS5_PBKDF1<T>::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 +{ + assert(derivedLen <= MaxDerivedKeyLength()); + assert(iterations > 0 || timeInSeconds > 0); + + if (!iterations) + iterations = 1; + + T hash; + hash.Update(password, passwordLen); + hash.Update(salt, saltLen); + + SecByteBlock buffer(hash.DigestSize()); + hash.Final(buffer); + + unsigned int i; + ThreadUserTimer timer; + + if (timeInSeconds) + timer.StartTimer(); + + for (i=1; i<iterations || (timeInSeconds && (i%128!=0 || timer.ElapsedTimeAsDouble() < timeInSeconds)); i++) + hash.CalculateDigest(buffer, buffer, buffer.size()); + + memcpy(derived, buffer, derivedLen); + return i; +} + +template <class T> +unsigned int PKCS5_PBKDF2_HMAC<T>::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 +{ + assert(derivedLen <= MaxDerivedKeyLength()); + assert(iterations > 0 || timeInSeconds > 0); + + if (!iterations) + iterations = 1; + + HMAC<T> hmac(password, passwordLen); + SecByteBlock buffer(hmac.DigestSize()); + ThreadUserTimer timer; + + unsigned int i=1; + while (derivedLen > 0) + { + hmac.Update(salt, saltLen); + unsigned int j; + for (j=0; j<4; j++) + { + byte b = byte(i >> ((3-j)*8)); + hmac.Update(&b, 1); + } + hmac.Final(buffer); + + size_t segmentLen = STDMIN(derivedLen, buffer.size()); + memcpy(derived, buffer, segmentLen); + + if (timeInSeconds) + { + timeInSeconds = timeInSeconds / ((derivedLen + buffer.size() - 1) / buffer.size()); + timer.StartTimer(); + } + + for (j=1; j<iterations || (timeInSeconds && (j%128!=0 || timer.ElapsedTimeAsDouble() < timeInSeconds)); j++) + { + hmac.CalculateDigest(buffer, buffer, buffer.size()); + xorbuf(derived, buffer, segmentLen); + } + + if (timeInSeconds) + { + iterations = j; + timeInSeconds = 0; + } + + derived += segmentLen; + derivedLen -= segmentLen; + i++; + } + + return iterations; +} + +//! PBKDF from PKCS #12, appendix B, T should be a HashTransformation class +template <class T> +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; +}; + +template <class T> +unsigned int PKCS12_PBKDF<T>::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 +{ + assert(derivedLen <= MaxDerivedKeyLength()); + assert(iterations > 0 || timeInSeconds > 0); + + 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; + SecByteBlock buffer(DLen + SLen + PLen); + byte *D = buffer, *S = buffer+DLen, *P = buffer+DLen+SLen, *I = S; + + memset(D, purpose, DLen); + size_t i; + for (i=0; i<SLen; i++) + S[i] = salt[i % saltLen]; + for (i=0; i<PLen; i++) + P[i] = password[i % passwordLen]; + + + T hash; + SecByteBlock Ai(T::DIGESTSIZE), B(v); + ThreadUserTimer timer; + + while (derivedLen > 0) + { + hash.CalculateDigest(Ai, buffer, buffer.size()); + + if (timeInSeconds) + { + timeInSeconds = timeInSeconds / ((derivedLen + Ai.size() - 1) / Ai.size()); + timer.StartTimer(); + } + + for (i=1; i<iterations || (timeInSeconds && (i%128!=0 || timer.ElapsedTimeAsDouble() < timeInSeconds)); i++) + hash.CalculateDigest(Ai, Ai, Ai.size()); + + if (timeInSeconds) + { + iterations = (unsigned int)i; + timeInSeconds = 0; + } + + for (i=0; i<B.size(); i++) + B[i] = Ai[i % Ai.size()]; + + Integer B1(B, B.size()); + ++B1; + for (i=0; i<ILen; i+=v) + (Integer(I+i, v) + B1).Encode(I+i, v); + + size_t segmentLen = STDMIN(derivedLen, Ai.size()); + memcpy(derived, Ai, segmentLen); + derived += segmentLen; + derivedLen -= segmentLen; + } + + return iterations; +} + +NAMESPACE_END + +#endif |