providers: add Argon2 KDF
authorČestmír Kalina <ckalina@redhat.com>
Mon, 27 Sep 2021 20:45:38 +0000 (22:45 +0200)
committerPauli <pauli@openssl.org>
Fri, 17 Mar 2023 00:12:47 +0000 (11:12 +1100)
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)

17 files changed:
CHANGES.md
Configure
apps/list.c
crypto/err/openssl.txt
doc/build.info
doc/man7/EVP_KDF-ARGON2.pod [new file with mode: 0644]
doc/man7/OSSL_PROVIDER-default.pod
include/openssl/core_names.h
include/openssl/proverr.h
providers/defltprov.c
providers/implementations/include/prov/implementations.h
providers/implementations/include/prov/names.h
providers/implementations/kdfs/argon2.c [new file with mode: 0644]
providers/implementations/kdfs/build.info
test/evp_test.c
test/recipes/30-test_evp.t
test/recipes/30-test_evp_data/evpkdf_argon2.txt [new file with mode: 0644]

index 711454ec43d7199de300e945fbcda6df1dc68412..860f91d5f8ddb3d746cee0a8c2fdb194489cdb68 100644 (file)
@@ -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
 -----------
 
index b6bbec0a85c41d30d981a5d152be42ad59c7ba5e..6ef882f5a3fb4d302e2f7628ba6c836a7e794424 100755 (executable)
--- 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" ]
     );
 
index 713a20cafab59b848a05185ae95157670d3aee09..2ccd41277ad21c0b7815eaa1e4f03fb32acfcf03 100644 (file)
@@ -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
index 018c7cbd33e0568d2ca94e8219308dc9c9623428..d0336d972981b80d72888e150d681dbc55c1987b 100644 (file)
@@ -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
index 14c7775b0cabb8bc4b02c9ad56a87e8e052b7375..f6a8b951f3a86b583a0ea7f14ce3f81500767943 100644 (file)
@@ -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 (file)
index 0000000..c44250e
--- /dev/null
@@ -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
index 5ac87ee6cc4f2dd3bb2a185615c9d785377803de..96409ae5ba590f42e7c79069a71e2a899f1efa16 100644 (file)
@@ -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
index 5e5be567a578156b0d2e398a9943d7edbb6fbb4c..0d07efcedc79d6e735ca2c0e06c13a2b4f9198fb 100644 (file)
@@ -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"
index d36b3916fb2040e01592a5a04b2325ce75f5510d..d9ef56815cf300e1791da9f1078cfeda153da250 100644 (file)
@@ -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
index cbb7a99ad148a972b528cc6845724189c831bfb9..274b2b4c92f4c45d7291351227c1996ce01e63bd 100644 (file)
@@ -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 }
 };
 
index d552b895fa65a2ea6cdc96030f2962b5891ebf02..804763159a745d3c2543c11b2e5bffd62cb40a42 100644 (file)
@@ -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[];
index 20978a3c11ce88c17d10e3bd1d4c2182be9fe724..dd40a6a8ed3562720d80ff4989e0ca88aa15a301 100644 (file)
 #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 (file)
