mirror of https://github.com/openssl/openssl.git
				
				
				
			
		
			
	
	
		
			1561 lines
		
	
	
		
			48 KiB
		
	
	
	
		
			C
		
	
	
	
		
		
			
		
	
	
			1561 lines
		
	
	
		
			48 KiB
		
	
	
	
		
			C
		
	
	
	
|  | /*
 | ||
|  |  * Copyright 2022-2023 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 <stdarg.h>
 | ||
|  | #include <string.h>
 | ||
|  | #include <openssl/e_os2.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 <openssl/proverr.h>
 | ||
|  | #include "internal/thread.h"
 | ||
|  | #include "internal/numbers.h"
 | ||
|  | #include "internal/endian.h"
 | ||
|  | #include "crypto/evp.h"
 | ||
|  | #include "prov/implementations.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; | ||
|  |     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, void *out); | ||
|  | 
 | ||
|  | 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 = 0xFFFFFFFFUL; | ||
|  |     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, void *out) | ||
|  | { | ||
|  |     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, 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_SIZE, &outlen); | ||
|  |     par[1] = OSSL_PARAM_construct_end(); | ||
|  | 
 | ||
|  |     ret = EVP_DigestInit_ex2(ctx, md, par) == 1 | ||
|  |         && EVP_DigestUpdate(ctx, in, inlen) == 1 | ||
|  |         && EVP_DigestFinal_ex(ctx, out, NULL) == 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_SIZE, &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_DigestFinal_ex(ctx, (outlen > BLAKE2B_OUTBYTES) ? outbuf : out, | ||
|  |                               NULL) == 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->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); | ||
|  | 
 | ||
|  |     EVP_MD_free(ctx->md); | ||
|  |     EVP_MAC_free(ctx->mac); | ||
|  | 
 | ||
|  |     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; | ||
|  | 
 | ||
|  |     if (ctx->mac == NULL) | ||
|  |         ctx->mac = EVP_MAC_fetch(ctx->libctx, "blake2bmac", ctx->propq); | ||
|  |     if (ctx->mac == NULL) { | ||
|  |         ERR_raise_data(ERR_LIB_PROV, PROV_R_MISSING_MAC, | ||
|  |                        "cannot fetch blake2bmac"); | ||
|  |         return 0; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (ctx->md == NULL) | ||
|  |         ctx->md = EVP_MD_fetch(ctx->libctx, "blake2b512", ctx->propq); | ||
|  |     if (ctx->md == NULL) { | ||
|  |         ERR_raise_data(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST, | ||
|  |                        "cannot fetch blake2b512"); | ||
|  |         return 0; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (ctx->salt == NULL || ctx->saltlen == 0) { | ||
|  |         ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_SALT); | ||
|  |         return 0; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (outlen != ctx->outlen) { | ||
|  |         if (OSSL_PARAM_locate((OSSL_PARAM *)params, "size") != NULL) { | ||
|  |             ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); | ||
|  |             return 0; | ||
|  |         } | ||
|  |         if (!kdf_argon2_ctx_set_out_length(ctx, (uint32_t) outlen)) | ||
|  |             return 0; | ||
|  |     } | ||
|  | 
 | ||
|  |     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"); | ||
|  |         return 0; | ||
|  |     } | ||
|  | 
 | ||
|  |     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); | ||
|  |         return 0; | ||
|  | # 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: %u", | ||
|  |                            ctx->threads, ossl_get_avail_threads(ctx->libctx)); | ||
|  |             return 0; | ||
|  |         } | ||
|  | # 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); | ||
|  |             return 0; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     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"); | ||
|  |         return 0; | ||
|  |     } | ||
|  | 
 | ||
|  |     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) | ||
|  |         return 0; | ||
|  | 
 | ||
|  |     if (fill_memory_blocks(ctx) != 1) | ||
|  |         return 0; | ||
|  | 
 | ||
|  |     finalize(ctx, out); | ||
|  | 
 | ||
|  |     return 1; | ||
|  | } | ||
|  | 
 | ||
|  | 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; | ||
|  | 
 | ||
|  |     EVP_MD_free(ctx->md); | ||
|  |     EVP_MAC_free(ctx->mac); | ||
|  | 
 | ||
|  |     OPENSSL_free(ctx->propq); | ||
|  | 
 | ||
|  |     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, so skip check  */ | ||
|  | 
 | ||
|  |     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, so skip check */ | ||
|  | 
 | ||
|  |     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. | ||
|  |      */ | ||
|  | 
 | ||
|  |     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; | ||
|  |     } | ||
|  |     EVP_MD_free(ctx->md); | ||
|  |     ctx->md = NULL; | ||
|  |     EVP_MAC_free(ctx->mac); | ||
|  |     ctx->mac = NULL; | ||
|  |     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 (ossl_param_is_empty(params)) | ||
|  |         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 }, | ||
|  |     OSSL_DISPATCH_END | ||
|  | }; | ||
|  | 
 | ||
|  | 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 }, | ||
|  |     OSSL_DISPATCH_END | ||
|  | }; | ||
|  | 
 | ||
|  | 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 }, | ||
|  |     OSSL_DISPATCH_END | ||
|  | }; | ||
|  | 
 | ||
|  | #endif
 |