Argon2 password hashing function (RFC 9106)

This commit is contained in:
Karel Miko 2026-04-11 17:19:38 +02:00 committed by Steffen Jaeckel
parent 3223b87588
commit 458b392d01
15 changed files with 819 additions and 76 deletions

View file

@ -7589,6 +7589,46 @@ The \textit{out} parameter shall be a pointer to a buffer of at least 32 octets,
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}

View file

@ -1407,6 +1407,14 @@
RelativePath="src\misc\zeromem.c"
>
</File>
<Filter
Name="argon2"
>
<File
RelativePath="src\misc\argon2\argon2.c"
>
</File>
</Filter>
<Filter
Name="base16"
>

View file

@ -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 \

View file

@ -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 \

View file

@ -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 \

View file

@ -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 \

View file

@ -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

View file

@ -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

View file

@ -53,6 +53,23 @@ 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_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,

568
src/misc/argon2/argon2.c Normal file
View file

@ -0,0 +1,568 @@
/* 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_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 {
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);
/* Validate inputs */
if (outlen < ARGON2_MIN_OUTLEN) return CRYPT_INVALID_ARG;
if (pwd == NULL && pwdlen != 0) return CRYPT_INVALID_ARG;
if (salt == NULL && saltlen != 0) return CRYPT_INVALID_ARG;
if (secret == NULL && secretlen != 0) return CRYPT_INVALID_ARG;
if (ad == NULL && adlen != 0) return CRYPT_INVALID_ARG;
if (t_cost < 1) return CRYPT_INVALID_ARG;
if (parallelism < 1) return CRYPT_INVALID_ARG;
if (m_cost < 8 * parallelism) return CRYPT_INVALID_ARG;
if (type != ARGON2_D && type != ARGON2_I && type != ARGON2_ID) return CRYPT_INVALID_ARG;
/* 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_MEM;
}
instance.memory = (argon2_block *)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;
zeromem(blockhash, ARGON2_PREHASH_SEED_LEN);
/* 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 */

View file

@ -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) " "

91
tests/argon2_test.c Normal file
View file

@ -0,0 +1,91 @@
/* LibTomCrypt, modular cryptographic library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
#include <tomcrypt_test.h>
#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];
/* Argon2d */
DO(argon2_hash(password, sizeof(password),
salt, sizeof(salt),
secret, sizeof(secret),
ad, sizeof(ad),
3, 32, 4,
ARGON2_D,
tag, sizeof(tag)));
COMPARE_TESTVECTOR(tag, sizeof(tag), expected_argon2d, sizeof(expected_argon2d), "Argon2d", 0);
/* Argon2i */
DO(argon2_hash(password, sizeof(password),
salt, sizeof(salt),
secret, sizeof(secret),
ad, sizeof(ad),
3, 32, 4,
ARGON2_I,
tag, sizeof(tag)));
COMPARE_TESTVECTOR(tag, sizeof(tag), expected_argon2i, sizeof(expected_argon2i), "Argon2i", 1);
/* Argon2id */
DO(argon2_hash(password, sizeof(password),
salt, sizeof(salt),
secret, sizeof(secret),
ad, sizeof(ad),
3, 32, 4,
ARGON2_ID,
tag, sizeof(tag)));
COMPARE_TESTVECTOR(tag, sizeof(tag), expected_argon2id, sizeof(expected_argon2id), "Argon2id", 2);
return CRYPT_OK;
}
#else
int argon2_test(void)
{
return CRYPT_NOP;
}
#endif

View file

@ -4,6 +4,9 @@
int misc_test(void)
{
#ifdef LTC_ARGON2
DO(argon2_test());
#endif
#ifdef LTC_BCRYPT
DO(bcrypt_test());
#endif

View file

@ -1,4 +1,5 @@
set(SOURCES
argon2_test.c
base16_test.c
base32_test.c
base64_test.c

View file

@ -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);