index 0000000..034c7e3
--- /dev/null
@@ -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 finished segments
+ *
+ * Pass 1+:
+ * This lane: (SYNC_POINTS - 1) last segments plus already constructed
+ *            blocks in this segment
+ * Other lanes: (SYNC_POINTS - 1) last segments
+ */
+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)
+{
+    uint32_t ref_area_sz;
+    uint64_t rel_pos;
+    uint32_t start_pos, abs_pos;
+
+    start_pos = 0;
+    switch (pass) {
+    case 0:
+        if (slice == 0)
+            ref_area_sz = index - 1;
+        else if (same_lane)
+            ref_area_sz = slice * ctx->segment_length + index - 1;
+        else
+            ref_area_sz = slice * ctx->segment_length +
+                ((index == 0) ? (-1) : 0);
+        break;
+    default:
+        if (same_lane)
+            ref_area_sz = ctx->lane_length - ctx->segment_length + index - 1;
+        else
+            ref_area_sz = ctx->lane_length - ctx->segment_length +
+                ((index == 0) ? (-1) : 0);
+        if (slice != ARGON2_SYNC_POINTS - 1)
+            start_pos = (slice + 1) * ctx->segment_length;
+        break;
+    }
+
+    rel_pos = pseudo_rand;
+    rel_pos = rel_pos * rel_pos >> 32;
+    rel_pos = ref_area_sz - 1 - (ref_area_sz * rel_pos >> 32);
+    abs_pos = (start_pos + rel_pos) % ctx->lane_length;
+
+    return abs_pos;
+}
+
+static void fill_segment(const KDF_ARGON2 *ctx, uint32_t pass, uint32_t lane,
+                         uint8_t slice)
+{
+    BLOCK *ref_block = NULL, *curr_block = NULL;
+    BLOCK address_block, input_block, zero_block;
+    uint64_t rnd, ref_index, ref_lane;
+    uint32_t prev_offset;
+    uint32_t start_idx;
+    uint32_t j;
+    uint32_t curr_offset; /* Offset of the current block */
+
+    memset(&input_block, 0, sizeof(BLOCK));
+
+    if (ctx == NULL)
+        return;
+
+    if (data_indep_addressing(ctx, pass, slice)) {
+        init_block_value(&zero_block, 0);
+        init_block_value(&input_block, 0);
+
+        input_block.v[0] = pass;
+        input_block.v[1] = lane;
+        input_block.v[2] = slice;
+        input_block.v[3] = ctx->memory_blocks;
+        input_block.v[4] = ctx->passes;
+        input_block.v[5] = ctx->type;
+    }
+
+    start_idx = 0;
+
+    /* We've generated the first two blocks. Generate the 1st block of addrs. */
+    if ((pass == 0) && (slice == 0)) {
+        start_idx = 2;
+        if (data_indep_addressing(ctx, pass, slice))
+            next_addresses(&address_block, &input_block, &zero_block);
+    }
+
+    curr_offset = lane * ctx->lane_length + slice * ctx->segment_length
+        + start_idx;
+
+    if ((curr_offset % ctx->lane_length) == 0)
+        prev_offset = curr_offset + ctx->lane_length - 1;
+    else
+        prev_offset = curr_offset - 1;
+
+    for (j = start_idx; j < ctx->segment_length; ++j, ++curr_offset, ++prev_offset) {
+        if (curr_offset % ctx->lane_length == 1)
+            prev_offset = curr_offset - 1;
+
+        /* Taking pseudo-random value from the previous block. */
+        if (data_indep_addressing(ctx, pass, slice)) {
+            if (j % ARGON2_ADDRESSES_IN_BLOCK == 0)
+                next_addresses(&address_block, &input_block, &zero_block);
+            rnd = address_block.v[j % ARGON2_ADDRESSES_IN_BLOCK];
+        } else {
+            rnd = ctx->memory[prev_offset].v[0];
+        }
+
+        /* Computing the lane of the reference block */
+        ref_lane = ((rnd >> 32)) % ctx->lanes;
+        /* Can not reference other lanes yet */
+        if ((pass == 0) && (slice == 0))
+            ref_lane = lane;
+
+        /* Computing the number of possible reference block within the lane. */
+        ref_index = index_alpha(ctx, pass, slice, j, rnd & 0xFFFFFFFF,
+                                ref_lane == lane);
+
+        /* Creating a new block */
+        ref_block = ctx->memory + ctx->lane_length * ref_lane + ref_index;
+        curr_block = ctx->memory + curr_offset;
+        if (ARGON2_VERSION_10 == ctx->version) {
+            /* Version 1.2.1 and earlier: overwrite, not XOR */
+            fill_block(ctx->memory + prev_offset, ref_block, curr_block, 0);
+            continue;
+        }
+
+        fill_block(ctx->memory + prev_offset, ref_block, curr_block,
+                   pass == 0 ? 0 : 1);
+    }
+}
+
+# if !defined(ARGON2_NO_THREADS)
+
+static uint32_t fill_segment_thr(void *thread_data)
+{
+    ARGON2_THREAD_DATA *my_data;
+
+    my_data = (ARGON2_THREAD_DATA *) thread_data;
+    fill_segment(my_data->ctx, my_data->pos.pass, my_data->pos.lane,
+                 my_data->pos.slice);
+
+    return 0;
+}
+
+static int fill_mem_blocks_mt(KDF_ARGON2 *ctx)
+{
+    uint32_t r, s, l, ll;
+    void **t;
+    ARGON2_THREAD_DATA *t_data;
+
+    t = OPENSSL_zalloc(sizeof(void *)*ctx->lanes);
+    t_data = OPENSSL_zalloc(ctx->lanes * sizeof(ARGON2_THREAD_DATA));
+
+    if (t == NULL || t_data == NULL)
+        goto fail;
+
+    for (r = 0; r < ctx->passes; ++r) {
+        for (s = 0; s < ARGON2_SYNC_POINTS; ++s) {
+            for (l = 0; l < ctx->lanes; ++l) {
+                ARGON2_POS p;
+                if (l >= ctx->threads) {
+                    if (ossl_crypto_thread_join(t[l - ctx->threads], NULL) == 0)
+                        goto fail;
+                    if (ossl_crypto_thread_clean(t[l - ctx->threads]) == 0)
+                        goto fail;
+                    t[l] = NULL;
+                }
+
+                p.pass = r;
+                p.lane = l;
+                p.slice = (uint8_t)s;
+                p.index = 0;
+
+                t_data[l].ctx = ctx;
+                memcpy(&(t_data[l].pos), &p, sizeof(ARGON2_POS));
+                t[l] = ossl_crypto_thread_start(ctx->libctx, &fill_segment_thr,
+                                                (void *) &t_data[l]);
+                if (t[l] == NULL) {
+                    for (ll = 0; ll < l; ++ll) {
+                        if (ossl_crypto_thread_join(t[ll], NULL) == 0)
+                            goto fail;
+                        if (ossl_crypto_thread_clean(t[ll]) == 0)
+                            goto fail;
+                        t[ll] = NULL;
+                    }
+                    goto fail;
+                }
+            }
+            for (l = ctx->lanes - ctx->threads; l < ctx->lanes; ++l) {
+                if (ossl_crypto_thread_join(t[l], NULL) == 0)
+                    goto fail;
+                if (ossl_crypto_thread_clean(t[l]) == 0)
+                    goto fail;
+                t[l] = NULL;
+            }
+        }
+    }
+
+    OPENSSL_free(t_data);
+    OPENSSL_free(t);
+
+    return 1;
+
+fail:
+    if (t_data != NULL)
+        OPENSSL_free(t_data);
+    if (t != NULL)
+        OPENSSL_free(t);
+    return 0;
+}
+
+# endif /* !defined(ARGON2_NO_THREADS) */
+
+static int fill_mem_blocks_st(KDF_ARGON2 *ctx)
+{
+    uint32_t r, s, l;
+
+    for (r = 0; r < ctx->passes; ++r)
+        for (s = 0; s < ARGON2_SYNC_POINTS; ++s)
+            for (l = 0; l < ctx->lanes; ++l)
+                fill_segment(ctx, r, l, s);
+    return 1;
+}
+
+static ossl_inline int fill_memory_blocks(KDF_ARGON2 *ctx)
+{
+# if !defined(ARGON2_NO_THREADS)
+    return ctx->threads == 1 ? fill_mem_blocks_st(ctx) : fill_mem_blocks_mt(ctx);
+# else
+    return ctx->threads == 1 ? fill_mem_blocks_st(ctx) : 0;
+# endif
+}
+
+static void initial_hash(uint8_t *blockhash, KDF_ARGON2 *ctx)
+{
+    EVP_MD_CTX *mdctx;
+    uint8_t value[sizeof(uint32_t)];
+    unsigned int tmp;
+    uint32_t args[7];
+
+    if (ctx == NULL || blockhash == NULL)
+        return;
+
+    args[0] = ctx->lanes;
+    args[1] = ctx->outlen;
+    args[2] = ctx->m_cost;
+    args[3] = ctx->t_cost;
+    args[4] = ctx->version;
+    args[5] = (uint32_t) ctx->type;
+    args[6] = ctx->pwdlen;
+
+    mdctx = EVP_MD_CTX_create();
+    if (mdctx == NULL || EVP_DigestInit_ex(mdctx, ctx->md, NULL) != 1)
+        goto fail;
+
+    for (tmp = 0; tmp < sizeof(args) / sizeof(uint32_t); ++tmp) {
+        store32((uint8_t *) &value, args[tmp]);
+        if (EVP_DigestUpdate(mdctx, &value, sizeof(value)) != 1)
+            goto fail;
+    }
+
+    if (ctx->pwd != NULL) {
+        if (EVP_DigestUpdate(mdctx, ctx->pwd, ctx->pwdlen) != 1)
+            goto fail;
+        if (ctx->early_clean) {
+            OPENSSL_cleanse(ctx->pwd, ctx->pwdlen);
+            ctx->pwdlen = 0;
+        }
+    }
+
+    store32((uint8_t *) &value, ctx->saltlen);
+
+    if (EVP_DigestUpdate(mdctx, &value, sizeof(value)) != 1)
+        goto fail;
+
+    if (ctx->salt != NULL)
+        if (EVP_DigestUpdate(mdctx, ctx->salt, ctx->saltlen) != 1)
+            goto fail;
+
+    store32((uint8_t *) &value, ctx->secretlen);
+    if (EVP_DigestUpdate(mdctx, &value, sizeof(value)) != 1)
+        goto fail;
+
+    if (ctx->secret != NULL) {
+        if (EVP_DigestUpdate(mdctx, ctx->secret, ctx->secretlen) != 1)
+            goto fail;
+        if (ctx->early_clean) {
+            OPENSSL_cleanse(ctx->secret, ctx->secretlen);
+            ctx->secretlen = 0;
+        }
+    }
+
+    store32((uint8_t *) &value, ctx->adlen);
+    if (EVP_DigestUpdate(mdctx, &value, sizeof(value)) != 1)
+        goto fail;
+
+    if (ctx->ad != NULL)
+        if (EVP_DigestUpdate(mdctx, ctx->ad, ctx->adlen) != 1)
+            goto fail;
+
+    tmp = ARGON2_PREHASH_DIGEST_LENGTH;
+    if (EVP_DigestFinal_ex(mdctx, blockhash, &tmp) != 1)
+        goto fail;
+
+fail:
+    EVP_MD_CTX_destroy(mdctx);
+}
+
+static int initialize(KDF_ARGON2 *ctx)
+{
+    uint8_t blockhash[ARGON2_PREHASH_SEED_LENGTH];
+
+    if (ctx == NULL)
+        return 0;
+
+    if (ctx->memory_blocks * sizeof(BLOCK) / sizeof(BLOCK) != ctx->memory_blocks)
+        return 0;
+
+    if (ctx->type != ARGON2_D)
+        ctx->memory = OPENSSL_secure_zalloc(ctx->memory_blocks *
+                                            sizeof(BLOCK));
+    else
+        ctx->memory = OPENSSL_zalloc(ctx->memory_blocks *
+                                     sizeof(BLOCK));
+
+    if (ctx->memory == NULL) {
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_MEMORY_SIZE,
+                       "cannot allocate required memory");
+        return 0;
+    }
+
+    initial_hash(blockhash, ctx);
+    OPENSSL_cleanse(blockhash + ARGON2_PREHASH_DIGEST_LENGTH,
+                    ARGON2_PREHASH_SEED_LENGTH - ARGON2_PREHASH_DIGEST_LENGTH);
+    fill_first_blocks(blockhash, ctx);
+    OPENSSL_cleanse(blockhash, ARGON2_PREHASH_SEED_LENGTH);
+
+    return 1;
+}
+
+static void finalize(const KDF_ARGON2 *ctx)
+{
+    BLOCK blockhash;
+    uint8_t blockhash_bytes[ARGON2_BLOCK_SIZE];
+    uint32_t last_block_in_lane;
+    uint32_t l;
+
+    if (ctx == NULL)
+        return;
+
+    copy_block(&blockhash, ctx->memory + ctx->lane_length - 1);
+
+    /* XOR the last blocks */
+    for (l = 1; l < ctx->lanes; ++l) {
+        last_block_in_lane = l * ctx->lane_length + (ctx->lane_length - 1);
+        xor_block(&blockhash, ctx->memory + last_block_in_lane);
+    }
+
+    /* Hash the result */
+    store_block(blockhash_bytes, &blockhash);
+    blake2b_long(ctx->md, ctx->mac, ctx->out, ctx->outlen, blockhash_bytes,
+                 ARGON2_BLOCK_SIZE);
+    OPENSSL_cleanse(blockhash.v, ARGON2_BLOCK_SIZE);
+    OPENSSL_cleanse(blockhash_bytes, ARGON2_BLOCK_SIZE);
+
+    if (ctx->type != ARGON2_D)
+        OPENSSL_secure_clear_free(ctx->memory,
+                                  ctx->memory_blocks * sizeof(BLOCK));
+    else
+        OPENSSL_clear_free(ctx->memory,
+                           ctx->memory_blocks * sizeof(BLOCK));
+}
+
+static int blake2b_mac(EVP_MAC *mac, void *out, size_t outlen, const void *in,
+                       size_t inlen, const void *key, size_t keylen)
+{
+    int ret = 0;
+    size_t par_n = 0, out_written;
+    EVP_MAC_CTX *ctx = NULL;
+    OSSL_PARAM par[3];
+
+    if ((ctx = EVP_MAC_CTX_new(mac)) == NULL)
+        goto fail;
+
+    par[par_n++] = OSSL_PARAM_construct_octet_string(OSSL_MAC_PARAM_KEY,
+                                                     (void *) key, keylen);
+    par[par_n++] = OSSL_PARAM_construct_size_t(OSSL_MAC_PARAM_SIZE, &outlen);
+    par[par_n++] = OSSL_PARAM_construct_end();
+
+    ret = EVP_MAC_CTX_set_params(ctx, par) == 1
+        && EVP_MAC_init(ctx, NULL, 0, NULL) == 1
+        && EVP_MAC_update(ctx, in, inlen) == 1
+        && EVP_MAC_final(ctx, out, (size_t *) &out_written, outlen) == 1;
+
+fail:
+    EVP_MAC_CTX_free(ctx);
+    return ret;
+}
+
+static int blake2b_md(EVP_MD *md, void *out, size_t outlen, const void *in,
+                      size_t inlen)
+{
+    int ret = 0;
+    EVP_MD_CTX *ctx = NULL;
+    OSSL_PARAM par[2];
+
+    if ((ctx = EVP_MD_CTX_create()) == NULL)
+        return 0;
+
+    par[0] = OSSL_PARAM_construct_size_t(OSSL_DIGEST_PARAM_XOFLEN, &outlen);
+    par[1] = OSSL_PARAM_construct_end();
+
+    ret = EVP_DigestInit_ex2(ctx, md, par) == 1
+        && EVP_DigestUpdate(ctx, in, inlen) == 1
+        && EVP_DigestFinalXOF(ctx, out, outlen) == 1;
+
+    EVP_MD_CTX_free(ctx);
+    return ret;
+}
+
+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)
+{
+    if (out == NULL || outlen == 0)
+        return 0;
+
+    if (key == NULL || keylen == 0)
+        return blake2b_md(md, out, outlen, in, inlen);
+
+    return blake2b_mac(mac, out, outlen, in, inlen, key, keylen);
+}
+
+static int blake2b_long(EVP_MD *md, EVP_MAC *mac, unsigned char *out,
+                        size_t outlen, const void *in, size_t inlen)
+{
+    int ret = 0;
+    EVP_MD_CTX *ctx = NULL;
+    uint32_t outlen_curr;
+    uint8_t outbuf[BLAKE2B_OUTBYTES];
+    uint8_t inbuf[BLAKE2B_OUTBYTES];
+    uint8_t outlen_bytes[sizeof(uint32_t)] = {0};
+    OSSL_PARAM par[2];
+    size_t outlen_md;
+
+    if (out == NULL || outlen == 0)
+        return 0;
+
+    /* Ensure little-endian byte order */
+    store32(outlen_bytes, (uint32_t)outlen);
+
+    if ((ctx = EVP_MD_CTX_create()) == NULL)
+        return 0;
+
+    outlen_md = (outlen <= BLAKE2B_OUTBYTES) ? outlen : BLAKE2B_OUTBYTES;
+    par[0] = OSSL_PARAM_construct_size_t(OSSL_DIGEST_PARAM_XOFLEN, &outlen_md);
+    par[1] = OSSL_PARAM_construct_end();
+
+    ret = EVP_DigestInit_ex2(ctx, md, par) == 1
+        && EVP_DigestUpdate(ctx, outlen_bytes, sizeof(outlen_bytes)) == 1
+        && EVP_DigestUpdate(ctx, in, inlen) == 1
+        && EVP_DigestFinalXOF(ctx, (outlen > BLAKE2B_OUTBYTES) ? outbuf : out,
+                outlen_md) == 1;
+
+    if (ret == 0)
+        goto fail;
+
+    if (outlen > BLAKE2B_OUTBYTES) {
+        memcpy(out, outbuf, BLAKE2B_OUTBYTES / 2);
+        out += BLAKE2B_OUTBYTES / 2;
+        outlen_curr = (uint32_t) outlen - BLAKE2B_OUTBYTES / 2;
+
+        while (outlen_curr > BLAKE2B_OUTBYTES) {
+            memcpy(inbuf, outbuf, BLAKE2B_OUTBYTES);
+            if (blake2b(md, mac, outbuf, BLAKE2B_OUTBYTES, inbuf,
+                        BLAKE2B_OUTBYTES, NULL, 0) != 1)
+                goto fail;
+            memcpy(out, outbuf, BLAKE2B_OUTBYTES / 2);
+            out += BLAKE2B_OUTBYTES / 2;
+            outlen_curr -= BLAKE2B_OUTBYTES / 2;
+        }
+
+        memcpy(inbuf, outbuf, BLAKE2B_OUTBYTES);
+        if (blake2b(md, mac, outbuf, outlen_curr, inbuf, BLAKE2B_OUTBYTES,
+                    NULL, 0) != 1)
+            goto fail;
+        memcpy(out, outbuf, outlen_curr);
+    }
+    ret = 1;
+
+fail:
+    EVP_MD_CTX_free(ctx);
+    return ret;
+}
+
+static void kdf_argon2_init(KDF_ARGON2 *c, ARGON2_TYPE type)
+{
+    OSSL_LIB_CTX *libctx;
+
+    libctx = c->libctx;
+    memset(c, 0, sizeof(*c));
+
+    c->libctx = libctx;
+    c->outlen = ARGON2_DEFAULT_OUTLEN;
+    c->t_cost = ARGON2_DEFAULT_T_COST;
+    c->m_cost = ARGON2_DEFAULT_M_COST;
+    c->lanes = ARGON2_DEFAULT_LANES;
+    c->threads = ARGON2_DEFAULT_THREADS;
+    c->version = ARGON2_DEFAULT_VERSION;
+    c->type = type;
+}
+
+static void *kdf_argon2d_new(void *provctx)
+{
+    KDF_ARGON2 *ctx;
+
+    if (!ossl_prov_is_running())
+        return NULL;
+
+    ctx = OPENSSL_zalloc(sizeof(*ctx));
+    if (ctx == NULL) {
+        ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    ctx->libctx = PROV_LIBCTX_OF(provctx);
+
+    kdf_argon2_init(ctx, ARGON2_D);
+    return ctx;
+}
+
+static void *kdf_argon2i_new(void *provctx)
+{
+    KDF_ARGON2 *ctx;
+
+    if (!ossl_prov_is_running())
+        return NULL;
+
+    ctx = OPENSSL_zalloc(sizeof(*ctx));
+    if (ctx == NULL) {
+        ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    ctx->libctx = PROV_LIBCTX_OF(provctx);
+
+    kdf_argon2_init(ctx, ARGON2_I);
+    return ctx;
+}
+
+static void *kdf_argon2id_new(void *provctx)
+{
+    KDF_ARGON2 *ctx;
+
+    if (!ossl_prov_is_running())
+        return NULL;
+
+    ctx = OPENSSL_zalloc(sizeof(*ctx));
+    if (ctx == NULL) {
+        ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
+        return NULL;
+    }
+
+    ctx->libctx = PROV_LIBCTX_OF(provctx);
+
+    kdf_argon2_init(ctx, ARGON2_ID);
+    return ctx;
+}
+
+static void kdf_argon2_free(void *vctx)
+{
+    KDF_ARGON2 *ctx = (KDF_ARGON2 *)vctx;
+
+    if (ctx == NULL)
+        return;
+
+    if (ctx->out != NULL)
+        OPENSSL_clear_free(ctx->out, ctx->outlen);
+
+    if (ctx->pwd != NULL)
+        OPENSSL_clear_free(ctx->pwd, ctx->pwdlen);
+
+    if (ctx->salt != NULL)
+        OPENSSL_clear_free(ctx->salt, ctx->saltlen);
+
+    if (ctx->secret != NULL)
+        OPENSSL_clear_free(ctx->secret, ctx->secretlen);
+
+    if (ctx->ad != NULL)
+        OPENSSL_clear_free(ctx->ad, ctx->adlen);
+
+    OPENSSL_free(ctx->propq);
+
+    memset(ctx, 0, sizeof(*ctx));
+
+    OPENSSL_free(ctx);
+}
+
+static int kdf_argon2_derive(void *vctx, unsigned char *out, size_t outlen,
+                             const OSSL_PARAM params[])
+{
+    KDF_ARGON2 *ctx;
+    uint32_t memory_blocks, segment_length;
+
+    ctx = (KDF_ARGON2 *)vctx;
+
+    if (!ossl_prov_is_running() || !kdf_argon2_set_ctx_params(vctx, params))
+        return 0;
+
+    ctx->mac = EVP_MAC_fetch(ctx->libctx, "blake2bmac", ctx->propq);
+    if (ctx->mac == NULL) {
+        OPENSSL_free(ctx);
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_MISSING_MAC,
+                       "cannot fetch blake2bmac");
+        return 0;
+    }
+
+    ctx->md = EVP_MD_fetch(ctx->libctx, "blake2b512", ctx->propq);
+    if (ctx->md == NULL) {
+        OPENSSL_free(ctx);
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST,
+                       "canot fetch blake2b512");
+        goto fail1;
+    }
+
+    if (ctx->salt == NULL || ctx->saltlen == 0) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_SALT);
+        goto fail2;
+    }
+
+    if (outlen != ctx->outlen) {
+        if (OSSL_PARAM_locate((OSSL_PARAM *)params, "size") != NULL) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+            goto fail2;
+        }
+        kdf_argon2_ctx_set_out_length(ctx, (uint32_t) outlen);
+    }
+
+    switch (ctx->type) {
+    case ARGON2_D:
+    case ARGON2_I:
+    case ARGON2_ID:
+        break;
+    default:
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_MODE, "invalid Argon2 type");
+        goto fail2;
+    }
+
+    if (ctx->threads > 1) {
+# ifdef ARGON2_NO_THREADS
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_THREAD_POOL_SIZE,
+                       "requested %u threads, single-threaded mode supported only",
+                       ctx->threads);
+        goto fail2;
+# else
+        if (ctx->threads > ossl_get_avail_threads(ctx->libctx)) {
+            ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_THREAD_POOL_SIZE,
+                           "requested %u threads, available: 1",
+                           ossl_get_avail_threads(ctx->libctx));
+            goto fail2;
+        }
+# endif
+        if (ctx->threads > ctx->lanes) {
+            ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_THREAD_POOL_SIZE,
+                           "requested more threads (%u) than lanes (%u)",
+                           ctx->threads, ctx->lanes);
+            goto fail2;
+        }
+    }
+
+    if (ctx->m_cost < 8 * ctx->lanes) {
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_MEMORY_SIZE,
+                       "m_cost must be greater or equal than 8 times the number of lanes");
+        goto fail2;
+    }
+
+    if (ctx->type != ARGON2_D)
+        ctx->out = OPENSSL_secure_zalloc(ctx->outlen + 1);
+    else
+        ctx->out = OPENSSL_zalloc(ctx->outlen + 1);
+
+    if (ctx->out == NULL)
+        goto fail2;
+
+    memory_blocks = ctx->m_cost;
+    if (memory_blocks < 2 * ARGON2_SYNC_POINTS * ctx->lanes)
+        memory_blocks = 2 * ARGON2_SYNC_POINTS * ctx->lanes;
+
+    /* Ensure that all segments have equal length */
+    segment_length = memory_blocks / (ctx->lanes * ARGON2_SYNC_POINTS);
+    memory_blocks = segment_length * (ctx->lanes * ARGON2_SYNC_POINTS);
+
+    ctx->memory = NULL;
+    ctx->memory_blocks = memory_blocks;
+    ctx->segment_length = segment_length;
+    ctx->passes = ctx->t_cost;
+    ctx->lane_length = segment_length * ARGON2_SYNC_POINTS;
+
+    if (initialize(ctx) != 1)
+        goto fail3;
+
+    if (fill_memory_blocks(ctx) != 1)
+        goto fail3;
+
+    finalize(ctx);
+    memcpy(out, ctx->out, outlen);
+
+    EVP_MAC_free(ctx->mac);
+    EVP_MD_free(ctx->md);
+
+    return 1;
+
+fail3:
+    if (ctx->type != ARGON2_D)
+        OPENSSL_secure_clear_free(ctx->out, ctx->outlen + 1);
+    else
+        OPENSSL_clear_free(ctx->out, ctx->outlen + 1);
+    ctx->out = NULL;
+
+fail2:
+    EVP_MD_free(ctx->md);
+fail1:
+    EVP_MAC_free(ctx->mac);
+
+    return 0;
+}
+
+static void kdf_argon2_reset(void *vctx)
+{
+    OSSL_LIB_CTX *libctx;
+    KDF_ARGON2 *ctx;
+    ARGON2_TYPE type;
+
+    ctx = (KDF_ARGON2 *) vctx;
+    type = ctx->type;
+    libctx = ctx->libctx;
+
+    if (ctx->out != NULL)
+        OPENSSL_clear_free(ctx->out, ctx->outlen);
+
+    if (ctx->pwd != NULL)
+        OPENSSL_clear_free(ctx->pwd, ctx->pwdlen);
+
+    if (ctx->salt != NULL)
+        OPENSSL_clear_free(ctx->salt, ctx->saltlen);
+
+    if (ctx->secret != NULL)
+        OPENSSL_clear_free(ctx->secret, ctx->secretlen);
+
+    if (ctx->ad != NULL)
+        OPENSSL_clear_free(ctx->ad, ctx->adlen);
+
+    memset(ctx, 0, sizeof(*ctx));
+    ctx->libctx = libctx;
+    kdf_argon2_init(ctx, type);
+}
+
+static int kdf_argon2_ctx_set_threads(KDF_ARGON2 *ctx, uint32_t threads)
+{
+    if (threads < ARGON2_MIN_THREADS) {
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_THREAD_POOL_SIZE,
+                       "min threads: %u", ARGON2_MIN_THREADS);
+        return 0;
+    }
+
+    if (threads > ARGON2_MAX_THREADS) {
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_THREAD_POOL_SIZE,
+                       "max threads: %u", ARGON2_MAX_THREADS);
+        return 0;
+    }
+
+    ctx->threads = threads;
+    return 1;
+}
+
+static int kdf_argon2_ctx_set_lanes(KDF_ARGON2 *ctx, uint32_t lanes)
+{
+    if (lanes > ARGON2_MAX_LANES) {
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER,
+                       "max lanes: %u", ARGON2_MAX_LANES);
+        return 0;
+    }
+
+    if (lanes < ARGON2_MIN_LANES) {
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER,
+                       "min lanes: %u", ARGON2_MIN_LANES);
+        return 0;
+    }
+
+    ctx->lanes = lanes;
+    return 1;
+}
+
+static int kdf_argon2_ctx_set_t_cost(KDF_ARGON2 *ctx, uint32_t t_cost)
+{
+    /* ARGON2_MAX_MEMORY == max m_cost value, skip check, enforce type */
+    ossl_static_assert_type_eq(uint32_t, t_cost);
+
+    if (t_cost < ARGON2_MIN_TIME) {
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_ITERATION_COUNT,
+                       "min: %u", ARGON2_MIN_TIME);
+        return 0;
+    }
+
+    ctx->t_cost = t_cost;
+    return 1;
+}
+
+static int kdf_argon2_ctx_set_m_cost(KDF_ARGON2 *ctx, uint32_t m_cost)
+{
+    /* ARGON2_MAX_MEMORY == max m_cost value, skip check, enforce type */
+    ossl_static_assert_type_eq(uint32_t, m_cost);
+
+    if (m_cost < ARGON2_MIN_MEMORY) {
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_MEMORY_SIZE, "min: %u",
+                       ARGON2_MIN_MEMORY);
+        return 0;
+    }
+
+    ctx->m_cost = m_cost;
+    return 1;
+}
+
+static int kdf_argon2_ctx_set_out_length(KDF_ARGON2 *ctx, uint32_t outlen)
+{
+    /*
+     * ARGON2_MAX_OUT_LENGTH == max outlen value, so upper bounds checks
+     * are always satisfied; to suppress compiler if statement tautology
+     * warnings, these checks are skipped; however, to ensure that these
+     * limits are met and implementation conforming to Argon2 RFC, we need
+     * to fix the type
+     */
+    ossl_static_assert_type_eq(uint32_t, outlen);
+
+    if (outlen < ARGON2_MIN_OUT_LENGTH) {
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_OUTPUT_LENGTH, "min: %u",
+                       ARGON2_MIN_OUT_LENGTH);
+        return 0;
+    }
+
+    ctx->outlen = outlen;
+    return 1;
+}
+
+static int kdf_argon2_ctx_set_secret(KDF_ARGON2 *ctx, const OSSL_PARAM *p)
+{
+    size_t buflen;
+
+    if (p->data == NULL)
+        return 0;
+
+    if (ctx->secret != NULL) {
+        OPENSSL_clear_free(ctx->secret, ctx->secretlen);
+        ctx->secret = NULL;
+        ctx->secretlen = 0U;
+    }
+
+    if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->secret, 0, &buflen))
+        return 0;
+
+    if (buflen > ARGON2_MAX_SECRET) {
+        OPENSSL_free(ctx->secret);
+        ctx->secret = NULL;
+        ctx->secretlen = 0U;
+        return 0;
+    }
+
+    ctx->secretlen = (uint32_t) buflen;
+    return 1;
+}
+
+static int kdf_argon2_ctx_set_pwd(KDF_ARGON2 *ctx, const OSSL_PARAM *p)
+{
+    size_t buflen;
+
+    if (p->data == NULL)
+        return 0;
+
+    if (ctx->pwd != NULL) {
+        OPENSSL_clear_free(ctx->pwd, ctx->pwdlen);
+        ctx->pwd = NULL;
+        ctx->pwdlen = 0U;
+    }
+
+    if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->pwd, 0, &buflen))
+        return 0;
+
+    if (buflen > ARGON2_MAX_PWD_LENGTH) {
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_SALT_LENGTH, "max: %u",
+                       ARGON2_MAX_PWD_LENGTH);
+        goto fail;
+    }
+
+    ctx->pwdlen = (uint32_t) buflen;
+    return 1;
+
+fail:
+    OPENSSL_free(ctx->pwd);
+    ctx->pwd = NULL;
+    ctx->pwdlen = 0U;
+    return 0;
+}
+
+static int kdf_argon2_ctx_set_salt(KDF_ARGON2 *ctx, const OSSL_PARAM *p)
+{
+    size_t buflen;
+
+    if (p->data == NULL)
+        return 0;
+
+    if (ctx->salt != NULL) {
+        OPENSSL_clear_free(ctx->salt, ctx->saltlen);
+        ctx->salt = NULL;
+        ctx->saltlen = 0U;
+    }
+
+    if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->salt, 0, &buflen))
+        return 0;
+
+    if (buflen < ARGON2_MIN_SALT_LENGTH) {
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_SALT_LENGTH, "min: %u",
+                       ARGON2_MIN_SALT_LENGTH);
+        goto fail;
+    }
+
+    if (buflen > ARGON2_MAX_SALT_LENGTH) {
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_SALT_LENGTH, "max: %u",
+                       ARGON2_MAX_SALT_LENGTH);
+        goto fail;
+    }
+
+    ctx->saltlen = (uint32_t) buflen;
+    return 1;
+
+fail:
+    OPENSSL_free(ctx->salt);
+    ctx->salt = NULL;
+    ctx->saltlen = 0U;
+    return 0;
+}
+
+static int kdf_argon2_ctx_set_ad(KDF_ARGON2 *ctx, const OSSL_PARAM *p)
+{
+    size_t buflen;
+
+    if (p->data == NULL)
+        return 0;
+
+    if (ctx->ad != NULL) {
+        OPENSSL_clear_free(ctx->ad, ctx->adlen);
+        ctx->ad = NULL;
+        ctx->adlen = 0U;
+    }
+
+    if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->ad, 0, &buflen))
+        return 0;
+
+    if (buflen > ARGON2_MAX_AD_LENGTH) {
+        OPENSSL_free(ctx->ad);
+        ctx->ad = NULL;
+        ctx->adlen = 0U;
+        return 0;
+    }
+
+    ctx->adlen = (uint32_t) buflen;
+    return 1;
+}
+
+static void kdf_argon2_ctx_set_flag_early_clean(KDF_ARGON2 *ctx, uint32_t f)
+{
+    ctx->early_clean = !!(f);
+}
+
+static int kdf_argon2_ctx_set_version(KDF_ARGON2 *ctx, uint32_t version)
+{
+    switch (version) {
+    case ARGON2_VERSION_10:
+    case ARGON2_VERSION_13:
+        ctx->version = version;
+        return 1;
+    default:
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_MODE,
+                       "invalid Argon2 version");
+        return 0;
+    }
+}
+
+static int set_property_query(KDF_ARGON2 *ctx, const char *propq)
+{
+    OPENSSL_free(ctx->propq);
+    ctx->propq = NULL;
+    if (propq != NULL) {
+        ctx->propq = OPENSSL_strdup(propq);
+        if (ctx->propq == NULL)
+            return 0;
+    }
+    return 1;
+}
+
+static int kdf_argon2_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+    const OSSL_PARAM *p;
+    KDF_ARGON2 *ctx;
+    uint32_t u32_value;
+
+    if (params == NULL)
+        return 1;
+
+    ctx = (KDF_ARGON2 *) vctx;
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PASSWORD)) != NULL)
+        if (!kdf_argon2_ctx_set_pwd(ctx, p))
+            return 0;
+
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SALT)) != NULL)
+        if (!kdf_argon2_ctx_set_salt(ctx, p))
+            return 0;
+
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SECRET)) != NULL)
+        if (!kdf_argon2_ctx_set_secret(ctx, p))
+            return 0;
+
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_ARGON2_AD)) != NULL)
+        if (!kdf_argon2_ctx_set_ad(ctx, p))
+            return 0;
+
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SIZE)) != NULL) {
+        if (!OSSL_PARAM_get_uint32(p, &u32_value))
+            return 0;
+        if (!kdf_argon2_ctx_set_out_length(ctx, u32_value))
+            return 0;
+    }
+
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_ITER)) != NULL) {
+        if (!OSSL_PARAM_get_uint32(p, &u32_value))
+            return 0;
+        if (!kdf_argon2_ctx_set_t_cost(ctx, u32_value))
+            return 0;
+    }
+
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_THREADS)) != NULL) {
+        if (!OSSL_PARAM_get_uint32(p, &u32_value))
+            return 0;
+        if (!kdf_argon2_ctx_set_threads(ctx, u32_value))
+            return 0;
+    }
+
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_ARGON2_LANES)) != NULL) {
+        if (!OSSL_PARAM_get_uint32(p, &u32_value))
+            return 0;
+        if (!kdf_argon2_ctx_set_lanes(ctx, u32_value))
+            return 0;
+    }
+
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_ARGON2_MEMCOST)) != NULL) {
+        if (!OSSL_PARAM_get_uint32(p, &u32_value))
+            return 0;
+        if (!kdf_argon2_ctx_set_m_cost(ctx, u32_value))
+            return 0;
+    }
+
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_EARLY_CLEAN)) != NULL) {
+        if (!OSSL_PARAM_get_uint32(p, &u32_value))
+            return 0;
+        kdf_argon2_ctx_set_flag_early_clean(ctx, u32_value);
+    }
+
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_ARGON2_VERSION)) != NULL) {
+        if (!OSSL_PARAM_get_uint32(p, &u32_value))
+            return 0;
+        if (!kdf_argon2_ctx_set_version(ctx, u32_value))
+            return 0;
+    }
+
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PROPERTIES)) != NULL) {
+        if (p->data_type != OSSL_PARAM_UTF8_STRING
+            || !set_property_query(ctx, p->data))
+            return 0;
+    }
+
+    return 1;
+}
+
+static const OSSL_PARAM *kdf_argon2_settable_ctx_params(ossl_unused void *ctx,
+                                                        ossl_unused void *p_ctx)
+{
+    static const OSSL_PARAM known_settable_ctx_params[] = {
+        OSSL_PARAM_octet_string(OSSL_KDF_PARAM_PASSWORD, NULL, 0),
+        OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, NULL, 0),
+        OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SECRET, NULL, 0),
+        OSSL_PARAM_octet_string(OSSL_KDF_PARAM_ARGON2_AD, NULL, 0),
+        OSSL_PARAM_uint32(OSSL_KDF_PARAM_SIZE, NULL),
+        OSSL_PARAM_uint32(OSSL_KDF_PARAM_ITER, NULL),
+        OSSL_PARAM_uint32(OSSL_KDF_PARAM_THREADS, NULL),
+        OSSL_PARAM_uint32(OSSL_KDF_PARAM_ARGON2_LANES, NULL),
+        OSSL_PARAM_uint32(OSSL_KDF_PARAM_ARGON2_MEMCOST, NULL),
+        OSSL_PARAM_uint32(OSSL_KDF_PARAM_EARLY_CLEAN, NULL),
+        OSSL_PARAM_uint32(OSSL_KDF_PARAM_ARGON2_VERSION, NULL),
+        OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0),
+        OSSL_PARAM_END
+    };
+
+    return known_settable_ctx_params;
+}
+
+static int kdf_argon2_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+    OSSL_PARAM *p;
+
+    (void) vctx;
+    if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL)
+        return OSSL_PARAM_set_size_t(p, SIZE_MAX);
+
+    return -2;
+}
+
+static const OSSL_PARAM *kdf_argon2_gettable_ctx_params(ossl_unused void *ctx,
+                                                        ossl_unused void *p_ctx)
+{
+    static const OSSL_PARAM known_gettable_ctx_params[] = {
+        OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL),
+        OSSL_PARAM_END
+    };
+
+    return known_gettable_ctx_params;
+}
+
+const OSSL_DISPATCH ossl_kdf_argon2i_functions[] = {
+    { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_argon2i_new },
+    { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_argon2_free },
+    { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_argon2_reset },
+    { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_argon2_derive },
+    { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS,
+      (void(*)(void))kdf_argon2_settable_ctx_params },
+    { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_argon2_set_ctx_params },
+    { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS,
+      (void(*)(void))kdf_argon2_gettable_ctx_params },
+    { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_argon2_get_ctx_params },
+    { 0, NULL }
+};
+
+const OSSL_DISPATCH ossl_kdf_argon2d_functions[] = {
+    { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_argon2d_new },
+    { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_argon2_free },
+    { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_argon2_reset },
+    { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_argon2_derive },
+    { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS,
+      (void(*)(void))kdf_argon2_settable_ctx_params },
+    { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_argon2_set_ctx_params },
+    { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS,
+      (void(*)(void))kdf_argon2_gettable_ctx_params },
+    { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_argon2_get_ctx_params },
+    { 0, NULL }
+};
+
+const OSSL_DISPATCH ossl_kdf_argon2id_functions[] = {
+    { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_argon2id_new },
+    { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_argon2_free },
+    { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_argon2_reset },
+    { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_argon2_derive },
+    { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS,
+      (void(*)(void))kdf_argon2_settable_ctx_params },
+    { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_argon2_set_ctx_params },
+    { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS,
+      (void(*)(void))kdf_argon2_gettable_ctx_params },
+    { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_argon2_get_ctx_params },
+    { 0, NULL }
+};
+
+#endif
index aa8b9da96956ab7f50586220f56dd4fb6ee197a7..3b7687b8f3a5b5987c495751c5b8342dfe15b9ca 100644 (file)
@@ -14,6 +14,7 @@ $SCRYPT_GOAL=../../libdefault.a
 $SSHKDF_GOAL=../../libdefault.a ../../libfips.a
 $X942KDF_GOAL=../../libdefault.a ../../libfips.a
 $HMAC_DRBG_KDF_GOAL=../../libdefault.a
