From 64d02e3a18ab253ca7ee2a508a453ba85e9184b6 Mon Sep 17 00:00:00 2001 From: Jeffrey Walton Date: Sat, 31 Mar 2018 20:09:38 -0400 Subject: [PATCH] Add scrypt key derivation function (GH #613, PR #626) --- Filelist.txt | 2 + cryptlib.vcxproj | 4 +- cryptlib.vcxproj.filters | 8 +- pwdbased.h | 1 + scrypt.cpp | 312 +++++++++++++++++++++++++++++++++++++++ scrypt.h | 96 ++++++++++++ test.cpp | 47 +++--- validat1.cpp | 1 + validat3.cpp | 88 +++++++++-- validate.h | 1 + 10 files changed, 526 insertions(+), 34 deletions(-) create mode 100644 scrypt.cpp create mode 100644 scrypt.h diff --git a/Filelist.txt b/Filelist.txt index b679a6cf..50882710 100644 --- a/Filelist.txt +++ b/Filelist.txt @@ -253,6 +253,8 @@ safer.cpp safer.h salsa.cpp salsa.h +scrypt.cpp +scrypt.h seal.cpp seal.h secblock.h diff --git a/cryptlib.vcxproj b/cryptlib.vcxproj index 0d4ab1cd..f57d8fe1 100644 --- a/cryptlib.vcxproj +++ b/cryptlib.vcxproj @@ -278,6 +278,7 @@ + @@ -468,6 +469,7 @@ + @@ -515,4 +517,4 @@ - + \ No newline at end of file diff --git a/cryptlib.vcxproj.filters b/cryptlib.vcxproj.filters index 04dec87d..e3060ef0 100644 --- a/cryptlib.vcxproj.filters +++ b/cryptlib.vcxproj.filters @@ -329,6 +329,9 @@ Source Files + + Source Files + Source Files @@ -801,6 +804,9 @@ Header Files + + Header Files + Header Files @@ -939,4 +945,4 @@ Miscellaneous - + \ No newline at end of file diff --git a/pwdbased.h b/pwdbased.h index 499d932a..06a4b025 100644 --- a/pwdbased.h +++ b/pwdbased.h @@ -11,6 +11,7 @@ #include "cryptlib.h" #include "hrtimer.h" #include "integer.h" +#include "argnames.h" #include "hmac.h" NAMESPACE_BEGIN(CryptoPP) diff --git a/scrypt.cpp b/scrypt.cpp new file mode 100644 index 00000000..17a1e7be --- /dev/null +++ b/scrypt.cpp @@ -0,0 +1,312 @@ +// scrypt.cpp - written and placed in public domain by Jeffrey Walton. +// Based on reference source code by Colin Percival and Simon Josefsson. + +#include "pch.h" + +#include "scrypt.h" +#include "argnames.h" +#include "pwdbased.h" +#include "stdcpp.h" +#include "salsa.h" +#include "misc.h" +#include "sha.h" + +#ifdef _OPENMP +# include +#endif + +#include +#include +#include + +ANONYMOUS_NAMESPACE_BEGIN + +using CryptoPP::rotlConstant; +using CryptoPP::AlignedSecByteBlock; +using CryptoPP::LITTLE_ENDIAN_ORDER; +using CryptoPP::ConditionalByteReverse; + +static inline void LE32ENC(uint8_t* out, uint32_t in) +{ + uint32_t* ptr = reinterpret_cast(out); + ConditionalByteReverse(LITTLE_ENDIAN_ORDER, ptr, &in, 4); +} + +static inline uint32_t LE32DEC(const uint8_t* in) +{ + uint32_t res; + const uint32_t* ptr = reinterpret_cast(in); + ConditionalByteReverse(LITTLE_ENDIAN_ORDER, &res, ptr, 4); + return res; +} + +static inline uint64_t LE64DEC(const uint8_t* in) +{ + uint64_t res; + const uint64_t* ptr = reinterpret_cast(in); + ConditionalByteReverse(LITTLE_ENDIAN_ORDER, &res, ptr, 8); + return res; +} + +static inline void BlockCopy(uint8_t * dest, uint8_t * src, size_t len) +{ + for (size_t i = 0; i < len; i++) + dest[i] = src[i]; +} + +static inline void BlockXOR(uint8_t * dest, uint8_t * src, size_t len) +{ + for (size_t i = 0; i < len; i++) + dest[i] ^= src[i]; +} + +static inline void PBKDF2_SHA256(uint8_t * buf, size_t dkLen, + const uint8_t * passwd, size_t passwdlen, + const uint8_t * salt, size_t saltlen, uint8_t count) +{ + using CryptoPP::SHA256; + using CryptoPP::PKCS5_PBKDF2_HMAC; + + PKCS5_PBKDF2_HMAC pbkdf; + pbkdf.DeriveKey(buf, dkLen, 0, passwd, passwdlen, salt, saltlen, count, 0.0f); +} + +static inline void Salsa20_8(uint8_t B[64]) +{ + uint32_t B32[16], x[16]; + size_t i = 0; + + for (i = 0; i < 16; i++) + B32[i] = LE32DEC(&B[i * 4]); + + for (i = 0; i < 16; i++) + x[i] = B32[i]; + + for (i = 0; i < 8; i += 2) + { + x[ 4] ^= rotlConstant< 7>(x[ 0]+x[12]); + x[ 8] ^= rotlConstant< 9>(x[ 4]+x[ 0]); + x[12] ^= rotlConstant<13>(x[ 8]+x[ 4]); + x[ 0] ^= rotlConstant<18>(x[12]+x[ 8]); + + x[ 9] ^= rotlConstant< 7>(x[ 5]+x[ 1]); + x[13] ^= rotlConstant< 9>(x[ 9]+x[ 5]); + x[ 1] ^= rotlConstant<13>(x[13]+x[ 9]); + x[ 5] ^= rotlConstant<18>(x[ 1]+x[13]); + + x[14] ^= rotlConstant< 7>(x[10]+x[ 6]); + x[ 2] ^= rotlConstant< 9>(x[14]+x[10]); + x[ 6] ^= rotlConstant<13>(x[ 2]+x[14]); + x[10] ^= rotlConstant<18>(x[ 6]+x[ 2]); + + x[ 3] ^= rotlConstant< 7>(x[15]+x[11]); + x[ 7] ^= rotlConstant< 9>(x[ 3]+x[15]); + x[11] ^= rotlConstant<13>(x[ 7]+x[ 3]); + x[15] ^= rotlConstant<18>(x[11]+x[ 7]); + + x[ 1] ^= rotlConstant< 7>(x[ 0]+x[ 3]); + x[ 2] ^= rotlConstant< 9>(x[ 1]+x[ 0]); + x[ 3] ^= rotlConstant<13>(x[ 2]+x[ 1]); + x[ 0] ^= rotlConstant<18>(x[ 3]+x[ 2]); + + x[ 6] ^= rotlConstant< 7>(x[ 5]+x[ 4]); + x[ 7] ^= rotlConstant< 9>(x[ 6]+x[ 5]); + x[ 4] ^= rotlConstant<13>(x[ 7]+x[ 6]); + x[ 5] ^= rotlConstant<18>(x[ 4]+x[ 7]); + + x[11] ^= rotlConstant< 7>(x[10]+x[ 9]); + x[ 8] ^= rotlConstant< 9>(x[11]+x[10]); + x[ 9] ^= rotlConstant<13>(x[ 8]+x[11]); + x[10] ^= rotlConstant<18>(x[ 9]+x[ 8]); + + x[12] ^= rotlConstant< 7>(x[15]+x[14]); + x[13] ^= rotlConstant< 9>(x[12]+x[15]); + x[14] ^= rotlConstant<13>(x[13]+x[12]); + x[15] ^= rotlConstant<18>(x[14]+x[13]); + } + + for (i = 0; i < 16; i++) + B32[i] += x[i]; + + for (i = 0; i < 16; i++) + LE32ENC(&B[4 * i], B32[i]); +} + +static inline void BlockMix(uint8_t * B, uint8_t * Y, size_t r) +{ + uint8_t X[64]; + size_t i; + + // 1: X <-- B_{2r - 1} + BlockCopy(X, &B[(2 * r - 1) * 64], 64); + + // 2: for i = 0 to 2r - 1 do + for (i = 0; i < 2 * r; i++) + { + // 3: X <-- H(X \xor B_i) + BlockXOR(X, &B[i * 64], 64); + Salsa20_8(X); + + // 4: Y_i <-- X + BlockCopy(&Y[i * 64], X, 64); + } + + // 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) + for (i = 0; i < r; i++) + BlockCopy(&B[i * 64], &Y[(i * 2) * 64], 64); + for (i = 0; i < r; i++) + BlockCopy(&B[(i + r) * 64], &Y[(i * 2 + 1) * 64], 64); +} + +static inline uint64_t Integerify(uint8_t * B, size_t r) +{ + uint8_t * X = &B[(2 * r - 1) * 64]; + return LE64DEC(X); +} + +static inline void Smix(uint8_t * B, size_t r, uint64_t N, uint8_t * V, uint8_t * XY) +{ + uint8_t * X = XY; + uint8_t * Y = XY+128*r; + uint64_t i, j; + + // 1: X <-- B + BlockCopy(X, B, 128 * r); + + // 2: for i = 0 to N - 1 do + for (i = 0; i < N; i++) + { + // 3: V_i <-- X + BlockCopy(&V[i * (128 * r)], X, 128 * r); + + // 4: X <-- H(X) + BlockMix(X, Y, r); + } + + // 6: for i = 0 to N - 1 do + for (i = 0; i < N; i++) { + // 7: j <-- Integerify(X) mod N + j = Integerify(X, r) & (N - 1); + + // 8: X <-- H(X \xor V_j) + BlockXOR(X, &V[j * (128 * r)], 128 * r); + BlockMix(X, Y, r); + } + + // 10: B' <-- X + BlockCopy(B, X, 128 * r); +} +ANONYMOUS_NAMESPACE_END + +NAMESPACE_BEGIN(CryptoPP) + +size_t Scrypt::GetValidDerivedLength(size_t keylength) const +{ + if (keylength > MaxDerivedLength()) + return MaxDerivedLength(); + return keylength; +} + +void Scrypt::ValidateParameters(size_t derivedLen, word64 cost, word64 blockSize, word64 parallelization) const +{ + // Optimizer should remove this on 64-bit platforms + if (std::numeric_limits::max() > std::numeric_limits::max()) + { + const uint64_t maxLen = ((static_cast(1) << 32) - 1) * 32; + if (derivedLen > maxLen) { + std::ostringstream oss; + oss << "derivedLen " << derivedLen << " is larger than " << maxLen; + throw InvalidArgument("Scrypt: " + oss.str()); + } + } + + if (IsPowerOf2(cost) == false) + throw InvalidArgument("Scrypt: cost must be a power of 2"); + + const uint64_t prod = static_cast(blockSize) * parallelization; + if (prod >= (1U << 30)) { + std::ostringstream oss; + oss << "r*p " << prod << " is larger than " << (1U << 30); + throw InvalidArgument("Scrypt: " + oss.str()); + } + + // Scrypt has several tests that effectively verify allocations like + // '128 * r * N' and '128 * r * p' do not overflow. They are the tests + // that set errno to ENOMEM. We can make the logic a little more clear + // using word128. At first blush the word128 may seem like overkill. + // However, this alogirthm is dominated by slow moving parts, so a + // one-time check is insignificant in the bigger picture. +#if defined(CRYPTOPP_WORD128_AVAILABLE) + const word128 maxElems = static_cast(SIZE_MAX); + bool bLimit = (maxElems >= static_cast(cost) * blockSize * 128U); + bool xyLimit = (maxElems >= static_cast(parallelization) * blockSize * 128U); + bool vLimit = (maxElems >= static_cast(blockSize) * 256U + 64U); + if (!bLimit || !xyLimit || !vLimit) + throw std::bad_alloc(); +#else + const word64 maxElems = static_cast(SIZE_MAX); + bool bLimit = (blockSize < maxElems / 128U / cost); + bool xyLimit = (blockSize < maxElems / 128U / parallelization); + bool vLimit = (blockSize < (maxElems - 64U) / 256U); + + if (!bLimit || !xyLimit || !vLimit) + throw std::bad_alloc(); +#endif +} + +size_t Scrypt::DeriveKey(byte *derived, size_t derivedLen, + const byte *secret, size_t secretLen, const NameValuePairs& params) const +{ + CRYPTOPP_ASSERT(secret /*&& secretLen*/); + CRYPTOPP_ASSERT(derived && derivedLen); + CRYPTOPP_ASSERT(derivedLen <= MaxDerivedLength()); + + word64 cost=0, blockSize=0, parallelization=0; + + if(params.GetValue("Cost", cost) == false) + cost = defaultCost; + + if(params.GetValue("BlockSize", blockSize) == false) + blockSize = defaultBlockSize; + + if(params.GetValue("Parallelization", parallelization) == false) + parallelization = defaultParallelization; + + ConstByteArrayParameter salt; + (void)params.GetValue("Salt", salt); + + return DeriveKey(derived, derivedLen, secret, secretLen, salt.begin(), salt.size(), cost, blockSize, parallelization); +} + +size_t Scrypt::DeriveKey(byte *derived, size_t derivedLen, const byte *secret, size_t secretLen, + const byte *salt, size_t saltLen, word64 cost, word64 blockSize, word64 parallel) const +{ + CRYPTOPP_ASSERT(secret /*&& secretLen*/); + CRYPTOPP_ASSERT(derived && derivedLen); + CRYPTOPP_ASSERT(derivedLen <= MaxDerivedLength()); + CRYPTOPP_ASSERT(IsPowerOf2(cost)); + + ThrowIfInvalidDerivedLength(derivedLen); + ValidateParameters(derivedLen, cost, blockSize, parallel); + + AlignedSecByteBlock B(static_cast(blockSize * parallel * 128U)); + AlignedSecByteBlock XY(static_cast(blockSize * 256U)); + AlignedSecByteBlock V(static_cast(blockSize * cost * 128U)); + + // 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) + PBKDF2_SHA256(B, B.size(), secret, secretLen, salt, saltLen, 1); + + // 2: for i = 0 to p - 1 do + for (unsigned int i = 0; i < parallel; i++) + { + // 3: B_i <-- MF(B_i, N) + Smix(B+static_cast(blockSize*i*128), static_cast(blockSize), cost, V, XY); + } + + // 5: DK <-- PBKDF2(P, B, 1, dkLen) + PBKDF2_SHA256(derived, derivedLen, secret, secretLen, B, static_cast(blockSize*parallel*128), 1); + + return 1; +} + +NAMESPACE_END diff --git a/scrypt.h b/scrypt.h new file mode 100644 index 00000000..ef63fb1a --- /dev/null +++ b/scrypt.h @@ -0,0 +1,96 @@ +// scrypt.h - written and placed in public domain by Jeffrey Walton. +// Based on reference source code by Colin Percival and Simon Josefsson. + +/// \file scrypt.h +/// \brief Classes for Scrypt from RFC 7914 +/// \sa Stronger Key Derivation via +/// Sequential Memory-Hard Functions, +/// The scrypt key derivation function +/// and RFC 7914, The scrypt Password-Based +/// Key Derivation Function +/// \since Crypto++ 6.2 + +#ifndef CRYPTOPP_SCRYPT_H +#define CRYPTOPP_SCRYPT_H + +#include "cryptlib.h" +#include "secblock.h" +#include "algparam.h" + +NAMESPACE_BEGIN(CryptoPP) + +/// \brief Scrypt key derivation function +/// \sa The scrypt key derivation function +/// and RFC 7914, The scrypt Password-Based +/// Key Derivation Function +/// \since Crypto++ 6.2 +class Scrypt : public KeyDerivationFunction +{ +public: + virtual ~Scrypt() {} + + static std::string StaticAlgorithmName () { + return "scrypt"; + } + + // KeyDerivationFunction interface + std::string AlgorithmName() const { + return StaticAlgorithmName(); + } + + // KeyDerivationFunction interface + size_t MaxDerivedLength() 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) const; + + /// \brief Derive a key from a seed + /// \param derived the derived output buffer + /// \param derivedLen the size of the derived buffer, in bytes + /// \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 cost the CPU/memory cost factor + /// \param blockSize the block size + /// \param parallelization the parallelization factor + /// \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 The CPU/Memory cost parameter ("N" in the documents) must be + /// larger than 1, a power of 2, and less than 2^(128 * r / 8). + /// \details The parameter blockSize ("r" in the documents) specifies the block + /// size. + /// \details The parallelization parameter ("p" in the documents) is a positive + /// integer less than or equal to ((2^32-1) * 32) / (128 * r). + /// \details Crypto++ uses size_t for its size datatype, and limits are + /// based on the 32-bit version of size_t. For example, cost is + /// limited to 0xffffffff instead of 2^(128 * r / 8). + /// \details Scrypt 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, word64 cost=2, word64 blockSize=8, word64 parallelization=1) const; + +protected: + enum {defaultCost=2, defaultBlockSize=8, defaultParallelization=1}; + + // KeyDerivationFunction interface + const Algorithm & GetAlgorithm() const { + return *this; + } + + inline void ValidateParameters(size_t derivedlen, word64 cost, word64 blockSize, word64 parallelization) const; +}; + +NAMESPACE_END + +#endif // CRYPTOPP_SCRYPT_H diff --git a/test.cpp b/test.cpp index 3568048d..109ca6c6 100644 --- a/test.cpp +++ b/test.cpp @@ -925,29 +925,30 @@ bool Validate(int alg, bool thorough, const char *seedInput) case 56: result = ValidateAdler32(); break; case 57: result = ValidateMD4(); break; case 58: result = ValidatePBKDF(); break; - case 59: result = ValidateESIGN(); break; - case 60: result = ValidateDLIES(); break; - case 61: result = ValidateBaseCode(); break; - case 62: result = ValidateSHACAL2(); break; - case 63: result = ValidateARIA(); break; - case 64: result = ValidateCamellia(); break; - case 65: result = ValidateWhirlpool(); break; - case 66: result = ValidateTTMAC(); break; - case 67: result = ValidateSalsa(); break; - case 68: result = ValidateSosemanuk(); break; - case 69: result = ValidateVMAC(); break; - case 70: result = ValidateCCM(); break; - case 71: result = ValidateGCM(); break; - case 72: result = ValidateCMAC(); break; - case 73: result = ValidateHKDF(); break; - case 74: result = ValidateSM3(); break; - case 75: result = ValidateBLAKE2s(); break; - case 76: result = ValidateBLAKE2b(); break; - case 77: result = ValidatePoly1305(); break; - case 78: result = ValidateSipHash(); break; - case 79: result = ValidateHashDRBG(); break; - case 80: result = ValidateHmacDRBG(); break; - case 90: result = ValidateNaCl(); break; + case 59: result = ValidateHKDF(); break; + case 60: result = ValidateScrypt(); break; + case 61: result = ValidateESIGN(); break; + case 62: result = ValidateDLIES(); break; + case 63: result = ValidateBaseCode(); break; + case 64: result = ValidateSHACAL2(); break; + case 65: result = ValidateARIA(); break; + case 66: result = ValidateCamellia(); break; + case 67: result = ValidateWhirlpool(); break; + case 68: result = ValidateTTMAC(); break; + case 69: result = ValidateSalsa(); break; + case 70: result = ValidateSosemanuk(); break; + case 71: result = ValidateVMAC(); break; + case 72: result = ValidateCCM(); break; + case 73: result = ValidateGCM(); break; + case 74: result = ValidateCMAC(); break; + case 75: result = ValidateSM3(); break; + case 76: result = ValidateBLAKE2s(); break; + case 77: result = ValidateBLAKE2b(); break; + case 78: result = ValidatePoly1305(); break; + case 79: result = ValidateSipHash(); break; + case 80: result = ValidateHashDRBG(); break; + case 81: result = ValidateHmacDRBG(); break; + case 82: result = ValidateNaCl(); break; #if defined(CRYPTOPP_EXTENDED_VALIDATION) // http://github.com/weidai11/cryptopp/issues/92 diff --git a/validat1.cpp b/validat1.cpp index f4f30de8..e87b24c2 100644 --- a/validat1.cpp +++ b/validat1.cpp @@ -146,6 +146,7 @@ bool ValidateAll(bool thorough) pass=ValidatePBKDF() && pass; pass=ValidateHKDF() && pass; + pass=ValidateScrypt() && pass; pass=ValidateDES() && pass; pass=ValidateCipherModes() && pass; diff --git a/validat3.cpp b/validat3.cpp index add46a89..f7a377ee 100644 --- a/validat3.cpp +++ b/validat3.cpp @@ -28,6 +28,7 @@ #include "ttmac.h" #include "integer.h" #include "pwdbased.h" +#include "scrypt.h" #include "filters.h" #include "files.h" #include "hex.h" @@ -671,9 +672,9 @@ bool TestHKDF(KeyDerivationFunction &kdf, const HKDF_TestTuple *testSet, unsigne AlgorithmParameters params; if (tuple.hexSalt) - params.operator()(Name::Salt(), ConstByteArrayParameter((const byte*)&salt[0], salt.size())); + params(Name::Salt(), ConstByteArrayParameter((const byte*)&salt[0], salt.size())); if (tuple.hexSalt) - params.operator()("Info", ConstByteArrayParameter((const byte*)&info[0], info.size())); + params("Info", ConstByteArrayParameter((const byte*)&info[0], info.size())); kdf.DeriveKey((byte*)&derived[0], derived.size(), (const byte*)&secret[0], secret.size(), params); @@ -700,7 +701,7 @@ bool ValidateHKDF() { // SHA-1 from RFC 5869, Appendix A, https://tools.ietf.org/html/rfc5869 - static const HKDF_TestTuple testSet[] = + const HKDF_TestTuple testSet[] = { // Test Case #4 {"0b0b0b0b0b0b0b0b0b0b0b", "000102030405060708090a0b0c", "f0f1f2f3f4f5f6f7f8f9", "085a01ea1b10f36933068b56efa5ad81 a4f14b822f5b091568a9cdd4f155fda2 c22e422478d305f3f896", 42}, @@ -720,7 +721,7 @@ bool ValidateHKDF() { // SHA-256 from RFC 5869, Appendix A, https://tools.ietf.org/html/rfc5869 - static const HKDF_TestTuple testSet[] = + const HKDF_TestTuple testSet[] = { // Test Case #1 {"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", "000102030405060708090a0b0c", "f0f1f2f3f4f5f6f7f8f9", "3cb25f25faacd57a90434f64d0362f2a 2d2d0a90cf1a5a4c5db02d56ecc4c5bf 34007208d5b887185865", 42}, @@ -738,7 +739,7 @@ bool ValidateHKDF() { // SHA-512, Crypto++ generated, based on RFC 5869, https://tools.ietf.org/html/rfc5869 - static const HKDF_TestTuple testSet[] = + const HKDF_TestTuple testSet[] = { // Test Case #0 {"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", "000102030405060708090a0b0c", "f0f1f2f3f4f5f6f7f8f9", "832390086CDA71FB47625BB5CEB168E4 C8E26A1A16ED34D9FC7FE92C14815793 38DA362CB8D9F925D7CB", 42}, @@ -758,7 +759,7 @@ bool ValidateHKDF() { // Whirlpool, Crypto++ generated, based on RFC 5869, https://tools.ietf.org/html/rfc5869 - static const HKDF_TestTuple testSet[] = + const HKDF_TestTuple testSet[] = { // Test Case #0 {"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", "000102030405060708090a0b0c", "f0f1f2f3f4f5f6f7f8f9", "0D29F74CCD8640F44B0DD9638111C1B5 766EFED752AF358109E2E7C9CD4A28EF 2F90B2AD461FBA0744D4", 42}, @@ -779,6 +780,75 @@ bool ValidateHKDF() return pass; } +struct Scrypt_TestTuple +{ + const char * passwd; + const char * salt; + uint64_t n; + uint32_t r; + uint32_t p; + const char * expect; +}; + +bool TestScrypt(KeyDerivationFunction &pbkdf, const Scrypt_TestTuple *testSet, unsigned int testSetSize) +{ + bool pass = true; + + for (unsigned int i=0; i