diff --git a/doc/crypt.tex b/doc/crypt.tex index 73fdeaae4..4e68dce55 100644 --- a/doc/crypt.tex +++ b/doc/crypt.tex @@ -7629,6 +7629,33 @@ \subsection{Argon2} 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} diff --git a/libtomcrypt_VS2008.vcproj b/libtomcrypt_VS2008.vcproj index a2c27ca14..69a5e2d69 100644 --- a/libtomcrypt_VS2008.vcproj +++ b/libtomcrypt_VS2008.vcproj @@ -1675,6 +1675,14 @@ > + + + + diff --git a/makefile.mingw b/makefile.mingw index 7d2b23065..5d388f55e 100644 --- a/makefile.mingw +++ b/makefile.mingw @@ -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 \ diff --git a/makefile.msvc b/makefile.msvc index 25bd5707b..9f530931c 100644 --- a/makefile.msvc +++ b/makefile.msvc @@ -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 \ diff --git a/makefile.unix b/makefile.unix index 36202fc9d..df169e288 100644 --- a/makefile.unix +++ b/makefile.unix @@ -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 \ diff --git a/makefile_include.mk b/makefile_include.mk index 3c37167fb..52013d7f3 100644 --- a/makefile_include.mk +++ b/makefile_include.mk @@ -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 \ diff --git a/sources.cmake b/sources.cmake index 9da7f144b..a192ed391 100644 --- a/sources.cmake +++ b/sources.cmake @@ -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 diff --git a/src/headers/tomcrypt_custom.h b/src/headers/tomcrypt_custom.h index 31b383b8d..970e9c67b 100644 --- a/src/headers/tomcrypt_custom.h +++ b/src/headers/tomcrypt_custom.h @@ -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 diff --git a/src/headers/tomcrypt_misc.h b/src/headers/tomcrypt_misc.h index 18a89e7a7..be63f5507 100644 --- a/src/headers/tomcrypt_misc.h +++ b/src/headers/tomcrypt_misc.h @@ -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, diff --git a/src/misc/crypt/crypt.c b/src/misc/crypt/crypt.c index cc95fe8eb..a07a191ba 100644 --- a/src/misc/crypt/crypt.c +++ b/src/misc/crypt/crypt.c @@ -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 diff --git a/src/misc/scrypt/scrypt.c b/src/misc/scrypt/scrypt.c new file mode 100644 index 000000000..4d2a1613e --- /dev/null +++ b/src/misc/scrypt/scrypt.c @@ -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 */ diff --git a/tests/misc_test.c b/tests/misc_test.c index 4fff9c5d3..d3d2945dc 100644 --- a/tests/misc_test.c +++ b/tests/misc_test.c @@ -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 diff --git a/tests/scrypt_test.c b/tests/scrypt_test.c new file mode 100644 index 000000000..b3f579208 --- /dev/null +++ b/tests/scrypt_test.c @@ -0,0 +1,106 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +#include + +#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 diff --git a/tests/sources.cmake b/tests/sources.cmake index d0e5b465d..ae348a644 100644 --- a/tests/sources.cmake +++ b/tests/sources.cmake @@ -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 diff --git a/tests/tomcrypt_test.h b/tests/tomcrypt_test.h index 70514f67f..747ede567 100644 --- a/tests/tomcrypt_test.h +++ b/tests/tomcrypt_test.h @@ -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);