+$ARGON2_GOAL=../../libdefault.a
 
 SOURCE[$TLS1_PRF_GOAL]=tls1_prf.c
 
@@ -42,3 +43,4 @@ SOURCE[$X942KDF_GOAL]=x942kdf.c
 DEPEND[x942kdf.o]=../../common/include/prov/der_wrap.h
 
 SOURCE[$HMAC_DRBG_KDF_GOAL]=hmacdrbg_kdf.c
+SOURCE[$ARGON2_GOAL]=argon2.c
index c154d89ab8ba786a6433ca5f6653ee9cfc8e5415..b783ab7ddfd2a98a09122eebdb9d6d2742c4cc62 100644 (file)
@@ -2785,6 +2785,48 @@ static int kdf_test_ctrl(EVP_TEST *t, EVP_KDF_CTX *kctx,
         goto end;
     }
 
+    if (strcmp(name, "lanes") == 0
+        && OSSL_PARAM_locate_const(defs, name) == NULL) {
+        TEST_info("skipping, setting 'lanes' is unsupported");
+        t->skip = 1;
+        goto end;
+    }
+
+    if (strcmp(name, "iter") == 0
+        && OSSL_PARAM_locate_const(defs, name) == NULL) {
+        TEST_info("skipping, setting 'iter' is unsupported");
+        t->skip = 1;
+        goto end;
+    }
+
+    if (strcmp(name, "memcost") == 0
+        && OSSL_PARAM_locate_const(defs, name) == NULL) {
+        TEST_info("skipping, setting 'memcost' is unsupported");
+        t->skip = 1;
+        goto end;
+    }
+
+    if (strcmp(name, "secret") == 0
+        && OSSL_PARAM_locate_const(defs, name) == NULL) {
+        TEST_info("skipping, setting 'secret' is unsupported");
+        t->skip = 1;
+        goto end;
+    }
+
+    if (strcmp(name, "pass") == 0
+        && OSSL_PARAM_locate_const(defs, name) == NULL) {
+        TEST_info("skipping, setting 'pass' is unsupported");
+        t->skip = 1;
+        goto end;
+    }
+
+    if (strcmp(name, "ad") == 0
+        && OSSL_PARAM_locate_const(defs, name) == NULL) {
+        TEST_info("skipping, setting 'ad' is unsupported");
+        t->skip = 1;
+        goto end;
+    }
+
     rv = OSSL_PARAM_allocate_from_text(kdata->p, defs, name, p,
                                        p != NULL ? strlen(p) : 0, NULL);
     *++kdata->p = OSSL_PARAM_construct_end();
