diff --git a/doc/crypt.tex b/doc/crypt.tex index 7f9d6492b..73fdeaae4 100644 --- a/doc/crypt.tex +++ b/doc/crypt.tex @@ -7589,6 +7589,46 @@ \subsection{bcrypt} where \textit{outlen} contains the available buffer size on input and the written size after the invocation. +\subsection{Argon2} +\index{Argon2} +\label{argon2} + +Argon2 is a memory-hard password hashing function defined in \href{https://datatracker.ietf.org/doc/html/rfc9106}{\texttt{RFC 9106}}. +It is the winner of the 2015 \href{https://www.password-hashing.net/}{Password Hashing Competition} and is recommended for new applications that require password hashing or key derivation from passwords. + +Three variants are provided: + +\begin{description} +\item[Argon2d] uses data-dependent memory access, which makes it faster but susceptible to side-channel attacks. Suitable for applications with no threats from side-channels. +\item[Argon2i] uses data-independent memory access, which is preferred when side-channel resistance is needed. +\item[Argon2id] is a hybrid that uses data-independent addressing for the first half of the first pass and data-dependent addressing for the remainder. This is the recommended variant for password hashing. +\end{description} + +The implementation uses the BLAKE2b hash function internally. To enable Argon2, define \texttt{LTC\_ARGON2} in \textit{tomcrypt\_custom.h} (it also requires \texttt{LTC\_BLAKE2B}). + +\index{argon2\_hash()} +\begin{alltt} +int argon2_hash(const unsigned char *pwd, unsigned long pwdlen, + const unsigned char *salt, unsigned long saltlen, + const unsigned char *secret, unsigned long secretlen, + const unsigned char *ad, unsigned long adlen, + unsigned int t_cost, unsigned int m_cost, + unsigned int parallelism, + argon2_type type, + unsigned char *out, unsigned long outlen); +\end{alltt} + +The \textit{pwd} parameter is the password of length \textit{pwdlen}. +The \textit{salt} parameter is a random salt of length \textit{saltlen}; a minimum of 16 bytes is recommended. +The \textit{secret} and \textit{ad} parameters are optional (may be \texttt{NULL} with a length of zero); they allow passing a secret key and associated data respectively. +The \textit{t\_cost} parameter is the number of passes over the memory (minimum 1). +The \textit{m\_cost} parameter is the memory usage in kibibytes (minimum $8 \times \textit{parallelism}$). +The \textit{parallelism} parameter is the number of lanes (minimum 1); note that this implementation is single-threaded, so increasing this value changes the algorithm output but does not improve performance. +The \textit{type} parameter selects the variant: \texttt{ARGON2\_D}, \texttt{ARGON2\_I}, or \texttt{ARGON2\_ID}. +The output tag of length \textit{outlen} (minimum 4 bytes) is written to \textit{out}. +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. + + \mysection{PKCS \#8} \index{PKCS \#8} \label{pkcs8} diff --git a/libtomcrypt_VS2008.vcproj b/libtomcrypt_VS2008.vcproj index 200dc670a..a2c27ca14 100644 --- a/libtomcrypt_VS2008.vcproj +++ b/libtomcrypt_VS2008.vcproj @@ -1407,6 +1407,14 @@ RelativePath="src\misc\zeromem.c" > + + + + diff --git a/makefile.mingw b/makefile.mingw index 2dc38b9c5..7d2b23065 100644 --- a/makefile.mingw +++ b/makefile.mingw @@ -92,22 +92,23 @@ src/mac/xcbc/xcbc_file.o src/mac/xcbc/xcbc_init.o src/mac/xcbc/xcbc_memory.o \ src/mac/xcbc/xcbc_memory_multi.o src/mac/xcbc/xcbc_process.o src/mac/xcbc/xcbc_test.o \ src/math/fp/ltc_ecc_fp_mulmod.o src/math/gmp_desc.o src/math/ltm_desc.o src/math/multi.o \ src/math/radix_to_bin.o src/math/rand_bn.o src/math/rand_prime.o src/math/tfm_desc.o src/misc/adler32.o \ -src/misc/base16/base16_decode.o src/misc/base16/base16_encode.o src/misc/base32/base32_decode.o \ -src/misc/base32/base32_encode.o src/misc/base64/base64_decode.o src/misc/base64/base64_encode.o \ -src/misc/bcrypt/bcrypt.o src/misc/burn_stack.o src/misc/compare_testvector.o src/misc/copy_or_zeromem.o \ -src/misc/crc32.o src/misc/crypt/crypt.o src/misc/crypt/crypt_argchk.o \ -src/misc/crypt/crypt_cipher_descriptor.o src/misc/crypt/crypt_cipher_is_valid.o \ -src/misc/crypt/crypt_constants.o src/misc/crypt/crypt_find_cipher.o \ -src/misc/crypt/crypt_find_cipher_any.o src/misc/crypt/crypt_find_cipher_id.o \ -src/misc/crypt/crypt_find_hash.o src/misc/crypt/crypt_find_hash_any.o \ -src/misc/crypt/crypt_find_hash_id.o src/misc/crypt/crypt_find_hash_oid.o \ -src/misc/crypt/crypt_find_prng.o src/misc/crypt/crypt_fsa.o src/misc/crypt/crypt_hash_descriptor.o \ -src/misc/crypt/crypt_hash_is_valid.o src/misc/crypt/crypt_inits.o \ -src/misc/crypt/crypt_ltc_mp_descriptor.o src/misc/crypt/crypt_prng_descriptor.o \ -src/misc/crypt/crypt_prng_is_valid.o src/misc/crypt/crypt_prng_rng_descriptor.o \ -src/misc/crypt/crypt_register_all_ciphers.o src/misc/crypt/crypt_register_all_hashes.o \ -src/misc/crypt/crypt_register_all_prngs.o src/misc/crypt/crypt_register_cipher.o \ -src/misc/crypt/crypt_register_hash.o src/misc/crypt/crypt_register_prng.o src/misc/crypt/crypt_sizes.o \ +src/misc/argon2/argon2.o src/misc/base16/base16_decode.o src/misc/base16/base16_encode.o \ +src/misc/base32/base32_decode.o src/misc/base32/base32_encode.o src/misc/base64/base64_decode.o \ +src/misc/base64/base64_encode.o src/misc/bcrypt/bcrypt.o src/misc/burn_stack.o \ +src/misc/compare_testvector.o src/misc/copy_or_zeromem.o src/misc/crc32.o src/misc/crypt/crypt.o \ +src/misc/crypt/crypt_argchk.o src/misc/crypt/crypt_cipher_descriptor.o \ +src/misc/crypt/crypt_cipher_is_valid.o src/misc/crypt/crypt_constants.o \ +src/misc/crypt/crypt_find_cipher.o src/misc/crypt/crypt_find_cipher_any.o \ +src/misc/crypt/crypt_find_cipher_id.o src/misc/crypt/crypt_find_hash.o \ +src/misc/crypt/crypt_find_hash_any.o src/misc/crypt/crypt_find_hash_id.o \ +src/misc/crypt/crypt_find_hash_oid.o src/misc/crypt/crypt_find_prng.o src/misc/crypt/crypt_fsa.o \ +src/misc/crypt/crypt_hash_descriptor.o src/misc/crypt/crypt_hash_is_valid.o \ +src/misc/crypt/crypt_inits.o src/misc/crypt/crypt_ltc_mp_descriptor.o \ +src/misc/crypt/crypt_prng_descriptor.o src/misc/crypt/crypt_prng_is_valid.o \ +src/misc/crypt/crypt_prng_rng_descriptor.o src/misc/crypt/crypt_register_all_ciphers.o \ +src/misc/crypt/crypt_register_all_hashes.o src/misc/crypt/crypt_register_all_prngs.o \ +src/misc/crypt/crypt_register_cipher.o src/misc/crypt/crypt_register_hash.o \ +src/misc/crypt/crypt_register_prng.o src/misc/crypt/crypt_sizes.o \ src/misc/crypt/crypt_unregister_cipher.o src/misc/crypt/crypt_unregister_hash.o \ src/misc/crypt/crypt_unregister_prng.o src/misc/deprecated.o src/misc/error_to_string.o \ src/misc/hkdf/hkdf.o src/misc/hkdf/hkdf_test.o src/misc/mem_neq.o src/misc/padding/padding_depad.o \ @@ -234,9 +235,9 @@ src/stream/sober128/sober128_test.o src/stream/sosemanuk/sosemanuk.o \ src/stream/sosemanuk/sosemanuk_memory.o src/stream/sosemanuk/sosemanuk_test.o #List of test objects to compile -TOBJECTS=tests/base16_test.o tests/base32_test.o tests/base64_test.o tests/bcrypt_test.o \ -tests/cipher_hash_test.o tests/common.o tests/deprecated_test.o tests/der_test.o tests/dh_test.o \ -tests/dsa_test.o tests/ecc_test.o tests/ed25519_test.o tests/file_test.o tests/mac_test.o \ +TOBJECTS=tests/argon2_test.o tests/base16_test.o tests/base32_test.o tests/base64_test.o \ +tests/bcrypt_test.o tests/cipher_hash_test.o tests/common.o tests/deprecated_test.o tests/der_test.o \ +tests/dh_test.o tests/dsa_test.o tests/ecc_test.o tests/ed25519_test.o tests/file_test.o tests/mac_test.o \ 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 \ diff --git a/makefile.msvc b/makefile.msvc index 6fdf20715..25bd5707b 100644 --- a/makefile.msvc +++ b/makefile.msvc @@ -85,22 +85,23 @@ src/mac/xcbc/xcbc_file.obj src/mac/xcbc/xcbc_init.obj src/mac/xcbc/xcbc_memory.o src/mac/xcbc/xcbc_memory_multi.obj src/mac/xcbc/xcbc_process.obj src/mac/xcbc/xcbc_test.obj \ src/math/fp/ltc_ecc_fp_mulmod.obj src/math/gmp_desc.obj src/math/ltm_desc.obj src/math/multi.obj \ src/math/radix_to_bin.obj src/math/rand_bn.obj src/math/rand_prime.obj src/math/tfm_desc.obj src/misc/adler32.obj \ -src/misc/base16/base16_decode.obj src/misc/base16/base16_encode.obj src/misc/base32/base32_decode.obj \ -src/misc/base32/base32_encode.obj src/misc/base64/base64_decode.obj src/misc/base64/base64_encode.obj \ -src/misc/bcrypt/bcrypt.obj src/misc/burn_stack.obj src/misc/compare_testvector.obj src/misc/copy_or_zeromem.obj \ -src/misc/crc32.obj src/misc/crypt/crypt.obj src/misc/crypt/crypt_argchk.obj \ -src/misc/crypt/crypt_cipher_descriptor.obj src/misc/crypt/crypt_cipher_is_valid.obj \ -src/misc/crypt/crypt_constants.obj src/misc/crypt/crypt_find_cipher.obj \ -src/misc/crypt/crypt_find_cipher_any.obj src/misc/crypt/crypt_find_cipher_id.obj \ -src/misc/crypt/crypt_find_hash.obj src/misc/crypt/crypt_find_hash_any.obj \ -src/misc/crypt/crypt_find_hash_id.obj src/misc/crypt/crypt_find_hash_oid.obj \ -src/misc/crypt/crypt_find_prng.obj src/misc/crypt/crypt_fsa.obj src/misc/crypt/crypt_hash_descriptor.obj \ -src/misc/crypt/crypt_hash_is_valid.obj src/misc/crypt/crypt_inits.obj \ -src/misc/crypt/crypt_ltc_mp_descriptor.obj src/misc/crypt/crypt_prng_descriptor.obj \ -src/misc/crypt/crypt_prng_is_valid.obj src/misc/crypt/crypt_prng_rng_descriptor.obj \ -src/misc/crypt/crypt_register_all_ciphers.obj src/misc/crypt/crypt_register_all_hashes.obj \ -src/misc/crypt/crypt_register_all_prngs.obj src/misc/crypt/crypt_register_cipher.obj \ -src/misc/crypt/crypt_register_hash.obj src/misc/crypt/crypt_register_prng.obj src/misc/crypt/crypt_sizes.obj \ +src/misc/argon2/argon2.obj src/misc/base16/base16_decode.obj src/misc/base16/base16_encode.obj \ +src/misc/base32/base32_decode.obj src/misc/base32/base32_encode.obj src/misc/base64/base64_decode.obj \ +src/misc/base64/base64_encode.obj src/misc/bcrypt/bcrypt.obj src/misc/burn_stack.obj \ +src/misc/compare_testvector.obj src/misc/copy_or_zeromem.obj src/misc/crc32.obj src/misc/crypt/crypt.obj \ +src/misc/crypt/crypt_argchk.obj src/misc/crypt/crypt_cipher_descriptor.obj \ +src/misc/crypt/crypt_cipher_is_valid.obj src/misc/crypt/crypt_constants.obj \ +src/misc/crypt/crypt_find_cipher.obj src/misc/crypt/crypt_find_cipher_any.obj \ +src/misc/crypt/crypt_find_cipher_id.obj src/misc/crypt/crypt_find_hash.obj \ +src/misc/crypt/crypt_find_hash_any.obj src/misc/crypt/crypt_find_hash_id.obj \ +src/misc/crypt/crypt_find_hash_oid.obj src/misc/crypt/crypt_find_prng.obj src/misc/crypt/crypt_fsa.obj \ +src/misc/crypt/crypt_hash_descriptor.obj src/misc/crypt/crypt_hash_is_valid.obj \ +src/misc/crypt/crypt_inits.obj src/misc/crypt/crypt_ltc_mp_descriptor.obj \ +src/misc/crypt/crypt_prng_descriptor.obj src/misc/crypt/crypt_prng_is_valid.obj \ +src/misc/crypt/crypt_prng_rng_descriptor.obj src/misc/crypt/crypt_register_all_ciphers.obj \ +src/misc/crypt/crypt_register_all_hashes.obj src/misc/crypt/crypt_register_all_prngs.obj \ +src/misc/crypt/crypt_register_cipher.obj src/misc/crypt/crypt_register_hash.obj \ +src/misc/crypt/crypt_register_prng.obj src/misc/crypt/crypt_sizes.obj \ src/misc/crypt/crypt_unregister_cipher.obj src/misc/crypt/crypt_unregister_hash.obj \ src/misc/crypt/crypt_unregister_prng.obj src/misc/deprecated.obj src/misc/error_to_string.obj \ src/misc/hkdf/hkdf.obj src/misc/hkdf/hkdf_test.obj src/misc/mem_neq.obj src/misc/padding/padding_depad.obj \ @@ -227,9 +228,9 @@ src/stream/sober128/sober128_test.obj src/stream/sosemanuk/sosemanuk.obj \ src/stream/sosemanuk/sosemanuk_memory.obj src/stream/sosemanuk/sosemanuk_test.obj #List of test objects to compile -TOBJECTS=tests/base16_test.obj tests/base32_test.obj tests/base64_test.obj tests/bcrypt_test.obj \ -tests/cipher_hash_test.obj tests/common.obj tests/deprecated_test.obj tests/der_test.obj tests/dh_test.obj \ -tests/dsa_test.obj tests/ecc_test.obj tests/ed25519_test.obj tests/file_test.obj tests/mac_test.obj \ +TOBJECTS=tests/argon2_test.obj tests/base16_test.obj tests/base32_test.obj tests/base64_test.obj \ +tests/bcrypt_test.obj tests/cipher_hash_test.obj tests/common.obj tests/deprecated_test.obj tests/der_test.obj \ +tests/dh_test.obj tests/dsa_test.obj tests/ecc_test.obj tests/ed25519_test.obj tests/file_test.obj tests/mac_test.obj \ 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 \ diff --git a/makefile.unix b/makefile.unix index d68a3850f..36202fc9d 100644 --- a/makefile.unix +++ b/makefile.unix @@ -106,22 +106,23 @@ src/mac/xcbc/xcbc_file.o src/mac/xcbc/xcbc_init.o src/mac/xcbc/xcbc_memory.o \ src/mac/xcbc/xcbc_memory_multi.o src/mac/xcbc/xcbc_process.o src/mac/xcbc/xcbc_test.o \ src/math/fp/ltc_ecc_fp_mulmod.o src/math/gmp_desc.o src/math/ltm_desc.o src/math/multi.o \ src/math/radix_to_bin.o src/math/rand_bn.o src/math/rand_prime.o src/math/tfm_desc.o src/misc/adler32.o \ -src/misc/base16/base16_decode.o src/misc/base16/base16_encode.o src/misc/base32/base32_decode.o \ -src/misc/base32/base32_encode.o src/misc/base64/base64_decode.o src/misc/base64/base64_encode.o \ -src/misc/bcrypt/bcrypt.o src/misc/burn_stack.o src/misc/compare_testvector.o src/misc/copy_or_zeromem.o \ -src/misc/crc32.o src/misc/crypt/crypt.o src/misc/crypt/crypt_argchk.o \ -src/misc/crypt/crypt_cipher_descriptor.o src/misc/crypt/crypt_cipher_is_valid.o \ -src/misc/crypt/crypt_constants.o src/misc/crypt/crypt_find_cipher.o \ -src/misc/crypt/crypt_find_cipher_any.o src/misc/crypt/crypt_find_cipher_id.o \ -src/misc/crypt/crypt_find_hash.o src/misc/crypt/crypt_find_hash_any.o \ -src/misc/crypt/crypt_find_hash_id.o src/misc/crypt/crypt_find_hash_oid.o \ -src/misc/crypt/crypt_find_prng.o src/misc/crypt/crypt_fsa.o src/misc/crypt/crypt_hash_descriptor.o \ -src/misc/crypt/crypt_hash_is_valid.o src/misc/crypt/crypt_inits.o \ -src/misc/crypt/crypt_ltc_mp_descriptor.o src/misc/crypt/crypt_prng_descriptor.o \ -src/misc/crypt/crypt_prng_is_valid.o src/misc/crypt/crypt_prng_rng_descriptor.o \ -src/misc/crypt/crypt_register_all_ciphers.o src/misc/crypt/crypt_register_all_hashes.o \ -src/misc/crypt/crypt_register_all_prngs.o src/misc/crypt/crypt_register_cipher.o \ -src/misc/crypt/crypt_register_hash.o src/misc/crypt/crypt_register_prng.o src/misc/crypt/crypt_sizes.o \ +src/misc/argon2/argon2.o src/misc/base16/base16_decode.o src/misc/base16/base16_encode.o \ +src/misc/base32/base32_decode.o src/misc/base32/base32_encode.o src/misc/base64/base64_decode.o \ +src/misc/base64/base64_encode.o src/misc/bcrypt/bcrypt.o src/misc/burn_stack.o \ +src/misc/compare_testvector.o src/misc/copy_or_zeromem.o src/misc/crc32.o src/misc/crypt/crypt.o \ +src/misc/crypt/crypt_argchk.o src/misc/crypt/crypt_cipher_descriptor.o \ +src/misc/crypt/crypt_cipher_is_valid.o src/misc/crypt/crypt_constants.o \ +src/misc/crypt/crypt_find_cipher.o src/misc/crypt/crypt_find_cipher_any.o \ +src/misc/crypt/crypt_find_cipher_id.o src/misc/crypt/crypt_find_hash.o \ +src/misc/crypt/crypt_find_hash_any.o src/misc/crypt/crypt_find_hash_id.o \ +src/misc/crypt/crypt_find_hash_oid.o src/misc/crypt/crypt_find_prng.o src/misc/crypt/crypt_fsa.o \ +src/misc/crypt/crypt_hash_descriptor.o src/misc/crypt/crypt_hash_is_valid.o \ +src/misc/crypt/crypt_inits.o src/misc/crypt/crypt_ltc_mp_descriptor.o \ +src/misc/crypt/crypt_prng_descriptor.o src/misc/crypt/crypt_prng_is_valid.o \ +src/misc/crypt/crypt_prng_rng_descriptor.o src/misc/crypt/crypt_register_all_ciphers.o \ +src/misc/crypt/crypt_register_all_hashes.o src/misc/crypt/crypt_register_all_prngs.o \ +src/misc/crypt/crypt_register_cipher.o src/misc/crypt/crypt_register_hash.o \ +src/misc/crypt/crypt_register_prng.o src/misc/crypt/crypt_sizes.o \ src/misc/crypt/crypt_unregister_cipher.o src/misc/crypt/crypt_unregister_hash.o \ src/misc/crypt/crypt_unregister_prng.o src/misc/deprecated.o src/misc/error_to_string.o \ src/misc/hkdf/hkdf.o src/misc/hkdf/hkdf_test.o src/misc/mem_neq.o src/misc/padding/padding_depad.o \ @@ -248,9 +249,9 @@ src/stream/sober128/sober128_test.o src/stream/sosemanuk/sosemanuk.o \ src/stream/sosemanuk/sosemanuk_memory.o src/stream/sosemanuk/sosemanuk_test.o #List of test objects to compile (all goes to libtomcrypt_prof.a) -TOBJECTS=tests/base16_test.o tests/base32_test.o tests/base64_test.o tests/bcrypt_test.o \ -tests/cipher_hash_test.o tests/common.o tests/deprecated_test.o tests/der_test.o tests/dh_test.o \ -tests/dsa_test.o tests/ecc_test.o tests/ed25519_test.o tests/file_test.o tests/mac_test.o \ +TOBJECTS=tests/argon2_test.o tests/base16_test.o tests/base32_test.o tests/base64_test.o \ +tests/bcrypt_test.o tests/cipher_hash_test.o tests/common.o tests/deprecated_test.o tests/der_test.o \ +tests/dh_test.o tests/dsa_test.o tests/ecc_test.o tests/ed25519_test.o tests/file_test.o tests/mac_test.o \ 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 \ diff --git a/makefile_include.mk b/makefile_include.mk index cc6f33e4e..bb0e989e4 100644 --- a/makefile_include.mk +++ b/makefile_include.mk @@ -277,22 +277,23 @@ src/mac/xcbc/xcbc_file.o src/mac/xcbc/xcbc_init.o src/mac/xcbc/xcbc_memory.o \ src/mac/xcbc/xcbc_memory_multi.o src/mac/xcbc/xcbc_process.o src/mac/xcbc/xcbc_test.o \ src/math/fp/ltc_ecc_fp_mulmod.o src/math/gmp_desc.o src/math/ltm_desc.o src/math/multi.o \ src/math/radix_to_bin.o src/math/rand_bn.o src/math/rand_prime.o src/math/tfm_desc.o src/misc/adler32.o \ -src/misc/base16/base16_decode.o src/misc/base16/base16_encode.o src/misc/base32/base32_decode.o \ -src/misc/base32/base32_encode.o src/misc/base64/base64_decode.o src/misc/base64/base64_encode.o \ -src/misc/bcrypt/bcrypt.o src/misc/burn_stack.o src/misc/compare_testvector.o src/misc/copy_or_zeromem.o \ -src/misc/crc32.o src/misc/crypt/crypt.o src/misc/crypt/crypt_argchk.o \ -src/misc/crypt/crypt_cipher_descriptor.o src/misc/crypt/crypt_cipher_is_valid.o \ -src/misc/crypt/crypt_constants.o src/misc/crypt/crypt_find_cipher.o \ -src/misc/crypt/crypt_find_cipher_any.o src/misc/crypt/crypt_find_cipher_id.o \ -src/misc/crypt/crypt_find_hash.o src/misc/crypt/crypt_find_hash_any.o \ -src/misc/crypt/crypt_find_hash_id.o src/misc/crypt/crypt_find_hash_oid.o \ -src/misc/crypt/crypt_find_prng.o src/misc/crypt/crypt_fsa.o src/misc/crypt/crypt_hash_descriptor.o \ -src/misc/crypt/crypt_hash_is_valid.o src/misc/crypt/crypt_inits.o \ -src/misc/crypt/crypt_ltc_mp_descriptor.o src/misc/crypt/crypt_prng_descriptor.o \ -src/misc/crypt/crypt_prng_is_valid.o src/misc/crypt/crypt_prng_rng_descriptor.o \ -src/misc/crypt/crypt_register_all_ciphers.o src/misc/crypt/crypt_register_all_hashes.o \ -src/misc/crypt/crypt_register_all_prngs.o src/misc/crypt/crypt_register_cipher.o \ -src/misc/crypt/crypt_register_hash.o src/misc/crypt/crypt_register_prng.o src/misc/crypt/crypt_sizes.o \ +src/misc/argon2/argon2.o src/misc/base16/base16_decode.o src/misc/base16/base16_encode.o \ +src/misc/base32/base32_decode.o src/misc/base32/base32_encode.o src/misc/base64/base64_decode.o \ +src/misc/base64/base64_encode.o src/misc/bcrypt/bcrypt.o src/misc/burn_stack.o \ +src/misc/compare_testvector.o src/misc/copy_or_zeromem.o src/misc/crc32.o src/misc/crypt/crypt.o \ +src/misc/crypt/crypt_argchk.o src/misc/crypt/crypt_cipher_descriptor.o \ +src/misc/crypt/crypt_cipher_is_valid.o src/misc/crypt/crypt_constants.o \ +src/misc/crypt/crypt_find_cipher.o src/misc/crypt/crypt_find_cipher_any.o \ +src/misc/crypt/crypt_find_cipher_id.o src/misc/crypt/crypt_find_hash.o \ +src/misc/crypt/crypt_find_hash_any.o src/misc/crypt/crypt_find_hash_id.o \ +src/misc/crypt/crypt_find_hash_oid.o src/misc/crypt/crypt_find_prng.o src/misc/crypt/crypt_fsa.o \ +src/misc/crypt/crypt_hash_descriptor.o src/misc/crypt/crypt_hash_is_valid.o \ +src/misc/crypt/crypt_inits.o src/misc/crypt/crypt_ltc_mp_descriptor.o \ +src/misc/crypt/crypt_prng_descriptor.o src/misc/crypt/crypt_prng_is_valid.o \ +src/misc/crypt/crypt_prng_rng_descriptor.o src/misc/crypt/crypt_register_all_ciphers.o \ +src/misc/crypt/crypt_register_all_hashes.o src/misc/crypt/crypt_register_all_prngs.o \ +src/misc/crypt/crypt_register_cipher.o src/misc/crypt/crypt_register_hash.o \ +src/misc/crypt/crypt_register_prng.o src/misc/crypt/crypt_sizes.o \ src/misc/crypt/crypt_unregister_cipher.o src/misc/crypt/crypt_unregister_hash.o \ src/misc/crypt/crypt_unregister_prng.o src/misc/deprecated.o src/misc/error_to_string.o \ src/misc/hkdf/hkdf.o src/misc/hkdf/hkdf_test.o src/misc/mem_neq.o src/misc/padding/padding_depad.o \ @@ -424,9 +425,9 @@ LTC_CFLAGS := $(LTC_CFLAGS) -Wno-shadow -Isrc/ciphers/aes -Isrc/ciphers/safer -I endif # List of test objects to compile (all goes to libtomcrypt_prof.a) -TOBJECTS=tests/base16_test.o tests/base32_test.o tests/base64_test.o tests/bcrypt_test.o \ -tests/cipher_hash_test.o tests/common.o tests/deprecated_test.o tests/der_test.o tests/dh_test.o \ -tests/dsa_test.o tests/ecc_test.o tests/ed25519_test.o tests/file_test.o tests/mac_test.o \ +TOBJECTS=tests/argon2_test.o tests/base16_test.o tests/base32_test.o tests/base64_test.o \ +tests/bcrypt_test.o tests/cipher_hash_test.o tests/common.o tests/deprecated_test.o tests/der_test.o \ +tests/dh_test.o tests/dsa_test.o tests/ecc_test.o tests/ed25519_test.o tests/file_test.o tests/mac_test.o \ 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 \ diff --git a/sources.cmake b/sources.cmake index 85cc37fde..9da7f144b 100644 --- a/sources.cmake +++ b/sources.cmake @@ -190,6 +190,7 @@ src/math/rand_bn.c src/math/rand_prime.c src/math/tfm_desc.c src/misc/adler32.c +src/misc/argon2/argon2.c src/misc/base16/base16_decode.c src/misc/base16/base16_encode.c src/misc/base32/base32_decode.c diff --git a/src/headers/tomcrypt_custom.h b/src/headers/tomcrypt_custom.h index 5013004b5..31b383b8d 100644 --- a/src/headers/tomcrypt_custom.h +++ b/src/headers/tomcrypt_custom.h @@ -514,6 +514,8 @@ #define LTC_BCRYPT_DEFAULT_ROUNDS 10 #endif +#define LTC_ARGON2 + /* Keep LTC_NO_HKDF for compatibility reasons * superseeded by LTC_NO_MISC*/ #ifndef LTC_NO_HKDF @@ -681,6 +683,10 @@ #error LTC_BCRYPT requires LTC_BLOWFISH #endif +#if defined(LTC_ARGON2) && !defined(LTC_BLAKE2B) + #error LTC_ARGON2 requires LTC_BLAKE2B +#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 573591c0c..18a89e7a7 100644 --- a/src/headers/tomcrypt_misc.h +++ b/src/headers/tomcrypt_misc.h @@ -53,6 +53,24 @@ int base16_decode(const char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen); #endif +/* ---- Argon2 password hashing function (RFC 9106) ---- */ +#ifdef LTC_ARGON2 +typedef enum argon2_type { + ARGON2_D = 0, + ARGON2_I = 1, + ARGON2_ID = 2 +} argon2_type; + +int argon2_hash(const unsigned char *pwd, unsigned long pwdlen, + const unsigned char *salt, unsigned long saltlen, + const unsigned char *secret, unsigned long secretlen, + const unsigned char *ad, unsigned long adlen, + unsigned int t_cost, unsigned int m_cost, + unsigned int parallelism, + argon2_type type, + unsigned char *out, unsigned long outlen); +#endif /* LTC_ARGON2 */ + #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/argon2/argon2.c b/src/misc/argon2/argon2.c new file mode 100644 index 000000000..1af2b2858 --- /dev/null +++ b/src/misc/argon2/argon2.c @@ -0,0 +1,564 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ +#include "tomcrypt_private.h" + +/** + @file argon2.c + Argon2 password hashing function (RFC 9106) +*/ +#ifdef LTC_ARGON2 + +#define ARGON2_BLOCK_SIZE 1024 +#define ARGON2_QWORDS_IN_BLOCK 128 +#define ARGON2_ADDRESSES_IN_BLOCK 128 +#define ARGON2_PREHASH_DIGEST_LEN 64 +#define ARGON2_PREHASH_SEED_LEN 72 +#define ARGON2_SYNC_POINTS 4 +#define ARGON2_VERSION 0x13 +#define ARGON2_MIN_OUTLEN 4 +#define ARGON2_BLAKE2B_OUTBYTES 64 + +/* 1024-byte memory block */ +typedef struct argon2_block { + ulong64 v[ARGON2_QWORDS_IN_BLOCK]; +} argon2_block; + +/* instance state */ +typedef struct argon2_instance { + argon2_block *memory; + ulong32 passes; + ulong32 memory_blocks; + ulong32 segment_length; + ulong32 lane_length; + ulong32 lanes; + int type; /* 0=d 1=i 2=id */ +} argon2_instance; + +/* position within the memory matrix */ +typedef struct argon2_position { + ulong32 pass; + ulong32 lane; + unsigned char slice; + ulong32 index; +} argon2_position; + +static void s_block_init(argon2_block *b, unsigned char v) +{ + XMEMSET(b->v, v, sizeof(b->v)); +} + +static void s_block_copy(argon2_block *dst, const argon2_block *src) +{ + XMEMCPY(dst->v, src->v, sizeof(dst->v)); +} + +static void s_block_xor(argon2_block *dst, const argon2_block *src) +{ + unsigned i; + for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) { + dst->v[i] ^= src->v[i]; + } +} + +static void s_block_load(argon2_block *dst, const unsigned char *input) +{ + unsigned i; + for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) { + LOAD64L(dst->v[i], input + i * 8); + } +} + +static void s_block_store(unsigned char *output, const argon2_block *src) +{ + unsigned i; + for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i) { + STORE64L(src->v[i], output + i * 8); + } +} + +/* Variable-length hash H' (RFC 9106 Section 3.2) */ +static int s_blake2b_hash(unsigned char *out, unsigned long outlen, const unsigned char *in, unsigned long inlen) +{ + hash_state md; + int err; + + if ((err = blake2b_init(&md, outlen, NULL, 0)) != CRYPT_OK) return err; + if ((err = blake2b_process(&md, in, inlen)) != CRYPT_OK) return err; + if ((err = blake2b_done(&md, out)) != CRYPT_OK) return err; + return CRYPT_OK; +} + +static int s_blake2b_long(unsigned char *out, unsigned long outlen, const unsigned char *in, unsigned long inlen) +{ + unsigned char outlen_le[4]; + int err; + + STORE32L((ulong32)outlen, outlen_le); + + if (outlen <= ARGON2_BLAKE2B_OUTBYTES) { + /* single hash with truncated output */ + hash_state md; + if ((err = blake2b_init(&md, outlen, NULL, 0)) != CRYPT_OK) return err; + if ((err = blake2b_process(&md, outlen_le, 4)) != CRYPT_OK) return err; + if ((err = blake2b_process(&md, in, inlen)) != CRYPT_OK) return err; + if ((err = blake2b_done(&md, out)) != CRYPT_OK) return err; + } + else { + /* chained hashing for longer outputs */ + ulong32 toproduce; + unsigned char out_buffer[ARGON2_BLAKE2B_OUTBYTES]; + unsigned char in_buffer[ARGON2_BLAKE2B_OUTBYTES]; + hash_state md; + + if ((err = blake2b_init(&md, ARGON2_BLAKE2B_OUTBYTES, NULL, 0)) != CRYPT_OK) return err; + if ((err = blake2b_process(&md, outlen_le, 4)) != CRYPT_OK) return err; + if ((err = blake2b_process(&md, in, inlen)) != CRYPT_OK) return err; + if ((err = blake2b_done(&md, out_buffer)) != CRYPT_OK) return err; + + XMEMCPY(out, out_buffer, ARGON2_BLAKE2B_OUTBYTES / 2); + out += ARGON2_BLAKE2B_OUTBYTES / 2; + toproduce = (ulong32)outlen - ARGON2_BLAKE2B_OUTBYTES / 2; + + while (toproduce > ARGON2_BLAKE2B_OUTBYTES) { + XMEMCPY(in_buffer, out_buffer, ARGON2_BLAKE2B_OUTBYTES); + if ((err = s_blake2b_hash(out_buffer, ARGON2_BLAKE2B_OUTBYTES, in_buffer, ARGON2_BLAKE2B_OUTBYTES)) != CRYPT_OK) return err; + XMEMCPY(out, out_buffer, ARGON2_BLAKE2B_OUTBYTES / 2); + out += ARGON2_BLAKE2B_OUTBYTES / 2; + toproduce -= ARGON2_BLAKE2B_OUTBYTES / 2; + } + + XMEMCPY(in_buffer, out_buffer, ARGON2_BLAKE2B_OUTBYTES); + if ((err = s_blake2b_hash(out_buffer, toproduce, in_buffer, ARGON2_BLAKE2B_OUTBYTES)) != CRYPT_OK) return err; + XMEMCPY(out, out_buffer, toproduce); + } + return CRYPT_OK; +} + +/* BlaMka compression G (RFC 9106 Section 3.4 / 3.5) */ +static LTC_INLINE ulong64 s_fBlaMka(ulong64 x, ulong64 y) +{ + ulong64 m = CONST64(0xFFFFFFFF); + ulong64 xy = (x & m) * (y & m); + return x + y + 2 * xy; +} + +#define ARGON2_GB(a, b, c, d) \ + do { \ + a = s_fBlaMka(a, b); \ + d = ROR64(d ^ a, 32); \ + c = s_fBlaMka(c, d); \ + b = ROR64(b ^ c, 24); \ + a = s_fBlaMka(a, b); \ + d = ROR64(d ^ a, 16); \ + c = s_fBlaMka(c, d); \ + b = ROR64(b ^ c, 63); \ + } while (0) + +#define ARGON2_ROUND(v0,v1,v2,v3,v4,v5,v6,v7,v8,v9,v10,v11,v12,v13,v14,v15) \ + do { \ + ARGON2_GB(v0, v4, v8, v12); \ + ARGON2_GB(v1, v5, v9, v13); \ + ARGON2_GB(v2, v6, v10, v14); \ + ARGON2_GB(v3, v7, v11, v15); \ + ARGON2_GB(v0, v5, v10, v15); \ + ARGON2_GB(v1, v6, v11, v12); \ + ARGON2_GB(v2, v7, v8, v13); \ + ARGON2_GB(v3, v4, v9, v14); \ + } while (0) + +/* Fill a new block from prev_block and ref_block. If with_xor, XOR result with existing next_block content. */ +static void s_fill_block(const argon2_block *prev_block, const argon2_block *ref_block, argon2_block *next_block, int with_xor) +{ + argon2_block blockR, block_tmp; + unsigned i; + + s_block_copy(&blockR, ref_block); + s_block_xor(&blockR, prev_block); + s_block_copy(&block_tmp, &blockR); + + if (with_xor) { + s_block_xor(&block_tmp, next_block); + } + + /* Apply P on columns: (0..15), (16..31), ..., (112..127) */ + for (i = 0; i < 8; ++i) { + ARGON2_ROUND( + blockR.v[16*i], blockR.v[16*i+1], blockR.v[16*i+2], blockR.v[16*i+3], + blockR.v[16*i+4], blockR.v[16*i+5], blockR.v[16*i+6], blockR.v[16*i+7], + blockR.v[16*i+8], blockR.v[16*i+9], blockR.v[16*i+10], blockR.v[16*i+11], + blockR.v[16*i+12], blockR.v[16*i+13], blockR.v[16*i+14], blockR.v[16*i+15]); + } + + /* Apply P on rows: (0,1,16,17,32,33,...,112,113), etc. */ + for (i = 0; i < 8; ++i) { + ARGON2_ROUND( + blockR.v[2*i], blockR.v[2*i+1], blockR.v[2*i+16], blockR.v[2*i+17], + blockR.v[2*i+32], blockR.v[2*i+33], blockR.v[2*i+48], blockR.v[2*i+49], + blockR.v[2*i+64], blockR.v[2*i+65], blockR.v[2*i+80], blockR.v[2*i+81], + blockR.v[2*i+96], blockR.v[2*i+97], blockR.v[2*i+112],blockR.v[2*i+113]); + } + + s_block_copy(next_block, &block_tmp); + s_block_xor(next_block, &blockR); +} + +/* Generate next pseudo-random addresses for data-independent indexing */ +static void s_next_addresses(argon2_block *address_block, argon2_block *input_block, const argon2_block *zero_block) +{ + input_block->v[6]++; + s_fill_block(zero_block, input_block, address_block, 0); + s_fill_block(zero_block, address_block, address_block, 0); +} + +/* Index computation (RFC 9106 Section 3.3) */ +static ulong32 s_index_alpha(const argon2_instance *instance, const argon2_position *position, ulong32 pseudo_rand, int same_lane) +{ + ulong32 reference_area_size; + ulong64 relative_position; + ulong32 start_position, absolute_position; + + if (position->pass == 0) { + if (position->slice == 0) { + reference_area_size = position->index - 1; + } + else { + if (same_lane) { + reference_area_size = position->slice * instance->segment_length + position->index - 1; + } + else { + reference_area_size = position->slice * instance->segment_length + ((position->index == 0) ? ((ulong32)-1) : 0); + } + } + } + else { + if (same_lane) { + reference_area_size = instance->lane_length - instance->segment_length + position->index - 1; + } + else { + reference_area_size = instance->lane_length - instance->segment_length + ((position->index == 0) ? ((ulong32)-1) : 0); + } + } + + /* Map pseudo_rand to 0..reference_area_size-1 */ + relative_position = (ulong64)pseudo_rand; + relative_position = relative_position * relative_position >> 32; + relative_position = reference_area_size - 1 - ((ulong64)reference_area_size * relative_position >> 32); + + start_position = 0; + if (position->pass != 0) { + start_position = (position->slice == ARGON2_SYNC_POINTS - 1) ? 0 : (position->slice + 1) * instance->segment_length; + } + absolute_position = (start_position + (ulong32)relative_position) % instance->lane_length; + return absolute_position; +} + +/* Fill one segment (lane x slice intersection) */ +static void s_fill_segment(const argon2_instance *instance, argon2_position position) +{ + argon2_block *ref_block, *curr_block; + argon2_block address_block, input_block, zero_block; + ulong64 pseudo_rand; + ulong32 ref_index, prev_offset, curr_offset; + ulong32 starting_index, i; + ulong64 ref_lane; + int data_independent; + + data_independent = (instance->type == ARGON2_I) || (instance->type == ARGON2_ID && position.pass == 0 && position.slice < ARGON2_SYNC_POINTS / 2); + + if (data_independent) { + s_block_init(&zero_block, 0); + s_block_init(&input_block, 0); + input_block.v[0] = position.pass; + input_block.v[1] = position.lane; + input_block.v[2] = position.slice; + input_block.v[3] = instance->memory_blocks; + input_block.v[4] = instance->passes; + input_block.v[5] = instance->type; + } + + starting_index = 0; + if (position.pass == 0 && position.slice == 0) { + starting_index = 2; + if (data_independent) { + s_next_addresses(&address_block, &input_block, &zero_block); + } + } + + curr_offset = position.lane * instance->lane_length + position.slice * instance->segment_length + starting_index; + + if (curr_offset % instance->lane_length == 0) { + prev_offset = curr_offset + instance->lane_length - 1; + } + else { + prev_offset = curr_offset - 1; + } + + for (i = starting_index; i < instance->segment_length; ++i, ++curr_offset, ++prev_offset) { + if (curr_offset % instance->lane_length == 1) { + prev_offset = curr_offset - 1; + } + + /* Get pseudo-random value */ + if (data_independent) { + if (i % ARGON2_ADDRESSES_IN_BLOCK == 0) { + s_next_addresses(&address_block, &input_block, &zero_block); + } + pseudo_rand = address_block.v[i % ARGON2_ADDRESSES_IN_BLOCK]; + } + else { + pseudo_rand = instance->memory[prev_offset].v[0]; + } + + ref_lane = (pseudo_rand >> 32) % instance->lanes; + if (position.pass == 0 && position.slice == 0) { + ref_lane = position.lane; + } + + position.index = i; + ref_index = s_index_alpha(instance, &position, (ulong32)(pseudo_rand & CONST64(0xFFFFFFFF)), ref_lane == position.lane); + ref_block = instance->memory + instance->lane_length * (ulong32)ref_lane + ref_index; + curr_block = instance->memory + curr_offset; + + if (position.pass == 0) { + s_fill_block(instance->memory + prev_offset, ref_block, curr_block, 0); + } + else { + s_fill_block(instance->memory + prev_offset, ref_block, curr_block, 1); + } + } +} + +/* Initial hash H_0 (RFC 9106 Section 3.1 step 1) */ +static int s_initial_hash(unsigned char *blockhash, + const unsigned char *pwd, unsigned long pwdlen, + const unsigned char *salt, unsigned long saltlen, + const unsigned char *secret, unsigned long secretlen, + const unsigned char *ad, unsigned long adlen, + ulong32 t_cost, ulong32 m_cost, ulong32 parallelism, + ulong32 outlen, int type) +{ + hash_state md; + unsigned char value[4]; + int err; + + if ((err = blake2b_init(&md, ARGON2_PREHASH_DIGEST_LEN, NULL, 0)) != CRYPT_OK) return err; + + STORE32L(parallelism, value); + if ((err = blake2b_process(&md, value, 4)) != CRYPT_OK) return err; + + STORE32L(outlen, value); + if ((err = blake2b_process(&md, value, 4)) != CRYPT_OK) return err; + + STORE32L(m_cost, value); + if ((err = blake2b_process(&md, value, 4)) != CRYPT_OK) return err; + + STORE32L(t_cost, value); + if ((err = blake2b_process(&md, value, 4)) != CRYPT_OK) return err; + + STORE32L((ulong32)ARGON2_VERSION, value); + if ((err = blake2b_process(&md, value, 4)) != CRYPT_OK) return err; + + STORE32L((ulong32)type, value); + if ((err = blake2b_process(&md, value, 4)) != CRYPT_OK) return err; + + STORE32L((ulong32)pwdlen, value); + if ((err = blake2b_process(&md, value, 4)) != CRYPT_OK) return err; + if (pwdlen > 0) { + if ((err = blake2b_process(&md, pwd, pwdlen)) != CRYPT_OK) return err; + } + + STORE32L((ulong32)saltlen, value); + if ((err = blake2b_process(&md, value, 4)) != CRYPT_OK) return err; + if (saltlen > 0) { + if ((err = blake2b_process(&md, salt, saltlen)) != CRYPT_OK) return err; + } + + STORE32L((ulong32)secretlen, value); + if ((err = blake2b_process(&md, value, 4)) != CRYPT_OK) return err; + if (secretlen > 0 && secret != NULL) { + if ((err = blake2b_process(&md, secret, secretlen)) != CRYPT_OK) return err; + } + + STORE32L((ulong32)adlen, value); + if ((err = blake2b_process(&md, value, 4)) != CRYPT_OK) return err; + if (adlen > 0 && ad != NULL) { + if ((err = blake2b_process(&md, ad, adlen)) != CRYPT_OK) return err; + } + + if ((err = blake2b_done(&md, blockhash)) != CRYPT_OK) return err; + return CRYPT_OK; +} + +/* Generate first two blocks in each lane */ + +static int s_fill_first_blocks(unsigned char *blockhash, const argon2_instance *instance) +{ + ulong32 l; + unsigned char blockhash_bytes[ARGON2_BLOCK_SIZE]; + int err; + + for (l = 0; l < instance->lanes; ++l) { + /* B[i][0] = H'(H_0 || LE32(0) || LE32(i)) */ + STORE32L(0, blockhash + ARGON2_PREHASH_DIGEST_LEN); + STORE32L(l, blockhash + ARGON2_PREHASH_DIGEST_LEN + 4); + if ((err = s_blake2b_long(blockhash_bytes, ARGON2_BLOCK_SIZE, blockhash, ARGON2_PREHASH_SEED_LEN)) != CRYPT_OK) return err; + s_block_load(&instance->memory[l * instance->lane_length + 0], blockhash_bytes); + + /* B[i][1] = H'(H_0 || LE32(1) || LE32(i)) */ + STORE32L(1, blockhash + ARGON2_PREHASH_DIGEST_LEN); + if ((err = s_blake2b_long(blockhash_bytes, ARGON2_BLOCK_SIZE, blockhash, ARGON2_PREHASH_SEED_LEN)) != CRYPT_OK) return err; + s_block_load(&instance->memory[l * instance->lane_length + 1], blockhash_bytes); + } + + zeromem(blockhash_bytes, ARGON2_BLOCK_SIZE); + return CRYPT_OK; +} + +/* Finalize: XOR last blocks, produce tag */ +static int s_finalize(unsigned char *out, unsigned long outlen, + const argon2_instance *instance) +{ + argon2_block blockhash; + unsigned char blockhash_bytes[ARGON2_BLOCK_SIZE]; + ulong32 l; + int err; + + s_block_copy(&blockhash, instance->memory + instance->lane_length - 1); + + for (l = 1; l < instance->lanes; ++l) { + ulong32 last = l * instance->lane_length + (instance->lane_length - 1); + s_block_xor(&blockhash, instance->memory + last); + } + + s_block_store(blockhash_bytes, &blockhash); + err = s_blake2b_long(out, outlen, blockhash_bytes, ARGON2_BLOCK_SIZE); + + zeromem(blockhash.v, ARGON2_BLOCK_SIZE); + zeromem(blockhash_bytes, ARGON2_BLOCK_SIZE); + return err; +} + +/* Fill the entire memory */ +static void s_fill_memory(argon2_instance *instance) +{ + ulong32 r, s, l; + + for (r = 0; r < instance->passes; ++r) { + for (s = 0; s < ARGON2_SYNC_POINTS; ++s) { + for (l = 0; l < instance->lanes; ++l) { + argon2_position position; + position.pass = r; + position.lane = l; + position.slice = (unsigned char)s; + position.index = 0; + s_fill_segment(instance, position); + } + } + } +} + +/** + Hash a password with Argon2 (RFC 9106) + + @param pwd Password (or message) + @param pwdlen Length of password + @param salt Salt + @param saltlen Length of salt + @param secret Optional secret value (may be NULL) + @param secretlen Length of secret + @param ad Optional associated data (may be NULL) + @param adlen Length of associated data + @param t_cost Number of passes (iterations), minimum 1 + @param m_cost Memory size in KiB, minimum 8*parallelism + @param parallelism Degree of parallelism (number of lanes), minimum 1 + @param type ARGON2_D, ARGON2_I, or ARGON2_ID + @param out [out] Output tag + @param outlen Desired output length (4..2^32-1) + @return CRYPT_OK on success +*/ +int argon2_hash(const unsigned char *pwd, unsigned long pwdlen, + const unsigned char *salt, unsigned long saltlen, + const unsigned char *secret, unsigned long secretlen, + const unsigned char *ad, unsigned long adlen, + unsigned int t_cost, unsigned int m_cost, + unsigned int parallelism, + argon2_type type, + unsigned char *out, unsigned long outlen) +{ + argon2_instance instance; + unsigned char blockhash[ARGON2_PREHASH_SEED_LEN]; + ulong32 memory_blocks, segment_length; + int err; + + LTC_ARGCHK(out != NULL); + LTC_ARGCHK(outlen >= ARGON2_MIN_OUTLEN); + LTC_ARGCHK(pwd != NULL || pwdlen == 0); + LTC_ARGCHK(salt != NULL || saltlen == 0); + LTC_ARGCHK(secret != NULL || secretlen == 0); + LTC_ARGCHK(ad != NULL || adlen == 0); + LTC_ARGCHK(t_cost >= 1); + LTC_ARGCHK(parallelism >= 1); + LTC_ARGCHK(m_cost >= 8 * parallelism); + LTC_ARGCHK(type == ARGON2_D || type == ARGON2_I || type == ARGON2_ID); + + /* Align memory: ensure memory_blocks is a multiple of 4*parallelism */ + memory_blocks = (ulong32)m_cost; + if (memory_blocks < 2 * ARGON2_SYNC_POINTS * (ulong32)parallelism) { + memory_blocks = 2 * ARGON2_SYNC_POINTS * (ulong32)parallelism; + } + segment_length = memory_blocks / ((ulong32)parallelism * ARGON2_SYNC_POINTS); + memory_blocks = segment_length * ((ulong32)parallelism * ARGON2_SYNC_POINTS); + + /* Set up instance */ + instance.passes = (ulong32)t_cost; + instance.memory_blocks = memory_blocks; + instance.segment_length = segment_length; + instance.lane_length = segment_length * ARGON2_SYNC_POINTS; + instance.lanes = (ulong32)parallelism; + instance.type = (int)type; + instance.memory = NULL; + + /* Allocate memory */ + { + unsigned long alloc_size = (unsigned long)memory_blocks * sizeof(argon2_block); + /* overflow check */ + if (alloc_size / sizeof(argon2_block) != memory_blocks) { + return CRYPT_OVERFLOW; + } + instance.memory = XMALLOC(alloc_size); + if (instance.memory == NULL) { + return CRYPT_MEM; + } + } + + /* Initial hash H_0 */ + err = s_initial_hash(blockhash, pwd, pwdlen, salt, saltlen, + secret, secretlen, ad, adlen, + (ulong32)t_cost, (ulong32)m_cost, + (ulong32)parallelism, (ulong32)outlen, + (int)type); + if (err != CRYPT_OK) goto cleanup; + + /* Zero the extra 8 bytes after digest */ + XMEMSET(blockhash + ARGON2_PREHASH_DIGEST_LEN, 0, ARGON2_PREHASH_SEED_LEN - ARGON2_PREHASH_DIGEST_LEN); + + /* Generate first blocks */ + err = s_fill_first_blocks(blockhash, &instance); + if (err != CRYPT_OK) goto cleanup; + + /* Fill memory */ + s_fill_memory(&instance); + + /* Finalize */ + err = s_finalize(out, outlen, &instance); + +cleanup: + if (instance.memory != NULL) { + zeromem(instance.memory, (unsigned long)memory_blocks * sizeof(argon2_block)); + XFREE(instance.memory); + } + zeromem(blockhash, ARGON2_PREHASH_SEED_LEN); + return err; +} + +#endif /* LTC_ARGON2 */ diff --git a/src/misc/crypt/crypt.c b/src/misc/crypt/crypt.c index 42fe2e306..cc95fe8eb 100644 --- a/src/misc/crypt/crypt.c +++ b/src/misc/crypt/crypt.c @@ -451,6 +451,9 @@ const char *crypt_build_settings = #if defined(LTC_BASE16) " BASE16 " #endif +#if defined(LTC_ARGON2) + " ARGON2 " +#endif #if defined(LTC_BCRYPT) " BCRYPT " " " NAME_VALUE(LTC_BCRYPT_DEFAULT_ROUNDS) " " diff --git a/tests/argon2_test.c b/tests/argon2_test.c new file mode 100644 index 000000000..5274f2722 --- /dev/null +++ b/tests/argon2_test.c @@ -0,0 +1,85 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ + +#include + +#ifdef LTC_ARGON2 + +/* RFC 9106 test vectors */ + +int argon2_test(void) +{ + static const unsigned char password[32] = { + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 + }; + static const unsigned char salt[16] = { + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 + }; + static const unsigned char secret[8] = { + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 + }; + static const unsigned char ad[12] = { + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04 + }; + + static const unsigned char expected_argon2d[32] = { + 0x51, 0x2b, 0x39, 0x1b, 0x6f, 0x11, 0x62, 0x97, + 0x53, 0x71, 0xd3, 0x09, 0x19, 0x73, 0x42, 0x94, + 0xf8, 0x68, 0xe3, 0xbe, 0x39, 0x84, 0xf3, 0xc1, + 0xa1, 0x3a, 0x4d, 0xb9, 0xfa, 0xbe, 0x4a, 0xcb + }; + static const unsigned char expected_argon2i[32] = { + 0xc8, 0x14, 0xd9, 0xd1, 0xdc, 0x7f, 0x37, 0xaa, + 0x13, 0xf0, 0xd7, 0x7f, 0x24, 0x94, 0xbd, 0xa1, + 0xc8, 0xde, 0x6b, 0x01, 0x6d, 0xd3, 0x88, 0xd2, + 0x99, 0x52, 0xa4, 0xc4, 0x67, 0x2b, 0x6c, 0xe8 + }; + static const unsigned char expected_argon2id[32] = { + 0x0d, 0x64, 0x0d, 0xf5, 0x8d, 0x78, 0x76, 0x6c, + 0x08, 0xc0, 0x37, 0xa3, 0x4a, 0x8b, 0x53, 0xc9, + 0xd0, 0x1e, 0xf0, 0x45, 0x2d, 0x75, 0xb6, 0x5e, + 0xb5, 0x25, 0x20, 0xe9, 0x6b, 0x01, 0xe6, 0x59 + }; + + unsigned char tag[32]; + + const struct { + const char *name; + argon2_type type; + const unsigned char *expected; + unsigned long elen; + } argon_testcase[] = { + { "Argon2d", ARGON2_D, expected_argon2d, sizeof(expected_argon2d) }, + { "Argon2i", ARGON2_I, expected_argon2i, sizeof(expected_argon2i) }, + { "Argon2id", ARGON2_ID, expected_argon2id, sizeof(expected_argon2id) }, + }; + + size_t n; + + for (n = 0; n < LTC_ARRAY_SIZE(argon_testcase); ++n) { + DO(argon2_hash(password, sizeof(password), + salt, sizeof(salt), + secret, sizeof(secret), + ad, sizeof(ad), + 3, 32, 4, + argon_testcase[n].type, + tag, sizeof(tag))); + COMPARE_TESTVECTOR(tag, sizeof(tag), argon_testcase[n].expected, argon_testcase[n].elen, argon_testcase[n].name, n); + } + + return CRYPT_OK; +} + +#else + +int argon2_test(void) +{ + return CRYPT_NOP; +} + +#endif diff --git a/tests/misc_test.c b/tests/misc_test.c index 5a60ff236..4fff9c5d3 100644 --- a/tests/misc_test.c +++ b/tests/misc_test.c @@ -4,6 +4,9 @@ int misc_test(void) { +#ifdef LTC_ARGON2 + DO(argon2_test()); +#endif #ifdef LTC_BCRYPT DO(bcrypt_test()); #endif diff --git a/tests/sources.cmake b/tests/sources.cmake index b6eed361f..d0e5b465d 100644 --- a/tests/sources.cmake +++ b/tests/sources.cmake @@ -1,4 +1,5 @@ set(SOURCES +argon2_test.c base16_test.c base32_test.c base64_test.c diff --git a/tests/tomcrypt_test.h b/tests/tomcrypt_test.h index 4488a450c..70514f67f 100644 --- a/tests/tomcrypt_test.h +++ b/tests/tomcrypt_test.h @@ -42,6 +42,7 @@ int padding_test(void); int x25519_test(void); int ed25519_test(void); int ssh_test(void); +int argon2_test(void); int bcrypt_test(void); int no_null_termination_check_test(void); int pk_oid_test(void);