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