From 6dfa998f7ea150f9c6d4e4727cf6d5c82a68a8da Mon Sep 17 00:00:00 2001 From: =?utf8?q?=C4=8Cestm=C3=ADr=20Kalina?= Date: Mon, 27 Sep 2021 22:45:38 +0200 Subject: [PATCH] providers: add Argon2 KDF MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit https://datatracker.ietf.org/doc/rfc9106/ Signed-off-by: Čestmír Kalina Reviewed-by: Tomas Mraz Reviewed-by: Paul Dale (Merged from https://github.com/openssl/openssl/pull/12256) --- CHANGES.md | 5 + Configure | 3 + apps/list.c | 3 + crypto/err/openssl.txt | 2 +- doc/build.info | 6 + doc/man7/EVP_KDF-ARGON2.pod | 192 ++ doc/man7/OSSL_PROVIDER-default.pod | 2 + include/openssl/core_names.h | 6 + include/openssl/proverr.h | 2 +- providers/defltprov.c | 5 + .../include/prov/implementations.h | 5 + .../implementations/include/prov/names.h | 3 + providers/implementations/kdfs/argon2.c | 1586 +++++++++++++++++ providers/implementations/kdfs/build.info | 2 + test/evp_test.c | 46 + test/recipes/30-test_evp.t | 2 + .../30-test_evp_data/evpkdf_argon2.txt | 168 ++ 17 files changed, 2036 insertions(+), 2 deletions(-) create mode 100644 doc/man7/EVP_KDF-ARGON2.pod create mode 100644 providers/implementations/kdfs/argon2.c create mode 100644 test/recipes/30-test_evp_data/evpkdf_argon2.txt 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 password-based KDF through the B +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) + +=item "salt" (B) + +=item "secret" (B) + +=item "iter" (B) + +=item "size" (B) + +These parameters work as described in L. + +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) + +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) + +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) + +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) + +Memory cost parameter (the number of 1k memory blocks used). + +=item "version" (B) + +Argon2 version. Supported values: 0x10, 0x13 (default). + +=item "early_clean" (B) + +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 /* strlen */ + #include /* OSSL_KDF_* */ + #include /* OSSL_PARAM_* */ + #include /* OSSL_set_max_threads */ + #include /* 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. + +=head1 SEE ALSO + +L, +L, +L, +L, +L, +L + +=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. + +=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 +=item ARGON2, see L + =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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal/thread.h" +#include "internal/numbers.h" +#include "internal/endian.h" +#include "prov/implementations.h" +#include +#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 diff --git a/providers/implementations/kdfs/build.info b/providers/implementations/kdfs/build.info index aa8b9da969..3b7687b8f3 100644 --- a/providers/implementations/kdfs/build.info +++ b/providers/implementations/kdfs/build.info @@ -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 diff --git a/test/evp_test.c b/test/evp_test.c index c154d89ab8..b783ab7ddf 100644 --- a/test/evp_test.c +++ b/test/evp_test.c @@ -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; } diff --git a/test/recipes/30-test_evp.t b/test/recipes/30-test_evp.t index adf0274605..d262dbfd05 100644 --- a/test/recipes/30-test_evp.t +++ b/test/recipes/30-test_evp.t @@ -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 index 0000000000..cfacf3e051 --- /dev/null +++ b/test/recipes/30-test_evp_data/evpkdf_argon2.txt @@ -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 -- 2.39.2