Add scrypt key derivation function (GH #613, PR #626)

pull/627/head
Jeffrey Walton 2018-03-31 20:09:38 -04:00 committed by GitHub
parent 8d81492f88
commit 64d02e3a18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 526 additions and 34 deletions

View File

@ -253,6 +253,8 @@ safer.cpp
safer.h safer.h
salsa.cpp salsa.cpp
salsa.h salsa.h
scrypt.cpp
scrypt.h
seal.cpp seal.cpp
seal.h seal.h
secblock.h secblock.h

View File

@ -278,6 +278,7 @@
<ClCompile Include="rw.cpp" /> <ClCompile Include="rw.cpp" />
<ClCompile Include="safer.cpp" /> <ClCompile Include="safer.cpp" />
<ClCompile Include="salsa.cpp" /> <ClCompile Include="salsa.cpp" />
<ClCompile Include="scrypt.cpp" />
<ClCompile Include="seal.cpp" /> <ClCompile Include="seal.cpp" />
<ClCompile Include="seed.cpp" /> <ClCompile Include="seed.cpp" />
<ClCompile Include="serpent.cpp" /> <ClCompile Include="serpent.cpp" />
@ -468,6 +469,7 @@
<ClInclude Include="rw.h" /> <ClInclude Include="rw.h" />
<ClInclude Include="safer.h" /> <ClInclude Include="safer.h" />
<ClInclude Include="salsa.h" /> <ClInclude Include="salsa.h" />
<ClInclude Include="scrypt.h" />
<ClInclude Include="seal.h" /> <ClInclude Include="seal.h" />
<ClInclude Include="secblock.h" /> <ClInclude Include="secblock.h" />
<ClInclude Include="seckey.h" /> <ClInclude Include="seckey.h" />
@ -515,4 +517,4 @@
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" /> <Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
</ImportGroup> </ImportGroup>
</Project> </Project>

View File

@ -329,6 +329,9 @@
<ClCompile Include="salsa.cpp"> <ClCompile Include="salsa.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="scrypt.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="seal.cpp"> <ClCompile Include="seal.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
@ -801,6 +804,9 @@
<ClInclude Include="salsa.h"> <ClInclude Include="salsa.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="scrypt.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="seal.h"> <ClInclude Include="seal.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
@ -939,4 +945,4 @@
<Filter>Miscellaneous</Filter> <Filter>Miscellaneous</Filter>
</CustomBuild> </CustomBuild>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -11,6 +11,7 @@
#include "cryptlib.h" #include "cryptlib.h"
#include "hrtimer.h" #include "hrtimer.h"
#include "integer.h" #include "integer.h"
#include "argnames.h"
#include "hmac.h" #include "hmac.h"
NAMESPACE_BEGIN(CryptoPP) NAMESPACE_BEGIN(CryptoPP)

312
scrypt.cpp Normal file
View File

@ -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 <omp.h>
#endif
#include <stdint.h>
#include <errno.h>
#include <sstream>
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<uint32_t*>(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<const uint32_t*>(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<const uint64_t*>(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<SHA256> 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<size_t>::max() > std::numeric_limits<uint32_t>::max())
{
const uint64_t maxLen = ((static_cast<uint64_t>(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<uint64_t>(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<word128>(SIZE_MAX);
bool bLimit = (maxElems >= static_cast<word128>(cost) * blockSize * 128U);
bool xyLimit = (maxElems >= static_cast<word128>(parallelization) * blockSize * 128U);
bool vLimit = (maxElems >= static_cast<word128>(blockSize) * 256U + 64U);
if (!bLimit || !xyLimit || !vLimit)
throw std::bad_alloc();
#else
const word64 maxElems = static_cast<word64>(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<size_t>(blockSize * parallel * 128U));
AlignedSecByteBlock XY(static_cast<size_t>(blockSize * 256U));
AlignedSecByteBlock V(static_cast<size_t>(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<ptrdiff_t>(blockSize*i*128), static_cast<size_t>(blockSize), cost, V, XY);
}
// 5: DK <-- PBKDF2(P, B, 1, dkLen)
PBKDF2_SHA256(derived, derivedLen, secret, secretLen, B, static_cast<size_t>(blockSize*parallel*128), 1);
return 1;
}
NAMESPACE_END

96
scrypt.h Normal file
View File

@ -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 <A HREF="https://www.tarsnap.com/scrypt/scrypt.pdf">Stronger Key Derivation via
/// Sequential Memory-Hard Functions</a>,
/// <A HREF="https://www.tarsnap.com/scrypt.html">The scrypt key derivation function</A>
/// and <A HREF="https://tools.ietf.org/html/rfc7914">RFC 7914, The scrypt Password-Based
/// Key Derivation Function</A>
/// \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 <A HREF="https://www.tarsnap.com/scrypt.html">The scrypt key derivation function</A>
/// and <A HREF="https://tools.ietf.org/html/rfc7914">RFC 7914, The scrypt Password-Based
/// Key Derivation Function</A>
/// \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<size_t>(-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 <tt>derivedLen</tt> 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 <tt>cost</tt> parameter ("N" in the documents) must be
/// larger than 1, a power of 2, and less than <tt>2^(128 * r / 8)</tt>.
/// \details The parameter <tt>blockSize</tt> ("r" in the documents) specifies the block
/// size.
/// \details The <tt>parallelization</tt> parameter ("p" in the documents) is a positive
/// integer less than or equal to <tt>((2^32-1) * 32) / (128 * r)</tt>.
/// \details Crypto++ uses <tt>size_t</tt> for its size datatype, and limits are
/// based on the 32-bit version of <tt>size_t</tt>. For example, <tt>cost</tt> is
/// limited to <tt>0xffffffff</tt> instead of <tt>2^(128 * r / 8)</tt>.
/// \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

View File

@ -925,29 +925,30 @@ bool Validate(int alg, bool thorough, const char *seedInput)
case 56: result = ValidateAdler32(); break; case 56: result = ValidateAdler32(); break;
case 57: result = ValidateMD4(); break; case 57: result = ValidateMD4(); break;
case 58: result = ValidatePBKDF(); break; case 58: result = ValidatePBKDF(); break;
case 59: result = ValidateESIGN(); break; case 59: result = ValidateHKDF(); break;
case 60: result = ValidateDLIES(); break; case 60: result = ValidateScrypt(); break;
case 61: result = ValidateBaseCode(); break; case 61: result = ValidateESIGN(); break;
case 62: result = ValidateSHACAL2(); break; case 62: result = ValidateDLIES(); break;
case 63: result = ValidateARIA(); break; case 63: result = ValidateBaseCode(); break;
case 64: result = ValidateCamellia(); break; case 64: result = ValidateSHACAL2(); break;
case 65: result = ValidateWhirlpool(); break; case 65: result = ValidateARIA(); break;
case 66: result = ValidateTTMAC(); break; case 66: result = ValidateCamellia(); break;
case 67: result = ValidateSalsa(); break; case 67: result = ValidateWhirlpool(); break;
case 68: result = ValidateSosemanuk(); break; case 68: result = ValidateTTMAC(); break;
case 69: result = ValidateVMAC(); break; case 69: result = ValidateSalsa(); break;
case 70: result = ValidateCCM(); break; case 70: result = ValidateSosemanuk(); break;
case 71: result = ValidateGCM(); break; case 71: result = ValidateVMAC(); break;
case 72: result = ValidateCMAC(); break; case 72: result = ValidateCCM(); break;
case 73: result = ValidateHKDF(); break; case 73: result = ValidateGCM(); break;
case 74: result = ValidateSM3(); break; case 74: result = ValidateCMAC(); break;
case 75: result = ValidateBLAKE2s(); break; case 75: result = ValidateSM3(); break;
case 76: result = ValidateBLAKE2b(); break; case 76: result = ValidateBLAKE2s(); break;
case 77: result = ValidatePoly1305(); break; case 77: result = ValidateBLAKE2b(); break;
case 78: result = ValidateSipHash(); break; case 78: result = ValidatePoly1305(); break;
case 79: result = ValidateHashDRBG(); break; case 79: result = ValidateSipHash(); break;
case 80: result = ValidateHmacDRBG(); break; case 80: result = ValidateHashDRBG(); break;
case 90: result = ValidateNaCl(); break; case 81: result = ValidateHmacDRBG(); break;
case 82: result = ValidateNaCl(); break;
#if defined(CRYPTOPP_EXTENDED_VALIDATION) #if defined(CRYPTOPP_EXTENDED_VALIDATION)
// http://github.com/weidai11/cryptopp/issues/92 // http://github.com/weidai11/cryptopp/issues/92

View File

@ -146,6 +146,7 @@ bool ValidateAll(bool thorough)
pass=ValidatePBKDF() && pass; pass=ValidatePBKDF() && pass;
pass=ValidateHKDF() && pass; pass=ValidateHKDF() && pass;
pass=ValidateScrypt() && pass;
pass=ValidateDES() && pass; pass=ValidateDES() && pass;
pass=ValidateCipherModes() && pass; pass=ValidateCipherModes() && pass;

View File

@ -28,6 +28,7 @@
#include "ttmac.h" #include "ttmac.h"
#include "integer.h" #include "integer.h"
#include "pwdbased.h" #include "pwdbased.h"
#include "scrypt.h"
#include "filters.h" #include "filters.h"
#include "files.h" #include "files.h"
#include "hex.h" #include "hex.h"
@ -671,9 +672,9 @@ bool TestHKDF(KeyDerivationFunction &kdf, const HKDF_TestTuple *testSet, unsigne
AlgorithmParameters params; AlgorithmParameters params;
if (tuple.hexSalt) 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) 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); 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 // 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 // Test Case #4
{"0b0b0b0b0b0b0b0b0b0b0b", "000102030405060708090a0b0c", "f0f1f2f3f4f5f6f7f8f9", "085a01ea1b10f36933068b56efa5ad81 a4f14b822f5b091568a9cdd4f155fda2 c22e422478d305f3f896", 42}, {"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 // 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 // Test Case #1
{"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", "000102030405060708090a0b0c", "f0f1f2f3f4f5f6f7f8f9", "3cb25f25faacd57a90434f64d0362f2a 2d2d0a90cf1a5a4c5db02d56ecc4c5bf 34007208d5b887185865", 42}, {"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 // 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 // Test Case #0
{"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", "000102030405060708090a0b0c", "f0f1f2f3f4f5f6f7f8f9", "832390086CDA71FB47625BB5CEB168E4 C8E26A1A16ED34D9FC7FE92C14815793 38DA362CB8D9F925D7CB", 42}, {"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 // 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 // Test Case #0
{"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", "000102030405060708090a0b0c", "f0f1f2f3f4f5f6f7f8f9", "0D29F74CCD8640F44B0DD9638111C1B5 766EFED752AF358109E2E7C9CD4A28EF 2F90B2AD461FBA0744D4", 42}, {"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", "000102030405060708090a0b0c", "f0f1f2f3f4f5f6f7f8f9", "0D29F74CCD8640F44B0DD9638111C1B5 766EFED752AF358109E2E7C9CD4A28EF 2F90B2AD461FBA0744D4", 42},
@ -779,6 +780,75 @@ bool ValidateHKDF()
return pass; 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<testSetSize; i++)
{
const Scrypt_TestTuple &tuple = testSet[i];
std::string password(tuple.passwd), salt(tuple.salt), expect;
StringSource(tuple.expect, true, new HexDecoder(new StringSink(expect)));
AlgorithmParameters params = MakeParameters("Cost", (word64)tuple.n)
("BlockSize", (word64)tuple.r)("Parallelization", (word64)tuple.p)
(Name::Salt(), ConstByteArrayParameter((const byte*)&salt[0], salt.size()));
SecByteBlock derived(expect.size());
pbkdf.DeriveKey(derived, derived.size(), (const byte *)password.data(), password.size(), params);
bool fail = !!memcmp(derived, expect.data(), expect.size()) != 0;
pass = pass && !fail;
if (password.empty()) {password="\"\"";}
if (salt.empty()) {salt="\"\"";}
HexEncoder enc(new FileSink(std::cout));
std::cout << (fail ? "FAILED " : "passed ");
std::cout << " " << password << " " << salt << " ";
std::cout << " " << tuple.n << " " << tuple.r;
std::cout << " " << tuple.p << " ";
enc.Put(derived, derived.size());
std::cout << std::endl;
}
return pass;
}
bool ValidateScrypt()
{
bool pass = true;
// https://tools.ietf.org/html/rfc7914
const Scrypt_TestTuple testSet[] =
{
{ "", "", 16, 1, 1, "77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906"},
{ "password", "NaCl", 1024, 8, 16, "fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b3731622eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640"},
{ "pleaseletmein", "SodiumChloride", 16384, 8, 1, "7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2d5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887"},
#ifndef CRYPTOPP_DEBUG
// This one takes too long in debug builds
{ "pleaseletmein", "SodiumChloride", 1048576, 8, 1, "2101cb9b6a511aaeaddbbe09cf70f881ec568d574a2ffd4dabe5ee9820adaa478e56fd8f4ba5d09ffa1c6d927c40f4c337304049e8a952fbcbf45c6fa77a41a4"}
#endif
};
Scrypt pbkdf;
std::cout << "\nRFC 7914 Scrypt validation suite running...\n\n";
pass = TestScrypt(pbkdf, testSet, COUNTOF(testSet)) && pass;
return pass;
}
struct Poly1305_TestTuples struct Poly1305_TestTuples
{ {
const char *key, *message, *nonce, *digest; const char *key, *message, *nonce, *digest;
@ -797,7 +867,7 @@ bool ValidatePoly1305()
} }
// Test data from http://cr.yp.to/mac/poly1305-20050329.pdf // Test data from http://cr.yp.to/mac/poly1305-20050329.pdf
static const Poly1305_TestTuples tests[] = const Poly1305_TestTuples tests[] =
{ {
// Appendix B, Test 1 // Appendix B, Test 1
{ {
@ -1094,7 +1164,7 @@ bool ValidateBLAKE2s()
pass = pass && !fail; pass = pass && !fail;
} }
static const BLAKE2_TestTuples tests[] = { const BLAKE2_TestTuples tests[] = {
{ {
NULLPTR, NULLPTR,
NULLPTR, NULLPTR,
@ -1525,7 +1595,7 @@ bool ValidateBLAKE2b()
pass = pass && !fail; pass = pass && !fail;
} }
static const BLAKE2_TestTuples tests[] = { const BLAKE2_TestTuples tests[] = {
{ {
NULLPTR, NULLPTR,
NULLPTR, NULLPTR,

View File

@ -57,6 +57,7 @@ bool ValidateTTMAC();
bool ValidateCipherModes(); bool ValidateCipherModes();
bool ValidatePBKDF(); bool ValidatePBKDF();
bool ValidateHKDF(); bool ValidateHKDF();
bool ValidateScrypt();
bool ValidateDES(); bool ValidateDES();
bool ValidateIDEA(); bool ValidateIDEA();