2018-04-01 00:09:38 +00:00
|
|
|
// scrypt.cpp - written and placed in public domain by Jeffrey Walton.
|
2018-04-01 10:58:00 +00:00
|
|
|
// Based on reference source code by Colin Percival.
|
2018-04-01 00:09:38 +00:00
|
|
|
|
|
|
|
#include "pch.h"
|
|
|
|
|
|
|
|
#include "scrypt.h"
|
2018-04-01 01:26:38 +00:00
|
|
|
#include "algparam.h"
|
2018-04-01 00:09:38 +00:00
|
|
|
#include "argnames.h"
|
|
|
|
#include "pwdbased.h"
|
|
|
|
#include "stdcpp.h"
|
|
|
|
#include "salsa.h"
|
|
|
|
#include "misc.h"
|
|
|
|
#include "sha.h"
|
|
|
|
|
2018-04-01 01:26:38 +00:00
|
|
|
#include <sstream>
|
2018-04-01 00:09:38 +00:00
|
|
|
#ifdef _OPENMP
|
|
|
|
# include <omp.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
ANONYMOUS_NAMESPACE_BEGIN
|
|
|
|
|
2018-04-01 00:25:58 +00:00
|
|
|
using CryptoPP::byte;
|
|
|
|
using CryptoPP::word32;
|
|
|
|
using CryptoPP::word64;
|
2018-04-01 00:09:38 +00:00
|
|
|
using CryptoPP::rotlConstant;
|
|
|
|
using CryptoPP::AlignedSecByteBlock;
|
|
|
|
using CryptoPP::LITTLE_ENDIAN_ORDER;
|
|
|
|
using CryptoPP::ConditionalByteReverse;
|
|
|
|
|
2018-04-01 00:19:07 +00:00
|
|
|
static inline void LE32ENC(byte* out, word32 in)
|
2018-04-01 00:09:38 +00:00
|
|
|
{
|
2018-04-01 00:19:07 +00:00
|
|
|
word32* ptr = reinterpret_cast<word32*>(out);
|
2018-04-01 00:09:38 +00:00
|
|
|
ConditionalByteReverse(LITTLE_ENDIAN_ORDER, ptr, &in, 4);
|
|
|
|
}
|
|
|
|
|
2018-04-01 00:19:07 +00:00
|
|
|
static inline word32 LE32DEC(const byte* in)
|
2018-04-01 00:09:38 +00:00
|
|
|
{
|
2018-04-01 00:19:07 +00:00
|
|
|
word32 res;
|
|
|
|
const word32* ptr = reinterpret_cast<const word32*>(in);
|
2018-04-01 00:09:38 +00:00
|
|
|
ConditionalByteReverse(LITTLE_ENDIAN_ORDER, &res, ptr, 4);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2018-04-01 00:19:07 +00:00
|
|
|
static inline word64 LE64DEC(const byte* in)
|
2018-04-01 00:09:38 +00:00
|
|
|
{
|
2018-04-01 00:19:07 +00:00
|
|
|
word64 res;
|
|
|
|
const word64* ptr = reinterpret_cast<const word64*>(in);
|
2018-04-01 00:09:38 +00:00
|
|
|
ConditionalByteReverse(LITTLE_ENDIAN_ORDER, &res, ptr, 8);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2018-04-01 10:58:00 +00:00
|
|
|
static inline void BlockCopy(byte* dest, byte* src, size_t len)
|
2018-04-01 00:09:38 +00:00
|
|
|
{
|
|
|
|
for (size_t i = 0; i < len; i++)
|
|
|
|
dest[i] = src[i];
|
|
|
|
}
|
|
|
|
|
2018-04-01 10:58:00 +00:00
|
|
|
static inline void BlockXOR(byte* dest, byte* src, size_t len)
|
2018-04-01 00:09:38 +00:00
|
|
|
{
|
2018-04-01 10:58:00 +00:00
|
|
|
#pragma omp simd
|
2018-04-01 00:09:38 +00:00
|
|
|
for (size_t i = 0; i < len; i++)
|
|
|
|
dest[i] ^= src[i];
|
|
|
|
}
|
|
|
|
|
2018-04-01 10:58:00 +00:00
|
|
|
static inline void PBKDF2_SHA256(byte* buf, size_t dkLen,
|
|
|
|
const byte* passwd, size_t passwdlen,
|
|
|
|
const byte* salt, size_t saltlen, byte count)
|
2018-04-01 00:09:38 +00:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-04-01 00:19:07 +00:00
|
|
|
static inline void Salsa20_8(byte B[64])
|
2018-04-01 00:09:38 +00:00
|
|
|
{
|
2018-04-01 00:19:07 +00:00
|
|
|
word32 B32[16], x[16];
|
2018-04-01 00:09:38 +00:00
|
|
|
|
2018-04-01 10:58:00 +00:00
|
|
|
for (size_t i = 0; i < 16; i++)
|
2018-04-01 00:09:38 +00:00
|
|
|
B32[i] = LE32DEC(&B[i * 4]);
|
|
|
|
|
2018-04-01 10:58:00 +00:00
|
|
|
for (size_t i = 0; i < 16; i++)
|
2018-04-01 00:09:38 +00:00
|
|
|
x[i] = B32[i];
|
|
|
|
|
2018-04-01 10:58:00 +00:00
|
|
|
for (size_t i = 0; i < 8; i += 2)
|
2018-04-01 00:09:38 +00:00
|
|
|
{
|
|
|
|
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]);
|
|
|
|
}
|
|
|
|
|
2018-04-01 10:58:00 +00:00
|
|
|
#pragma omp simd
|
|
|
|
for (size_t i = 0; i < 16; i++)
|
2018-04-01 00:09:38 +00:00
|
|
|
B32[i] += x[i];
|
|
|
|
|
2018-04-01 10:58:00 +00:00
|
|
|
for (size_t i = 0; i < 16; i++)
|
2018-04-01 00:09:38 +00:00
|
|
|
LE32ENC(&B[4 * i], B32[i]);
|
|
|
|
}
|
|
|
|
|
2018-04-01 10:58:00 +00:00
|
|
|
static inline void BlockMix(byte* B, byte* Y, size_t r)
|
2018-04-01 00:09:38 +00:00
|
|
|
{
|
2018-04-01 00:19:07 +00:00
|
|
|
byte X[64];
|
2018-04-01 00:09:38 +00:00
|
|
|
|
|
|
|
// 1: X <-- B_{2r - 1}
|
|
|
|
BlockCopy(X, &B[(2 * r - 1) * 64], 64);
|
|
|
|
|
|
|
|
// 2: for i = 0 to 2r - 1 do
|
2018-04-01 10:58:00 +00:00
|
|
|
for (size_t i = 0; i < 2 * r; i++)
|
2018-04-01 00:09:38 +00:00
|
|
|
{
|
|
|
|
// 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})
|
2018-04-01 10:58:00 +00:00
|
|
|
for (size_t i = 0; i < r; i++)
|
2018-04-01 00:09:38 +00:00
|
|
|
BlockCopy(&B[i * 64], &Y[(i * 2) * 64], 64);
|
2018-04-01 10:58:00 +00:00
|
|
|
|
|
|
|
for (size_t i = 0; i < r; i++)
|
2018-04-01 00:09:38 +00:00
|
|
|
BlockCopy(&B[(i + r) * 64], &Y[(i * 2 + 1) * 64], 64);
|
|
|
|
}
|
|
|
|
|
2018-04-01 10:58:00 +00:00
|
|
|
static inline word64 Integerify(byte* B, size_t r)
|
2018-04-01 00:09:38 +00:00
|
|
|
{
|
2018-04-01 10:58:00 +00:00
|
|
|
byte* X = &B[(2 * r - 1) * 64];
|
2018-04-01 00:09:38 +00:00
|
|
|
return LE64DEC(X);
|
|
|
|
}
|
|
|
|
|
2018-04-01 10:58:00 +00:00
|
|
|
static inline void Smix(byte* B, size_t r, word64 N, byte* V, byte* XY)
|
2018-04-01 00:09:38 +00:00
|
|
|
{
|
2018-04-01 10:58:00 +00:00
|
|
|
byte* X = XY;
|
|
|
|
byte* Y = XY+128*r;
|
2018-04-01 00:09:38 +00:00
|
|
|
|
|
|
|
// 1: X <-- B
|
|
|
|
BlockCopy(X, B, 128 * r);
|
|
|
|
|
|
|
|
// 2: for i = 0 to N - 1 do
|
2018-04-01 10:58:00 +00:00
|
|
|
for (word64 i = 0; i < N; i++)
|
2018-04-01 00:09:38 +00:00
|
|
|
{
|
|
|
|
// 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
|
2018-04-01 10:58:00 +00:00
|
|
|
for (word64 i = 0; i < N; i++)
|
|
|
|
{
|
2018-04-01 00:09:38 +00:00
|
|
|
// 7: j <-- Integerify(X) mod N
|
2018-04-01 10:58:00 +00:00
|
|
|
word64 j = Integerify(X, r) & (N - 1);
|
2018-04-01 00:09:38 +00:00
|
|
|
|
|
|
|
// 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);
|
|
|
|
}
|
2018-04-01 01:26:38 +00:00
|
|
|
|
2018-04-01 00:09:38 +00:00
|
|
|
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
|
2018-04-01 00:19:07 +00:00
|
|
|
if (std::numeric_limits<size_t>::max() > std::numeric_limits<word32>::max())
|
2018-04-01 00:09:38 +00:00
|
|
|
{
|
2018-04-01 00:19:07 +00:00
|
|
|
const word64 maxLen = ((static_cast<word64>(1) << 32) - 1) * 32;
|
2018-04-01 00:09:38 +00:00
|
|
|
if (derivedLen > maxLen) {
|
|
|
|
std::ostringstream oss;
|
|
|
|
oss << "derivedLen " << derivedLen << " is larger than " << maxLen;
|
|
|
|
throw InvalidArgument("Scrypt: " + oss.str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-01 19:49:00 +00:00
|
|
|
CRYPTOPP_ASSERT(IsPowerOf2(cost));
|
2018-04-01 00:09:38 +00:00
|
|
|
if (IsPowerOf2(cost) == false)
|
|
|
|
throw InvalidArgument("Scrypt: cost must be a power of 2");
|
|
|
|
|
2018-04-01 00:19:07 +00:00
|
|
|
const word64 prod = static_cast<word64>(blockSize) * parallelization;
|
2018-04-01 19:49:00 +00:00
|
|
|
CRYPTOPP_ASSERT(prod < (1U << 30));
|
|
|
|
|
2018-04-01 00:09:38 +00:00
|
|
|
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);
|
|
|
|
#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);
|
2018-04-01 01:26:38 +00:00
|
|
|
#endif
|
2018-04-01 00:09:38 +00:00
|
|
|
|
2018-04-01 01:26:38 +00:00
|
|
|
CRYPTOPP_ASSERT(bLimit); CRYPTOPP_ASSERT(xyLimit); CRYPTOPP_ASSERT(vLimit);
|
2018-04-01 00:09:38 +00:00
|
|
|
if (!bLimit || !xyLimit || !vLimit)
|
|
|
|
throw std::bad_alloc();
|
|
|
|
}
|
|
|
|
|
2018-04-01 10:58:00 +00:00
|
|
|
size_t Scrypt::DeriveKey(byte*derived, size_t derivedLen,
|
|
|
|
const byte*secret, size_t secretLen, const NameValuePairs& params) const
|
2018-04-01 00:09:38 +00:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-04-01 10:58:00 +00:00
|
|
|
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
|
2018-04-01 00:09:38 +00:00
|
|
|
{
|
|
|
|
CRYPTOPP_ASSERT(secret /*&& secretLen*/);
|
|
|
|
CRYPTOPP_ASSERT(derived && derivedLen);
|
|
|
|
CRYPTOPP_ASSERT(derivedLen <= MaxDerivedLength());
|
|
|
|
|
|
|
|
ThrowIfInvalidDerivedLength(derivedLen);
|
|
|
|
ValidateParameters(derivedLen, cost, blockSize, parallel);
|
|
|
|
|
|
|
|
AlignedSecByteBlock B(static_cast<size_t>(blockSize * parallel * 128U));
|
|
|
|
|
|
|
|
// 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen)
|
|
|
|
PBKDF2_SHA256(B, B.size(), secret, secretLen, salt, saltLen, 1);
|
|
|
|
|
2018-04-01 10:58:00 +00:00
|
|
|
if (parallel == 1)
|
2018-04-01 00:09:38 +00:00
|
|
|
{
|
2018-04-01 10:58:00 +00:00
|
|
|
AlignedSecByteBlock XY(static_cast<size_t>(blockSize * 256U));
|
|
|
|
AlignedSecByteBlock V(static_cast<size_t>(blockSize * cost * 128U));
|
|
|
|
|
|
|
|
// 2: for i = 0 to p - 1 do
|
2018-04-01 00:09:38 +00:00
|
|
|
// 3: B_i <-- MF(B_i, N)
|
2018-04-01 10:58:00 +00:00
|
|
|
Smix(B, static_cast<size_t>(blockSize), cost, V, XY);
|
|
|
|
XY.SetMark(16); V.SetMark(16);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// 2: for i = 0 to p - 1 do
|
|
|
|
#pragma omp parallel for
|
|
|
|
for (unsigned int i = 0; i < parallel; i++)
|
|
|
|
{
|
|
|
|
AlignedSecByteBlock XY(static_cast<size_t>(blockSize * 256U));
|
|
|
|
AlignedSecByteBlock V(static_cast<size_t>(blockSize * cost * 128U));
|
|
|
|
|
|
|
|
// 3: B_i <-- MF(B_i, N)
|
|
|
|
const ptrdiff_t offset = static_cast<ptrdiff_t>(blockSize*i*128);
|
|
|
|
Smix(B+offset, static_cast<size_t>(blockSize), cost, V, XY);
|
|
|
|
XY.SetMark(16); V.SetMark(16);
|
|
|
|
}
|
2018-04-01 00:09:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 5: DK <-- PBKDF2(P, B, 1, dkLen)
|
2018-04-01 10:58:00 +00:00
|
|
|
PBKDF2_SHA256(derived, derivedLen, secret, secretLen, B, B.size(), 1);
|
2018-04-01 00:09:38 +00:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
NAMESPACE_END
|