@@ -4156,6 +4198,10 @@ static int is_kdf_disabled(const char *name)
 #ifdef OPENSSL_NO_SCRYPT
     if (HAS_CASE_SUFFIX(name, "SCRYPT"))
         return 1;
+#endif
+#ifdef OPENSSL_NO_ARGON2
+    if (HAS_CASE_SUFFIX(name, "ARGON2"))
+        return 1;
 #endif
     return 0;
 }
index adf0274605ab0fe1085ccaa2c19d4356dc7d73d3..d262dbfd05ee057f2a76815aa04a3d84959eb439 100644 (file)
@@ -29,6 +29,7 @@ my $no_ec = disabled("ec");
 my $no_ec2m = disabled("ec2m");
 my $no_sm2 = disabled("sm2");
 my $no_siv = disabled("siv");
+my $no_argon2 = disabled("argon2");
 
 # Default config depends on if the legacy module is built or not
 my $defaultcnf = $no_legacy ? 'default.cnf' : 'default-and-legacy.cnf';
@@ -126,6 +127,7 @@ push @defltfiles, qw(evppkey_dsa_rfc6979.txt) unless $no_dsa;
 push @defltfiles, qw(evppkey_sm2.txt) unless $no_sm2;
 push @defltfiles, qw(evpciph_aes_gcm_siv.txt) unless $no_siv;
 push @defltfiles, qw(evpciph_aes_siv.txt) unless $no_siv;
