The scrypt Password-Based Key Derivation Function (RFC 7914)

This commit is contained in:
Karel Miko 2026-04-11 18:25:48 +02:00 committed by Steffen Jaeckel
parent 94287f0480
commit 3aed12c410
15 changed files with 390 additions and 32 deletions

View file

@ -7629,6 +7629,33 @@ The output tag of length \textit{outlen} (minimum 4 bytes) is written to \textit
The function returns \texttt{CRYPT\_OK} on success, \texttt{CRYPT\_MEM} if memory allocation fails, or \texttt{CRYPT\_INVALID\_ARG} if any parameter is out of range.
\subsection{scrypt}
\index{scrypt}
\label{scrypt}
scrypt is a memory-hard password-based key derivation function defined in \href{https://datatracker.ietf.org/doc/html/rfc7914}{\texttt{RFC 7914}}.
It is designed so that a large amount of memory is required for evaluation, making hardware brute-force attacks more costly.
Internally it uses PBKDF2-HMAC-SHA-256 and a reduced-round Salsa20 core.
To enable scrypt, define \texttt{LTC\_SCRYPT} in \textit{tomcrypt\_custom.h} (it also requires \texttt{LTC\_PKCS\_5} and \texttt{LTC\_SHA256}).
\index{scrypt\_pbkdf()}
\begin{alltt}
int scrypt_pbkdf(const unsigned char *password, unsigned long password_len,
const unsigned char *salt, unsigned long salt_len,
unsigned long N, unsigned long r, unsigned long p,
unsigned char *out, unsigned long outlen);
\end{alltt}
The \textit{password} parameter is the password of length \textit{password\_len}.
The \textit{salt} parameter is a random salt of length \textit{salt\_len}.
The \textit{N} parameter is the CPU/memory cost; it must be greater than 1 and a power of 2.
The \textit{r} parameter is the block size (minimum 1; a typical value is 8).
The \textit{p} parameter is the parallelisation factor (minimum 1); this implementation is single-threaded, so increasing \textit{p} raises the computational cost without improving performance.
The derived key of length \textit{outlen} is written to \textit{out}.
The function returns \texttt{CRYPT\_OK} on success or an error code on failure.
\mysection{PKCS \#8}
\index{PKCS \#8}
\label{pkcs8}

View file

@ -1675,6 +1675,14 @@
>
</File>
</Filter>
<Filter
Name="scrypt"
>
<File
RelativePath="src\misc\scrypt\scrypt.c"
>
</File>
</Filter>
<Filter
Name="ssh"
>

View file

@ -116,13 +116,14 @@ src/misc/padding/padding_pad.o src/misc/password_free.o src/misc/pbes/pbes.o src
src/misc/pbes/pbes2.o src/misc/pem/pem.o src/misc/pem/pem_pkcs.o src/misc/pem/pem_read.o \
src/misc/pem/pem_ssh.o src/misc/pkcs12/pkcs12_kdf.o src/misc/pkcs12/pkcs12_utf8_to_utf16.o \
src/misc/pkcs5/pkcs_5_1.o src/misc/pkcs5/pkcs_5_2.o src/misc/pkcs5/pkcs_5_test.o \
src/misc/ssh/ssh_decode_sequence_multi.o src/misc/ssh/ssh_encode_sequence_multi.o src/misc/zeromem.o \
src/modes/cbc/cbc_decrypt.o src/modes/cbc/cbc_done.o src/modes/cbc/cbc_encrypt.o \
src/modes/cbc/cbc_getiv.o src/modes/cbc/cbc_setiv.o src/modes/cbc/cbc_start.o \
src/modes/cfb/cfb_decrypt.o src/modes/cfb/cfb_done.o src/modes/cfb/cfb_encrypt.o \
src/modes/cfb/cfb_getiv.o src/modes/cfb/cfb_setiv.o src/modes/cfb/cfb_start.o \
src/modes/ctr/ctr_decrypt.o src/modes/ctr/ctr_done.o src/modes/ctr/ctr_encrypt.o \
src/modes/ctr/ctr_getiv.o src/modes/ctr/ctr_setiv.o src/modes/ctr/ctr_start.o src/modes/ctr/ctr_test.o \
src/misc/scrypt/scrypt.o src/misc/ssh/ssh_decode_sequence_multi.o \
src/misc/ssh/ssh_encode_sequence_multi.o src/misc/zeromem.o src/modes/cbc/cbc_decrypt.o \
src/modes/cbc/cbc_done.o src/modes/cbc/cbc_encrypt.o src/modes/cbc/cbc_getiv.o \
src/modes/cbc/cbc_setiv.o src/modes/cbc/cbc_start.o src/modes/cfb/cfb_decrypt.o \
src/modes/cfb/cfb_done.o src/modes/cfb/cfb_encrypt.o src/modes/cfb/cfb_getiv.o \
src/modes/cfb/cfb_setiv.o src/modes/cfb/cfb_start.o src/modes/ctr/ctr_decrypt.o \
src/modes/ctr/ctr_done.o src/modes/ctr/ctr_encrypt.o src/modes/ctr/ctr_getiv.o \
src/modes/ctr/ctr_setiv.o src/modes/ctr/ctr_start.o src/modes/ctr/ctr_test.o \
src/modes/ecb/ecb_decrypt.o src/modes/ecb/ecb_done.o src/modes/ecb/ecb_encrypt.o \
src/modes/ecb/ecb_start.o src/modes/f8/f8_decrypt.o src/modes/f8/f8_done.o src/modes/f8/f8_encrypt.o \
src/modes/f8/f8_getiv.o src/modes/f8/f8_setiv.o src/modes/f8/f8_start.o src/modes/f8/f8_test_mode.o \
@ -242,7 +243,7 @@ tests/misc_test.o tests/modes_test.o tests/mpi_test.o tests/multi_test.o \
tests/no_null_termination_check_test.o tests/no_prng.o tests/padding_test.o tests/pem_test.o \
tests/pk_oid_test.o tests/pkcs_1_eme_test.o tests/pkcs_1_emsa_test.o tests/pkcs_1_oaep_test.o \
tests/pkcs_1_pss_test.o tests/pkcs_1_test.o tests/prng_test.o tests/rotate_test.o tests/rsa_test.o \
tests/ssh_test.o tests/store_test.o tests/test.o tests/x25519_test.o
tests/scrypt_test.o tests/ssh_test.o tests/store_test.o tests/test.o tests/x25519_test.o
#The following headers will be installed by "make install"
HEADERS_PUB=src/headers/tomcrypt.h src/headers/tomcrypt_argchk.h src/headers/tomcrypt_cfg.h \

View file

@ -109,13 +109,14 @@ src/misc/padding/padding_pad.obj src/misc/password_free.obj src/misc/pbes/pbes.o
src/misc/pbes/pbes2.obj src/misc/pem/pem.obj src/misc/pem/pem_pkcs.obj src/misc/pem/pem_read.obj \
src/misc/pem/pem_ssh.obj src/misc/pkcs12/pkcs12_kdf.obj src/misc/pkcs12/pkcs12_utf8_to_utf16.obj \
src/misc/pkcs5/pkcs_5_1.obj src/misc/pkcs5/pkcs_5_2.obj src/misc/pkcs5/pkcs_5_test.obj \
src/misc/ssh/ssh_decode_sequence_multi.obj src/misc/ssh/ssh_encode_sequence_multi.obj src/misc/zeromem.obj \
src/modes/cbc/cbc_decrypt.obj src/modes/cbc/cbc_done.obj src/modes/cbc/cbc_encrypt.obj \
src/modes/cbc/cbc_getiv.obj src/modes/cbc/cbc_setiv.obj src/modes/cbc/cbc_start.obj \
src/modes/cfb/cfb_decrypt.obj src/modes/cfb/cfb_done.obj src/modes/cfb/cfb_encrypt.obj \
src/modes/cfb/cfb_getiv.obj src/modes/cfb/cfb_setiv.obj src/modes/cfb/cfb_start.obj \
src/modes/ctr/ctr_decrypt.obj src/modes/ctr/ctr_done.obj src/modes/ctr/ctr_encrypt.obj \
src/modes/ctr/ctr_getiv.obj src/modes/ctr/ctr_setiv.obj src/modes/ctr/ctr_start.obj src/modes/ctr/ctr_test.obj \
src/misc/scrypt/scrypt.obj src/misc/ssh/ssh_decode_sequence_multi.obj \
src/misc/ssh/ssh_encode_sequence_multi.obj src/misc/zeromem.obj src/modes/cbc/cbc_decrypt.obj \
src/modes/cbc/cbc_done.obj src/modes/cbc/cbc_encrypt.obj src/modes/cbc/cbc_getiv.obj \
src/modes/cbc/cbc_setiv.obj src/modes/cbc/cbc_start.obj src/modes/cfb/cfb_decrypt.obj \
src/modes/cfb/cfb_done.obj src/modes/cfb/cfb_encrypt.obj src/modes/cfb/cfb_getiv.obj \
src/modes/cfb/cfb_setiv.obj src/modes/cfb/cfb_start.obj src/modes/ctr/ctr_decrypt.obj \
src/modes/ctr/ctr_done.obj src/modes/ctr/ctr_encrypt.obj src/modes/ctr/ctr_getiv.obj \
src/modes/ctr/ctr_setiv.obj src/modes/ctr/ctr_start.obj src/modes/ctr/ctr_test.obj \
src/modes/ecb/ecb_decrypt.obj src/modes/ecb/ecb_done.obj src/modes/ecb/ecb_encrypt.obj \
src/modes/ecb/ecb_start.obj src/modes/f8/f8_decrypt.obj src/modes/f8/f8_done.obj src/modes/f8/f8_encrypt.obj \
src/modes/f8/f8_getiv.obj src/modes/f8/f8_setiv.obj src/modes/f8/f8_start.obj src/modes/f8/f8_test_mode.obj \
@ -235,7 +236,7 @@ tests/misc_test.obj tests/modes_test.obj tests/mpi_test.obj tests/multi_test.obj
tests/no_null_termination_check_test.obj tests/no_prng.obj tests/padding_test.obj tests/pem_test.obj \
tests/pk_oid_test.obj tests/pkcs_1_eme_test.obj tests/pkcs_1_emsa_test.obj tests/pkcs_1_oaep_test.obj \
tests/pkcs_1_pss_test.obj tests/pkcs_1_test.obj tests/prng_test.obj tests/rotate_test.obj tests/rsa_test.obj \
tests/ssh_test.obj tests/store_test.obj tests/test.obj tests/x25519_test.obj
tests/scrypt_test.obj tests/ssh_test.obj tests/store_test.obj tests/test.obj tests/x25519_test.obj
#The following headers will be installed by "make install"
HEADERS_PUB=src/headers/tomcrypt.h src/headers/tomcrypt_argchk.h src/headers/tomcrypt_cfg.h \

View file

@ -130,13 +130,14 @@ src/misc/padding/padding_pad.o src/misc/password_free.o src/misc/pbes/pbes.o src
src/misc/pbes/pbes2.o src/misc/pem/pem.o src/misc/pem/pem_pkcs.o src/misc/pem/pem_read.o \
src/misc/pem/pem_ssh.o src/misc/pkcs12/pkcs12_kdf.o src/misc/pkcs12/pkcs12_utf8_to_utf16.o \
src/misc/pkcs5/pkcs_5_1.o src/misc/pkcs5/pkcs_5_2.o src/misc/pkcs5/pkcs_5_test.o \
src/misc/ssh/ssh_decode_sequence_multi.o src/misc/ssh/ssh_encode_sequence_multi.o src/misc/zeromem.o \
src/modes/cbc/cbc_decrypt.o src/modes/cbc/cbc_done.o src/modes/cbc/cbc_encrypt.o \
src/modes/cbc/cbc_getiv.o src/modes/cbc/cbc_setiv.o src/modes/cbc/cbc_start.o \
src/modes/cfb/cfb_decrypt.o src/modes/cfb/cfb_done.o src/modes/cfb/cfb_encrypt.o \
src/modes/cfb/cfb_getiv.o src/modes/cfb/cfb_setiv.o src/modes/cfb/cfb_start.o \
src/modes/ctr/ctr_decrypt.o src/modes/ctr/ctr_done.o src/modes/ctr/ctr_encrypt.o \
src/modes/ctr/ctr_getiv.o src/modes/ctr/ctr_setiv.o src/modes/ctr/ctr_start.o src/modes/ctr/ctr_test.o \
src/misc/scrypt/scrypt.o src/misc/ssh/ssh_decode_sequence_multi.o \
src/misc/ssh/ssh_encode_sequence_multi.o src/misc/zeromem.o src/modes/cbc/cbc_decrypt.o \
src/modes/cbc/cbc_done.o src/modes/cbc/cbc_encrypt.o src/modes/cbc/cbc_getiv.o \
src/modes/cbc/cbc_setiv.o src/modes/cbc/cbc_start.o src/modes/cfb/cfb_decrypt.o \
src/modes/cfb/cfb_done.o src/modes/cfb/cfb_encrypt.o src/modes/cfb/cfb_getiv.o \
src/modes/cfb/cfb_setiv.o src/modes/cfb/cfb_start.o src/modes/ctr/ctr_decrypt.o \
src/modes/ctr/ctr_done.o src/modes/ctr/ctr_encrypt.o src/modes/ctr/ctr_getiv.o \
src/modes/ctr/ctr_setiv.o src/modes/ctr/ctr_start.o src/modes/ctr/ctr_test.o \
src/modes/ecb/ecb_decrypt.o src/modes/ecb/ecb_done.o src/modes/ecb/ecb_encrypt.o \
src/modes/ecb/ecb_start.o src/modes/f8/f8_decrypt.o src/modes/f8/f8_done.o src/modes/f8/f8_encrypt.o \
src/modes/f8/f8_getiv.o src/modes/f8/f8_setiv.o src/modes/f8/f8_start.o src/modes/f8/f8_test_mode.o \
@ -256,7 +257,7 @@ tests/misc_test.o tests/modes_test.o tests/mpi_test.o tests/multi_test.o \
tests/no_null_termination_check_test.o tests/no_prng.o tests/padding_test.o tests/pem_test.o \
tests/pk_oid_test.o tests/pkcs_1_eme_test.o tests/pkcs_1_emsa_test.o tests/pkcs_1_oaep_test.o \
tests/pkcs_1_pss_test.o tests/pkcs_1_test.o tests/prng_test.o tests/rotate_test.o tests/rsa_test.o \
tests/ssh_test.o tests/store_test.o tests/test.o tests/x25519_test.o
tests/scrypt_test.o tests/ssh_test.o tests/store_test.o tests/test.o tests/x25519_test.o
#The following headers will be installed by "make install"
HEADERS_PUB=src/headers/tomcrypt.h src/headers/tomcrypt_argchk.h src/headers/tomcrypt_cfg.h \

View file

@ -301,13 +301,14 @@ src/misc/padding/padding_pad.o src/misc/password_free.o src/misc/pbes/pbes.o src
src/misc/pbes/pbes2.o src/misc/pem/pem.o src/misc/pem/pem_pkcs.o src/misc/pem/pem_read.o \
src/misc/pem/pem_ssh.o src/misc/pkcs12/pkcs12_kdf.o src/misc/pkcs12/pkcs12_utf8_to_utf16.o \
src/misc/pkcs5/pkcs_5_1.o src/misc/pkcs5/pkcs_5_2.o src/misc/pkcs5/pkcs_5_test.o \
src/misc/ssh/ssh_decode_sequence_multi.o src/misc/ssh/ssh_encode_sequence_multi.o src/misc/zeromem.o \
src/modes/cbc/cbc_decrypt.o src/modes/cbc/cbc_done.o src/modes/cbc/cbc_encrypt.o \
src/modes/cbc/cbc_getiv.o src/modes/cbc/cbc_setiv.o src/modes/cbc/cbc_start.o \
src/modes/cfb/cfb_decrypt.o src/modes/cfb/cfb_done.o src/modes/cfb/cfb_encrypt.o \
src/modes/cfb/cfb_getiv.o src/modes/cfb/cfb_setiv.o src/modes/cfb/cfb_start.o \
src/modes/ctr/ctr_decrypt.o src/modes/ctr/ctr_done.o src/modes/ctr/ctr_encrypt.o \
src/modes/ctr/ctr_getiv.o src/modes/ctr/ctr_setiv.o src/modes/ctr/ctr_start.o src/modes/ctr/ctr_test.o \
src/misc/scrypt/scrypt.o src/misc/ssh/ssh_decode_sequence_multi.o \
src/misc/ssh/ssh_encode_sequence_multi.o src/misc/zeromem.o src/modes/cbc/cbc_decrypt.o \
src/modes/cbc/cbc_done.o src/modes/cbc/cbc_encrypt.o src/modes/cbc/cbc_getiv.o \
src/modes/cbc/cbc_setiv.o src/modes/cbc/cbc_start.o src/modes/cfb/cfb_decrypt.o \
src/modes/cfb/cfb_done.o src/modes/cfb/cfb_encrypt.o src/modes/cfb/cfb_getiv.o \
src/modes/cfb/cfb_setiv.o src/modes/cfb/cfb_start.o src/modes/ctr/ctr_decrypt.o \
src/modes/ctr/ctr_done.o src/modes/ctr/ctr_encrypt.o src/modes/ctr/ctr_getiv.o \
src/modes/ctr/ctr_setiv.o src/modes/ctr/ctr_start.o src/modes/ctr/ctr_test.o \
src/modes/ecb/ecb_decrypt.o src/modes/ecb/ecb_done.o src/modes/ecb/ecb_encrypt.o \
src/modes/ecb/ecb_start.o src/modes/f8/f8_decrypt.o src/modes/f8/f8_done.o src/modes/f8/f8_encrypt.o \
src/modes/f8/f8_getiv.o src/modes/f8/f8_setiv.o src/modes/f8/f8_start.o src/modes/f8/f8_test_mode.o \
@ -432,7 +433,7 @@ tests/misc_test.o tests/modes_test.o tests/mpi_test.o tests/multi_test.o \
tests/no_null_termination_check_test.o tests/no_prng.o tests/padding_test.o tests/pem_test.o \
tests/pk_oid_test.o tests/pkcs_1_eme_test.o tests/pkcs_1_emsa_test.o tests/pkcs_1_oaep_test.o \
tests/pkcs_1_pss_test.o tests/pkcs_1_test.o tests/prng_test.o tests/rotate_test.o tests/rsa_test.o \
tests/ssh_test.o tests/store_test.o tests/test.o tests/x25519_test.o
tests/scrypt_test.o tests/ssh_test.o tests/store_test.o tests/test.o tests/x25519_test.o
# The following headers will be installed by "make install"
HEADERS_PUB=src/headers/tomcrypt.h src/headers/tomcrypt_argchk.h src/headers/tomcrypt_cfg.h \

View file

@ -253,6 +253,7 @@ src/misc/pkcs12/pkcs12_utf8_to_utf16.c
src/misc/pkcs5/pkcs_5_1.c
src/misc/pkcs5/pkcs_5_2.c
src/misc/pkcs5/pkcs_5_test.c
src/misc/scrypt/scrypt.c
src/misc/ssh/ssh_decode_sequence_multi.c
src/misc/ssh/ssh_encode_sequence_multi.c
src/misc/zeromem.c

View file

@ -516,6 +516,8 @@
#define LTC_ARGON2
#define LTC_SCRYPT
/* Keep LTC_NO_HKDF for compatibility reasons
* superseeded by LTC_NO_MISC*/
#ifndef LTC_NO_HKDF
@ -687,6 +689,10 @@
#error LTC_ARGON2 requires LTC_BLAKE2B
#endif
#if defined(LTC_SCRYPT) && (!defined(LTC_PKCS_5) || !defined(LTC_SHA256))
#error LTC_SCRYPT requires LTC_PKCS_5 and LTC_SHA256
#endif
#if defined(LTC_CHACHA20POLY1305_MODE) && (!defined(LTC_CHACHA) || !defined(LTC_POLY1305))
#error LTC_CHACHA20POLY1305_MODE requires LTC_CHACHA + LTC_POLY1305
#endif

View file

@ -71,6 +71,14 @@ int argon2_hash(const unsigned char *pwd, unsigned long pwdlen,
unsigned char *out, unsigned long outlen);
#endif /* LTC_ARGON2 */
/* ---- scrypt password-based KDF (RFC 7914) ---- */
#ifdef LTC_SCRYPT
int scrypt_pbkdf(const unsigned char *password, unsigned long password_len,
const unsigned char *salt, unsigned long salt_len,
unsigned long N, unsigned long r, unsigned long p,
unsigned char *out, unsigned long outlen);
#endif /* LTC_SCRYPT */
#ifdef LTC_BCRYPT
int bcrypt_pbkdf_openbsd(const void *secret, unsigned long secret_len,
const unsigned char *salt, unsigned long salt_len,

View file

@ -458,6 +458,9 @@ const char *crypt_build_settings =
" BCRYPT "
" " NAME_VALUE(LTC_BCRYPT_DEFAULT_ROUNDS) " "
#endif
#if defined(LTC_SCRYPT)
" SCRYPT "
#endif
#if defined(LTC_CRC32)
" CRC32 "
#endif

190
src/misc/scrypt/scrypt.c Normal file
View file

@ -0,0 +1,190 @@
/* LibTomCrypt, modular cryptographic library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
#include "tomcrypt_private.h"
/**
@file scrypt.c
scrypt password-based key derivation function (RFC 7914)
*/
#ifdef LTC_SCRYPT
/* Salsa20/8 Core (RFC 7914 Section 3) */
#define SCRYPT_QR(a,b,c,d) \
x[b] ^= ROL(x[a] + x[d], 7); \
x[c] ^= ROL(x[b] + x[a], 9); \
x[d] ^= ROL(x[c] + x[b], 13); \
x[a] ^= ROL(x[d] + x[c], 18);
static void s_salsa20_8(unsigned char B[64])
{
ulong32 x[16], b32[16];
int i;
for (i = 0; i < 16; ++i) {
LOAD32L(b32[i], B + i * 4);
}
XMEMCPY(x, b32, sizeof(x));
for (i = 8; i > 0; i -= 2) {
SCRYPT_QR( 0, 4, 8,12)
SCRYPT_QR( 5, 9,13, 1)
SCRYPT_QR(10,14, 2, 6)
SCRYPT_QR(15, 3, 7,11)
SCRYPT_QR( 0, 1, 2, 3)
SCRYPT_QR( 5, 6, 7, 4)
SCRYPT_QR(10,11, 8, 9)
SCRYPT_QR(15,12,13,14)
}
for (i = 0; i < 16; ++i) {
STORE32L(x[i] + b32[i], B + i * 4);
}
}
/* scryptBlockMix (RFC 7914 Section 4) */
static void s_blockmix(unsigned char *B, unsigned char *Y, unsigned long r)
{
unsigned char X[64];
unsigned long i;
unsigned long blen = 128 * r;
/* 1: X = B[2r - 1] */
XMEMCPY(X, B + blen - 64, 64);
/* 2: for i = 0 to 2r-1 */
for (i = 0; i < 2 * r; ++i) {
unsigned long j;
for (j = 0; j < 64; ++j) X[j] ^= B[i * 64 + j];
s_salsa20_8(X);
XMEMCPY(Y + i * 64, X, 64);
}
/* 3: B' = (Y[0], Y[2], ..., Y[2r-2], Y[1], Y[3], ..., Y[2r-1]) */
for (i = 0; i < r; ++i) {
XMEMCPY(B + i * 64, Y + (2 * i) * 64, 64);
}
for (i = 0; i < r; ++i) {
XMEMCPY(B + (i + r) * 64, Y + (2 * i + 1) * 64, 64);
}
}
/* Integerify: interpret last 64-byte block as little-endian and return low 64 bits */
static LTC_INLINE ulong64 s_integerify(const unsigned char *B, unsigned long r)
{
const unsigned char *X = B + (2 * r - 1) * 64;
ulong64 v;
LOAD64L(v, X);
return v;
}
/* scryptROMix (RFC 7914 Section 5) */
static void s_romix(unsigned char *B, unsigned long r, ulong64 N, unsigned char *V, unsigned char *XY)
{
unsigned char *X = XY;
unsigned char *Y = XY + 128 * r;
unsigned long blen = 128 * r;
ulong64 i, j;
/* 1: X = B */
XMEMCPY(X, B, blen);
/* 2: for i = 0 to N-1: V[i] = X; X = BlockMix(X) */
for (i = 0; i < N; ++i) {
XMEMCPY(V + i * blen, X, blen);
s_blockmix(X, Y, r);
}
/* 3: for i = 0 to N-1 */
for (i = 0; i < N; ++i) {
j = s_integerify(X, r) & (N - 1);
{
unsigned long k;
unsigned char *Vj = V + j * blen;
for (k = 0; k < blen; ++k) X[k] ^= Vj[k];
}
s_blockmix(X, Y, r);
}
/* 4: B' = X */
XMEMCPY(B, X, blen);
}
/**
Derive a key using scrypt (RFC 7914)
@param password Password
@param password_len Length of password
@param salt Salt
@param salt_len Length of salt
@param N CPU/memory cost parameter (must be > 1 and a power of 2)
@param r Block size parameter (minimum 1)
@param p Parallelisation parameter (minimum 1)
@param out [out] Derived key
@param outlen Desired output length
@return CRYPT_OK on success
*/
int scrypt_pbkdf(const unsigned char *password, unsigned long password_len,
const unsigned char *salt, unsigned long salt_len,
unsigned long N, unsigned long r, unsigned long p,
unsigned char *out, unsigned long outlen)
{
unsigned char *B = NULL, *V = NULL, *XY = NULL;
const unsigned char *pwd;
unsigned long pwd_len, Blen, Vlen, XYlen, i;
unsigned char zero_byte = 0;
int err, hash_idx;
LTC_ARGCHK(out != NULL);
LTC_ARGCHK(password != NULL || password_len == 0);
LTC_ARGCHK(salt != NULL || salt_len == 0);
LTC_ARGCHK(N >= 2 && (N & (N - 1)) == 0); /* must be > 1 and power of 2 */
LTC_ARGCHK(r >= 1);
LTC_ARGCHK(p >= 1);
LTC_ARGCHK(outlen >= 1);
LTC_ARGCHK(r <= ULONG_MAX / 128 / p);
LTC_ARGCHK(r <= ULONG_MAX / 256);
LTC_ARGCHK(N <= ULONG_MAX / 128 / r);
hash_idx = find_hash("sha256");
if (hash_idx == -1) return CRYPT_INVALID_HASH;
/* WORKAROUND: HMAC rejects zero-length keys; a single zero byte
* produces the same zero-padded key block as an empty key. */
pwd = password;
pwd_len = password_len;
if (pwd_len == 0) {
pwd = &zero_byte;
pwd_len = 1;
}
Blen = 128 * r * p;
Vlen = 128 * r * N;
XYlen = 256 * r;
B = (unsigned char *)XMALLOC(Blen);
V = (unsigned char *)XMALLOC(Vlen);
XY = (unsigned char *)XMALLOC(XYlen);
if (B == NULL || V == NULL || XY == NULL) {
err = CRYPT_MEM;
goto cleanup;
}
/* 1: B = PBKDF2-HMAC-SHA256(password, salt, 1, p * 128 * r) */
{
unsigned long blen_out = Blen;
err = pkcs_5_alg2(pwd, pwd_len, salt, salt_len, 1, hash_idx, B, &blen_out);
if (err != CRYPT_OK) goto cleanup;
}
/* 2: for i = 0 to p-1: B[i] = ROMix(r, B[i], N) */
for (i = 0; i < p; ++i) {
s_romix(B + i * 128 * r, r, (ulong64)N, V, XY);
}
/* 3: DK = PBKDF2-HMAC-SHA256(password, B, 1, dkLen) */
{
unsigned long outlen_out = outlen;
err = pkcs_5_alg2(pwd, pwd_len, B, Blen, 1, hash_idx, out, &outlen_out);
}
cleanup:
if (XY != NULL) { zeromem(XY, XYlen); XFREE(XY); }
if (V != NULL) { zeromem(V, Vlen); XFREE(V); }
if (B != NULL) { zeromem(B, Blen); XFREE(B); }
return err;
}
#endif /* LTC_SCRYPT */

View file

@ -10,6 +10,9 @@ int misc_test(void)
#ifdef LTC_BCRYPT
DO(bcrypt_test());
#endif
#ifdef LTC_SCRYPT
DO(scrypt_test());
#endif
#ifdef LTC_HKDF
DO(hkdf_test());
#endif

106
tests/scrypt_test.c Normal file
View file

@ -0,0 +1,106 @@
/* LibTomCrypt, modular cryptographic library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
#include <tomcrypt_test.h>
#ifdef LTC_SCRYPT
/* RFC 7914 test vectors */
typedef struct {
const char *password;
unsigned long password_len;
const char *salt;
unsigned long salt_len;
unsigned long N, r, p;
const unsigned char expected[64];
const char *name;
} scrypt_testcase;
static const scrypt_testcase cases[] = {
{
"", 0, "", 0, 16, 1, 1,
{
0x77, 0xd6, 0x57, 0x62, 0x38, 0x65, 0x7b, 0x20,
0x3b, 0x19, 0xca, 0x42, 0xc1, 0x8a, 0x04, 0x97,
0xf1, 0x6b, 0x48, 0x44, 0xe3, 0x07, 0x4a, 0xe8,
0xdf, 0xdf, 0xfa, 0x3f, 0xed, 0xe2, 0x14, 0x42,
0xfc, 0xd0, 0x06, 0x9d, 0xed, 0x09, 0x48, 0xf8,
0x32, 0x6a, 0x75, 0x3a, 0x0f, 0xc8, 0x1f, 0x17,
0xe8, 0xd3, 0xe0, 0xfb, 0x2e, 0x0d, 0x36, 0x28,
0xcf, 0x35, 0xe2, 0x0c, 0x38, 0xd1, 0x89, 0x06
},
"scrypt(\"\", \"\", 16, 1, 1)"
},
{
"password", 8, "NaCl", 4, 1024, 8, 16,
{
0xfd, 0xba, 0xbe, 0x1c, 0x9d, 0x34, 0x72, 0x00,
0x78, 0x56, 0xe7, 0x19, 0x0d, 0x01, 0xe9, 0xfe,
0x7c, 0x6a, 0xd7, 0xcb, 0xc8, 0x23, 0x78, 0x30,
0xe7, 0x73, 0x76, 0x63, 0x4b, 0x37, 0x31, 0x62,
0x2e, 0xaf, 0x30, 0xd9, 0x2e, 0x22, 0xa3, 0x88,
0x6f, 0xf1, 0x09, 0x27, 0x9d, 0x98, 0x30, 0xda,
0xc7, 0x27, 0xaf, 0xb9, 0x4a, 0x83, 0xee, 0x6d,
0x83, 0x60, 0xcb, 0xdf, 0xa2, 0xcc, 0x06, 0x40
},
"scrypt(\"password\", \"NaCl\", 1024, 8, 16)"
},
{
"pleaseletmein", 13, "SodiumChloride", 14, 16384, 8, 1,
{
0x70, 0x23, 0xbd, 0xcb, 0x3a, 0xfd, 0x73, 0x48,
0x46, 0x1c, 0x06, 0xcd, 0x81, 0xfd, 0x38, 0xeb,
0xfd, 0xa8, 0xfb, 0xba, 0x90, 0x4f, 0x8e, 0x3e,
0xa9, 0xb5, 0x43, 0xf6, 0x54, 0x5d, 0xa1, 0xf2,
0xd5, 0x43, 0x29, 0x55, 0x61, 0x3f, 0x0f, 0xcf,
0x62, 0xd4, 0x97, 0x05, 0x24, 0x2a, 0x9a, 0xf9,
0xe6, 0x1e, 0x85, 0xdc, 0x0d, 0x65, 0x1e, 0x40,
0xdf, 0xcf, 0x01, 0x7b, 0x45, 0x57, 0x58, 0x87
},
"scrypt(\"pleaseletmein\", \"SodiumChloride\", 16384, 8, 1)"
},
#ifdef LTC_SCRYPT_TEST_1GB
/* Define LTC_SCRYPT_TEST_1GB via CFLAGS to enable this test vector
* which allocates ~1 GiB of memory and takes a long time to run. */
{
"pleaseletmein", 13, "SodiumChloride", 14, 1048576, 8, 1,
{
0x21, 0x01, 0xcb, 0x9b, 0x6a, 0x51, 0x1a, 0xae,
0xad, 0xdb, 0xbe, 0x09, 0xcf, 0x70, 0xf8, 0x81,
0xec, 0x56, 0x8d, 0x57, 0x4a, 0x2f, 0xfd, 0x4d,
0xab, 0xe5, 0xee, 0x98, 0x20, 0xad, 0xaa, 0x47,
0x8e, 0x56, 0xfd, 0x8f, 0x4b, 0xa5, 0xd0, 0x9f,
0xfa, 0x1c, 0x6d, 0x92, 0x7c, 0x40, 0xf4, 0xc3,
0x37, 0x30, 0x40, 0x49, 0xe8, 0xa9, 0x52, 0xfb,
0xcb, 0xf4, 0x5c, 0x6f, 0xa7, 0x7a, 0x41, 0xa4
},
"scrypt(\"pleaseletmein\", \"SodiumChloride\", 1048576, 8, 1)"
},
#endif
};
int scrypt_test(void)
{
unsigned char dk[64];
unsigned int n;
for (n = 0; n < LTC_ARRAY_SIZE(cases); ++n) {
DO(scrypt_pbkdf((const unsigned char *)cases[n].password, cases[n].password_len,
(const unsigned char *)cases[n].salt, cases[n].salt_len,
cases[n].N, cases[n].r, cases[n].p,
dk, sizeof(dk)));
COMPARE_TESTVECTOR(dk, sizeof(dk), cases[n].expected, sizeof(cases[n].expected), cases[n].name, n);
}
return CRYPT_OK;
}
#else
int scrypt_test(void)
{
return CRYPT_NOP;
}
#endif

View file

@ -31,6 +31,7 @@ pkcs_1_test.c
prng_test.c
rotate_test.c
rsa_test.c
scrypt_test.c
ssh_test.c
store_test.c
test.c

View file

@ -44,6 +44,7 @@ int ed25519_test(void);
int ssh_test(void);
int argon2_test(void);
int bcrypt_test(void);
int scrypt_test(void);
int no_null_termination_check_test(void);
int pk_oid_test(void);
int deprecated_test(void);