summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorČestmír Kalina <ckalina@redhat.com>2021-09-27 22:45:38 +0200
committerPauli <pauli@openssl.org>2023-03-17 11:12:47 +1100
commit6dfa998f7ea150f9c6d4e4727cf6d5c82a68a8da (patch)
tree3d9fcb93c1e3bd8b55f661e09fa30ac519b74368
parent0e200d2a19185dab9d73eee90bd6cd0246416a9e (diff)
providers: add Argon2 KDF
https://datatracker.ietf.org/doc/rfc9106/ Signed-off-by: Čestmír Kalina <ckalina@redhat.com> Reviewed-by: Tomas Mraz <tomas@openssl.org> Reviewed-by: Paul Dale <pauli@openssl.org> (Merged from https://github.com/openssl/openssl/pull/12256)
-rw-r--r--CHANGES.md5
-rwxr-xr-xConfigure3
-rw-r--r--apps/list.c3
-rw-r--r--crypto/err/openssl.txt2
-rw-r--r--doc/build.info6
-rw-r--r--doc/man7/EVP_KDF-ARGON2.pod192
-rw-r--r--doc/man7/OSSL_PROVIDER-default.pod2
-rw-r--r--include/openssl/core_names.h6
-rw-r--r--include/openssl/proverr.h2
-rw-r--r--providers/defltprov.c5
-rw-r--r--providers/implementations/include/prov/implementations.h5
-rw-r--r--providers/implementations/include/prov/names.h3
-rw-r--r--providers/implementations/kdfs/argon2.c1586
-rw-r--r--providers/implementations/kdfs/build.info2
-rw-r--r--test/evp_test.c46
-rw-r--r--test/recipes/30-test_evp.t2
-rw-r--r--test/recipes/30-test_evp_data/evpkdf_argon2.txt168
17 files changed, 2036 insertions, 2 deletions
diff --git a/CHANGES.md b/CHANGES.md
index 711454ec43..860f91d5f8 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -232,6 +232,11 @@ OpenSSL 3.2
*Hubert Kario*
+ * Support for Argon2d, Argon2i, Argon2id KDFs has been added along with
+ basic thread pool implementation for select platforms.
+
+ *Čestmír Kalina*
+
OpenSSL 3.1
-----------
diff --git a/Configure b/Configure
index b6bbec0a85..6ef882f5a3 100755
--- a/Configure
+++ b/Configure
@@ -408,6 +408,7 @@ my @dtls = qw(dtls1 dtls1_2);
my @disablables = (
"acvp-tests",
"afalgeng",
+ "argon2",
"aria",
"asan",
"asm",
@@ -669,6 +670,8 @@ my @disable_cascades = (
"threads" => [ "thread-pool" ],
"thread-pool" => [ "default-thread-pool" ],
+ "blake2" => [ "argon2" ],
+
"deprecated-3.0" => [ "engine", "srp" ]
);
diff --git a/apps/list.c b/apps/list.c
index 713a20cafa..2ccd41277a 100644
--- a/apps/list.c
+++ b/apps/list.c
@@ -1286,6 +1286,9 @@ static void list_engines(void)
static void list_disabled(void)
{
BIO_puts(bio_out, "Disabled algorithms:\n");
+#ifdef OPENSSL_NO_ARGON2
+ BIO_puts(bio_out, "ARGON2\n");
+#endif
#ifdef OPENSSL_NO_ARIA
BIO_puts(bio_out, "ARIA\n");
#endif
diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt
index 018c7cbd33..d0336d9729 100644
--- a/crypto/err/openssl.txt
+++ b/crypto/err/openssl.txt
@@ -1043,7 +1043,7 @@ PROV_R_INVALID_KDF:232:invalid kdf
PROV_R_INVALID_KEY:158:invalid key
PROV_R_INVALID_KEY_LENGTH:105:invalid key length
PROV_R_INVALID_MAC:151:invalid mac
-PROV_R_INVALID_MEMORY_SIZE:233:invalid memory size
+PROV_R_INVALID_MEMORY_SIZE:235:invalid memory size
PROV_R_INVALID_MGF1_MD:167:invalid mgf1 md
PROV_R_INVALID_MODE:125:invalid mode
PROV_R_INVALID_OUTPUT_LENGTH:217:invalid output length
diff --git a/doc/build.info b/doc/build.info
index 14c7775b0c..f6a8b951f3 100644
--- a/doc/build.info
+++ b/doc/build.info
@@ -4289,6 +4289,10 @@ DEPEND[html/man7/EVP_CIPHER-SM4.html]=man7/EVP_CIPHER-SM4.pod
GENERATE[html/man7/EVP_CIPHER-SM4.html]=man7/EVP_CIPHER-SM4.pod
DEPEND[man/man7/EVP_CIPHER-SM4.7]=man7/EVP_CIPHER-SM4.pod
GENERATE[man/man7/EVP_CIPHER-SM4.7]=man7/EVP_CIPHER-SM4.pod
+DEPEND[html/man7/EVP_KDF-ARGON2.html]=man7/EVP_KDF-ARGON2.pod
+GENERATE[html/man7/EVP_KDF-ARGON2.html]=man7/EVP_KDF-ARGON2.pod
+DEPEND[man/man7/EVP_KDF-ARGON2.7]=man7/EVP_KDF-ARGON2.pod
+GENERATE[man/man7/EVP_KDF-ARGON2.7]=man7/EVP_KDF-ARGON2.pod
DEPEND[html/man7/EVP_KDF-HKDF.html]=man7/EVP_KDF-HKDF.pod
GENERATE[html/man7/EVP_KDF-HKDF.html]=man7/EVP_KDF-HKDF.pod
DEPEND[man/man7/EVP_KDF-HKDF.7]=man7/EVP_KDF-HKDF.pod
@@ -4773,6 +4777,7 @@ html/man7/EVP_CIPHER-RC4.html \
html/man7/EVP_CIPHER-RC5.html \
html/man7/EVP_CIPHER-SEED.html \
html/man7/EVP_CIPHER-SM4.html \
+html/man7/EVP_KDF-ARGON2.html \
html/man7/EVP_KDF-HKDF.html \
html/man7/EVP_KDF-HMAC-DRBG.html \
html/man7/EVP_KDF-KB.html \
@@ -4904,6 +4909,7 @@ man/man7/EVP_CIPHER-RC4.7 \
man/man7/EVP_CIPHER-RC5.7 \
man/man7/EVP_CIPHER-SEED.7 \
man/man7/EVP_CIPHER-SM4.7 \
+man/man7/EVP_KDF-ARGON2.7 \
man/man7/EVP_KDF-HKDF.7 \
man/man7/EVP_KDF-HMAC-DRBG.7 \
man/man7/EVP_KDF-KB.7 \
diff --git a/doc/man7/EVP_KDF-ARGON2.pod b/doc/man7/EVP_KDF-ARGON2.pod
new file mode 100644
index 0000000000..c44250e10e
--- /dev/null
+++ b/doc/man7/EVP_KDF-ARGON2.pod
@@ -0,0 +1,192 @@
+=pod
+
+=head1 NAME
+
+EVP_KDF-ARGON2 - The Argon2 EVP KDF implementation
+
+=head1 DESCRIPTION
+
+Support for computing the B<argon2> password-based KDF through the B<EVP_KDF>
+API.
+
+The EVP_KDF-ARGON2 algorithm implements the Argon2 password-based key
+derivation function, as described in IETF RFC 9106. It is memory-hard in
+the sense that it deliberately requires a significant amount of RAM for efficient
+computation. The intention of this is to render brute forcing of passwords on
+systems that lack large amounts of main memory (such as GPUs or ASICs)
+computationally infeasible.
+
+Argon2d (Argon2i) uses data-dependent (data-independent) memory access and
+primary seek to address trade-off (side-channel) attacks.
+
+Argon2id is a hybrid construction which, in the first two slices of the first
+pass, generates reference addresses data-independently as in Argon2i, whereas
+in later slices and next passess it generates them data-dependently as in
+Argon2d.
+
+Sbox-hardened version Argon2ds is not supported.
+
+For more information, please refer to RFC 9106.
+
+=head2 Supported parameters
+
+The supported parameters are:
+
+=over 4
+
+=item "pass" (B<OSSL_KDF_PARAM_PASSWORD>) <octet string>
+
+=item "salt" (B<OSSL_KDF_PARAM_SALT>) <octet string>
+
+=item "secret" (B<OSSL_KDF_PARAM_SECRET>) <octet string>
+
+=item "iter" (B<OSSL_KDF_PARAM_ITER>) <unsigned integer>
+
+=item "size" (B<OSSL_KDF_PARAM_SIZE>) <unsigned integer>
+
+These parameters work as described in L<EVP_KDF(3)/PARAMETERS>.
+
+Note that RFC 9106 recommends 128 bits salt for most applications, or 64 bits
+salt in the case of space constraints. At least 128 bits output length is
+recommended.
+
+Note that secret (or pepper) is an optional secret data used along the
+password.
+
+=item "threads" (B<OSSL_KDF_PARAM_THREADS>) <unsigned integer>
+
+The number of threads, bounded above by the number of lanes.
+
+This can only be used with built-in thread support. Threading must be
+explicitly enabled. See EXAMPLES section for more information.
+
+=item "ad" (B<OSSL_KDF_PARAM_ARGON2_AD>) <octet string>
+
+Optional associated data, may be used to "tag" a group of keys, or tie them
+to a particular public key, without having to modify salt.
+
+=item "lanes" (B<OSSL_KDF_PARAM_ARGON2_LANES>) <unsigned integer>
+
+Argon2 splits the requested memory size into lanes, each of which is designed
+to be processed in parallel. For example, on a system with p cores, it's
+recommended to use p lanes.
+
+The number of lanes is used to derive the key. It is possible to specify
+more lanes than the number of available computational threads. This is
+especially encouraged if multi-threading is disabled.
+
+=item "memcost" (B<OSSL_KDF_PARAM_ARGON2_MEMCOST>) <unsigned integer>
+
+Memory cost parameter (the number of 1k memory blocks used).
+
+=item "version" (B<OSSL_KDF_PARAM_ARGON2_VERSION>) <unsigned integer>
+
+Argon2 version. Supported values: 0x10, 0x13 (default).
+
+=item "early_clean" (B<OSSL_KDF_PARAM_EARLY_CLEAN>) <unsigned integer>
+
+If set (nonzero), password and secret stored in Argon2 context are zeroed
+early during initial hash computation, as soon as they are not needed.
+Otherwise, they are zeroed along the rest of Argon2 context data on clear,
+free, reset.
+
+This can be useful if, for example, multiple keys with different ad value
+are to be generated from a single password and secret.
+
+=back
+
+=head1 EXAMPLES
+
+This example uses Argon2d with password "1234567890", salt "saltsalt",
+using 2 lanes, 2 threads, and memory cost of 65536:
+
+ #include <string.h> /* strlen */
+ #include <openssl/core_names.h> /* OSSL_KDF_* */
+ #include <openssl/params.h> /* OSSL_PARAM_* */
+ #include <openssl/thread.h> /* OSSL_set_max_threads */
+ #include <openssl/kdf.h> /* EVP_KDF_* */
+
+ int main(void)
+ {
+ int retval = 1;
+
+ EVP_KDF *kdf = NULL;
+ EVP_KDF_CTX *kctx = NULL;
+ OSSL_PARAM params[6], *p = params;
+
+ /* argon2 params, please refer to RFC9106 for recommended defaults */
+ uint32_t lanes = 2, threads = 2, memcost = 65536;
+ char pwd[] = "1234567890", salt[] = "saltsalt";
+
+ /* derive result */
+ size_t outlen = 128;
+ unsigned char result[outlen];
+
+ /* required if threads > 1 */
+ if (OSSL_set_max_threads(NULL, threads) != 1)
+ goto fail;
+
+ p = params;
+ *p++ = OSSL_PARAM_construct_uint32(OSSL_KDF_PARAM_THREADS, &threads);
+ *p++ = OSSL_PARAM_construct_uint32(OSSL_KDF_PARAM_ARGON2_LANES,
+ &lanes);
+ *p++ = OSSL_PARAM_construct_uint32(OSSL_KDF_PARAM_ARGON2_MEMCOST,
+ &memcost);
+ *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT,
+ salt,
+ strlen((const char *)salt));
+ *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_PASSWORD,
+ pwd,
+ strlen((const char *)pwd));
+ *p++ = OSSL_PARAM_construct_end();
+
+ if ((kdf = EVP_KDF_fetch(NULL, "ARGON2D", NULL)) == NULL)
+ goto fail;
+ if ((kctx = EVP_KDF_CTX_new(kdf)) == NULL)
+ goto fail;
+ if (EVP_KDF_derive(kctx, &result[0], outlen, params) != 1)
+ goto fail;
+
+ printf("Output = %s\n", OPENSSL_buf2hexstr(result, outlen));
+ retval = 0;
+
+ fail:
+ EVP_KDF_free(kdf);
+ EVP_KDF_CTX_free(kctx);
+ OSSL_set_max_threads(NULL, 0);
+
+ return retval;
+ }
+
+=head1 NOTES
+
+"ARGON2I", "ARGON2D", and "ARGON2ID" are the names for this implementation; it
+can be used with the EVP_KDF_fetch() function.
+
+=head1 CONFORMING TO
+
+RFC 9106 Argon2, see L<https://www.rfc-editor.org/rfc/rfc9106.txt>.
+
+=head1 SEE ALSO
+
+L<EVP_KDF(3)>,
+L<EVP_KDF_CTX_new(3)>,
+L<EVP_KDF_CTX_free(3)>,
+L<EVP_KDF_CTX_set_params(3)>,
+L<EVP_KDF_derive(3)>,
+L<EVP_KDF(3)/PARAMETERS>
+
+=head1 HISTORY
+
+This functionality was added to OpenSSL 3.2.
+
+=head1 COPYRIGHT
+
+Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+
+Licensed under the Apache License 2.0 (the "License"). You may not use
+this file except in compliance with the License. You can obtain a copy
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
diff --git a/doc/man7/OSSL_PROVIDER-default.pod b/doc/man7/OSSL_PROVIDER-default.pod
index 5ac87ee6cc..96409ae5ba 100644
--- a/doc/man7/OSSL_PROVIDER-default.pod
+++ b/doc/man7/OSSL_PROVIDER-default.pod
@@ -151,6 +151,8 @@ The OpenSSL default provider supports these operations and algorithms:
=item HMAC-DRBG, see L<EVP_KDF-HMAC-DRBG(7)>
+=item ARGON2, see L<EVP_KDF-ARGON2(7)>
+
=back
=head2 Key Exchange
diff --git a/include/openssl/core_names.h b/include/openssl/core_names.h
index 5e5be567a5..0d07efcedc 100644
--- a/include/openssl/core_names.h
+++ b/include/openssl/core_names.h
@@ -233,6 +233,12 @@ extern "C" {
#define OSSL_KDF_PARAM_X942_USE_KEYBITS "use-keybits"
#define OSSL_KDF_PARAM_HMACDRBG_ENTROPY "entropy"
#define OSSL_KDF_PARAM_HMACDRBG_NONCE "nonce"
+#define OSSL_KDF_PARAM_THREADS "threads" /* uint32_t */
+#define OSSL_KDF_PARAM_EARLY_CLEAN "early_clean" /* uint32_t */
+#define OSSL_KDF_PARAM_ARGON2_AD "ad" /* octet string */
+#define OSSL_KDF_PARAM_ARGON2_LANES "lanes" /* uint32_t */
+#define OSSL_KDF_PARAM_ARGON2_MEMCOST "memcost" /* uint32_t */
+#define OSSL_KDF_PARAM_ARGON2_VERSION "version" /* uint32_t */
/* Known KDF names */
#define OSSL_KDF_NAME_HKDF "HKDF"
diff --git a/include/openssl/proverr.h b/include/openssl/proverr.h
index d36b3916fb..d9ef56815c 100644
--- a/include/openssl/proverr.h
+++ b/include/openssl/proverr.h
@@ -68,7 +68,7 @@
# define PROV_R_INVALID_KEY 158
# define PROV_R_INVALID_KEY_LENGTH 105
# define PROV_R_INVALID_MAC 151
-# define PROV_R_INVALID_MEMORY_SIZE 233
+# define PROV_R_INVALID_MEMORY_SIZE 235
# define PROV_R_INVALID_MGF1_MD 167
# define PROV_R_INVALID_MODE 125
# define PROV_R_INVALID_OUTPUT_LENGTH 217
diff --git a/providers/defltprov.c b/providers/defltprov.c
index cbb7a99ad1..274b2b4c92 100644
--- a/providers/defltprov.c
+++ b/providers/defltprov.c
@@ -355,6 +355,11 @@ static const OSSL_ALGORITHM deflt_kdfs[] = {
{ PROV_NAMES_KRB5KDF, "provider=default", ossl_kdf_krb5kdf_functions },
{ PROV_NAMES_HMAC_DRBG_KDF, "provider=default",
ossl_kdf_hmac_drbg_functions },
+#ifndef OPENSSL_NO_ARGON2
+ { PROV_NAMES_ARGON2I, "provider=default", ossl_kdf_argon2i_functions },
+ { PROV_NAMES_ARGON2D, "provider=default", ossl_kdf_argon2d_functions },
+ { PROV_NAMES_ARGON2ID, "provider=default", ossl_kdf_argon2id_functions },
+#endif
{ NULL, NULL, NULL }
};
diff --git a/providers/implementations/include/prov/implementations.h b/providers/implementations/include/prov/implementations.h
index d552b895fa..804763159a 100644
--- a/providers/implementations/include/prov/implementations.h
+++ b/providers/implementations/include/prov/implementations.h
@@ -280,6 +280,11 @@ extern const OSSL_DISPATCH ossl_kdf_kbkdf_functions[];
extern const OSSL_DISPATCH ossl_kdf_x942_kdf_functions[];
extern const OSSL_DISPATCH ossl_kdf_krb5kdf_functions[];
extern const OSSL_DISPATCH ossl_kdf_hmac_drbg_functions[];
+#ifndef OPENSSL_NO_ARGON2
+extern const OSSL_DISPATCH ossl_kdf_argon2i_functions[];
+extern const OSSL_DISPATCH ossl_kdf_argon2d_functions[];
+extern const OSSL_DISPATCH ossl_kdf_argon2id_functions[];
+#endif
/* RNGs */
extern const OSSL_DISPATCH ossl_test_rng_functions[];
diff --git a/providers/implementations/include/prov/names.h b/providers/implementations/include/prov/names.h
index 20978a3c11..dd40a6a8ed 100644
--- a/providers/implementations/include/prov/names.h
+++ b/providers/implementations/include/prov/names.h
@@ -279,6 +279,9 @@
#define PROV_DESCS_SCRYPT_SIGN "OpenSSL SCRYPT via EVP_PKEY implementation"
#define PROV_NAMES_KRB5KDF "KRB5KDF"
#define PROV_NAMES_HMAC_DRBG_KDF "HMAC-DRBG-KDF"
+#define PROV_NAMES_ARGON2I "ARGON2I"
+#define PROV_NAMES_ARGON2D "ARGON2D"
+#define PROV_NAMES_ARGON2ID "ARGON2ID"
/*-
* MACs
diff --git a/providers/implementations/kdfs/argon2.c b/providers/implementations/kdfs/argon2.c
new file mode 100644
index 0000000000..034c7e3003
--- /dev/null
+++ b/providers/implementations/kdfs/argon2.c
@@ -0,0 +1,1586 @@
+/*
+ * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ *
+ * RFC 9106 Argon2 (see https://www.rfc-editor.org/rfc/rfc9106.txt)
+ *
+ */
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <string.h>
+#include <crypto/evp.h>
+#include <openssl/evp.h>
+#include <openssl/objects.h>
+#include <openssl/crypto.h>
+#include <openssl/kdf.h>
+#include <openssl/err.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/thread.h>
+#include "internal/thread.h"
+#include "internal/numbers.h"
+#include "internal/endian.h"
+#include "prov/implementations.h"
+#include <openssl/proverr.h>
+#include "prov/provider_ctx.h"
+#include "prov/providercommon.h"
+#include "prov/blake2.h"
+
+#if defined(OPENSSL_NO_DEFAULT_THREAD_POOL) && defined(OPENSSL_NO_THREAD_POOL)
+# define ARGON2_NO_THREADS
+#endif
+
+#if !defined(OPENSSL_THREADS)
+# define ARGON2_NO_THREADS
+#endif
+
+#ifndef OPENSSL_NO_ARGON2
+
+# define ARGON2_MIN_LANES 1u
+# define ARGON2_MAX_LANES 0xFFFFFFu
+# define ARGON2_MIN_THREADS 1u
+# define ARGON2_MAX_THREADS 0xFFFFFFu
+# define ARGON2_SYNC_POINTS 4u
+# define ARGON2_MIN_OUT_LENGTH 4u
+# define ARGON2_MAX_OUT_LENGTH 0xFFFFFFFFu
+# define ARGON2_MIN_MEMORY (2 * ARGON2_SYNC_POINTS)
+# define ARGON2_MIN(a, b) ((a) < (b) ? (a) : (b))
+# define ARGON2_MAX_MEMORY 0xFFFFFFFFu
+# define ARGON2_MIN_TIME 1u
+# define ARGON2_MAX_TIME 0xFFFFFFFFu
+# define ARGON2_MIN_PWD_LENGTH 0u
+# define ARGON2_MAX_PWD_LENGTH 0xFFFFFFFFu
+# define ARGON2_MIN_AD_LENGTH 0u
+# define ARGON2_MAX_AD_LENGTH 0xFFFFFFFFu
+# define ARGON2_MIN_SALT_LENGTH 8u
+# define ARGON2_MAX_SALT_LENGTH 0xFFFFFFFFu
+# define ARGON2_MIN_SECRET 0u
+# define ARGON2_MAX_SECRET 0xFFFFFFFFu
+# define ARGON2_BLOCK_SIZE 1024
+# define ARGON2_QWORDS_IN_BLOCK ((ARGON2_BLOCK_SIZE) / 8)
+# define ARGON2_OWORDS_IN_BLOCK ((ARGON2_BLOCK_SIZE) / 16)
+# define ARGON2_HWORDS_IN_BLOCK ((ARGON2_BLOCK_SIZE) / 32)
+# define ARGON2_512BIT_WORDS_IN_BLOCK ((ARGON2_BLOCK_SIZE) / 64)
+# define ARGON2_ADDRESSES_IN_BLOCK 128
+# define ARGON2_PREHASH_DIGEST_LENGTH 64
+# define ARGON2_PREHASH_SEED_LENGTH \
+ (ARGON2_PREHASH_DIGEST_LENGTH + (2 * sizeof(uint32_t)))
+
+# define ARGON2_DEFAULT_OUTLEN 64u
+# define ARGON2_DEFAULT_T_COST 3u
+# define ARGON2_DEFAULT_M_COST ARGON2_MIN_MEMORY
+# define ARGON2_DEFAULT_LANES 1u
+# define ARGON2_DEFAULT_THREADS 1u
+# define ARGON2_DEFAULT_VERSION ARGON2_VERSION_NUMBER
+
+# undef G
+# define G(a, b, c, d) \
+ do { \
+ a = a + b + 2 * mul_lower(a, b); \
+ d = rotr64(d ^ a, 32); \
+ c = c + d + 2 * mul_lower(c, d); \
+ b = rotr64(b ^ c, 24); \
+ a = a + b + 2 * mul_lower(a, b); \
+ d = rotr64(d ^ a, 16); \
+ c = c + d + 2 * mul_lower(c, d); \
+ b = rotr64(b ^ c, 63); \
+ } while ((void)0, 0)
+
+# undef PERMUTATION_P
+# define PERMUTATION_P(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, \
+ v12, v13, v14, v15) \
+ do { \
+ G(v0, v4, v8, v12); \
+ G(v1, v5, v9, v13); \
+ G(v2, v6, v10, v14); \
+ G(v3, v7, v11, v15); \
+ G(v0, v5, v10, v15); \
+ G(v1, v6, v11, v12); \
+ G(v2, v7, v8, v13); \
+ G(v3, v4, v9, v14); \
+ } while ((void)0, 0)
+
+# undef PERMUTATION_P_COLUMN
+# define PERMUTATION_P_COLUMN(x, i) \
+ do { \
+ uint64_t *base = &x[16 * i]; \
+ PERMUTATION_P( \
+ *base, *(base + 1), *(base + 2), *(base + 3), \
+ *(base + 4), *(base + 5), *(base + 6), *(base + 7), \
+ *(base + 8), *(base + 9), *(base + 10), *(base + 11), \
+ *(base + 12), *(base + 13), *(base + 14), *(base + 15) \
+ ); \
+ } while ((void)0, 0)
+
+# undef PERMUTATION_P_ROW
+# define PERMUTATION_P_ROW(x, i) \
+ do { \
+ uint64_t *base = &x[2 * i]; \
+ PERMUTATION_P( \
+ *base, *(base + 1), *(base + 16), *(base + 17), \
+ *(base + 32), *(base + 33), *(base + 48), *(base + 49), \
+ *(base + 64), *(base + 65), *(base + 80), *(base + 81), \
+ *(base + 96), *(base + 97), *(base + 112), *(base + 113) \
+ ); \
+ } while ((void)0, 0)
+
+typedef struct {
+ uint64_t v[ARGON2_QWORDS_IN_BLOCK];
+} BLOCK;
+
+typedef enum {
+ ARGON2_VERSION_10 = 0x10,
+ ARGON2_VERSION_13 = 0x13,
+ ARGON2_VERSION_NUMBER = ARGON2_VERSION_13
+} ARGON2_VERSION;
+
+typedef enum {
+ ARGON2_D = 0,
+ ARGON2_I = 1,
+ ARGON2_ID = 2
+} ARGON2_TYPE;
+
+typedef struct {
+ uint32_t pass;
+ uint32_t lane;
+ uint8_t slice;
+ uint32_t index;
+} ARGON2_POS;
+
+typedef struct {
+ void *provctx;
+ uint8_t *out;
+ uint32_t outlen;
+ uint8_t *pwd;
+ uint32_t pwdlen;
+ uint8_t *salt;
+ uint32_t saltlen;
+ uint8_t *secret;
+ uint32_t secretlen;
+ uint8_t *ad;
+ uint32_t adlen;
+ uint32_t t_cost;
+ uint32_t m_cost;
+ uint32_t lanes;
+ uint32_t threads;
+ uint32_t version;
+ uint32_t early_clean;
+ ARGON2_TYPE type;
+ BLOCK *memory;
+ uint32_t passes;
+ uint32_t memory_blocks;
+ uint32_t segment_length;
+ uint32_t lane_length;
+ OSSL_LIB_CTX *libctx;
+ EVP_MD *md;
+ EVP_MAC *mac;
+ char *propq;
+} KDF_ARGON2;
+
+typedef struct {
+ ARGON2_POS pos;
+ KDF_ARGON2 *ctx;
+} ARGON2_THREAD_DATA;
+
+static OSSL_FUNC_kdf_newctx_fn kdf_argon2i_new;
+static OSSL_FUNC_kdf_newctx_fn kdf_argon2d_new;
+static OSSL_FUNC_kdf_newctx_fn kdf_argon2id_new;
+static OSSL_FUNC_kdf_freectx_fn kdf_argon2_free;
+static OSSL_FUNC_kdf_reset_fn kdf_argon2_reset;
+static OSSL_FUNC_kdf_derive_fn kdf_argon2_derive;
+static OSSL_FUNC_kdf_settable_ctx_params_fn kdf_argon2_settable_ctx_params;
+static OSSL_FUNC_kdf_set_ctx_params_fn kdf_argon2_set_ctx_params;
+
+static void kdf_argon2_init(KDF_ARGON2 *ctx, ARGON2_TYPE t);
+static void *kdf_argon2d_new(void *provctx);
+static void *kdf_argon2i_new(void *provctx);
+static void *kdf_argon2id_new(void *provctx);
+static void kdf_argon2_free(void *vctx);
+static int kdf_argon2_derive(void *vctx, unsigned char *out, size_t outlen,
+ const OSSL_PARAM params[]);
+static void kdf_argon2_reset(void *vctx);
+static int kdf_argon2_ctx_set_threads(KDF_ARGON2 *ctx, uint32_t threads);
+static int kdf_argon2_ctx_set_lanes(KDF_ARGON2 *ctx, uint32_t lanes);
+static int kdf_argon2_ctx_set_t_cost(KDF_ARGON2 *ctx, uint32_t t_cost);
+static int kdf_argon2_ctx_set_m_cost(KDF_ARGON2 *ctx, uint32_t m_cost);
+static int kdf_argon2_ctx_set_out_length(KDF_ARGON2 *ctx, uint32_t outlen);
+static int kdf_argon2_ctx_set_secret(KDF_ARGON2 *ctx, const OSSL_PARAM *p);
+static int kdf_argon2_ctx_set_pwd(KDF_ARGON2 *ctx, const OSSL_PARAM *p);
+static int kdf_argon2_ctx_set_salt(KDF_ARGON2 *ctx, const OSSL_PARAM *p);
+static int kdf_argon2_ctx_set_ad(KDF_ARGON2 *ctx, const OSSL_PARAM *p);
+static int kdf_argon2_set_ctx_params(void *vctx, const OSSL_PARAM params[]);
+static int kdf_argon2_get_ctx_params(void *vctx, OSSL_PARAM params[]);
+static int kdf_argon2_ctx_set_version(KDF_ARGON2 *ctx, uint32_t version);
+static const OSSL_PARAM *kdf_argon2_settable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *p_ctx);
+static const OSSL_PARAM *kdf_argon2_gettable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *p_ctx);
+
+static ossl_inline uint64_t load64(const uint8_t *src);
+static ossl_inline void store32(uint8_t *dst, uint32_t w);
+static ossl_inline void store64(uint8_t *dst, uint64_t w);
+static ossl_inline uint64_t rotr64(const uint64_t w, const unsigned int c);
+static ossl_inline uint64_t mul_lower(uint64_t x, uint64_t y);
+
+static void init_block_value(BLOCK *b, uint8_t in);
+static void copy_block(BLOCK *dst, const BLOCK *src);
+static void xor_block(BLOCK *dst, const BLOCK *src);
+static void load_block(BLOCK *dst, const void *input);
+static void store_block(void *output, const BLOCK *src);
+static void fill_first_blocks(uint8_t *blockhash, const KDF_ARGON2 *ctx);
+static void fill_block(const BLOCK *prev, const BLOCK *ref, BLOCK *next,
+ int with_xor);
+
+static void next_addresses(BLOCK *address_block, BLOCK *input_block,
+ const BLOCK *zero_block);
+static int data_indep_addressing(const KDF_ARGON2 *ctx, uint32_t pass,
+ uint8_t slice);
+static uint32_t index_alpha(const KDF_ARGON2 *ctx, uint32_t pass,
+ uint8_t slice, uint32_t index,
+ uint32_t pseudo_rand, int same_lane);
+
+static void fill_segment(const KDF_ARGON2 *ctx, uint32_t pass, uint32_t lane,
+ uint8_t slice);
+
+# if !defined(ARGON2_NO_THREADS)
+static uint32_t fill_segment_thr(void *thread_data);
+static int fill_mem_blocks_mt(KDF_ARGON2 *ctx);
+# endif
+
+static int fill_mem_blocks_st(KDF_ARGON2 *ctx);
+static ossl_inline int fill_memory_blocks(KDF_ARGON2 *ctx);
+
+static void initial_hash(uint8_t *blockhash, KDF_ARGON2 *ctx);
+static int initialize(KDF_ARGON2 *ctx);
+static void finalize(const KDF_ARGON2 *ctx);
+
+static int blake2b(EVP_MD *md, EVP_MAC *mac, void *out, size_t outlen,
+ const void *in, size_t inlen, const void *key,
+ size_t keylen);
+static int blake2b_long(EVP_MD *md, EVP_MAC *mac, unsigned char *out,
+ size_t outlen, const void *in, size_t inlen);
+
+static ossl_inline uint64_t load64(const uint8_t *src)
+{
+ return
+ (((uint64_t)src[0]) << 0)
+ | (((uint64_t)src[1]) << 8)
+ | (((uint64_t)src[2]) << 16)
+ | (((uint64_t)src[3]) << 24)
+ | (((uint64_t)src[4]) << 32)
+ | (((uint64_t)src[5]) << 40)
+ | (((uint64_t)src[6]) << 48)
+ | (((uint64_t)src[7]) << 56);
+}
+
+static ossl_inline void store32(uint8_t *dst, uint32_t w)
+{
+ dst[0] = (uint8_t)(w >> 0);
+ dst[1] = (uint8_t)(w >> 8);
+ dst[2] = (uint8_t)(w >> 16);
+ dst[3] = (uint8_t)(w >> 24);
+}
+
+static ossl_inline void store64(uint8_t *dst, uint64_t w)
+{
+ dst[0] = (uint8_t)(w >> 0);
+ dst[1] = (uint8_t)(w >> 8);
+ dst[2] = (uint8_t)(w >> 16);
+ dst[3] = (uint8_t)(w >> 24);
+ dst[4] = (uint8_t)(w >> 32);
+ dst[5] = (uint8_t)(w >> 40);
+ dst[6] = (uint8_t)(w >> 48);
+ dst[7] = (uint8_t)(w >> 56);
+}
+
+static ossl_inline uint64_t rotr64(const uint64_t w, const unsigned int c)
+{
+ return (w >> c) | (w << (64 - c));
+}
+
+static ossl_inline uint64_t mul_lower(uint64_t x, uint64_t y)
+{
+ const uint64_t m = UINT64_C(0xFFFFFFFF);
+ return (x & m) * (y & m);
+}
+
+static void init_block_value(BLOCK *b, uint8_t in)
+{
+ memset(b->v, in, sizeof(b->v));
+}
+
+static void copy_block(BLOCK *dst, const BLOCK *src)
+{
+ memcpy(dst->v, src->v, sizeof(uint64_t) * ARGON2_QWORDS_IN_BLOCK);
+}
+
+static void xor_block(BLOCK *dst, const BLOCK *src)
+{
+ int i;
+
+ for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i)
+ dst->v[i] ^= src->v[i];
+}
+
+static void load_block(BLOCK *dst, const void *input)
+{
+ unsigned i;
+
+ for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i)
+ dst->v[i] = load64((const uint8_t *)input + i * sizeof(dst->v[i]));
+}
+
+static void store_block(void *output, const BLOCK *src)
+{
+ unsigned i;
+
+ for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i)
+ store64((uint8_t *)output + i * sizeof(src->v[i]), src->v[i]);
+}
+
+static void fill_first_blocks(uint8_t *blockhash, const KDF_ARGON2 *ctx)
+{
+ uint32_t l;
+ uint8_t blockhash_bytes[ARGON2_BLOCK_SIZE];
+
+ /*
+ * Make the first and second block in each lane as G(H0||0||i)
+ * or G(H0||1||i).
+ */
+ for (l = 0; l < ctx->lanes; ++l) {
+ store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, 0);
+ store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH + 4, l);
+ blake2b_long(ctx->md, ctx->mac, blockhash_bytes, ARGON2_BLOCK_SIZE,
+ blockhash, ARGON2_PREHASH_SEED_LENGTH);
+ load_block(&ctx->memory[l * ctx->lane_length + 0],
+ blockhash_bytes);
+ store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, 1);
+ blake2b_long(ctx->md, ctx->mac, blockhash_bytes, ARGON2_BLOCK_SIZE,
+ blockhash, ARGON2_PREHASH_SEED_LENGTH);
+ load_block(&ctx->memory[l * ctx->lane_length + 1],
+ blockhash_bytes);
+ }
+ OPENSSL_cleanse(blockhash_bytes, ARGON2_BLOCK_SIZE);
+}
+
+static void fill_block(const BLOCK *prev, const BLOCK *ref,
+ BLOCK *next, int with_xor)
+{
+ BLOCK blockR, tmp;
+ unsigned i;
+
+ copy_block(&blockR, ref);
+ xor_block(&blockR, prev);
+ copy_block(&tmp, &blockR);
+
+ if (with_xor)
+ xor_block(&tmp, next);
+
+ for (i = 0; i < 8; ++i)
+ PERMUTATION_P_COLUMN(blockR.v, i);
+
+ for (i = 0; i < 8; ++i)
+ PERMUTATION_P_ROW(blockR.v, i);
+
+ copy_block(next, &tmp);
+ xor_block(next, &blockR);
+}
+
+static void next_addresses(BLOCK *address_block, BLOCK *input_block,
+ const BLOCK *zero_block)
+{
+ input_block->v[6]++;
+ fill_block(zero_block, input_block, address_block, 0);
+ fill_block(zero_block, address_block, address_block, 0);
+}
+
+static int data_indep_addressing(const KDF_ARGON2 *ctx, uint32_t pass,
+ uint8_t slice)
+{
+ switch (ctx->type) {
+ case ARGON2_I:
+ return 1;
+ case ARGON2_ID:
+ return (pass == 0) && (slice < ARGON2_SYNC_POINTS / 2);
+ case ARGON2_D:
+ default:
+ return 0;
+ }
+}
+
+/*
+ * Pass 0 (pass = 0):
+ * This lane: all already finished segments plus already constructed blocks
+ * in this segment
+ * Other lanes: all already