+push @defltfiles, qw(evpkdf_argon2.txt) unless $no_argon2;
 
 plan tests =>
     + (scalar(@configs) * scalar(@files))
diff --git a/test/recipes/30-test_evp_data/evpkdf_argon2.txt b/test/recipes/30-test_evp_data/evpkdf_argon2.txt
new file mode 100644 (file)
index 0000000..cfacf3e
--- /dev/null
@@ -0,0 +1,168 @@
+Title = Argon2 tests (from rfc 9106 and others)
+
+KDF = ARGON2D
+Ctrl.lanes = lanes:4
+Ctrl.iter = iter:3
+Ctrl.memcost = memcost:32
+Ctrl.secret = hexsecret:0303030303030303
+Ctrl.pass = hexpass:0101010101010101010101010101010101010101010101010101010101010101
+Ctrl.salt = hexsalt:02020202020202020202020202020202
+Ctrl.ad = hexad:040404040404040404040404
+Output = 512B391B6F1162975371D30919734294F868E3BE3984F3C1A13A4DB9FABE4ACB
+
+KDF = ARGON2D
+Ctrl.lanes = lanes:4
+Ctrl.iter = iter:3
+Ctrl.memcost = memcost:32
+Ctrl.secret = hexsecret:0303030303030303
+Ctrl.pass = hexpass:0101010101010101010101010101010101010101010101010101010101010101
+Ctrl.salt = hexsalt:02020202020202020202020202020202
+Ctrl.ad = hexad:040404040404040404040404
+Ctrl.early_clean = early_clean:1
+Output = 512B391B6F1162975371D30919734294F868E3BE3984F3C1A13A4DB9FABE4ACB
+
+KDF = ARGON2I
+Ctrl.lanes = lanes:4
+Ctrl.iter = iter:3
+Ctrl.memcost = memcost:32
+Ctrl.secret = hexsecret:0303030303030303
+Ctrl.pass = hexpass:0101010101010101010101010101010101010101010101010101010101010101
+Ctrl.salt = hexsalt:02020202020202020202020202020202
+Ctrl.ad = hexad:040404040404040404040404
+Output = C814D9D1DC7F37AA13F0D77F2494BDA1C8DE6B016DD388D29952A4C4672B6CE8
+
+KDF = ARGON2ID
+Ctrl.lanes = lanes:4
+Ctrl.iter = iter:3
+Ctrl.memcost = memcost:32
+Ctrl.secret = hexsecret:0303030303030303
+Ctrl.pass = hexpass:0101010101010101010101010101010101010101010101010101010101010101
+Ctrl.salt = hexsalt:02020202020202020202020202020202
+Ctrl.ad = hexad:040404040404040404040404
+Output = 0D640DF58D78766C08C037A34A8B53C9D01EF0452D75B65EB52520E96B01E659
+
+KDF = ARGON2D
+Ctrl.pass = hexpass:31323334353637383930
+Ctrl.salt = hexsalt:73616C7473616C74
+Output = D16AD773B1C6400D3193BC3E66271603E9DE72BACE20AF3F89C236F5434CDEC99072DDFC6B9C77EA9F386C0E8D7CB0C37CEC6EC3277A22C92D5BE58EF67C7EAA
+
+KDF = ARGON2ID
+Ctrl.lanes = lanes:4
+Ctrl.iter = iter:3
+Ctrl.memcost = memcost:32
+Ctrl.salt = hexsalt:02020202020202020202020202020202
+Output = 0A34F1ABDE67086C82E785EAF17C68382259A264F4E61B91CD2763CB75AC189A
+
+KDF = ARGON2D
+Ctrl.pass = hexpass:31323334353637383930
+Ctrl.salt = hexsalt:73616C7473616C74
+Output = D16AD773B1C6400D3193BC3E66271603E9DE72BACE20AF3F89C236F5434CDEC99072DDFC6B9C77EA9F386C0E8D7CB0C37CEC6EC3277A22C92D5BE58EF67C7EAA
+
+KDF = ARGON2D
+Ctrl.lanes = lanes:2
+Ctrl.memcost = memcost:65536
+Ctrl.pass = hexpass:31323334353637383930
+Ctrl.salt = hexsalt:73616C7473616C74
+Output = 5CA0AB135DE1241454840172696C301C7B8FD99A788CD11CF9699044CADF7FCA0A6E3762CB3043A71ADF6553DB3FD7925101B0CCF8868B098492A4ADDB2486BC
+
+KDF = ARGON2I
+Ctrl.lanes = lanes:4
+Ctrl.iter = iter:3
+Ctrl.memcost = memcost:32
+Ctrl.pass = hexpass:0101010101010101010101010101010101010101010101010101010101010101
+Ctrl.salt = hexsalt:02020202020202020202020202020202
+Output = A9A7510E6DB4D588BA3414CD0E094D480D683F97B9CCB612A544FE8EF65BA8E0
+
+KDF = ARGON2ID
+Ctrl.lanes = lanes:4
+Ctrl.iter = iter:3
+Ctrl.memcost = memcost:32
+Ctrl.pass = hexpass:0101010101010101010101010101010101010101010101010101010101010101
+Ctrl.salt = hexsalt:02020202020202020202020202020202
+Output = 03AAB965C12001C9D7D0D2DE33192C0494B684BB148196D73C1DF1ACAF6D0C2E
+
+KDF = ARGON2D
+Threads = 2
+Ctrl.threads = threads:2
+Ctrl.lanes = lanes:2
+Ctrl.memcost = memcost:65536
+Ctrl.pass = pass:1234567890
+Ctrl.salt = hexsalt:73616C7473616C74
+Output = A86C83A19F0B234ECBA8C275D16D059153F961E4C39EC9B1BE98B3E73D791789363682443AD594334048634E91C493AFFED0BC29FD329A0E553C00149D6DB19AF4E4A354AEC14DBD575D78BA87D4A4BC4746666E7A4E6EE1572BBFFC2EBA308A2D825CB7B41FDE3A95D5CFF0DFA2D0FDD636B32AEA8B4A3C532742D330BD1B90
+
+KDF = ARGON2ID
+Threads = 2
+Ctrl.threads = threads:2
+Ctrl.lanes = lanes:4
+Ctrl.iter = iter:3
+Ctrl.memcost = memcost:32
+Ctrl.pass = hexpass:0101010101010101010101010101010101010101010101010101010101010101
+Ctrl.salt = hexsalt:02020202020202020202020202020202
+Output = 03AAB965C12001C9D7D0D2DE33192C0494B684BB148196D73C1DF1ACAF6D0C2E
+
+# Expected fail on condition violation: m_cost < 8 * lanes
+
+KDF = ARGON2D
+Ctrl.lanes = lanes:100
+Ctrl.memcost = memcost:799
+Ctrl.salt = hexsalt:02020202020202020202020202020202
+Output = 03AAB965C12001C9D7D0D2DE33192C0494B684BB148196D73C1DF1ACAF6D0C2E
+Result = KDF_DERIVE_ERROR
+
+# Expected fail on condition violation: m_cost < 2 * syncpoints = 8
+
+KDF = ARGON2D
+Ctrl.memcost = memcost:7
+Ctrl.pass = hexpass:31323334353637383930
+Ctrl.salt = hexsalt:73616C7473616C74
+Result = KDF_CTRL_ERROR
+
+# Expected fail on condition violation: threads > avail threads
+
+KDF = ARGON2D
+Ctrl.threads = threads:2
+Ctrl.lanes = lanes:2
+Ctrl.memcost = memcost:65536
+Ctrl.pass = hexpass:31323334353637383930
+Ctrl.salt = hexsalt:73616C7473616C74
+Result = KDF_DERIVE_ERROR
+
+# Expected fail on condition violation: lanes >= 1
+KDF = ARGON2D
+Ctrl.lanes = lanes:0
+Result = KDF_CTRL_ERROR
+
+# Expected fail on condition violation: lanes <= 0xFFFFFF
+KDF = ARGON2D
+Ctrl.lanes = lanes:0x1000000
+Result = KDF_CTRL_ERROR
+
+# Expected fail on condition violation: threads >= 1
+KDF = ARGON2D
+Ctrl.lanes = threads:0
+Result = KDF_CTRL_ERROR
+
+# Expected fail on condition violation: threads <= 0xFFFFFF
+KDF = ARGON2D
+Ctrl.lanes = threads:0x1000000
+Result = KDF_CTRL_ERROR
+
+# Expected fail on condition violation: outlen >= 4
+KDF = ARGON2D
+Ctrl.size = size:3
+Result = KDF_CTRL_ERROR
+
+# Expected fail on condition violation: iter >= 1
+KDF = ARGON2D
+Ctrl.iter = iter:0
+Result = KDF_CTRL_ERROR
+
+# Expected fail on condition violation: saltlen > 8
+KDF = ARGON2D
+Ctrl.salt = hexsalt:0202
+Result = KDF_CTRL_ERROR
+
+# Expected fail on condition violation: invalid version
+KDF = ARGON2D
+Ctrl.version = version:1
+Result = KDF_CTRL_ERROR