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