diff options
Diffstat (limited to 'crypto/openssl/providers/implementations')
286 files changed, 70594 insertions, 0 deletions
diff --git a/crypto/openssl/providers/implementations/asymciphers/build.info b/crypto/openssl/providers/implementations/asymciphers/build.info new file mode 100644 index 000000000000..dbca47368488 --- /dev/null +++ b/crypto/openssl/providers/implementations/asymciphers/build.info @@ -0,0 +1,11 @@ +# We make separate GOAL variables for each algorithm, to make it easy to +# switch each to the Legacy provider when needed. + +$RSA_GOAL=../../libdefault.a ../../libfips.a +$SM2_GOAL=../../libdefault.a + +SOURCE[$RSA_GOAL]=rsa_enc.c + +IF[{- !$disabled{"sm2"} -}] + SOURCE[$SM2_GOAL]=sm2_enc.c +ENDIF diff --git a/crypto/openssl/providers/implementations/asymciphers/rsa_enc.c b/crypto/openssl/providers/implementations/asymciphers/rsa_enc.c new file mode 100644 index 000000000000..e6b676d0f8fa --- /dev/null +++ b/crypto/openssl/providers/implementations/asymciphers/rsa_enc.c @@ -0,0 +1,648 @@ +/* + * Copyright 2019-2025 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 + */ + +/* + * RSA low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include <openssl/crypto.h> +#include <openssl/evp.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/rsa.h> +#include <openssl/params.h> +#include <openssl/err.h> +#include <openssl/proverr.h> +/* Just for SSL_MAX_MASTER_KEY_LENGTH */ +#include <openssl/prov_ssl.h> +#include "internal/constant_time.h" +#include "internal/sizes.h" +#include "crypto/rsa.h" +#include "prov/provider_ctx.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" +#include "prov/securitycheck.h" +#include <stdlib.h> + +static OSSL_FUNC_asym_cipher_newctx_fn rsa_newctx; +static OSSL_FUNC_asym_cipher_encrypt_init_fn rsa_encrypt_init; +static OSSL_FUNC_asym_cipher_encrypt_fn rsa_encrypt; +static OSSL_FUNC_asym_cipher_decrypt_init_fn rsa_decrypt_init; +static OSSL_FUNC_asym_cipher_decrypt_fn rsa_decrypt; +static OSSL_FUNC_asym_cipher_freectx_fn rsa_freectx; +static OSSL_FUNC_asym_cipher_dupctx_fn rsa_dupctx; +static OSSL_FUNC_asym_cipher_get_ctx_params_fn rsa_get_ctx_params; +static OSSL_FUNC_asym_cipher_gettable_ctx_params_fn rsa_gettable_ctx_params; +static OSSL_FUNC_asym_cipher_set_ctx_params_fn rsa_set_ctx_params; +static OSSL_FUNC_asym_cipher_settable_ctx_params_fn rsa_settable_ctx_params; + +static OSSL_ITEM padding_item[] = { + { RSA_PKCS1_PADDING, OSSL_PKEY_RSA_PAD_MODE_PKCSV15 }, + { RSA_NO_PADDING, OSSL_PKEY_RSA_PAD_MODE_NONE }, + { RSA_PKCS1_OAEP_PADDING, OSSL_PKEY_RSA_PAD_MODE_OAEP }, /* Correct spelling first */ + { RSA_PKCS1_OAEP_PADDING, "oeap" }, + { 0, NULL } +}; + +/* + * What's passed as an actual key is defined by the KEYMGMT interface. + * We happen to know that our KEYMGMT simply passes RSA structures, so + * we use that here too. + */ + +typedef struct { + OSSL_LIB_CTX *libctx; + RSA *rsa; + int pad_mode; + int operation; + /* OAEP message digest */ + EVP_MD *oaep_md; + /* message digest for MGF1 */ + EVP_MD *mgf1_md; + /* OAEP label */ + unsigned char *oaep_label; + size_t oaep_labellen; + /* TLS padding */ + unsigned int client_version; + unsigned int alt_version; + /* PKCS#1 v1.5 decryption mode */ + unsigned int implicit_rejection; + OSSL_FIPS_IND_DECLARE +} PROV_RSA_CTX; + +static void *rsa_newctx(void *provctx) +{ + PROV_RSA_CTX *prsactx; + + if (!ossl_prov_is_running()) + return NULL; + prsactx = OPENSSL_zalloc(sizeof(PROV_RSA_CTX)); + if (prsactx == NULL) + return NULL; + prsactx->libctx = PROV_LIBCTX_OF(provctx); + OSSL_FIPS_IND_INIT(prsactx) + + return prsactx; +} + +static int rsa_init(void *vprsactx, void *vrsa, const OSSL_PARAM params[], + int operation, const char *desc) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + int protect = 0; + + if (!ossl_prov_is_running() || prsactx == NULL || vrsa == NULL) + return 0; + + if (!ossl_rsa_key_op_get_protect(vrsa, operation, &protect)) + return 0; + if (!RSA_up_ref(vrsa)) + return 0; + RSA_free(prsactx->rsa); + prsactx->rsa = vrsa; + prsactx->operation = operation; + prsactx->implicit_rejection = 1; + + switch (RSA_test_flags(prsactx->rsa, RSA_FLAG_TYPE_MASK)) { + case RSA_FLAG_TYPE_RSA: + prsactx->pad_mode = RSA_PKCS1_PADDING; + break; + default: + /* This should not happen due to the check above */ + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + return 0; + } + + OSSL_FIPS_IND_SET_APPROVED(prsactx) + if (!rsa_set_ctx_params(prsactx, params)) + return 0; +#ifdef FIPS_MODULE + if (!ossl_fips_ind_rsa_key_check(OSSL_FIPS_IND_GET(prsactx), + OSSL_FIPS_IND_SETTABLE0, prsactx->libctx, + prsactx->rsa, desc, protect)) + return 0; +#endif + return 1; +} + +static int rsa_encrypt_init(void *vprsactx, void *vrsa, + const OSSL_PARAM params[]) +{ + return rsa_init(vprsactx, vrsa, params, EVP_PKEY_OP_ENCRYPT, + "RSA Encrypt Init"); +} + +static int rsa_decrypt_init(void *vprsactx, void *vrsa, + const OSSL_PARAM params[]) +{ + return rsa_init(vprsactx, vrsa, params, EVP_PKEY_OP_DECRYPT, + "RSA Decrypt Init"); +} + +static int rsa_encrypt(void *vprsactx, unsigned char *out, size_t *outlen, + size_t outsize, const unsigned char *in, size_t inlen) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + size_t len = RSA_size(prsactx->rsa); + int ret; + + if (!ossl_prov_is_running()) + return 0; + +#ifdef FIPS_MODULE + if ((prsactx->pad_mode == RSA_PKCS1_PADDING + || prsactx->pad_mode == RSA_PKCS1_WITH_TLS_PADDING) + && !OSSL_FIPS_IND_ON_UNAPPROVED(prsactx, OSSL_FIPS_IND_SETTABLE1, + prsactx->libctx, "RSA Encrypt", + "PKCS#1 v1.5 padding", + ossl_fips_config_rsa_pkcs15_padding_disabled)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_PADDING_MODE); + return 0; + } +#endif + + if (len == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY); + return 0; + } + + if (out == NULL) { + *outlen = len; + return 1; + } + + if (outsize < len) { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + return 0; + } + + if (prsactx->pad_mode == RSA_PKCS1_OAEP_PADDING) { + int rsasize = RSA_size(prsactx->rsa); + unsigned char *tbuf; + + if ((tbuf = OPENSSL_malloc(rsasize)) == NULL) + return 0; + if (prsactx->oaep_md == NULL) { + prsactx->oaep_md = EVP_MD_fetch(prsactx->libctx, "SHA-1", NULL); + if (prsactx->oaep_md == NULL) { + OPENSSL_free(tbuf); + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + return 0; + } + } + ret = + ossl_rsa_padding_add_PKCS1_OAEP_mgf1_ex(prsactx->libctx, tbuf, + rsasize, in, inlen, + prsactx->oaep_label, + prsactx->oaep_labellen, + prsactx->oaep_md, + prsactx->mgf1_md); + + if (!ret) { + OPENSSL_free(tbuf); + return 0; + } + ret = RSA_public_encrypt(rsasize, tbuf, out, prsactx->rsa, + RSA_NO_PADDING); + OPENSSL_free(tbuf); + } else { + ret = RSA_public_encrypt(inlen, in, out, prsactx->rsa, + prsactx->pad_mode); + } + /* A ret value of 0 is not an error */ + if (ret < 0) + return ret; + *outlen = ret; + return 1; +} + +static int rsa_decrypt(void *vprsactx, unsigned char *out, size_t *outlen, + size_t outsize, const unsigned char *in, size_t inlen) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + int ret; + int pad_mode; + size_t len = RSA_size(prsactx->rsa); + + if (!ossl_prov_is_running()) + return 0; + + if (prsactx->pad_mode == RSA_PKCS1_WITH_TLS_PADDING) { + if (out == NULL) { + *outlen = SSL_MAX_MASTER_KEY_LENGTH; + return 1; + } + if (outsize < SSL_MAX_MASTER_KEY_LENGTH) { + ERR_raise(ERR_LIB_PROV, PROV_R_BAD_LENGTH); + return 0; + } + } else { + if (out == NULL) { + if (len == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY); + return 0; + } + *outlen = len; + return 1; + } + + if (outsize < len) { + ERR_raise(ERR_LIB_PROV, PROV_R_BAD_LENGTH); + return 0; + } + } + + if (prsactx->pad_mode == RSA_PKCS1_OAEP_PADDING + || prsactx->pad_mode == RSA_PKCS1_WITH_TLS_PADDING) { + unsigned char *tbuf; + + if ((tbuf = OPENSSL_malloc(len)) == NULL) + return 0; + ret = RSA_private_decrypt(inlen, in, tbuf, prsactx->rsa, + RSA_NO_PADDING); + /* + * With no padding then, on success ret should be len, otherwise an + * error occurred (non-constant time) + */ + if (ret != (int)len) { + OPENSSL_free(tbuf); + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_DECRYPT); + return 0; + } + if (prsactx->pad_mode == RSA_PKCS1_OAEP_PADDING) { + if (prsactx->oaep_md == NULL) { + prsactx->oaep_md = EVP_MD_fetch(prsactx->libctx, "SHA-1", NULL); + if (prsactx->oaep_md == NULL) { + OPENSSL_free(tbuf); + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + return 0; + } + } + ret = RSA_padding_check_PKCS1_OAEP_mgf1(out, outsize, tbuf, + len, len, + prsactx->oaep_label, + prsactx->oaep_labellen, + prsactx->oaep_md, + prsactx->mgf1_md); + } else { + /* RSA_PKCS1_WITH_TLS_PADDING */ + if (prsactx->client_version <= 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_BAD_TLS_CLIENT_VERSION); + OPENSSL_free(tbuf); + return 0; + } + ret = ossl_rsa_padding_check_PKCS1_type_2_TLS( + prsactx->libctx, out, outsize, tbuf, len, + prsactx->client_version, prsactx->alt_version); + } + OPENSSL_free(tbuf); + } else { + if ((prsactx->implicit_rejection == 0) && + (prsactx->pad_mode == RSA_PKCS1_PADDING)) + pad_mode = RSA_PKCS1_NO_IMPLICIT_REJECT_PADDING; + else + pad_mode = prsactx->pad_mode; + ret = RSA_private_decrypt(inlen, in, out, prsactx->rsa, pad_mode); + } + *outlen = constant_time_select_s(constant_time_msb_s(ret), *outlen, ret); + ret = constant_time_select_int(constant_time_msb(ret), 0, 1); + return ret; +} + +static void rsa_freectx(void *vprsactx) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + + RSA_free(prsactx->rsa); + + EVP_MD_free(prsactx->oaep_md); + EVP_MD_free(prsactx->mgf1_md); + OPENSSL_free(prsactx->oaep_label); + + OPENSSL_free(prsactx); +} + +static void *rsa_dupctx(void *vprsactx) +{ + PROV_RSA_CTX *srcctx = (PROV_RSA_CTX *)vprsactx; + PROV_RSA_CTX *dstctx; + + if (!ossl_prov_is_running()) + return NULL; + + dstctx = OPENSSL_zalloc(sizeof(*srcctx)); + if (dstctx == NULL) + return NULL; + + *dstctx = *srcctx; + if (dstctx->rsa != NULL && !RSA_up_ref(dstctx->rsa)) { + OPENSSL_free(dstctx); + return NULL; + } + + if (dstctx->oaep_md != NULL && !EVP_MD_up_ref(dstctx->oaep_md)) { + RSA_free(dstctx->rsa); + OPENSSL_free(dstctx); + return NULL; + } + + if (dstctx->mgf1_md != NULL && !EVP_MD_up_ref(dstctx->mgf1_md)) { + RSA_free(dstctx->rsa); + EVP_MD_free(dstctx->oaep_md); + OPENSSL_free(dstctx); + return NULL; + } + + return dstctx; +} + +static int rsa_get_ctx_params(void *vprsactx, OSSL_PARAM *params) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + OSSL_PARAM *p; + + if (prsactx == NULL) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_ASYM_CIPHER_PARAM_PAD_MODE); + if (p != NULL) + switch (p->data_type) { + case OSSL_PARAM_INTEGER: /* Support for legacy pad mode number */ + if (!OSSL_PARAM_set_int(p, prsactx->pad_mode)) + return 0; + break; + case OSSL_PARAM_UTF8_STRING: + { + int i; + const char *word = NULL; + + for (i = 0; padding_item[i].id != 0; i++) { + if (prsactx->pad_mode == (int)padding_item[i].id) { + word = padding_item[i].ptr; + break; + } + } + + if (word != NULL) { + if (!OSSL_PARAM_set_utf8_string(p, word)) + return 0; + } else { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + } + } + break; + default: + return 0; + } + + p = OSSL_PARAM_locate(params, OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST); + if (p != NULL && !OSSL_PARAM_set_utf8_string(p, prsactx->oaep_md == NULL + ? "" + : EVP_MD_get0_name(prsactx->oaep_md))) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST); + if (p != NULL) { + EVP_MD *mgf1_md = prsactx->mgf1_md == NULL ? prsactx->oaep_md + : prsactx->mgf1_md; + + if (!OSSL_PARAM_set_utf8_string(p, mgf1_md == NULL + ? "" + : EVP_MD_get0_name(mgf1_md))) + return 0; + } + + p = OSSL_PARAM_locate(params, OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL); + if (p != NULL && + !OSSL_PARAM_set_octet_ptr(p, prsactx->oaep_label, + prsactx->oaep_labellen)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_ASYM_CIPHER_PARAM_TLS_CLIENT_VERSION); + if (p != NULL && !OSSL_PARAM_set_uint(p, prsactx->client_version)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_ASYM_CIPHER_PARAM_TLS_NEGOTIATED_VERSION); + if (p != NULL && !OSSL_PARAM_set_uint(p, prsactx->alt_version)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION); + if (p != NULL && !OSSL_PARAM_set_uint(p, prsactx->implicit_rejection)) + return 0; + if (!OSSL_FIPS_IND_GET_CTX_PARAM(prsactx, params)) + return 0; + return 1; +} + +static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_PAD_MODE, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST, NULL, 0), + OSSL_PARAM_DEFN(OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL, OSSL_PARAM_OCTET_PTR, + NULL, 0), + OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_TLS_CLIENT_VERSION, NULL), + OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_TLS_NEGOTIATED_VERSION, NULL), + OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION, NULL), + OSSL_FIPS_IND_GETTABLE_CTX_PARAM() + OSSL_PARAM_END +}; + +static const OSSL_PARAM *rsa_gettable_ctx_params(ossl_unused void *vprsactx, + ossl_unused void *provctx) +{ + return known_gettable_ctx_params; +} + +static int rsa_set_ctx_params(void *vprsactx, const OSSL_PARAM params[]) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + const OSSL_PARAM *p; + char mdname[OSSL_MAX_NAME_SIZE]; + char mdprops[OSSL_MAX_PROPQUERY_SIZE] = { '\0' }; + char *str = NULL; + + if (prsactx == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 1; + + if (!OSSL_FIPS_IND_SET_CTX_PARAM(prsactx, OSSL_FIPS_IND_SETTABLE0, params, + OSSL_ASYM_CIPHER_PARAM_FIPS_KEY_CHECK)) + return 0; + if (!OSSL_FIPS_IND_SET_CTX_PARAM(prsactx, OSSL_FIPS_IND_SETTABLE1, params, + OSSL_ASYM_CIPHER_PARAM_FIPS_RSA_PKCS15_PAD_DISABLED)) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST); + if (p != NULL) { + str = mdname; + if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(mdname))) + return 0; + + p = OSSL_PARAM_locate_const(params, + OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST_PROPS); + if (p != NULL) { + str = mdprops; + if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(mdprops))) + return 0; + } + + EVP_MD_free(prsactx->oaep_md); + prsactx->oaep_md = EVP_MD_fetch(prsactx->libctx, mdname, mdprops); + + if (prsactx->oaep_md == NULL) + return 0; + } + + p = OSSL_PARAM_locate_const(params, OSSL_ASYM_CIPHER_PARAM_PAD_MODE); + if (p != NULL) { + int pad_mode = 0; + + switch (p->data_type) { + case OSSL_PARAM_INTEGER: /* Support for legacy pad mode number */ + if (!OSSL_PARAM_get_int(p, &pad_mode)) + return 0; + break; + case OSSL_PARAM_UTF8_STRING: + { + int i; + + if (p->data == NULL) + return 0; + + for (i = 0; padding_item[i].id != 0; i++) { + if (strcmp(p->data, padding_item[i].ptr) == 0) { + pad_mode = padding_item[i].id; + break; + } + } + } + break; + default: + return 0; + } + + /* + * PSS padding is for signatures only so is not compatible with + * asymmetric cipher use. + */ + if (pad_mode == RSA_PKCS1_PSS_PADDING) + return 0; + if (pad_mode == RSA_PKCS1_OAEP_PADDING && prsactx->oaep_md == NULL) { + prsactx->oaep_md = EVP_MD_fetch(prsactx->libctx, "SHA1", mdprops); + if (prsactx->oaep_md == NULL) + return 0; + } + prsactx->pad_mode = pad_mode; + } + + p = OSSL_PARAM_locate_const(params, OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST); + if (p != NULL) { + str = mdname; + if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(mdname))) + return 0; + + p = OSSL_PARAM_locate_const(params, + OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST_PROPS); + if (p != NULL) { + str = mdprops; + if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(mdprops))) + return 0; + } else { + str = NULL; + } + + EVP_MD_free(prsactx->mgf1_md); + prsactx->mgf1_md = EVP_MD_fetch(prsactx->libctx, mdname, str); + + if (prsactx->mgf1_md == NULL) + return 0; + } + + p = OSSL_PARAM_locate_const(params, OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL); + if (p != NULL) { + void *tmp_label = NULL; + size_t tmp_labellen; + + if (!OSSL_PARAM_get_octet_string(p, &tmp_label, 0, &tmp_labellen)) + return 0; + OPENSSL_free(prsactx->oaep_label); + prsactx->oaep_label = (unsigned char *)tmp_label; + prsactx->oaep_labellen = tmp_labellen; + } + + p = OSSL_PARAM_locate_const(params, OSSL_ASYM_CIPHER_PARAM_TLS_CLIENT_VERSION); + if (p != NULL) { + unsigned int client_version; + + if (!OSSL_PARAM_get_uint(p, &client_version)) + return 0; + prsactx->client_version = client_version; + } + + p = OSSL_PARAM_locate_const(params, OSSL_ASYM_CIPHER_PARAM_TLS_NEGOTIATED_VERSION); + if (p != NULL) { + unsigned int alt_version; + + if (!OSSL_PARAM_get_uint(p, &alt_version)) + return 0; + prsactx->alt_version = alt_version; + } + p = OSSL_PARAM_locate_const(params, OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION); + if (p != NULL) { + unsigned int implicit_rejection; + + if (!OSSL_PARAM_get_uint(p, &implicit_rejection)) + return 0; + prsactx->implicit_rejection = implicit_rejection; + } + return 1; +} + +static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST_PROPS, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_PAD_MODE, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST_PROPS, NULL, 0), + OSSL_PARAM_octet_string(OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL, NULL, 0), + OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_TLS_CLIENT_VERSION, NULL), + OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_TLS_NEGOTIATED_VERSION, NULL), + OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION, NULL), + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_ASYM_CIPHER_PARAM_FIPS_KEY_CHECK) + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_ASYM_CIPHER_PARAM_FIPS_RSA_PKCS15_PAD_DISABLED) + OSSL_PARAM_END +}; + +static const OSSL_PARAM *rsa_settable_ctx_params(ossl_unused void *vprsactx, + ossl_unused void *provctx) +{ + return known_settable_ctx_params; +} + +const OSSL_DISPATCH ossl_rsa_asym_cipher_functions[] = { + { OSSL_FUNC_ASYM_CIPHER_NEWCTX, (void (*)(void))rsa_newctx }, + { OSSL_FUNC_ASYM_CIPHER_ENCRYPT_INIT, (void (*)(void))rsa_encrypt_init }, + { OSSL_FUNC_ASYM_CIPHER_ENCRYPT, (void (*)(void))rsa_encrypt }, + { OSSL_FUNC_ASYM_CIPHER_DECRYPT_INIT, (void (*)(void))rsa_decrypt_init }, + { OSSL_FUNC_ASYM_CIPHER_DECRYPT, (void (*)(void))rsa_decrypt }, + { OSSL_FUNC_ASYM_CIPHER_FREECTX, (void (*)(void))rsa_freectx }, + { OSSL_FUNC_ASYM_CIPHER_DUPCTX, (void (*)(void))rsa_dupctx }, + { OSSL_FUNC_ASYM_CIPHER_GET_CTX_PARAMS, + (void (*)(void))rsa_get_ctx_params }, + { OSSL_FUNC_ASYM_CIPHER_GETTABLE_CTX_PARAMS, + (void (*)(void))rsa_gettable_ctx_params }, + { OSSL_FUNC_ASYM_CIPHER_SET_CTX_PARAMS, + (void (*)(void))rsa_set_ctx_params }, + { OSSL_FUNC_ASYM_CIPHER_SETTABLE_CTX_PARAMS, + (void (*)(void))rsa_settable_ctx_params }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/asymciphers/sm2_enc.c b/crypto/openssl/providers/implementations/asymciphers/sm2_enc.c new file mode 100644 index 000000000000..747190f3f9b5 --- /dev/null +++ b/crypto/openssl/providers/implementations/asymciphers/sm2_enc.c @@ -0,0 +1,234 @@ +/* + * Copyright 2020-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 + */ + +#include "internal/deprecated.h" + +#include <openssl/crypto.h> +#include <openssl/evp.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/params.h> +#include <openssl/err.h> +#include <openssl/proverr.h> +#include "crypto/sm2.h" +#include "prov/provider_ctx.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" +#include "prov/provider_util.h" + +static OSSL_FUNC_asym_cipher_newctx_fn sm2_newctx; +static OSSL_FUNC_asym_cipher_encrypt_init_fn sm2_init; +static OSSL_FUNC_asym_cipher_encrypt_fn sm2_asym_encrypt; +static OSSL_FUNC_asym_cipher_decrypt_init_fn sm2_init; +static OSSL_FUNC_asym_cipher_decrypt_fn sm2_asym_decrypt; +static OSSL_FUNC_asym_cipher_freectx_fn sm2_freectx; +static OSSL_FUNC_asym_cipher_dupctx_fn sm2_dupctx; +static OSSL_FUNC_asym_cipher_get_ctx_params_fn sm2_get_ctx_params; +static OSSL_FUNC_asym_cipher_gettable_ctx_params_fn sm2_gettable_ctx_params; +static OSSL_FUNC_asym_cipher_set_ctx_params_fn sm2_set_ctx_params; +static OSSL_FUNC_asym_cipher_settable_ctx_params_fn sm2_settable_ctx_params; + +/* + * What's passed as an actual key is defined by the KEYMGMT interface. + * We happen to know that our KEYMGMT simply passes EC_KEY structures, so + * we use that here too. + */ + +typedef struct { + OSSL_LIB_CTX *libctx; + EC_KEY *key; + PROV_DIGEST md; +} PROV_SM2_CTX; + +static void *sm2_newctx(void *provctx) +{ + PROV_SM2_CTX *psm2ctx = OPENSSL_zalloc(sizeof(PROV_SM2_CTX)); + + if (psm2ctx == NULL) + return NULL; + psm2ctx->libctx = PROV_LIBCTX_OF(provctx); + + return psm2ctx; +} + +static int sm2_init(void *vpsm2ctx, void *vkey, const OSSL_PARAM params[]) +{ + PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx; + + if (psm2ctx == NULL || vkey == NULL || !EC_KEY_up_ref(vkey)) + return 0; + EC_KEY_free(psm2ctx->key); + psm2ctx->key = vkey; + + return sm2_set_ctx_params(psm2ctx, params); +} + +static const EVP_MD *sm2_get_md(PROV_SM2_CTX *psm2ctx) +{ + const EVP_MD *md = ossl_prov_digest_md(&psm2ctx->md); + + if (md == NULL) + md = ossl_prov_digest_fetch(&psm2ctx->md, psm2ctx->libctx, "SM3", NULL); + + return md; +} + +static int sm2_asym_encrypt(void *vpsm2ctx, unsigned char *out, size_t *outlen, + size_t outsize, const unsigned char *in, + size_t inlen) +{ + PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx; + const EVP_MD *md = sm2_get_md(psm2ctx); + + if (md == NULL) + return 0; + + if (out == NULL) { + if (!ossl_sm2_ciphertext_size(psm2ctx->key, md, inlen, outlen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY); + return 0; + } + return 1; + } + + return ossl_sm2_encrypt(psm2ctx->key, md, in, inlen, out, outlen); +} + +static int sm2_asym_decrypt(void *vpsm2ctx, unsigned char *out, size_t *outlen, + size_t outsize, const unsigned char *in, + size_t inlen) +{ + PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx; + const EVP_MD *md = sm2_get_md(psm2ctx); + + if (md == NULL) + return 0; + + if (out == NULL) { + if (!ossl_sm2_plaintext_size(in, inlen, outlen)) + return 0; + return 1; + } + + return ossl_sm2_decrypt(psm2ctx->key, md, in, inlen, out, outlen); +} + +static void sm2_freectx(void *vpsm2ctx) +{ + PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx; + + EC_KEY_free(psm2ctx->key); + ossl_prov_digest_reset(&psm2ctx->md); + + OPENSSL_free(psm2ctx); +} + +static void *sm2_dupctx(void *vpsm2ctx) +{ + PROV_SM2_CTX *srcctx = (PROV_SM2_CTX *)vpsm2ctx; + PROV_SM2_CTX *dstctx; + + dstctx = OPENSSL_zalloc(sizeof(*srcctx)); + if (dstctx == NULL) + return NULL; + + *dstctx = *srcctx; + memset(&dstctx->md, 0, sizeof(dstctx->md)); + + if (dstctx->key != NULL && !EC_KEY_up_ref(dstctx->key)) { + OPENSSL_free(dstctx); + return NULL; + } + + if (!ossl_prov_digest_copy(&dstctx->md, &srcctx->md)) { + sm2_freectx(dstctx); + return NULL; + } + + return dstctx; +} + +static int sm2_get_ctx_params(void *vpsm2ctx, OSSL_PARAM *params) +{ + PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx; + OSSL_PARAM *p; + + if (vpsm2ctx == NULL) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_ASYM_CIPHER_PARAM_DIGEST); + if (p != NULL) { + const EVP_MD *md = ossl_prov_digest_md(&psm2ctx->md); + + if (!OSSL_PARAM_set_utf8_string(p, md == NULL ? "" + : EVP_MD_get0_name(md))) + return 0; + } + + return 1; +} + +static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_END +}; + +static const OSSL_PARAM *sm2_gettable_ctx_params(ossl_unused void *vpsm2ctx, + ossl_unused void *provctx) +{ + return known_gettable_ctx_params; +} + +static int sm2_set_ctx_params(void *vpsm2ctx, const OSSL_PARAM params[]) +{ + PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx; + + if (psm2ctx == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 1; + + if (!ossl_prov_digest_load_from_params(&psm2ctx->md, params, + psm2ctx->libctx)) + return 0; + + return 1; +} + +static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_ENGINE, NULL, 0), + OSSL_PARAM_END +}; + +static const OSSL_PARAM *sm2_settable_ctx_params(ossl_unused void *vpsm2ctx, + ossl_unused void *provctx) +{ + return known_settable_ctx_params; +} + +const OSSL_DISPATCH ossl_sm2_asym_cipher_functions[] = { + { OSSL_FUNC_ASYM_CIPHER_NEWCTX, (void (*)(void))sm2_newctx }, + { OSSL_FUNC_ASYM_CIPHER_ENCRYPT_INIT, (void (*)(void))sm2_init }, + { OSSL_FUNC_ASYM_CIPHER_ENCRYPT, (void (*)(void))sm2_asym_encrypt }, + { OSSL_FUNC_ASYM_CIPHER_DECRYPT_INIT, (void (*)(void))sm2_init }, + { OSSL_FUNC_ASYM_CIPHER_DECRYPT, (void (*)(void))sm2_asym_decrypt }, + { OSSL_FUNC_ASYM_CIPHER_FREECTX, (void (*)(void))sm2_freectx }, + { OSSL_FUNC_ASYM_CIPHER_DUPCTX, (void (*)(void))sm2_dupctx }, + { OSSL_FUNC_ASYM_CIPHER_GET_CTX_PARAMS, + (void (*)(void))sm2_get_ctx_params }, + { OSSL_FUNC_ASYM_CIPHER_GETTABLE_CTX_PARAMS, + (void (*)(void))sm2_gettable_ctx_params }, + { OSSL_FUNC_ASYM_CIPHER_SET_CTX_PARAMS, + (void (*)(void))sm2_set_ctx_params }, + { OSSL_FUNC_ASYM_CIPHER_SETTABLE_CTX_PARAMS, + (void (*)(void))sm2_settable_ctx_params }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/build.info b/crypto/openssl/providers/implementations/build.info new file mode 100644 index 000000000000..5cf054f5f32b --- /dev/null +++ b/crypto/openssl/providers/implementations/build.info @@ -0,0 +1,2 @@ +SUBDIRS=digests ciphers rands macs kdfs exchange keymgmt signature asymciphers \ + encode_decode storemgmt kem skeymgmt diff --git a/crypto/openssl/providers/implementations/ciphers/build.info b/crypto/openssl/providers/implementations/ciphers/build.info new file mode 100644 index 000000000000..1837070c2111 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/build.info @@ -0,0 +1,208 @@ +# We make separate GOAL variables for each algorithm, to make it easy to +# switch each to the Legacy provider when needed. +# +# $TDES_1_GOAL and $TDES_2_GOAL separate FIPSable and non-FIPSable TDES. +# The latter may become legacy sooner, so it's comfortable to have two +# variables already now, to switch the non-FIPSable TDES to legacy if needed. + +$COMMON_GOAL=../../libcommon.a + +$NULL_GOAL=../../libdefault.a +$AES_GOAL=../../libdefault.a ../../libfips.a +$TDES_1_GOAL=../../libdefault.a ../../libfips.a +$TDES_2_GOAL=../../libdefault.a +$ARIA_GOAL=../../libdefault.a +$CAMELLIA_GOAL=../../libdefault.a +$DES_GOAL=../../liblegacy.a +$BLOWFISH_GOAL=../../liblegacy.a +$IDEA_GOAL=../../liblegacy.a +$CAST5_GOAL=../../liblegacy.a +$RC2_GOAL=../../liblegacy.a +$RC4_GOAL=../../liblegacy.a +$RC5_GOAL=../../liblegacy.a +$SEED_GOAL=../../liblegacy.a +$SM4_GOAL=../../libdefault.a +$CHACHA_GOAL=../../libdefault.a +$CHACHAPOLY_GOAL=../../libdefault.a +$SIV_GOAL=../../libdefault.a +$SIV_GCM_GOAL=../../libdefault.a + +IF[{- !$disabled{asm} -}] + $GHASHDEF_x86=GHASH_ASM + $GHASHDEF_x86_sse2=OPENSSL_IA32_SSE2 + + $GHASHDEF_x86_64=GHASH_ASM + $GHASHDEF_x86_64_sse2=OPENSSL_IA32_SSE2 + + # ghash-ia64.s doesn't work on VMS + IF[{- $config{target} !~ /^vms-/ -}] + $GHASHDEF_ia64=GHASH_ASM + ENDIF + + $GHASHDEF_sparcv9=GHASH_ASM + + $GHASHDEF_alpha=GHASH_ASM + + $GHASHDEF_s390x=GHASH_ASM + + $GHASHDEF_armv4=GHASH_ASM + $GHASHDEF_aarch64= + + $GHASHDEF_parisc11=GHASH_ASM + $GHASHDEF_parisc20_64=$GHASHDEF_parisc11 + + $GHASHDEF_ppc32= + $GHASHDEF_ppc64=$GHASHDEF_ppc32 + + $GHASHDEF_c64xplus=GHASH_ASM + + $GHASHDEF_riscv64=GHASH_ASM + + # Now that we have defined all the arch specific variables, use the + # appropriate one, and define the appropriate macros + + IF[$GHASHDEF_{- $target{asm_arch} -}] + $GHASHDEF=$GHASHDEF_{- $target{asm_arch} -} + IF[{- !$disabled{sse2} -}] + IF[$GHASHDEF_{- $target{asm_arch} -}_sse2] + $GHASHDEF=$GHASHDEF_{- $target{asm_arch} -}_sse2 + ENDIF + ENDIF + ENDIF +ENDIF + +IF[{- !$disabled{asm} -}] + IF[{- ($target{perlasm_scheme} // '') ne '31' -}] + $AESXTSDEF_s390x=AES_XTS_S390X + ENDIF + + # Now that we have defined all the arch specific variables, use the + # appropriate one, and define the appropriate macros + + IF[$AESXTSDEF_{- $target{asm_arch} -}] + $AESXTSDEF=$AESXTSDEF_{- $target{asm_arch} -} + ENDIF +ENDIF + +# This source is common building blocks for all ciphers in all our providers. +SOURCE[$COMMON_GOAL]=\ + ciphercommon.c ciphercommon_hw.c ciphercommon_block.c \ + ciphercommon_gcm.c ciphercommon_gcm_hw.c \ + ciphercommon_ccm.c ciphercommon_ccm_hw.c + +IF[{- !$disabled{des} -}] + SOURCE[$TDES_1_GOAL]=cipher_tdes.c cipher_tdes_common.c cipher_tdes_hw.c +ENDIF + +SOURCE[$NULL_GOAL]=\ + cipher_null.c + +SOURCE[$AES_GOAL]=\ + cipher_aes.c cipher_aes_hw.c \ + cipher_aes_xts.c cipher_aes_xts_hw.c \ + cipher_aes_gcm.c cipher_aes_gcm_hw.c \ + cipher_aes_ccm.c cipher_aes_ccm_hw.c \ + cipher_aes_wrp.c \ + cipher_aes_cbc_hmac_sha.c \ + cipher_aes_cbc_hmac_sha256_hw.c cipher_aes_cbc_hmac_sha1_hw.c \ + cipher_cts.c +DEFINE[$AES_GOAL]=$AESXTSDEF + +# Extra code to satisfy the FIPS and non-FIPS separation. +# When the AES-xxx-XTS moves to legacy, cipher_aes_xts_fips.c can be removed. +SOURCE[$AES_GOAL]=cipher_aes_xts_fips.c + +IF[{- !$disabled{siv} -}] + DEFINE[$SIV_GCM_GOAL]=$GHASHDEF + SOURCE[$SIV_GCM_GOAL]=\ + cipher_aes_gcm_siv.c cipher_aes_gcm_siv_hw.c \ + cipher_aes_gcm_siv_polyval.c + SOURCE[$SIV_GOAL]=cipher_aes_siv.c cipher_aes_siv_hw.c +ENDIF + +IF[{- !$disabled{des} -}] + SOURCE[$TDES_2_GOAL]=\ + cipher_tdes_default.c cipher_tdes_default_hw.c \ + cipher_tdes_wrap.c cipher_tdes_wrap_hw.c + SOURCE[$DES_GOAL]=\ + cipher_desx.c cipher_desx_hw.c \ + cipher_des.c cipher_des_hw.c + IF[{- !$disabled{module} -}] + SOURCE[$DES_GOAL]=\ + cipher_tdes_common.c + ENDIF +ENDIF + +IF[{- !$disabled{aria} -}] + SOURCE[$ARIA_GOAL]=\ + cipher_aria.c cipher_aria_hw.c \ + cipher_aria_gcm.c cipher_aria_gcm_hw.c \ + cipher_aria_ccm.c cipher_aria_ccm_hw.c +ENDIF + +IF[{- !$disabled{camellia} -}] + SOURCE[$CAMELLIA_GOAL]=\ + cipher_camellia.c cipher_camellia_hw.c +ENDIF + +IF[{- !$disabled{bf} -}] + SOURCE[$BLOWFISH_GOAL]=\ + cipher_blowfish.c cipher_blowfish_hw.c +ENDIF + +IF[{- !$disabled{idea} -}] + SOURCE[$IDEA_GOAL]=\ + cipher_idea.c cipher_idea_hw.c +ENDIF + +IF[{- !$disabled{cast} -}] + SOURCE[$CAST5_GOAL]=\ + cipher_cast5.c cipher_cast5_hw.c +ENDIF + +IF[{- !$disabled{seed} -}] + SOURCE[$SEED_GOAL]=\ + cipher_seed.c cipher_seed_hw.c +ENDIF + +IF[{- !$disabled{sm4} -}] + SOURCE[$SM4_GOAL]=\ + cipher_sm4.c cipher_sm4_hw.c \ + cipher_sm4_gcm.c cipher_sm4_gcm_hw.c \ + cipher_sm4_ccm.c cipher_sm4_ccm_hw.c \ + cipher_sm4_xts.c cipher_sm4_xts_hw.c + +ENDIF + +IF[{- !$disabled{ocb} -}] + SOURCE[$AES_GOAL]=\ + cipher_aes_ocb.c cipher_aes_ocb_hw.c +ENDIF + +IF[{- !$disabled{rc4} -}] + SOURCE[$RC4_GOAL]=\ + cipher_rc4.c cipher_rc4_hw.c + IF[{- !$disabled{md5} -}] + SOURCE[$RC4_GOAL]=\ + cipher_rc4_hmac_md5.c cipher_rc4_hmac_md5_hw.c + ENDIF +ENDIF + +IF[{- !$disabled{rc5} -}] + SOURCE[$RC5_GOAL]=\ + cipher_rc5.c cipher_rc5_hw.c +ENDIF + +IF[{- !$disabled{rc2} -}] + SOURCE[$RC2_GOAL]=\ + cipher_rc2.c cipher_rc2_hw.c +ENDIF + +IF[{- !$disabled{chacha} -}] + SOURCE[$CHACHA_GOAL]=\ + cipher_chacha20.c cipher_chacha20_hw.c + IF[{- !$disabled{poly1305} -}] + SOURCE[$CHACHAPOLY_GOAL]=\ + cipher_chacha20_poly1305.c cipher_chacha20_poly1305_hw.c + ENDIF +ENDIF diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes.c new file mode 100644 index 000000000000..280be2dddcaf --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes.c @@ -0,0 +1,93 @@ +/* + * Copyright 2019-2020 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 + */ + +/* + * AES low level APIs are deprecated for public use, but still ok for internal + * use where we're using them to implement the higher level EVP interface, as is + * the case here. + */ +#include "internal/deprecated.h" + +/* Dispatch functions for AES cipher modes ecb, cbc, ofb, cfb, ctr */ + +#include "cipher_aes.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" + +static OSSL_FUNC_cipher_freectx_fn aes_freectx; +static OSSL_FUNC_cipher_dupctx_fn aes_dupctx; + +static void aes_freectx(void *vctx) +{ + PROV_AES_CTX *ctx = (PROV_AES_CTX *)vctx; + + ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx); + OPENSSL_clear_free(ctx, sizeof(*ctx)); +} + +static void *aes_dupctx(void *ctx) +{ + PROV_AES_CTX *in = (PROV_AES_CTX *)ctx; + PROV_AES_CTX *ret; + + if (!ossl_prov_is_running()) + return NULL; + + ret = OPENSSL_malloc(sizeof(*ret)); + if (ret == NULL) + return NULL; + in->base.hw->copyctx(&ret->base, &in->base); + + return ret; +} + +/* ossl_aes256ecb_functions */ +IMPLEMENT_generic_cipher(aes, AES, ecb, ECB, 0, 256, 128, 0, block) +/* ossl_aes192ecb_functions */ +IMPLEMENT_generic_cipher(aes, AES, ecb, ECB, 0, 192, 128, 0, block) +/* ossl_aes128ecb_functions */ +IMPLEMENT_generic_cipher(aes, AES, ecb, ECB, 0, 128, 128, 0, block) +/* ossl_aes256cbc_functions */ +IMPLEMENT_generic_cipher(aes, AES, cbc, CBC, 0, 256, 128, 128, block) +/* ossl_aes192cbc_functions */ +IMPLEMENT_generic_cipher(aes, AES, cbc, CBC, 0, 192, 128, 128, block) +/* ossl_aes128cbc_functions */ +IMPLEMENT_generic_cipher(aes, AES, cbc, CBC, 0, 128, 128, 128, block) +/* ossl_aes256ofb_functions */ +IMPLEMENT_generic_cipher(aes, AES, ofb, OFB, 0, 256, 8, 128, stream) +/* ossl_aes192ofb_functions */ +IMPLEMENT_generic_cipher(aes, AES, ofb, OFB, 0, 192, 8, 128, stream) +/* ossl_aes128ofb_functions */ +IMPLEMENT_generic_cipher(aes, AES, ofb, OFB, 0, 128, 8, 128, stream) +/* ossl_aes256cfb_functions */ +IMPLEMENT_generic_cipher(aes, AES, cfb, CFB, 0, 256, 8, 128, stream) +/* ossl_aes192cfb_functions */ +IMPLEMENT_generic_cipher(aes, AES, cfb, CFB, 0, 192, 8, 128, stream) +/* ossl_aes128cfb_functions */ +IMPLEMENT_generic_cipher(aes, AES, cfb, CFB, 0, 128, 8, 128, stream) +/* ossl_aes256cfb1_functions */ +IMPLEMENT_generic_cipher(aes, AES, cfb1, CFB, 0, 256, 8, 128, stream) +/* ossl_aes192cfb1_functions */ +IMPLEMENT_generic_cipher(aes, AES, cfb1, CFB, 0, 192, 8, 128, stream) +/* ossl_aes128cfb1_functions */ +IMPLEMENT_generic_cipher(aes, AES, cfb1, CFB, 0, 128, 8, 128, stream) +/* ossl_aes256cfb8_functions */ +IMPLEMENT_generic_cipher(aes, AES, cfb8, CFB, 0, 256, 8, 128, stream) +/* ossl_aes192cfb8_functions */ +IMPLEMENT_generic_cipher(aes, AES, cfb8, CFB, 0, 192, 8, 128, stream) +/* ossl_aes128cfb8_functions */ +IMPLEMENT_generic_cipher(aes, AES, cfb8, CFB, 0, 128, 8, 128, stream) +/* ossl_aes256ctr_functions */ +IMPLEMENT_generic_cipher(aes, AES, ctr, CTR, 0, 256, 8, 128, stream) +/* ossl_aes192ctr_functions */ +IMPLEMENT_generic_cipher(aes, AES, ctr, CTR, 0, 192, 8, 128, stream) +/* ossl_aes128ctr_functions */ +IMPLEMENT_generic_cipher(aes, AES, ctr, CTR, 0, 128, 8, 128, stream) + +#include "cipher_aes_cts.inc" diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes.h b/crypto/openssl/providers/implementations/ciphers/cipher_aes.h new file mode 100644 index 000000000000..c62ac5e7eaeb --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes.h @@ -0,0 +1,61 @@ +/* + * Copyright 2019-2024 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 + */ + +#include <openssl/aes.h> +#include "prov/ciphercommon.h" +#include "crypto/aes_platform.h" + +typedef struct prov_aes_ctx_st { + PROV_CIPHER_CTX base; /* Must be first */ + union { + OSSL_UNION_ALIGN; + AES_KEY ks; + } ks; + + /* Platform specific data */ + union { + int dummy; +#if defined(OPENSSL_CPUID_OBJ) && defined(__s390__) + struct { + union { + OSSL_UNION_ALIGN; + /*- + * KM-AES parameter block - begin + * (see z/Architecture Principles of Operation >= SA22-7832-06) + */ + struct { + unsigned char k[32]; + } km; + /* KM-AES parameter block - end */ + /*- + * KMO-AES/KMF-AES parameter block - begin + * (see z/Architecture Principles of Operation >= SA22-7832-08) + */ + struct { + unsigned char cv[16]; + unsigned char k[32]; + } kmo_kmf; + /* KMO-AES/KMF-AES parameter block - end */ + } param; + unsigned int fc; + } s390x; +#endif /* defined(OPENSSL_CPUID_OBJ) && defined(__s390__) */ + } plat; + +} PROV_AES_CTX; + +#define ossl_prov_cipher_hw_aes_ofb ossl_prov_cipher_hw_aes_ofb128 +#define ossl_prov_cipher_hw_aes_cfb ossl_prov_cipher_hw_aes_cfb128 +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_ecb(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_cbc(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_ofb128(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_cfb128(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_cfb1(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_cfb8(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_ctr(size_t keybits); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha.c new file mode 100644 index 000000000000..76f53e0c7cce --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha.c @@ -0,0 +1,442 @@ +/* + * Copyright 2019-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 + */ + +/* + * AES low level APIs are deprecated for public use, but still ok for internal + * use where we're using them to implement the higher level EVP interface, as is + * the case here. + */ +#include "internal/deprecated.h" + +/* Dispatch functions for AES_CBC_HMAC_SHA ciphers */ + +/* For SSL3_VERSION and TLS1_VERSION */ +#include <openssl/prov_ssl.h> +#include <openssl/proverr.h> +#include "cipher_aes_cbc_hmac_sha.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" + +#ifndef AES_CBC_HMAC_SHA_CAPABLE +# define IMPLEMENT_CIPHER(nm, sub, kbits, blkbits, ivbits, flags) \ +const OSSL_DISPATCH ossl_##nm##kbits##sub##_functions[] = { \ + OSSL_DISPATCH_END \ +}; +#else + +# define AES_CBC_HMAC_SHA_FLAGS (PROV_CIPHER_FLAG_AEAD \ + | PROV_CIPHER_FLAG_TLS1_MULTIBLOCK) + +static OSSL_FUNC_cipher_encrypt_init_fn aes_einit; +static OSSL_FUNC_cipher_decrypt_init_fn aes_dinit; +static OSSL_FUNC_cipher_freectx_fn aes_cbc_hmac_sha1_freectx; +static OSSL_FUNC_cipher_freectx_fn aes_cbc_hmac_sha256_freectx; +static OSSL_FUNC_cipher_get_ctx_params_fn aes_get_ctx_params; +static OSSL_FUNC_cipher_gettable_ctx_params_fn aes_gettable_ctx_params; +static OSSL_FUNC_cipher_set_ctx_params_fn aes_set_ctx_params; +static OSSL_FUNC_cipher_settable_ctx_params_fn aes_settable_ctx_params; +# define aes_gettable_params ossl_cipher_generic_gettable_params +# define aes_update ossl_cipher_generic_stream_update +# define aes_final ossl_cipher_generic_stream_final +# define aes_cipher ossl_cipher_generic_cipher + +static int aes_einit(void *ctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + if (!ossl_cipher_generic_einit(ctx, key, keylen, iv, ivlen, NULL)) + return 0; + return aes_set_ctx_params(ctx, params); +} + +static int aes_dinit(void *ctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + if (!ossl_cipher_generic_dinit(ctx, key, keylen, iv, ivlen, NULL)) + return 0; + return aes_set_ctx_params(ctx, params); +} + +static const OSSL_PARAM cipher_aes_known_settable_ctx_params[] = { + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_MAC_KEY, NULL, 0), + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TLS1_AAD, NULL, 0), +# if !defined(OPENSSL_NO_MULTIBLOCK) + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_MAX_SEND_FRAGMENT, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_AAD, NULL), + OSSL_PARAM_uint(OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_INTERLEAVE, NULL), + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_ENC, NULL, 0), + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_ENC_IN, NULL, 0), +# endif /* !defined(OPENSSL_NO_MULTIBLOCK) */ + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), + OSSL_PARAM_END +}; +const OSSL_PARAM *aes_settable_ctx_params(ossl_unused void *cctx, + ossl_unused void *provctx) +{ + return cipher_aes_known_settable_ctx_params; +} + +static int aes_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx; + PROV_CIPHER_HW_AES_HMAC_SHA *hw = + (PROV_CIPHER_HW_AES_HMAC_SHA *)ctx->hw; + const OSSL_PARAM *p; + int ret = 1; +# if !defined(OPENSSL_NO_MULTIBLOCK) + EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM mb_param; +# endif + + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_MAC_KEY); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_OCTET_STRING) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + hw->init_mac_key(ctx, p->data, p->data_size); + } + +# if !defined(OPENSSL_NO_MULTIBLOCK) + p = OSSL_PARAM_locate_const(params, + OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_MAX_SEND_FRAGMENT); + if (p != NULL + && !OSSL_PARAM_get_size_t(p, &ctx->multiblock_max_send_fragment)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + /* + * The inputs to tls1_multiblock_aad are: + * mb_param->inp + * mb_param->len + * mb_param->interleave + * The outputs of tls1_multiblock_aad are written to: + * ctx->multiblock_interleave + * ctx->multiblock_aad_packlen + */ + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_AAD); + if (p != NULL) { + const OSSL_PARAM *p1 = OSSL_PARAM_locate_const(params, + OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_INTERLEAVE); + if (p->data_type != OSSL_PARAM_OCTET_STRING + || p1 == NULL + || !OSSL_PARAM_get_uint(p1, &mb_param.interleave)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + mb_param.inp = p->data; + mb_param.len = p->data_size; + if (hw->tls1_multiblock_aad(vctx, &mb_param) <= 0) + return 0; + } + + /* + * The inputs to tls1_multiblock_encrypt are: + * mb_param->inp + * mb_param->len + * mb_param->interleave + * mb_param->out + * The outputs of tls1_multiblock_encrypt are: + * ctx->multiblock_encrypt_len + */ + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_ENC); + if (p != NULL) { + const OSSL_PARAM *p1 = OSSL_PARAM_locate_const(params, + OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_INTERLEAVE); + const OSSL_PARAM *pin = OSSL_PARAM_locate_const(params, + OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_ENC_IN); + + if (p->data_type != OSSL_PARAM_OCTET_STRING + || pin == NULL + || pin->data_type != OSSL_PARAM_OCTET_STRING + || p1 == NULL + || !OSSL_PARAM_get_uint(p1, &mb_param.interleave)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + mb_param.out = p->data; + mb_param.inp = pin->data; + mb_param.len = pin->data_size; + if (hw->tls1_multiblock_encrypt(vctx, &mb_param) <= 0) + return 0; + } +# endif /* !defined(OPENSSL_NO_MULTIBLOCK) */ + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TLS1_AAD); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_OCTET_STRING) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + if (hw->set_tls1_aad(ctx, p->data, p->data_size) <= 0) + return 0; + } + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_KEYLEN); + if (p != NULL) { + size_t keylen; + + if (!OSSL_PARAM_get_size_t(p, &keylen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + if (ctx->base.keylen != keylen) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + } + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_TLS_VERSION); + if (p != NULL) { + if (!OSSL_PARAM_get_uint(p, &ctx->base.tlsversion)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + if (ctx->base.tlsversion == SSL3_VERSION + || ctx->base.tlsversion == TLS1_VERSION) { + if (!ossl_assert(ctx->base.removetlsfixed >= AES_BLOCK_SIZE)) { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + return 0; + } + /* + * There is no explicit IV with these TLS versions, so don't attempt + * to remove it. + */ + ctx->base.removetlsfixed -= AES_BLOCK_SIZE; + } + } + return ret; +} + +static int aes_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx; + OSSL_PARAM *p; + +# if !defined(OPENSSL_NO_MULTIBLOCK) + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_MAX_BUFSIZE); + if (p != NULL) { + PROV_CIPHER_HW_AES_HMAC_SHA *hw = + (PROV_CIPHER_HW_AES_HMAC_SHA *)ctx->hw; + size_t len = hw->tls1_multiblock_max_bufsize(ctx); + + if (!OSSL_PARAM_set_size_t(p, len)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + } + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_INTERLEAVE); + if (p != NULL && !OSSL_PARAM_set_uint(p, ctx->multiblock_interleave)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_AAD_PACKLEN); + if (p != NULL && !OSSL_PARAM_set_uint(p, ctx->multiblock_aad_packlen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_ENC_LEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->multiblock_encrypt_len)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } +# endif /* !defined(OPENSSL_NO_MULTIBLOCK) */ + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TLS1_AAD_PAD); + if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->tls_aad_pad)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->base.keylen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->base.ivlen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IV); + if (p != NULL + && !OSSL_PARAM_set_octet_string(p, ctx->base.oiv, ctx->base.ivlen) + && !OSSL_PARAM_set_octet_ptr(p, &ctx->base.oiv, ctx->base.ivlen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_UPDATED_IV); + if (p != NULL + && !OSSL_PARAM_set_octet_string(p, ctx->base.iv, ctx->base.ivlen) + && !OSSL_PARAM_set_octet_ptr(p, &ctx->base.iv, ctx->base.ivlen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + return 1; +} + +static const OSSL_PARAM cipher_aes_known_gettable_ctx_params[] = { +# if !defined(OPENSSL_NO_MULTIBLOCK) + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_MAX_BUFSIZE, NULL), + OSSL_PARAM_uint(OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_INTERLEAVE, NULL), + OSSL_PARAM_uint(OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_AAD_PACKLEN, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_ENC_LEN, NULL), +# endif /* !defined(OPENSSL_NO_MULTIBLOCK) */ + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_TLS1_AAD_PAD, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL), + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_IV, NULL, 0), + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_UPDATED_IV, NULL, 0), + OSSL_PARAM_END +}; +const OSSL_PARAM *aes_gettable_ctx_params(ossl_unused void *cctx, + ossl_unused void *provctx) +{ + return cipher_aes_known_gettable_ctx_params; +} + +static void base_init(void *provctx, PROV_AES_HMAC_SHA_CTX *ctx, + const PROV_CIPHER_HW_AES_HMAC_SHA *meths, + size_t kbits, size_t blkbits, size_t ivbits, + uint64_t flags) +{ + ossl_cipher_generic_initkey(&ctx->base, kbits, blkbits, ivbits, + EVP_CIPH_CBC_MODE, flags, + &meths->base, provctx); + ctx->hw = (PROV_CIPHER_HW_AES_HMAC_SHA *)ctx->base.hw; +} + +static void *aes_cbc_hmac_sha1_newctx(void *provctx, size_t kbits, + size_t blkbits, size_t ivbits, + uint64_t flags) +{ + PROV_AES_HMAC_SHA1_CTX *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + ctx = OPENSSL_zalloc(sizeof(*ctx)); + if (ctx != NULL) + base_init(provctx, &ctx->base_ctx, + ossl_prov_cipher_hw_aes_cbc_hmac_sha1(), kbits, blkbits, + ivbits, flags); + return ctx; +} + +static void *aes_cbc_hmac_sha1_dupctx(void *provctx) +{ + PROV_AES_HMAC_SHA1_CTX *ctx = provctx; + + if (!ossl_prov_is_running()) + return NULL; + + if (ctx == NULL) + return NULL; + + return OPENSSL_memdup(ctx, sizeof(*ctx)); +} + +static void aes_cbc_hmac_sha1_freectx(void *vctx) +{ + PROV_AES_HMAC_SHA1_CTX *ctx = (PROV_AES_HMAC_SHA1_CTX *)vctx; + + if (ctx != NULL) { + ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx); + OPENSSL_clear_free(ctx, sizeof(*ctx)); + } +} + +static void *aes_cbc_hmac_sha256_newctx(void *provctx, size_t kbits, + size_t blkbits, size_t ivbits, + uint64_t flags) +{ + PROV_AES_HMAC_SHA256_CTX *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + ctx = OPENSSL_zalloc(sizeof(*ctx)); + if (ctx != NULL) + base_init(provctx, &ctx->base_ctx, + ossl_prov_cipher_hw_aes_cbc_hmac_sha256(), kbits, blkbits, + ivbits, flags); + return ctx; +} + +static void *aes_cbc_hmac_sha256_dupctx(void *provctx) +{ + PROV_AES_HMAC_SHA256_CTX *ctx = provctx; + + if (!ossl_prov_is_running()) + return NULL; + + return OPENSSL_memdup(ctx, sizeof(*ctx)); +} + +static void aes_cbc_hmac_sha256_freectx(void *vctx) +{ + PROV_AES_HMAC_SHA256_CTX *ctx = (PROV_AES_HMAC_SHA256_CTX *)vctx; + + if (ctx != NULL) { + ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx); + OPENSSL_clear_free(ctx, sizeof(*ctx)); + } +} + +# define IMPLEMENT_CIPHER(nm, sub, kbits, blkbits, ivbits, flags) \ +static OSSL_FUNC_cipher_newctx_fn nm##_##kbits##_##sub##_newctx; \ +static void *nm##_##kbits##_##sub##_newctx(void *provctx) \ +{ \ + return nm##_##sub##_newctx(provctx, kbits, blkbits, ivbits, flags); \ +} \ +static OSSL_FUNC_cipher_get_params_fn nm##_##kbits##_##sub##_get_params; \ +static int nm##_##kbits##_##sub##_get_params(OSSL_PARAM params[]) \ +{ \ + return ossl_cipher_generic_get_params(params, EVP_CIPH_CBC_MODE, \ + flags, kbits, blkbits, ivbits); \ +} \ +const OSSL_DISPATCH ossl_##nm##kbits##sub##_functions[] = { \ + { OSSL_FUNC_CIPHER_NEWCTX, (void (*)(void))nm##_##kbits##_##sub##_newctx },\ + { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))nm##_##sub##_freectx }, \ + { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void))nm##_##sub##_dupctx}, \ + { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))nm##_einit }, \ + { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))nm##_dinit }, \ + { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))nm##_update }, \ + { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))nm##_final }, \ + { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))nm##_cipher }, \ + { OSSL_FUNC_CIPHER_GET_PARAMS, \ + (void (*)(void))nm##_##kbits##_##sub##_get_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \ + (void (*)(void))nm##_gettable_params }, \ + { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, \ + (void (*)(void))nm##_get_ctx_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \ + (void (*)(void))nm##_gettable_ctx_params }, \ + { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \ + (void (*)(void))nm##_set_ctx_params }, \ + { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \ + (void (*)(void))nm##_settable_ctx_params }, \ + OSSL_DISPATCH_END \ +}; + +#endif /* AES_CBC_HMAC_SHA_CAPABLE */ + +/* ossl_aes128cbc_hmac_sha1_functions */ +IMPLEMENT_CIPHER(aes, cbc_hmac_sha1, 128, 128, 128, AES_CBC_HMAC_SHA_FLAGS) +/* ossl_aes256cbc_hmac_sha1_functions */ +IMPLEMENT_CIPHER(aes, cbc_hmac_sha1, 256, 128, 128, AES_CBC_HMAC_SHA_FLAGS) +/* ossl_aes128cbc_hmac_sha256_functions */ +IMPLEMENT_CIPHER(aes, cbc_hmac_sha256, 128, 128, 128, AES_CBC_HMAC_SHA_FLAGS) +/* ossl_aes256cbc_hmac_sha256_functions */ +IMPLEMENT_CIPHER(aes, cbc_hmac_sha256, 256, 128, 128, AES_CBC_HMAC_SHA_FLAGS) diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha.h b/crypto/openssl/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha.h new file mode 100644 index 000000000000..6aaf3f06fb49 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha.h @@ -0,0 +1,65 @@ +/* + * Copyright 2019-2020 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 + */ + +#include "prov/ciphercommon.h" +#include "crypto/aes_platform.h" + +int ossl_cipher_capable_aes_cbc_hmac_sha1(void); +int ossl_cipher_capable_aes_cbc_hmac_sha256(void); + +typedef struct prov_cipher_hw_aes_hmac_sha_ctx_st { + PROV_CIPHER_HW base; /* must be first */ + void (*init_mac_key)(void *ctx, const unsigned char *inkey, size_t inlen); + int (*set_tls1_aad)(void *ctx, unsigned char *aad_rec, int aad_len); +# if !defined(OPENSSL_NO_MULTIBLOCK) + int (*tls1_multiblock_max_bufsize)(void *ctx); + int (*tls1_multiblock_aad)( + void *vctx, EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM *param); + int (*tls1_multiblock_encrypt)( + void *ctx, EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM *param); +# endif /* OPENSSL_NO_MULTIBLOCK) */ +} PROV_CIPHER_HW_AES_HMAC_SHA; + +const PROV_CIPHER_HW_AES_HMAC_SHA *ossl_prov_cipher_hw_aes_cbc_hmac_sha1(void); +const PROV_CIPHER_HW_AES_HMAC_SHA *ossl_prov_cipher_hw_aes_cbc_hmac_sha256(void); + +#ifdef AES_CBC_HMAC_SHA_CAPABLE +# include <openssl/aes.h> +# include <openssl/sha.h> + +typedef struct prov_aes_hmac_sha_ctx_st { + PROV_CIPHER_CTX base; + AES_KEY ks; + size_t payload_length; /* AAD length in decrypt case */ + union { + unsigned int tls_ver; + unsigned char tls_aad[16]; /* 13 used */ + } aux; + const PROV_CIPHER_HW_AES_HMAC_SHA *hw; + /* some value that are setup by set methods - that can be retrieved */ + unsigned int multiblock_interleave; + unsigned int multiblock_aad_packlen; + size_t multiblock_max_send_fragment; + size_t multiblock_encrypt_len; + size_t tls_aad_pad; +} PROV_AES_HMAC_SHA_CTX; + +typedef struct prov_aes_hmac_sha1_ctx_st { + PROV_AES_HMAC_SHA_CTX base_ctx; + SHA_CTX head, tail, md; +} PROV_AES_HMAC_SHA1_CTX; + +typedef struct prov_aes_hmac_sha256_ctx_st { + PROV_AES_HMAC_SHA_CTX base_ctx; + SHA256_CTX head, tail, md; +} PROV_AES_HMAC_SHA256_CTX; + +# define NO_PAYLOAD_LENGTH ((size_t)-1) + +#endif /* AES_CBC_HMAC_SHA_CAPABLE */ diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha1_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha1_hw.c new file mode 100644 index 000000000000..76674d530434 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha1_hw.c @@ -0,0 +1,795 @@ +/* + * Copyright 2011-2021 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 + */ + +/* + * All low level APIs are deprecated for public use, but still ok for internal + * use where we're using them to implement the higher level EVP interface, as is + * the case here. + */ +#include "internal/deprecated.h" + +#include "cipher_aes_cbc_hmac_sha.h" + +#if !defined(AES_CBC_HMAC_SHA_CAPABLE) || !defined(AESNI_CAPABLE) +int ossl_cipher_capable_aes_cbc_hmac_sha1(void) +{ + return 0; +} + +const PROV_CIPHER_HW_AES_HMAC_SHA *ossl_prov_cipher_hw_aes_cbc_hmac_sha1(void) +{ + return NULL; +} +#else + +# include <openssl/rand.h> +# include "crypto/evp.h" +# include "internal/constant_time.h" + +void sha1_block_data_order(void *c, const void *p, size_t len); +void aesni_cbc_sha1_enc(const void *inp, void *out, size_t blocks, + const AES_KEY *key, unsigned char iv[16], + SHA_CTX *ctx, const void *in0); + +int ossl_cipher_capable_aes_cbc_hmac_sha1(void) +{ + return AESNI_CBC_HMAC_SHA_CAPABLE; +} + +static int aesni_cbc_hmac_sha1_init_key(PROV_CIPHER_CTX *vctx, + const unsigned char *key, size_t keylen) +{ + int ret; + PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx; + PROV_AES_HMAC_SHA1_CTX *sctx = (PROV_AES_HMAC_SHA1_CTX *)vctx; + + if (ctx->base.enc) + ret = aesni_set_encrypt_key(key, keylen * 8, &ctx->ks); + else + ret = aesni_set_decrypt_key(key, keylen * 8, &ctx->ks); + + SHA1_Init(&sctx->head); /* handy when benchmarking */ + sctx->tail = sctx->head; + sctx->md = sctx->head; + + ctx->payload_length = NO_PAYLOAD_LENGTH; + + vctx->removetlspad = 1; + vctx->removetlsfixed = SHA_DIGEST_LENGTH + AES_BLOCK_SIZE; + + return ret < 0 ? 0 : 1; +} + +static void sha1_update(SHA_CTX *c, const void *data, size_t len) +{ + const unsigned char *ptr = data; + size_t res; + + if ((res = c->num)) { + res = SHA_CBLOCK - res; + if (len < res) + res = len; + SHA1_Update(c, ptr, res); + ptr += res; + len -= res; + } + + res = len % SHA_CBLOCK; + len -= res; + + if (len) { + sha1_block_data_order(c, ptr, len / SHA_CBLOCK); + + ptr += len; + c->Nh += len >> 29; + c->Nl += len <<= 3; + if (c->Nl < (unsigned int)len) + c->Nh++; + } + + if (res) + SHA1_Update(c, ptr, res); +} + +# if !defined(OPENSSL_NO_MULTIBLOCK) + +typedef struct { + unsigned int A[8], B[8], C[8], D[8], E[8]; +} SHA1_MB_CTX; + +typedef struct { + const unsigned char *ptr; + int blocks; +} HASH_DESC; + +typedef struct { + const unsigned char *inp; + unsigned char *out; + int blocks; + u64 iv[2]; +} CIPH_DESC; + +void sha1_multi_block(SHA1_MB_CTX *, const HASH_DESC *, int); +void aesni_multi_cbc_encrypt(CIPH_DESC *, void *, int); + +static size_t tls1_multi_block_encrypt(void *vctx, + unsigned char *out, + const unsigned char *inp, + size_t inp_len, int n4x) +{ /* n4x is 1 or 2 */ + PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx; + PROV_AES_HMAC_SHA1_CTX *sctx = (PROV_AES_HMAC_SHA1_CTX *)vctx; + HASH_DESC hash_d[8], edges[8]; + CIPH_DESC ciph_d[8]; + unsigned char storage[sizeof(SHA1_MB_CTX) + 32]; + union { + u64 q[16]; + u32 d[32]; + u8 c[128]; + } blocks[8]; + SHA1_MB_CTX *mctx; + unsigned int frag, last, packlen, i; + unsigned int x4 = 4 * n4x, minblocks, processed = 0; + size_t ret = 0; + u8 *IVs; +# if defined(BSWAP8) + u64 seqnum; +# endif + + /* ask for IVs in bulk */ + if (RAND_bytes_ex(ctx->base.libctx, (IVs = blocks[0].c), 16 * x4, 0) <= 0) + return 0; + + mctx = (SHA1_MB_CTX *) (storage + 32 - ((size_t)storage % 32)); /* align */ + + frag = (unsigned int)inp_len >> (1 + n4x); + last = (unsigned int)inp_len + frag - (frag << (1 + n4x)); + if (last > frag && ((last + 13 + 9) % 64) < (x4 - 1)) { + frag++; + last -= x4 - 1; + } + + packlen = 5 + 16 + ((frag + 20 + 16) & -16); + + /* populate descriptors with pointers and IVs */ + hash_d[0].ptr = inp; + ciph_d[0].inp = inp; + /* 5+16 is place for header and explicit IV */ + ciph_d[0].out = out + 5 + 16; + memcpy(ciph_d[0].out - 16, IVs, 16); + memcpy(ciph_d[0].iv, IVs, 16); + IVs += 16; + + for (i = 1; i < x4; i++) { + ciph_d[i].inp = hash_d[i].ptr = hash_d[i - 1].ptr + frag; + ciph_d[i].out = ciph_d[i - 1].out + packlen; + memcpy(ciph_d[i].out - 16, IVs, 16); + memcpy(ciph_d[i].iv, IVs, 16); + IVs += 16; + } + +# if defined(BSWAP8) + memcpy(blocks[0].c, sctx->md.data, 8); + seqnum = BSWAP8(blocks[0].q[0]); +# endif + for (i = 0; i < x4; i++) { + unsigned int len = (i == (x4 - 1) ? last : frag); +# if !defined(BSWAP8) + unsigned int carry, j; +# endif + + mctx->A[i] = sctx->md.h0; + mctx->B[i] = sctx->md.h1; + mctx->C[i] = sctx->md.h2; + mctx->D[i] = sctx->md.h3; + mctx->E[i] = sctx->md.h4; + + /* fix seqnum */ +# if defined(BSWAP8) + blocks[i].q[0] = BSWAP8(seqnum + i); +# else + for (carry = i, j = 8; j--;) { + blocks[i].c[j] = ((u8 *)sctx->md.data)[j] + carry; + carry = (blocks[i].c[j] - carry) >> (sizeof(carry) * 8 - 1); + } +# endif + blocks[i].c[8] = ((u8 *)sctx->md.data)[8]; + blocks[i].c[9] = ((u8 *)sctx->md.data)[9]; + blocks[i].c[10] = ((u8 *)sctx->md.data)[10]; + /* fix length */ + blocks[i].c[11] = (u8)(len >> 8); + blocks[i].c[12] = (u8)(len); + + memcpy(blocks[i].c + 13, hash_d[i].ptr, 64 - 13); + hash_d[i].ptr += 64 - 13; + hash_d[i].blocks = (len - (64 - 13)) / 64; + + edges[i].ptr = blocks[i].c; + edges[i].blocks = 1; + } + + /* hash 13-byte headers and first 64-13 bytes of inputs */ + sha1_multi_block(mctx, edges, n4x); + /* hash bulk inputs */ +# define MAXCHUNKSIZE 2048 +# if MAXCHUNKSIZE%64 +# error "MAXCHUNKSIZE is not divisible by 64" +# elif MAXCHUNKSIZE + /* + * goal is to minimize pressure on L1 cache by moving in shorter steps, + * so that hashed data is still in the cache by the time we encrypt it + */ + minblocks = ((frag <= last ? frag : last) - (64 - 13)) / 64; + if (minblocks > MAXCHUNKSIZE / 64) { + for (i = 0; i < x4; i++) { + edges[i].ptr = hash_d[i].ptr; + edges[i].blocks = MAXCHUNKSIZE / 64; + ciph_d[i].blocks = MAXCHUNKSIZE / 16; + } + do { + sha1_multi_block(mctx, edges, n4x); + aesni_multi_cbc_encrypt(ciph_d, &ctx->ks, n4x); + + for (i = 0; i < x4; i++) { + edges[i].ptr = hash_d[i].ptr += MAXCHUNKSIZE; + hash_d[i].blocks -= MAXCHUNKSIZE / 64; + edges[i].blocks = MAXCHUNKSIZE / 64; + ciph_d[i].inp += MAXCHUNKSIZE; + ciph_d[i].out += MAXCHUNKSIZE; + ciph_d[i].blocks = MAXCHUNKSIZE / 16; + memcpy(ciph_d[i].iv, ciph_d[i].out - 16, 16); + } + processed += MAXCHUNKSIZE; + minblocks -= MAXCHUNKSIZE / 64; + } while (minblocks > MAXCHUNKSIZE / 64); + } +# endif +# undef MAXCHUNKSIZE + sha1_multi_block(mctx, hash_d, n4x); + + memset(blocks, 0, sizeof(blocks)); + for (i = 0; i < x4; i++) { + unsigned int len = (i == (x4 - 1) ? last : frag), + off = hash_d[i].blocks * 64; + const unsigned char *ptr = hash_d[i].ptr + off; + + off = (len - processed) - (64 - 13) - off; /* remainder actually */ + memcpy(blocks[i].c, ptr, off); + blocks[i].c[off] = 0x80; + len += 64 + 13; /* 64 is HMAC header */ + len *= 8; /* convert to bits */ + if (off < (64 - 8)) { +# ifdef BSWAP4 + blocks[i].d[15] = BSWAP4(len); +# else + PUTU32(blocks[i].c + 60, len); +# endif + edges[i].blocks = 1; + } else { +# ifdef BSWAP4 + blocks[i].d[31] = BSWAP4(len); +# else + PUTU32(blocks[i].c + 124, len); +# endif + edges[i].blocks = 2; + } + edges[i].ptr = blocks[i].c; + } + + /* hash input tails and finalize */ + sha1_multi_block(mctx, edges, n4x); + + memset(blocks, 0, sizeof(blocks)); + for (i = 0; i < x4; i++) { +# ifdef BSWAP4 + blocks[i].d[0] = BSWAP4(mctx->A[i]); + mctx->A[i] = sctx->tail.h0; + blocks[i].d[1] = BSWAP4(mctx->B[i]); + mctx->B[i] = sctx->tail.h1; + blocks[i].d[2] = BSWAP4(mctx->C[i]); + mctx->C[i] = sctx->tail.h2; + blocks[i].d[3] = BSWAP4(mctx->D[i]); + mctx->D[i] = sctx->tail.h3; + blocks[i].d[4] = BSWAP4(mctx->E[i]); + mctx->E[i] = sctx->tail.h4; + blocks[i].c[20] = 0x80; + blocks[i].d[15] = BSWAP4((64 + 20) * 8); +# else + PUTU32(blocks[i].c + 0, mctx->A[i]); + mctx->A[i] = sctx->tail.h0; + PUTU32(blocks[i].c + 4, mctx->B[i]); + mctx->B[i] = sctx->tail.h1; + PUTU32(blocks[i].c + 8, mctx->C[i]); + mctx->C[i] = sctx->tail.h2; + PUTU32(blocks[i].c + 12, mctx->D[i]); + mctx->D[i] = sctx->tail.h3; + PUTU32(blocks[i].c + 16, mctx->E[i]); + mctx->E[i] = sctx->tail.h4; + blocks[i].c[20] = 0x80; + PUTU32(blocks[i].c + 60, (64 + 20) * 8); +# endif /* BSWAP */ + edges[i].ptr = blocks[i].c; + edges[i].blocks = 1; + } + + /* finalize MACs */ + sha1_multi_block(mctx, edges, n4x); + + for (i = 0; i < x4; i++) { + unsigned int len = (i == (x4 - 1) ? last : frag), pad, j; + unsigned char *out0 = out; + + memcpy(ciph_d[i].out, ciph_d[i].inp, len - processed); + ciph_d[i].inp = ciph_d[i].out; + + out += 5 + 16 + len; + + /* write MAC */ + PUTU32(out + 0, mctx->A[i]); + PUTU32(out + 4, mctx->B[i]); + PUTU32(out + 8, mctx->C[i]); + PUTU32(out + 12, mctx->D[i]); + PUTU32(out + 16, mctx->E[i]); + out += 20; + len += 20; + + /* pad */ + pad = 15 - len % 16; + for (j = 0; j <= pad; j++) + *(out++) = pad; + len += pad + 1; + + ciph_d[i].blocks = (len - processed) / 16; + len += 16; /* account for explicit iv */ + + /* arrange header */ + out0[0] = ((u8 *)sctx->md.data)[8]; + out0[1] = ((u8 *)sctx->md.data)[9]; + out0[2] = ((u8 *)sctx->md.data)[10]; + out0[3] = (u8)(len >> 8); + out0[4] = (u8)(len); + + ret += len + 5; + inp += frag; + } + + aesni_multi_cbc_encrypt(ciph_d, &ctx->ks, n4x); + + OPENSSL_cleanse(blocks, sizeof(blocks)); + OPENSSL_cleanse(mctx, sizeof(*mctx)); + + ctx->multiblock_encrypt_len = ret; + return ret; +} +# endif /* OPENSSL_NO_MULTIBLOCK */ + +static int aesni_cbc_hmac_sha1_cipher(PROV_CIPHER_CTX *vctx, + unsigned char *out, + const unsigned char *in, size_t len) +{ + PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx; + PROV_AES_HMAC_SHA1_CTX *sctx = (PROV_AES_HMAC_SHA1_CTX *)vctx; + unsigned int l; + size_t plen = ctx->payload_length; + size_t iv = 0; /* explicit IV in TLS 1.1 and later */ + size_t aes_off = 0, blocks; + size_t sha_off = SHA_CBLOCK - sctx->md.num; + + ctx->payload_length = NO_PAYLOAD_LENGTH; + + if (len % AES_BLOCK_SIZE) + return 0; + + if (ctx->base.enc) { + if (plen == NO_PAYLOAD_LENGTH) + plen = len; + else if (len != + ((plen + SHA_DIGEST_LENGTH + + AES_BLOCK_SIZE) & -AES_BLOCK_SIZE)) + return 0; + else if (ctx->aux.tls_ver >= TLS1_1_VERSION) + iv = AES_BLOCK_SIZE; + + if (plen > (sha_off + iv) + && (blocks = (plen - (sha_off + iv)) / SHA_CBLOCK)) { + sha1_update(&sctx->md, in + iv, sha_off); + + aesni_cbc_sha1_enc(in, out, blocks, &ctx->ks, ctx->base.iv, + &sctx->md, in + iv + sha_off); + blocks *= SHA_CBLOCK; + aes_off += blocks; + sha_off += blocks; + sctx->md.Nh += blocks >> 29; + sctx->md.Nl += blocks <<= 3; + if (sctx->md.Nl < (unsigned int)blocks) + sctx->md.Nh++; + } else { + sha_off = 0; + } + sha_off += iv; + sha1_update(&sctx->md, in + sha_off, plen - sha_off); + + if (plen != len) { /* "TLS" mode of operation */ + if (in != out) + memcpy(out + aes_off, in + aes_off, plen - aes_off); + + /* calculate HMAC and append it to payload */ + SHA1_Final(out + plen, &sctx->md); + sctx->md = sctx->tail; + sha1_update(&sctx->md, out + plen, SHA_DIGEST_LENGTH); + SHA1_Final(out + plen, &sctx->md); + + /* pad the payload|hmac */ + plen += SHA_DIGEST_LENGTH; + for (l = len - plen - 1; plen < len; plen++) + out[plen] = l; + /* encrypt HMAC|padding at once */ + aesni_cbc_encrypt(out + aes_off, out + aes_off, len - aes_off, + &ctx->ks, ctx->base.iv, 1); + } else { + aesni_cbc_encrypt(in + aes_off, out + aes_off, len - aes_off, + &ctx->ks, ctx->base.iv, 1); + } + } else { + union { + unsigned int u[SHA_DIGEST_LENGTH / sizeof(unsigned int)]; + unsigned char c[32 + SHA_DIGEST_LENGTH]; + } mac, *pmac; + + /* arrange cache line alignment */ + pmac = (void *)(((size_t)mac.c + 31) & ((size_t)0 - 32)); + + if (plen != NO_PAYLOAD_LENGTH) { /* "TLS" mode of operation */ + size_t inp_len, mask, j, i; + unsigned int res, maxpad, pad, bitlen; + int ret = 1; + union { + unsigned int u[SHA_LBLOCK]; + unsigned char c[SHA_CBLOCK]; + } *data = (void *)sctx->md.data; + + if ((ctx->aux.tls_aad[plen - 4] << 8 | ctx->aux.tls_aad[plen - 3]) + >= TLS1_1_VERSION) { + if (len < (AES_BLOCK_SIZE + SHA_DIGEST_LENGTH + 1)) + return 0; + + /* omit explicit iv */ + memcpy(ctx->base.iv, in, AES_BLOCK_SIZE); + + in += AES_BLOCK_SIZE; + out += AES_BLOCK_SIZE; + len -= AES_BLOCK_SIZE; + } else if (len < (SHA_DIGEST_LENGTH + 1)) + return 0; + + /* decrypt HMAC|padding at once */ + aesni_cbc_encrypt(in, out, len, &ctx->ks, ctx->base.iv, 0); + + /* figure out payload length */ + pad = out[len - 1]; + maxpad = len - (SHA_DIGEST_LENGTH + 1); + maxpad |= (255 - maxpad) >> (sizeof(maxpad) * 8 - 8); + maxpad &= 255; + + mask = constant_time_ge(maxpad, pad); + ret &= mask; + /* + * If pad is invalid then we will fail the above test but we must + * continue anyway because we are in constant time code. However, + * we'll use the maxpad value instead of the supplied pad to make + * sure we perform well defined pointer arithmetic. + */ + pad = constant_time_select(mask, pad, maxpad); + + inp_len = len - (SHA_DIGEST_LENGTH + pad + 1); + + ctx->aux.tls_aad[plen - 2] = inp_len >> 8; + ctx->aux.tls_aad[plen - 1] = inp_len; + + /* calculate HMAC */ + sctx->md = sctx->head; + sha1_update(&sctx->md, ctx->aux.tls_aad, plen); + + /* code containing lucky-13 fix */ + len -= SHA_DIGEST_LENGTH; /* amend mac */ + if (len >= (256 + SHA_CBLOCK)) { + j = (len - (256 + SHA_CBLOCK)) & (0 - SHA_CBLOCK); + j += SHA_CBLOCK - sctx->md.num; + sha1_update(&sctx->md, out, j); + out += j; + len -= j; + inp_len -= j; + } + + /* but pretend as if we hashed padded payload */ + bitlen = sctx->md.Nl + (inp_len << 3); /* at most 18 bits */ +# ifdef BSWAP4 + bitlen = BSWAP4(bitlen); +# else + mac.c[0] = 0; + mac.c[1] = (unsigned char)(bitlen >> 16); + mac.c[2] = (unsigned char)(bitlen >> 8); + mac.c[3] = (unsigned char)bitlen; + bitlen = mac.u[0]; +# endif /* BSWAP */ + + pmac->u[0] = 0; + pmac->u[1] = 0; + pmac->u[2] = 0; + pmac->u[3] = 0; + pmac->u[4] = 0; + + for (res = sctx->md.num, j = 0; j < len; j++) { + size_t c = out[j]; + mask = (j - inp_len) >> (sizeof(j) * 8 - 8); + c &= mask; + c |= 0x80 & ~mask & ~((inp_len - j) >> (sizeof(j) * 8 - 8)); + data->c[res++] = (unsigned char)c; + + if (res != SHA_CBLOCK) + continue; + + /* j is not incremented yet */ + mask = 0 - ((inp_len + 7 - j) >> (sizeof(j) * 8 - 1)); + data->u[SHA_LBLOCK - 1] |= bitlen & mask; + sha1_block_data_order(&sctx->md, data, 1); + mask &= 0 - ((j - inp_len - 72) >> (sizeof(j) * 8 - 1)); + pmac->u[0] |= sctx->md.h0 & mask; + pmac->u[1] |= sctx->md.h1 & mask; + pmac->u[2] |= sctx->md.h2 & mask; + pmac->u[3] |= sctx->md.h3 & mask; + pmac->u[4] |= sctx->md.h4 & mask; + res = 0; + } + + for (i = res; i < SHA_CBLOCK; i++, j++) + data->c[i] = 0; + + if (res > SHA_CBLOCK - 8) { + mask = 0 - ((inp_len + 8 - j) >> (sizeof(j) * 8 - 1)); + data->u[SHA_LBLOCK - 1] |= bitlen & mask; + sha1_block_data_order(&sctx->md, data, 1); + mask &= 0 - ((j - inp_len - 73) >> (sizeof(j) * 8 - 1)); + pmac->u[0] |= sctx->md.h0 & mask; + pmac->u[1] |= sctx->md.h1 & mask; + pmac->u[2] |= sctx->md.h2 & mask; + pmac->u[3] |= sctx->md.h3 & mask; + pmac->u[4] |= sctx->md.h4 & mask; + + memset(data, 0, SHA_CBLOCK); + j += 64; + } + data->u[SHA_LBLOCK - 1] = bitlen; + sha1_block_data_order(&sctx->md, data, 1); + mask = 0 - ((j - inp_len - 73) >> (sizeof(j) * 8 - 1)); + pmac->u[0] |= sctx->md.h0 & mask; + pmac->u[1] |= sctx->md.h1 & mask; + pmac->u[2] |= sctx->md.h2 & mask; + pmac->u[3] |= sctx->md.h3 & mask; + pmac->u[4] |= sctx->md.h4 & mask; + +# ifdef BSWAP4 + pmac->u[0] = BSWAP4(pmac->u[0]); + pmac->u[1] = BSWAP4(pmac->u[1]); + pmac->u[2] = BSWAP4(pmac->u[2]); + pmac->u[3] = BSWAP4(pmac->u[3]); + pmac->u[4] = BSWAP4(pmac->u[4]); +# else + for (i = 0; i < 5; i++) { + res = pmac->u[i]; + pmac->c[4 * i + 0] = (unsigned char)(res >> 24); + pmac->c[4 * i + 1] = (unsigned char)(res >> 16); + pmac->c[4 * i + 2] = (unsigned char)(res >> 8); + pmac->c[4 * i + 3] = (unsigned char)res; + } +# endif /* BSWAP4 */ + len += SHA_DIGEST_LENGTH; + sctx->md = sctx->tail; + sha1_update(&sctx->md, pmac->c, SHA_DIGEST_LENGTH); + SHA1_Final(pmac->c, &sctx->md); + + /* verify HMAC */ + out += inp_len; + len -= inp_len; + /* version of code with lucky-13 fix */ + { + unsigned char *p = out + len - 1 - maxpad - SHA_DIGEST_LENGTH; + size_t off = out - p; + unsigned int c, cmask; + + for (res = 0, i = 0, j = 0; j < maxpad + SHA_DIGEST_LENGTH; j++) { + c = p[j]; + cmask = + ((int)(j - off - SHA_DIGEST_LENGTH)) >> (sizeof(int) * + 8 - 1); + res |= (c ^ pad) & ~cmask; /* ... and padding */ + cmask &= ((int)(off - 1 - j)) >> (sizeof(int) * 8 - 1); + res |= (c ^ pmac->c[i]) & cmask; + i += 1 & cmask; + } + + res = 0 - ((0 - res) >> (sizeof(res) * 8 - 1)); + ret &= (int)~res; + } + return ret; + } else { + /* decrypt HMAC|padding at once */ + aesni_cbc_encrypt(in, out, len, &ctx->ks, ctx->base.iv, 0); + sha1_update(&sctx->md, out, len); + } + } + + return 1; +} + +/* EVP_CTRL_AEAD_SET_MAC_KEY */ +static void aesni_cbc_hmac_sha1_set_mac_key(void *vctx, + const unsigned char *mac, size_t len) +{ + PROV_AES_HMAC_SHA1_CTX *ctx = (PROV_AES_HMAC_SHA1_CTX *)vctx; + unsigned int i; + unsigned char hmac_key[64]; + + memset(hmac_key, 0, sizeof(hmac_key)); + + if (len > (int)sizeof(hmac_key)) { + SHA1_Init(&ctx->head); + sha1_update(&ctx->head, mac, len); + SHA1_Final(hmac_key, &ctx->head); + } else { + memcpy(hmac_key, mac, len); + } + + for (i = 0; i < sizeof(hmac_key); i++) + hmac_key[i] ^= 0x36; /* ipad */ + SHA1_Init(&ctx->head); + sha1_update(&ctx->head, hmac_key, sizeof(hmac_key)); + + for (i = 0; i < sizeof(hmac_key); i++) + hmac_key[i] ^= 0x36 ^ 0x5c; /* opad */ + SHA1_Init(&ctx->tail); + sha1_update(&ctx->tail, hmac_key, sizeof(hmac_key)); + + OPENSSL_cleanse(hmac_key, sizeof(hmac_key)); +} + +/* EVP_CTRL_AEAD_TLS1_AAD */ +static int aesni_cbc_hmac_sha1_set_tls1_aad(void *vctx, + unsigned char *aad_rec, int aad_len) +{ + PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx; + PROV_AES_HMAC_SHA1_CTX *sctx = (PROV_AES_HMAC_SHA1_CTX *)vctx; + unsigned char *p = aad_rec; + unsigned int len; + + if (aad_len != EVP_AEAD_TLS1_AAD_LEN) + return -1; + + len = p[aad_len - 2] << 8 | p[aad_len - 1]; + + if (ctx->base.enc) { + ctx->payload_length = len; + if ((ctx->aux.tls_ver = + p[aad_len - 4] << 8 | p[aad_len - 3]) >= TLS1_1_VERSION) { + if (len < AES_BLOCK_SIZE) + return 0; + len -= AES_BLOCK_SIZE; + p[aad_len - 2] = len >> 8; + p[aad_len - 1] = len; + } + sctx->md = sctx->head; + sha1_update(&sctx->md, p, aad_len); + ctx->tls_aad_pad = (int)(((len + SHA_DIGEST_LENGTH + + AES_BLOCK_SIZE) & -AES_BLOCK_SIZE) + - len); + return 1; + } else { + memcpy(ctx->aux.tls_aad, aad_rec, aad_len); + ctx->payload_length = aad_len; + ctx->tls_aad_pad = SHA_DIGEST_LENGTH; + return 1; + } +} + +# if !defined(OPENSSL_NO_MULTIBLOCK) + +/* EVP_CTRL_TLS1_1_MULTIBLOCK_MAX_BUFSIZE */ +static int aesni_cbc_hmac_sha1_tls1_multiblock_max_bufsize(void *vctx) +{ + PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx; + + OPENSSL_assert(ctx->multiblock_max_send_fragment != 0); + return (int)(5 + 16 + + (((int)ctx->multiblock_max_send_fragment + 20 + 16) & -16)); +} + +/* EVP_CTRL_TLS1_1_MULTIBLOCK_AAD */ +static int aesni_cbc_hmac_sha1_tls1_multiblock_aad( + void *vctx, EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM *param) +{ + PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx; + PROV_AES_HMAC_SHA1_CTX *sctx = (PROV_AES_HMAC_SHA1_CTX *)vctx; + unsigned int n4x = 1, x4; + unsigned int frag, last, packlen, inp_len; + + inp_len = param->inp[11] << 8 | param->inp[12]; + ctx->multiblock_interleave = param->interleave; + + if (ctx->base.enc) { + if ((param->inp[9] << 8 | param->inp[10]) < TLS1_1_VERSION) + return -1; + + if (inp_len) { + if (inp_len < 4096) + return 0; /* too short */ + + if (inp_len >= 8192 && OPENSSL_ia32cap_P[2] & (1 << 5)) + n4x = 2; /* AVX2 */ + } else if ((n4x = param->interleave / 4) && n4x <= 2) + inp_len = param->len; + else + return -1; + + sctx->md = sctx->head; + sha1_update(&sctx->md, param->inp, 13); + + x4 = 4 * n4x; + n4x += 1; + + frag = inp_len >> n4x; + last = inp_len + frag - (frag << n4x); + if (last > frag && ((last + 13 + 9) % 64 < (x4 - 1))) { + frag++; + last -= x4 - 1; + } + + packlen = 5 + 16 + ((frag + 20 + 16) & -16); + packlen = (packlen << n4x) - packlen; + packlen += 5 + 16 + ((last + 20 + 16) & -16); + + param->interleave = x4; + /* The returned values used by get need to be stored */ + ctx->multiblock_interleave = x4; + ctx->multiblock_aad_packlen = packlen; + return 1; + } + return -1; /* not yet */ +} + +/* EVP_CTRL_TLS1_1_MULTIBLOCK_ENCRYPT */ +static int aesni_cbc_hmac_sha1_tls1_multiblock_encrypt( + void *ctx, EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM *param) +{ + return (int)tls1_multi_block_encrypt(ctx, param->out, + param->inp, param->len, + param->interleave / 4); +} + +# endif /* OPENSSL_NO_MULTIBLOCK */ + +static const PROV_CIPHER_HW_AES_HMAC_SHA cipher_hw_aes_hmac_sha1 = { + { + aesni_cbc_hmac_sha1_init_key, + aesni_cbc_hmac_sha1_cipher + }, + aesni_cbc_hmac_sha1_set_mac_key, + aesni_cbc_hmac_sha1_set_tls1_aad, +# if !defined(OPENSSL_NO_MULTIBLOCK) + aesni_cbc_hmac_sha1_tls1_multiblock_max_bufsize, + aesni_cbc_hmac_sha1_tls1_multiblock_aad, + aesni_cbc_hmac_sha1_tls1_multiblock_encrypt +# endif +}; + +const PROV_CIPHER_HW_AES_HMAC_SHA *ossl_prov_cipher_hw_aes_cbc_hmac_sha1(void) +{ + return &cipher_hw_aes_hmac_sha1; +} + +#endif /* !defined(AES_CBC_HMAC_SHA_CAPABLE) || !defined(AESNI_CAPABLE) */ diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha256_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha256_hw.c new file mode 100644 index 000000000000..f5b2f8b6da32 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha256_hw.c @@ -0,0 +1,846 @@ +/* + * Copyright 2011-2021 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 + */ + +/* + * All low level APIs are deprecated for public use, but still ok for internal + * use where we're using them to implement the higher level EVP interface, as is + * the case here. + */ +#include "internal/deprecated.h" + +#include "cipher_aes_cbc_hmac_sha.h" + +#if !defined(AES_CBC_HMAC_SHA_CAPABLE) || !defined(AESNI_CAPABLE) +int ossl_cipher_capable_aes_cbc_hmac_sha256(void) +{ + return 0; +} + +const PROV_CIPHER_HW_AES_HMAC_SHA *ossl_prov_cipher_hw_aes_cbc_hmac_sha256(void) +{ + return NULL; +} +#else + +# include <openssl/rand.h> +# include "crypto/evp.h" +# include "internal/constant_time.h" + +void sha256_block_data_order(void *c, const void *p, size_t len); +int aesni_cbc_sha256_enc(const void *inp, void *out, size_t blocks, + const AES_KEY *key, unsigned char iv[16], + SHA256_CTX *ctx, const void *in0); + +int ossl_cipher_capable_aes_cbc_hmac_sha256(void) +{ + return AESNI_CBC_HMAC_SHA_CAPABLE + && aesni_cbc_sha256_enc(NULL, NULL, 0, NULL, NULL, NULL, NULL); +} + +static int aesni_cbc_hmac_sha256_init_key(PROV_CIPHER_CTX *vctx, + const unsigned char *key, + size_t keylen) +{ + int ret; + PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx; + PROV_AES_HMAC_SHA256_CTX *sctx = (PROV_AES_HMAC_SHA256_CTX *)vctx; + + if (ctx->base.enc) + ret = aesni_set_encrypt_key(key, ctx->base.keylen * 8, &ctx->ks); + else + ret = aesni_set_decrypt_key(key, ctx->base.keylen * 8, &ctx->ks); + + SHA256_Init(&sctx->head); /* handy when benchmarking */ + sctx->tail = sctx->head; + sctx->md = sctx->head; + + ctx->payload_length = NO_PAYLOAD_LENGTH; + + vctx->removetlspad = 1; + vctx->removetlsfixed = SHA256_DIGEST_LENGTH + AES_BLOCK_SIZE; + + return ret < 0 ? 0 : 1; +} + +void sha256_block_data_order(void *c, const void *p, size_t len); + +static void sha256_update(SHA256_CTX *c, const void *data, size_t len) +{ + const unsigned char *ptr = data; + size_t res; + + if ((res = c->num)) { + res = SHA256_CBLOCK - res; + if (len < res) + res = len; + SHA256_Update(c, ptr, res); + ptr += res; + len -= res; + } + + res = len % SHA256_CBLOCK; + len -= res; + + if (len) { + sha256_block_data_order(c, ptr, len / SHA256_CBLOCK); + + ptr += len; + c->Nh += len >> 29; + c->Nl += len <<= 3; + if (c->Nl < (unsigned int)len) + c->Nh++; + } + + if (res) + SHA256_Update(c, ptr, res); +} + +# if !defined(OPENSSL_NO_MULTIBLOCK) + +typedef struct { + unsigned int A[8], B[8], C[8], D[8], E[8], F[8], G[8], H[8]; +} SHA256_MB_CTX; + +typedef struct { + const unsigned char *ptr; + int blocks; +} HASH_DESC; + +typedef struct { + const unsigned char *inp; + unsigned char *out; + int blocks; + u64 iv[2]; +} CIPH_DESC; + +void sha256_multi_block(SHA256_MB_CTX *, const HASH_DESC *, int); +void aesni_multi_cbc_encrypt(CIPH_DESC *, void *, int); + +static size_t tls1_multi_block_encrypt(void *vctx, + unsigned char *out, + const unsigned char *inp, + size_t inp_len, int n4x) +{ /* n4x is 1 or 2 */ + PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx; + PROV_AES_HMAC_SHA256_CTX *sctx = (PROV_AES_HMAC_SHA256_CTX *)vctx; + HASH_DESC hash_d[8], edges[8]; + CIPH_DESC ciph_d[8]; + unsigned char storage[sizeof(SHA256_MB_CTX) + 32]; + union { + u64 q[16]; + u32 d[32]; + u8 c[128]; + } blocks[8]; + SHA256_MB_CTX *mctx; + unsigned int frag, last, packlen, i; + unsigned int x4 = 4 * n4x, minblocks, processed = 0; + size_t ret = 0; + u8 *IVs; +# if defined(BSWAP8) + u64 seqnum; +# endif + + /* ask for IVs in bulk */ + if (RAND_bytes_ex(ctx->base.libctx, (IVs = blocks[0].c), 16 * x4, 0) <= 0) + return 0; + + mctx = (SHA256_MB_CTX *) (storage + 32 - ((size_t)storage % 32)); /* align */ + + frag = (unsigned int)inp_len >> (1 + n4x); + last = (unsigned int)inp_len + frag - (frag << (1 + n4x)); + if (last > frag && ((last + 13 + 9) % 64) < (x4 - 1)) { + frag++; + last -= x4 - 1; + } + + packlen = 5 + 16 + ((frag + 32 + 16) & -16); + + /* populate descriptors with pointers and IVs */ + hash_d[0].ptr = inp; + ciph_d[0].inp = inp; + /* 5+16 is place for header and explicit IV */ + ciph_d[0].out = out + 5 + 16; + memcpy(ciph_d[0].out - 16, IVs, 16); + memcpy(ciph_d[0].iv, IVs, 16); + IVs += 16; + + for (i = 1; i < x4; i++) { + ciph_d[i].inp = hash_d[i].ptr = hash_d[i - 1].ptr + frag; + ciph_d[i].out = ciph_d[i - 1].out + packlen; + memcpy(ciph_d[i].out - 16, IVs, 16); + memcpy(ciph_d[i].iv, IVs, 16); + IVs += 16; + } + +# if defined(BSWAP8) + memcpy(blocks[0].c, sctx->md.data, 8); + seqnum = BSWAP8(blocks[0].q[0]); +# endif + + for (i = 0; i < x4; i++) { + unsigned int len = (i == (x4 - 1) ? last : frag); +# if !defined(BSWAP8) + unsigned int carry, j; +# endif + + mctx->A[i] = sctx->md.h[0]; + mctx->B[i] = sctx->md.h[1]; + mctx->C[i] = sctx->md.h[2]; + mctx->D[i] = sctx->md.h[3]; + mctx->E[i] = sctx->md.h[4]; + mctx->F[i] = sctx->md.h[5]; + mctx->G[i] = sctx->md.h[6]; + mctx->H[i] = sctx->md.h[7]; + + /* fix seqnum */ +# if defined(BSWAP8) + blocks[i].q[0] = BSWAP8(seqnum + i); +# else + for (carry = i, j = 8; j--;) { + blocks[i].c[j] = ((u8 *)sctx->md.data)[j] + carry; + carry = (blocks[i].c[j] - carry) >> (sizeof(carry) * 8 - 1); + } +# endif + blocks[i].c[8] = ((u8 *)sctx->md.data)[8]; + blocks[i].c[9] = ((u8 *)sctx->md.data)[9]; + blocks[i].c[10] = ((u8 *)sctx->md.data)[10]; + /* fix length */ + blocks[i].c[11] = (u8)(len >> 8); + blocks[i].c[12] = (u8)(len); + + memcpy(blocks[i].c + 13, hash_d[i].ptr, 64 - 13); + hash_d[i].ptr += 64 - 13; + hash_d[i].blocks = (len - (64 - 13)) / 64; + + edges[i].ptr = blocks[i].c; + edges[i].blocks = 1; + } + + /* hash 13-byte headers and first 64-13 bytes of inputs */ + sha256_multi_block(mctx, edges, n4x); + /* hash bulk inputs */ +# define MAXCHUNKSIZE 2048 +# if MAXCHUNKSIZE%64 +# error "MAXCHUNKSIZE is not divisible by 64" +# elif MAXCHUNKSIZE + /* + * goal is to minimize pressure on L1 cache by moving in shorter steps, + * so that hashed data is still in the cache by the time we encrypt it + */ + minblocks = ((frag <= last ? frag : last) - (64 - 13)) / 64; + if (minblocks > MAXCHUNKSIZE / 64) { + for (i = 0; i < x4; i++) { + edges[i].ptr = hash_d[i].ptr; + edges[i].blocks = MAXCHUNKSIZE / 64; + ciph_d[i].blocks = MAXCHUNKSIZE / 16; + } + do { + sha256_multi_block(mctx, edges, n4x); + aesni_multi_cbc_encrypt(ciph_d, &ctx->ks, n4x); + + for (i = 0; i < x4; i++) { + edges[i].ptr = hash_d[i].ptr += MAXCHUNKSIZE; + hash_d[i].blocks -= MAXCHUNKSIZE / 64; + edges[i].blocks = MAXCHUNKSIZE / 64; + ciph_d[i].inp += MAXCHUNKSIZE; + ciph_d[i].out += MAXCHUNKSIZE; + ciph_d[i].blocks = MAXCHUNKSIZE / 16; + memcpy(ciph_d[i].iv, ciph_d[i].out - 16, 16); + } + processed += MAXCHUNKSIZE; + minblocks -= MAXCHUNKSIZE / 64; + } while (minblocks > MAXCHUNKSIZE / 64); + } +# endif +# undef MAXCHUNKSIZE + sha256_multi_block(mctx, hash_d, n4x); + + memset(blocks, 0, sizeof(blocks)); + for (i = 0; i < x4; i++) { + unsigned int len = (i == (x4 - 1) ? last : frag), + off = hash_d[i].blocks * 64; + const unsigned char *ptr = hash_d[i].ptr + off; + + off = (len - processed) - (64 - 13) - off; /* remainder actually */ + memcpy(blocks[i].c, ptr, off); + blocks[i].c[off] = 0x80; + len += 64 + 13; /* 64 is HMAC header */ + len *= 8; /* convert to bits */ + if (off < (64 - 8)) { +# ifdef BSWAP4 + blocks[i].d[15] = BSWAP4(len); +# else + PUTU32(blocks[i].c + 60, len); +# endif + edges[i].blocks = 1; + } else { +# ifdef BSWAP4 + blocks[i].d[31] = BSWAP4(len); +# else + PUTU32(blocks[i].c + 124, len); +# endif + edges[i].blocks = 2; + } + edges[i].ptr = blocks[i].c; + } + + /* hash input tails and finalize */ + sha256_multi_block(mctx, edges, n4x); + + memset(blocks, 0, sizeof(blocks)); + for (i = 0; i < x4; i++) { +# ifdef BSWAP4 + blocks[i].d[0] = BSWAP4(mctx->A[i]); + mctx->A[i] = sctx->tail.h[0]; + blocks[i].d[1] = BSWAP4(mctx->B[i]); + mctx->B[i] = sctx->tail.h[1]; + blocks[i].d[2] = BSWAP4(mctx->C[i]); + mctx->C[i] = sctx->tail.h[2]; + blocks[i].d[3] = BSWAP4(mctx->D[i]); + mctx->D[i] = sctx->tail.h[3]; + blocks[i].d[4] = BSWAP4(mctx->E[i]); + mctx->E[i] = sctx->tail.h[4]; + blocks[i].d[5] = BSWAP4(mctx->F[i]); + mctx->F[i] = sctx->tail.h[5]; + blocks[i].d[6] = BSWAP4(mctx->G[i]); + mctx->G[i] = sctx->tail.h[6]; + blocks[i].d[7] = BSWAP4(mctx->H[i]); + mctx->H[i] = sctx->tail.h[7]; + blocks[i].c[32] = 0x80; + blocks[i].d[15] = BSWAP4((64 + 32) * 8); +# else + PUTU32(blocks[i].c + 0, mctx->A[i]); + mctx->A[i] = sctx->tail.h[0]; + PUTU32(blocks[i].c + 4, mctx->B[i]); + mctx->B[i] = sctx->tail.h[1]; + PUTU32(blocks[i].c + 8, mctx->C[i]); + mctx->C[i] = sctx->tail.h[2]; + PUTU32(blocks[i].c + 12, mctx->D[i]); + mctx->D[i] = sctx->tail.h[3]; + PUTU32(blocks[i].c + 16, mctx->E[i]); + mctx->E[i] = sctx->tail.h[4]; + PUTU32(blocks[i].c + 20, mctx->F[i]); + mctx->F[i] = sctx->tail.h[5]; + PUTU32(blocks[i].c + 24, mctx->G[i]); + mctx->G[i] = sctx->tail.h[6]; + PUTU32(blocks[i].c + 28, mctx->H[i]); + mctx->H[i] = sctx->tail.h[7]; + blocks[i].c[32] = 0x80; + PUTU32(blocks[i].c + 60, (64 + 32) * 8); +# endif /* BSWAP */ + edges[i].ptr = blocks[i].c; + edges[i].blocks = 1; + } + + /* finalize MACs */ + sha256_multi_block(mctx, edges, n4x); + + for (i = 0; i < x4; i++) { + unsigned int len = (i == (x4 - 1) ? last : frag), pad, j; + unsigned char *out0 = out; + + memcpy(ciph_d[i].out, ciph_d[i].inp, len - processed); + ciph_d[i].inp = ciph_d[i].out; + + out += 5 + 16 + len; + + /* write MAC */ + PUTU32(out + 0, mctx->A[i]); + PUTU32(out + 4, mctx->B[i]); + PUTU32(out + 8, mctx->C[i]); + PUTU32(out + 12, mctx->D[i]); + PUTU32(out + 16, mctx->E[i]); + PUTU32(out + 20, mctx->F[i]); + PUTU32(out + 24, mctx->G[i]); + PUTU32(out + 28, mctx->H[i]); + out += 32; + len += 32; + + /* pad */ + pad = 15 - len % 16; + for (j = 0; j <= pad; j++) + *(out++) = pad; + len += pad + 1; + + ciph_d[i].blocks = (len - processed) / 16; + len += 16; /* account for explicit iv */ + + /* arrange header */ + out0[0] = ((u8 *)sctx->md.data)[8]; + out0[1] = ((u8 *)sctx->md.data)[9]; + out0[2] = ((u8 *)sctx->md.data)[10]; + out0[3] = (u8)(len >> 8); + out0[4] = (u8)(len); + + ret += len + 5; + inp += frag; + } + + aesni_multi_cbc_encrypt(ciph_d, &ctx->ks, n4x); + + OPENSSL_cleanse(blocks, sizeof(blocks)); + OPENSSL_cleanse(mctx, sizeof(*mctx)); + + ctx->multiblock_encrypt_len = ret; + return ret; +} +# endif /* !OPENSSL_NO_MULTIBLOCK */ + +static int aesni_cbc_hmac_sha256_cipher(PROV_CIPHER_CTX *vctx, + unsigned char *out, + const unsigned char *in, size_t len) +{ + PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx; + PROV_AES_HMAC_SHA256_CTX *sctx = (PROV_AES_HMAC_SHA256_CTX *)vctx; + unsigned int l; + size_t plen = ctx->payload_length; + size_t iv = 0; /* explicit IV in TLS 1.1 and * later */ + size_t aes_off = 0, blocks; + size_t sha_off = SHA256_CBLOCK - sctx->md.num; + + ctx->payload_length = NO_PAYLOAD_LENGTH; + + if (len % AES_BLOCK_SIZE) + return 0; + + if (ctx->base.enc) { + if (plen == NO_PAYLOAD_LENGTH) + plen = len; + else if (len != + ((plen + SHA256_DIGEST_LENGTH + + AES_BLOCK_SIZE) & -AES_BLOCK_SIZE)) + return 0; + else if (ctx->aux.tls_ver >= TLS1_1_VERSION) + iv = AES_BLOCK_SIZE; + + /* + * Assembly stitch handles AVX-capable processors, but its + * performance is not optimal on AMD Jaguar, ~40% worse, for + * unknown reasons. Incidentally processor in question supports + * AVX, but not AMD-specific XOP extension, which can be used + * to identify it and avoid stitch invocation. So that after we + * establish that current CPU supports AVX, we even see if it's + * either even XOP-capable Bulldozer-based or GenuineIntel one. + * But SHAEXT-capable go ahead... + */ + if (((OPENSSL_ia32cap_P[2] & (1 << 29)) || /* SHAEXT? */ + ((OPENSSL_ia32cap_P[1] & (1 << (60 - 32))) && /* AVX? */ + ((OPENSSL_ia32cap_P[1] & (1 << (43 - 32))) /* XOP? */ + | (OPENSSL_ia32cap_P[0] & (1 << 30))))) && /* "Intel CPU"? */ + plen > (sha_off + iv) && + (blocks = (plen - (sha_off + iv)) / SHA256_CBLOCK)) { + sha256_update(&sctx->md, in + iv, sha_off); + + (void)aesni_cbc_sha256_enc(in, out, blocks, &ctx->ks, + ctx->base.iv, + &sctx->md, in + iv + sha_off); + blocks *= SHA256_CBLOCK; + aes_off += blocks; + sha_off += blocks; + sctx->md.Nh += blocks >> 29; + sctx->md.Nl += blocks <<= 3; + if (sctx->md.Nl < (unsigned int)blocks) + sctx->md.Nh++; + } else { + sha_off = 0; + } + sha_off += iv; + sha256_update(&sctx->md, in + sha_off, plen - sha_off); + + if (plen != len) { /* "TLS" mode of operation */ + if (in != out) + memcpy(out + aes_off, in + aes_off, plen - aes_off); + + /* calculate HMAC and append it to payload */ + SHA256_Final(out + plen, &sctx->md); + sctx->md = sctx->tail; + sha256_update(&sctx->md, out + plen, SHA256_DIGEST_LENGTH); + SHA256_Final(out + plen, &sctx->md); + + /* pad the payload|hmac */ + plen += SHA256_DIGEST_LENGTH; + for (l = len - plen - 1; plen < len; plen++) + out[plen] = l; + /* encrypt HMAC|padding at once */ + aesni_cbc_encrypt(out + aes_off, out + aes_off, len - aes_off, + &ctx->ks, ctx->base.iv, 1); + } else { + aesni_cbc_encrypt(in + aes_off, out + aes_off, len - aes_off, + &ctx->ks, ctx->base.iv, 1); + } + } else { + union { + unsigned int u[SHA256_DIGEST_LENGTH / sizeof(unsigned int)]; + unsigned char c[64 + SHA256_DIGEST_LENGTH]; + } mac, *pmac; + + /* arrange cache line alignment */ + pmac = (void *)(((size_t)mac.c + 63) & ((size_t)0 - 64)); + + /* decrypt HMAC|padding at once */ + aesni_cbc_encrypt(in, out, len, &ctx->ks, + ctx->base.iv, 0); + + if (plen != NO_PAYLOAD_LENGTH) { /* "TLS" mode of operation */ + size_t inp_len, mask, j, i; + unsigned int res, maxpad, pad, bitlen; + int ret = 1; + union { + unsigned int u[SHA_LBLOCK]; + unsigned char c[SHA256_CBLOCK]; + } *data = (void *)sctx->md.data; + + if ((ctx->aux.tls_aad[plen - 4] << 8 | ctx->aux.tls_aad[plen - 3]) + >= TLS1_1_VERSION) + iv = AES_BLOCK_SIZE; + + if (len < (iv + SHA256_DIGEST_LENGTH + 1)) + return 0; + + /* omit explicit iv */ + out += iv; + len -= iv; + + /* figure out payload length */ + pad = out[len - 1]; + maxpad = len - (SHA256_DIGEST_LENGTH + 1); + maxpad |= (255 - maxpad) >> (sizeof(maxpad) * 8 - 8); + maxpad &= 255; + + mask = constant_time_ge(maxpad, pad); + ret &= mask; + /* + * If pad is invalid then we will fail the above test but we must + * continue anyway because we are in constant time code. However, + * we'll use the maxpad value instead of the supplied pad to make + * sure we perform well defined pointer arithmetic. + */ + pad = constant_time_select(mask, pad, maxpad); + + inp_len = len - (SHA256_DIGEST_LENGTH + pad + 1); + + ctx->aux.tls_aad[plen - 2] = inp_len >> 8; + ctx->aux.tls_aad[plen - 1] = inp_len; + + /* calculate HMAC */ + sctx->md = sctx->head; + sha256_update(&sctx->md, ctx->aux.tls_aad, plen); + + /* code with lucky-13 fix */ + len -= SHA256_DIGEST_LENGTH; /* amend mac */ + if (len >= (256 + SHA256_CBLOCK)) { + j = (len - (256 + SHA256_CBLOCK)) & (0 - SHA256_CBLOCK); + j += SHA256_CBLOCK - sctx->md.num; + sha256_update(&sctx->md, out, j); + out += j; + len -= j; + inp_len -= j; + } + + /* but pretend as if we hashed padded payload */ + bitlen = sctx->md.Nl + (inp_len << 3); /* at most 18 bits */ +# ifdef BSWAP4 + bitlen = BSWAP4(bitlen); +# else + mac.c[0] = 0; + mac.c[1] = (unsigned char)(bitlen >> 16); + mac.c[2] = (unsigned char)(bitlen >> 8); + mac.c[3] = (unsigned char)bitlen; + bitlen = mac.u[0]; +# endif /* BSWAP */ + + pmac->u[0] = 0; + pmac->u[1] = 0; + pmac->u[2] = 0; + pmac->u[3] = 0; + pmac->u[4] = 0; + pmac->u[5] = 0; + pmac->u[6] = 0; + pmac->u[7] = 0; + + for (res = sctx->md.num, j = 0; j < len; j++) { + size_t c = out[j]; + mask = (j - inp_len) >> (sizeof(j) * 8 - 8); + c &= mask; + c |= 0x80 & ~mask & ~((inp_len - j) >> (sizeof(j) * 8 - 8)); + data->c[res++] = (unsigned char)c; + + if (res != SHA256_CBLOCK) + continue; + + /* j is not incremented yet */ + mask = 0 - ((inp_len + 7 - j) >> (sizeof(j) * 8 - 1)); + data->u[SHA_LBLOCK - 1] |= bitlen & mask; + sha256_block_data_order(&sctx->md, data, 1); + mask &= 0 - ((j - inp_len - 72) >> (sizeof(j) * 8 - 1)); + pmac->u[0] |= sctx->md.h[0] & mask; + pmac->u[1] |= sctx->md.h[1] & mask; + pmac->u[2] |= sctx->md.h[2] & mask; + pmac->u[3] |= sctx->md.h[3] & mask; + pmac->u[4] |= sctx->md.h[4] & mask; + pmac->u[5] |= sctx->md.h[5] & mask; + pmac->u[6] |= sctx->md.h[6] & mask; + pmac->u[7] |= sctx->md.h[7] & mask; + res = 0; + } + + for (i = res; i < SHA256_CBLOCK; i++, j++) + data->c[i] = 0; + + if (res > SHA256_CBLOCK - 8) { + mask = 0 - ((inp_len + 8 - j) >> (sizeof(j) * 8 - 1)); + data->u[SHA_LBLOCK - 1] |= bitlen & mask; + sha256_block_data_order(&sctx->md, data, 1); + mask &= 0 - ((j - inp_len - 73) >> (sizeof(j) * 8 - 1)); + pmac->u[0] |= sctx->md.h[0] & mask; + pmac->u[1] |= sctx->md.h[1] & mask; + pmac->u[2] |= sctx->md.h[2] & mask; + pmac->u[3] |= sctx->md.h[3] & mask; + pmac->u[4] |= sctx->md.h[4] & mask; + pmac->u[5] |= sctx->md.h[5] & mask; + pmac->u[6] |= sctx->md.h[6] & mask; + pmac->u[7] |= sctx->md.h[7] & mask; + + memset(data, 0, SHA256_CBLOCK); + j += 64; + } + data->u[SHA_LBLOCK - 1] = bitlen; + sha256_block_data_order(&sctx->md, data, 1); + mask = 0 - ((j - inp_len - 73) >> (sizeof(j) * 8 - 1)); + pmac->u[0] |= sctx->md.h[0] & mask; + pmac->u[1] |= sctx->md.h[1] & mask; + pmac->u[2] |= sctx->md.h[2] & mask; + pmac->u[3] |= sctx->md.h[3] & mask; + pmac->u[4] |= sctx->md.h[4] & mask; + pmac->u[5] |= sctx->md.h[5] & mask; + pmac->u[6] |= sctx->md.h[6] & mask; + pmac->u[7] |= sctx->md.h[7] & mask; + +# ifdef BSWAP4 + pmac->u[0] = BSWAP4(pmac->u[0]); + pmac->u[1] = BSWAP4(pmac->u[1]); + pmac->u[2] = BSWAP4(pmac->u[2]); + pmac->u[3] = BSWAP4(pmac->u[3]); + pmac->u[4] = BSWAP4(pmac->u[4]); + pmac->u[5] = BSWAP4(pmac->u[5]); + pmac->u[6] = BSWAP4(pmac->u[6]); + pmac->u[7] = BSWAP4(pmac->u[7]); +# else + for (i = 0; i < 8; i++) { + res = pmac->u[i]; + pmac->c[4 * i + 0] = (unsigned char)(res >> 24); + pmac->c[4 * i + 1] = (unsigned char)(res >> 16); + pmac->c[4 * i + 2] = (unsigned char)(res >> 8); + pmac->c[4 * i + 3] = (unsigned char)res; + } +# endif /* BSWAP */ + len += SHA256_DIGEST_LENGTH; + sctx->md = sctx->tail; + sha256_update(&sctx->md, pmac->c, SHA256_DIGEST_LENGTH); + SHA256_Final(pmac->c, &sctx->md); + + /* verify HMAC */ + out += inp_len; + len -= inp_len; + /* code containing lucky-13 fix */ + { + unsigned char *p = + out + len - 1 - maxpad - SHA256_DIGEST_LENGTH; + size_t off = out - p; + unsigned int c, cmask; + + for (res = 0, i = 0, j = 0; + j < maxpad + SHA256_DIGEST_LENGTH; + j++) { + c = p[j]; + cmask = + ((int)(j - off - SHA256_DIGEST_LENGTH)) >> + (sizeof(int) * 8 - 1); + res |= (c ^ pad) & ~cmask; /* ... and padding */ + cmask &= ((int)(off - 1 - j)) >> (sizeof(int) * 8 - 1); + res |= (c ^ pmac->c[i]) & cmask; + i += 1 & cmask; + } + + res = 0 - ((0 - res) >> (sizeof(res) * 8 - 1)); + ret &= (int)~res; + } + return ret; + } else { + sha256_update(&sctx->md, out, len); + } + } + + return 1; +} + +/* EVP_CTRL_AEAD_SET_MAC_KEY */ +static void aesni_cbc_hmac_sha256_set_mac_key(void *vctx, + const unsigned char *mackey, + size_t len) +{ + PROV_AES_HMAC_SHA256_CTX *ctx = (PROV_AES_HMAC_SHA256_CTX *)vctx; + unsigned int i; + unsigned char hmac_key[64]; + + memset(hmac_key, 0, sizeof(hmac_key)); + + if (len > sizeof(hmac_key)) { + SHA256_Init(&ctx->head); + sha256_update(&ctx->head, mackey, len); + SHA256_Final(hmac_key, &ctx->head); + } else { + memcpy(hmac_key, mackey, len); + } + + for (i = 0; i < sizeof(hmac_key); i++) + hmac_key[i] ^= 0x36; /* ipad */ + SHA256_Init(&ctx->head); + sha256_update(&ctx->head, hmac_key, sizeof(hmac_key)); + + for (i = 0; i < sizeof(hmac_key); i++) + hmac_key[i] ^= 0x36 ^ 0x5c; /* opad */ + SHA256_Init(&ctx->tail); + sha256_update(&ctx->tail, hmac_key, sizeof(hmac_key)); + + OPENSSL_cleanse(hmac_key, sizeof(hmac_key)); +} + +/* EVP_CTRL_AEAD_TLS1_AAD */ +static int aesni_cbc_hmac_sha256_set_tls1_aad(void *vctx, + unsigned char *aad_rec, int aad_len) +{ + PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx; + PROV_AES_HMAC_SHA256_CTX *sctx = (PROV_AES_HMAC_SHA256_CTX *)vctx; + unsigned char *p = aad_rec; + unsigned int len; + + if (aad_len != EVP_AEAD_TLS1_AAD_LEN) + return -1; + + len = p[aad_len - 2] << 8 | p[aad_len - 1]; + + if (ctx->base.enc) { + ctx->payload_length = len; + if ((ctx->aux.tls_ver = + p[aad_len - 4] << 8 | p[aad_len - 3]) >= TLS1_1_VERSION) { + if (len < AES_BLOCK_SIZE) + return 0; + len -= AES_BLOCK_SIZE; + p[aad_len - 2] = len >> 8; + p[aad_len - 1] = len; + } + sctx->md = sctx->head; + sha256_update(&sctx->md, p, aad_len); + ctx->tls_aad_pad = (int)(((len + SHA256_DIGEST_LENGTH + + AES_BLOCK_SIZE) & -AES_BLOCK_SIZE) + - len); + return 1; + } else { + memcpy(ctx->aux.tls_aad, p, aad_len); + ctx->payload_length = aad_len; + ctx->tls_aad_pad = SHA256_DIGEST_LENGTH; + return 1; + } +} + +# if !defined(OPENSSL_NO_MULTIBLOCK) +/* EVP_CTRL_TLS1_1_MULTIBLOCK_MAX_BUFSIZE */ +static int aesni_cbc_hmac_sha256_tls1_multiblock_max_bufsize( + void *vctx) +{ + PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx; + + OPENSSL_assert(ctx->multiblock_max_send_fragment != 0); + return (int)(5 + 16 + + (((int)ctx->multiblock_max_send_fragment + 32 + 16) & -16)); +} + +/* EVP_CTRL_TLS1_1_MULTIBLOCK_AAD */ +static int aesni_cbc_hmac_sha256_tls1_multiblock_aad( + void *vctx, EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM *param) +{ + PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx; + PROV_AES_HMAC_SHA256_CTX *sctx = (PROV_AES_HMAC_SHA256_CTX *)vctx; + unsigned int n4x = 1, x4; + unsigned int frag, last, packlen, inp_len; + + inp_len = param->inp[11] << 8 | param->inp[12]; + + if (ctx->base.enc) { + if ((param->inp[9] << 8 | param->inp[10]) < TLS1_1_VERSION) + return -1; + + if (inp_len) { + if (inp_len < 4096) + return 0; /* too short */ + + if (inp_len >= 8192 && OPENSSL_ia32cap_P[2] & (1 << 5)) + n4x = 2; /* AVX2 */ + } else if ((n4x = param->interleave / 4) && n4x <= 2) + inp_len = param->len; + else + return -1; + + sctx->md = sctx->head; + sha256_update(&sctx->md, param->inp, 13); + + x4 = 4 * n4x; + n4x += 1; + + frag = inp_len >> n4x; + last = inp_len + frag - (frag << n4x); + if (last > frag && ((last + 13 + 9) % 64 < (x4 - 1))) { + frag++; + last -= x4 - 1; + } + + packlen = 5 + 16 + ((frag + 32 + 16) & -16); + packlen = (packlen << n4x) - packlen; + packlen += 5 + 16 + ((last + 32 + 16) & -16); + + param->interleave = x4; + /* The returned values used by get need to be stored */ + ctx->multiblock_interleave = x4; + ctx->multiblock_aad_packlen = packlen; + return 1; + } + return -1; /* not yet */ +} + +/* EVP_CTRL_TLS1_1_MULTIBLOCK_ENCRYPT */ +static int aesni_cbc_hmac_sha256_tls1_multiblock_encrypt( + void *ctx, EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM *param) +{ + return (int)tls1_multi_block_encrypt(ctx, param->out, + param->inp, param->len, + param->interleave / 4); +} +# endif + +static const PROV_CIPHER_HW_AES_HMAC_SHA cipher_hw_aes_hmac_sha256 = { + { + aesni_cbc_hmac_sha256_init_key, + aesni_cbc_hmac_sha256_cipher + }, + aesni_cbc_hmac_sha256_set_mac_key, + aesni_cbc_hmac_sha256_set_tls1_aad, +# if !defined(OPENSSL_NO_MULTIBLOCK) + aesni_cbc_hmac_sha256_tls1_multiblock_max_bufsize, + aesni_cbc_hmac_sha256_tls1_multiblock_aad, + aesni_cbc_hmac_sha256_tls1_multiblock_encrypt +# endif +}; + +const PROV_CIPHER_HW_AES_HMAC_SHA *ossl_prov_cipher_hw_aes_cbc_hmac_sha256(void) +{ + return &cipher_hw_aes_hmac_sha256; +} + +#endif /* !defined(AES_CBC_HMAC_SHA_CAPABLE) || !defined(AESNI_CAPABLE) */ diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm.c new file mode 100644 index 000000000000..e36ac03e6136 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm.c @@ -0,0 +1,72 @@ +/* + * Copyright 2019-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 + */ + +/* + * AES low level APIs are deprecated for public use, but still ok for internal + * use where we're using them to implement the higher level EVP interface, as is + * the case here. + */ +#include "internal/deprecated.h" + +/* Dispatch functions for AES CCM mode */ + +#include "cipher_aes_ccm.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" + +static void *aes_ccm_newctx(void *provctx, size_t keybits) +{ + PROV_AES_CCM_CTX *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + ctx = OPENSSL_zalloc(sizeof(*ctx)); + if (ctx != NULL) + ossl_ccm_initctx(&ctx->base, keybits, ossl_prov_aes_hw_ccm(keybits)); + return ctx; +} + +static void *aes_ccm_dupctx(void *provctx) +{ + PROV_AES_CCM_CTX *ctx = provctx; + PROV_AES_CCM_CTX *dupctx = NULL; + + if (!ossl_prov_is_running()) + return NULL; + + if (ctx == NULL) + return NULL; + dupctx = OPENSSL_memdup(provctx, sizeof(*ctx)); + if (dupctx == NULL) + return NULL; + /* + * ossl_cm_initctx, via the ossl_prov_aes_hw_ccm functions assign a + * provctx->ccm.ks.ks to the ccm context key so we need to point it to + * the memduped copy + */ + dupctx->base.ccm_ctx.key = &dupctx->ccm.ks.ks; + + return dupctx; +} + +static OSSL_FUNC_cipher_freectx_fn aes_ccm_freectx; +static void aes_ccm_freectx(void *vctx) +{ + PROV_AES_CCM_CTX *ctx = (PROV_AES_CCM_CTX *)vctx; + + OPENSSL_clear_free(ctx, sizeof(*ctx)); +} + +/* ossl_aes128ccm_functions */ +IMPLEMENT_aead_cipher(aes, ccm, CCM, AEAD_FLAGS, 128, 8, 96); +/* ossl_aes192ccm_functions */ +IMPLEMENT_aead_cipher(aes, ccm, CCM, AEAD_FLAGS, 192, 8, 96); +/* ossl_aes256ccm_functions */ +IMPLEMENT_aead_cipher(aes, ccm, CCM, AEAD_FLAGS, 256, 8, 96); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm.h b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm.h new file mode 100644 index 000000000000..fd35080db3dd --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm.h @@ -0,0 +1,48 @@ +/* + * Copyright 2019-2020 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 + */ + +#include <openssl/aes.h> +#include "prov/ciphercommon.h" +#include "prov/ciphercommon_ccm.h" +#include "crypto/aes_platform.h" + +typedef struct prov_aes_ccm_ctx_st { + PROV_CCM_CTX base; /* Must be first */ + union { + OSSL_UNION_ALIGN; + /*- + * Padding is chosen so that s390x.kmac.k overlaps with ks.ks and + * fc with ks.ks.rounds. Remember that on s390x, an AES_KEY's + * rounds field is used to store the function code and that the key + * schedule is not stored (if aes hardware support is detected). + */ + struct { + unsigned char pad[16]; + AES_KEY ks; + } ks; +#if defined(OPENSSL_CPUID_OBJ) && defined(__s390__) + struct { + S390X_KMAC_PARAMS kmac; + unsigned long long blocks; + union { + unsigned long long g[2]; + unsigned char b[AES_BLOCK_SIZE]; + } nonce; + union { + unsigned long long g[2]; + unsigned char b[AES_BLOCK_SIZE]; + } buf; + unsigned char dummy_pad[168]; + unsigned int fc; /* fc has same offset as ks.ks.rounds */ + } s390x; +#endif /* defined(OPENSSL_CPUID_OBJ) && defined(__s390__) */ + } ccm; +} PROV_AES_CCM_CTX; + +const PROV_CCM_HW *ossl_prov_aes_hw_ccm(size_t keylen); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw.c new file mode 100644 index 000000000000..b050cf3edd88 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw.c @@ -0,0 +1,73 @@ +/* + * Copyright 2019-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 + */ + +/* AES CCM mode */ + +/* + * This file uses the low level AES functions (which are deprecated for + * non-internal use) in order to implement provider AES ciphers. + */ +#include "internal/deprecated.h" + +#include "cipher_aes_ccm.h" + +#define AES_HW_CCM_SET_KEY_FN(fn_set_enc_key, fn_blk, fn_ccm_enc, fn_ccm_dec) \ + fn_set_enc_key(key, keylen * 8, &actx->ccm.ks.ks); \ + CRYPTO_ccm128_init(&ctx->ccm_ctx, ctx->m, ctx->l, &actx->ccm.ks.ks, \ + (block128_f)fn_blk); \ + ctx->str = ctx->enc ? (ccm128_f)fn_ccm_enc : (ccm128_f)fn_ccm_dec; \ + ctx->key_set = 1; + +static int ccm_generic_aes_initkey(PROV_CCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + PROV_AES_CCM_CTX *actx = (PROV_AES_CCM_CTX *)ctx; + +#ifdef HWAES_CAPABLE + if (HWAES_CAPABLE) { + AES_HW_CCM_SET_KEY_FN(HWAES_set_encrypt_key, HWAES_encrypt, NULL, NULL); + } else +#endif /* HWAES_CAPABLE */ + +#ifdef VPAES_CAPABLE + if (VPAES_CAPABLE) { + AES_HW_CCM_SET_KEY_FN(vpaes_set_encrypt_key, vpaes_encrypt, NULL, NULL); + } else +#endif + { + AES_HW_CCM_SET_KEY_FN(AES_set_encrypt_key, AES_encrypt, NULL, NULL) + } + return 1; +} + +static const PROV_CCM_HW aes_ccm = { + ccm_generic_aes_initkey, + ossl_ccm_generic_setiv, + ossl_ccm_generic_setaad, + ossl_ccm_generic_auth_encrypt, + ossl_ccm_generic_auth_decrypt, + ossl_ccm_generic_gettag +}; + +#if defined(S390X_aes_128_CAPABLE) +# include "cipher_aes_ccm_hw_s390x.inc" +#elif defined(AESNI_CAPABLE) +# include "cipher_aes_ccm_hw_aesni.inc" +#elif defined(SPARC_AES_CAPABLE) +# include "cipher_aes_ccm_hw_t4.inc" +#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 +# include "cipher_aes_ccm_hw_rv64i.inc" +#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 +# include "cipher_aes_ccm_hw_rv32i.inc" +#else +const PROV_CCM_HW *ossl_prov_aes_hw_ccm(size_t keybits) +{ + return &aes_ccm; +} +#endif diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_aesni.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_aesni.inc new file mode 100644 index 000000000000..579e5a3d4f13 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_aesni.inc @@ -0,0 +1,38 @@ +/* + * Copyright 2001-2021 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 + */ + +/*- + * AES-NI support for AES CCM. + * This file is included by cipher_aes_ccm_hw.c + */ + +static int ccm_aesni_initkey(PROV_CCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + PROV_AES_CCM_CTX *actx = (PROV_AES_CCM_CTX *)ctx; + + AES_HW_CCM_SET_KEY_FN(aesni_set_encrypt_key, aesni_encrypt, + aesni_ccm64_encrypt_blocks, + aesni_ccm64_decrypt_blocks); + return 1; +} + +static const PROV_CCM_HW aesni_ccm = { + ccm_aesni_initkey, + ossl_ccm_generic_setiv, + ossl_ccm_generic_setaad, + ossl_ccm_generic_auth_encrypt, + ossl_ccm_generic_auth_decrypt, + ossl_ccm_generic_gettag +}; + +const PROV_CCM_HW *ossl_prov_aes_hw_ccm(size_t keybits) +{ + return AESNI_CAPABLE ? &aesni_ccm : &aes_ccm; +} diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_rv32i.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_rv32i.inc new file mode 100644 index 000000000000..7cfe0fc4ce8b --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_rv32i.inc @@ -0,0 +1,60 @@ +/* + * 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 + */ + +/*- + * RISC-V 32 ZKND ZKNE support for AES CCM. + * This file is included by cipher_aes_ccm_hw.c + */ + +static int ccm_rv32i_zknd_zkne_initkey(PROV_CCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + PROV_AES_CCM_CTX *actx = (PROV_AES_CCM_CTX *)ctx; + + AES_HW_CCM_SET_KEY_FN(rv32i_zkne_set_encrypt_key, rv32i_zkne_encrypt, + NULL, NULL); + return 1; +} + +static int ccm_rv32i_zbkb_zknd_zkne_initkey(PROV_CCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + PROV_AES_CCM_CTX *actx = (PROV_AES_CCM_CTX *)ctx; + + AES_HW_CCM_SET_KEY_FN(rv32i_zbkb_zkne_set_encrypt_key, rv32i_zkne_encrypt, + NULL, NULL); + return 1; +} + +static const PROV_CCM_HW rv32i_zknd_zkne_ccm = { + ccm_rv32i_zknd_zkne_initkey, + ossl_ccm_generic_setiv, + ossl_ccm_generic_setaad, + ossl_ccm_generic_auth_encrypt, + ossl_ccm_generic_auth_decrypt, + ossl_ccm_generic_gettag +}; + +static const PROV_CCM_HW rv32i_zbkb_zknd_zkne_ccm = { + ccm_rv32i_zbkb_zknd_zkne_initkey, + ossl_ccm_generic_setiv, + ossl_ccm_generic_setaad, + ossl_ccm_generic_auth_encrypt, + ossl_ccm_generic_auth_decrypt, + ossl_ccm_generic_gettag +}; + +const PROV_CCM_HW *ossl_prov_aes_hw_ccm(size_t keybits) +{ + if (RISCV_HAS_ZBKB_AND_ZKND_AND_ZKNE()) + return &rv32i_zbkb_zknd_zkne_ccm; + if (RISCV_HAS_ZKND_AND_ZKNE()) + return &rv32i_zknd_zkne_ccm; + return &aes_ccm; +} diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_rv64i.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_rv64i.inc new file mode 100644 index 000000000000..f2353bb3b8f8 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_rv64i.inc @@ -0,0 +1,71 @@ +/* + * 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 + */ + +/*- + * RISC-V 64 ZKND ZKNE support for AES CCM. + * This file is included by cipher_aes_ccm_hw.c + */ + +static int ccm_rv64i_zknd_zkne_initkey(PROV_CCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + PROV_AES_CCM_CTX *actx = (PROV_AES_CCM_CTX *)ctx; + + AES_HW_CCM_SET_KEY_FN(rv64i_zkne_set_encrypt_key, rv64i_zkne_encrypt, + NULL, NULL); + return 1; +} + +static const PROV_CCM_HW rv64i_zknd_zkne_ccm = { + ccm_rv64i_zknd_zkne_initkey, + ossl_ccm_generic_setiv, + ossl_ccm_generic_setaad, + ossl_ccm_generic_auth_encrypt, + ossl_ccm_generic_auth_decrypt, + ossl_ccm_generic_gettag +}; + +/*- + * RISC-V RV64 ZVKNED support for AES CCM. + * This file is included by cipher_aes_ccm_hw.c + */ + +static int ccm_rv64i_zvkned_initkey(PROV_CCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + PROV_AES_CCM_CTX *actx = (PROV_AES_CCM_CTX *)ctx; + + /* Zvkned only supports 128 and 256 bit keys for key schedule generation. */ + if (keylen * 8 == 128 || keylen * 8 == 256) { + AES_HW_CCM_SET_KEY_FN(rv64i_zvkned_set_encrypt_key, rv64i_zvkned_encrypt, + NULL, NULL); + } else { + AES_HW_CCM_SET_KEY_FN(AES_set_encrypt_key, rv64i_zvkned_encrypt, NULL, NULL) + } + return 1; +} + +static const PROV_CCM_HW rv64i_zvkned_ccm = { + ccm_rv64i_zvkned_initkey, + ossl_ccm_generic_setiv, + ossl_ccm_generic_setaad, + ossl_ccm_generic_auth_encrypt, + ossl_ccm_generic_auth_decrypt, + ossl_ccm_generic_gettag +}; + +const PROV_CCM_HW *ossl_prov_aes_hw_ccm(size_t keybits) +{ + if (RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) + return &rv64i_zvkned_ccm; + else if (RISCV_HAS_ZKND_AND_ZKNE()) + return &rv64i_zknd_zkne_ccm; + else + return &aes_ccm; +} diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_s390x.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_s390x.inc new file mode 100644 index 000000000000..7253f03a7ef1 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_s390x.inc @@ -0,0 +1,268 @@ +/* + * Copyright 2001-2020 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 + */ + +/*- + * S390X support for AES CCM. + * This file is included by cipher_aes_ccm_hw.c + */ + +#define S390X_CCM_AAD_FLAG 0x40 + +static int s390x_aes_ccm_initkey(PROV_CCM_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx; + + sctx->ccm.s390x.fc = S390X_AES_FC(keylen); + memcpy(&sctx->ccm.s390x.kmac.k, key, keylen); + /* Store encoded m and l. */ + sctx->ccm.s390x.nonce.b[0] = ((ctx->l - 1) & 0x7) + | (((ctx->m - 2) >> 1) & 0x7) << 3; + memset(sctx->ccm.s390x.nonce.b + 1, 0, sizeof(sctx->ccm.s390x.nonce.b)); + sctx->ccm.s390x.blocks = 0; + ctx->key_set = 1; + return 1; +} + +static int s390x_aes_ccm_setiv(PROV_CCM_CTX *ctx, + const unsigned char *nonce, size_t noncelen, + size_t mlen) +{ + PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx; + + sctx->ccm.s390x.nonce.b[0] &= ~S390X_CCM_AAD_FLAG; + sctx->ccm.s390x.nonce.g[1] = mlen; + memcpy(sctx->ccm.s390x.nonce.b + 1, nonce, 15 - ctx->l); + return 1; +} + +/*- + * Process additional authenticated data. Code is big-endian. + */ +static int s390x_aes_ccm_setaad(PROV_CCM_CTX *ctx, + const unsigned char *aad, size_t alen) +{ + PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx; + unsigned char *ptr; + int i, rem; + + if (!alen) + return 1; + + sctx->ccm.s390x.nonce.b[0] |= S390X_CCM_AAD_FLAG; + + /* Suppress 'type-punned pointer dereference' warning. */ + ptr = sctx->ccm.s390x.buf.b; + + if (alen < ((1 << 16) - (1 << 8))) { + *(uint16_t *)ptr = alen; + i = 2; + } else if (sizeof(alen) == 8 + && alen >= (size_t)1 << (32 % (sizeof(alen) * 8))) { + *(uint16_t *)ptr = 0xffff; + *(uint64_t *)(ptr + 2) = alen; + i = 10; + } else { + *(uint16_t *)ptr = 0xfffe; + *(uint32_t *)(ptr + 2) = alen; + i = 6; + } + + while (i < 16 && alen) { + sctx->ccm.s390x.buf.b[i] = *aad; + ++aad; + --alen; + ++i; + } + while (i < 16) { + sctx->ccm.s390x.buf.b[i] = 0; + ++i; + } + + sctx->ccm.s390x.kmac.icv.g[0] = 0; + sctx->ccm.s390x.kmac.icv.g[1] = 0; + s390x_kmac(sctx->ccm.s390x.nonce.b, 32, sctx->ccm.s390x.fc, + &sctx->ccm.s390x.kmac); + sctx->ccm.s390x.blocks += 2; + + rem = alen & 0xf; + alen &= ~(size_t)0xf; + if (alen) { + s390x_kmac(aad, alen, sctx->ccm.s390x.fc, &sctx->ccm.s390x.kmac); + sctx->ccm.s390x.blocks += alen >> 4; + aad += alen; + } + if (rem) { + for (i = 0; i < rem; i++) + sctx->ccm.s390x.kmac.icv.b[i] ^= aad[i]; + + s390x_km(sctx->ccm.s390x.kmac.icv.b, 16, + sctx->ccm.s390x.kmac.icv.b, sctx->ccm.s390x.fc, + sctx->ccm.s390x.kmac.k); + sctx->ccm.s390x.blocks++; + } + return 1; +} + +/*- + * En/de-crypt plain/cipher-text. Compute tag from plaintext. Returns 1 for + * success. + */ +static int s390x_aes_ccm_auth_encdec(PROV_CCM_CTX *ctx, + const unsigned char *in, + unsigned char *out, size_t len, int enc) +{ + PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx; + size_t n, rem; + unsigned int i, l, num; + unsigned char flags; + + flags = sctx->ccm.s390x.nonce.b[0]; + if (!(flags & S390X_CCM_AAD_FLAG)) { + s390x_km(sctx->ccm.s390x.nonce.b, 16, sctx->ccm.s390x.kmac.icv.b, + sctx->ccm.s390x.fc, sctx->ccm.s390x.kmac.k); + sctx->ccm.s390x.blocks++; + } + l = flags & 0x7; + sctx->ccm.s390x.nonce.b[0] = l; + + /*- + * Reconstruct length from encoded length field + * and initialize it with counter value. + */ + n = 0; + for (i = 15 - l; i < 15; i++) { + n |= sctx->ccm.s390x.nonce.b[i]; + sctx->ccm.s390x.nonce.b[i] = 0; + n <<= 8; + } + n |= sctx->ccm.s390x.nonce.b[15]; + sctx->ccm.s390x.nonce.b[15] = 1; + + if (n != len) + return 0; /* length mismatch */ + + if (enc) { + /* Two operations per block plus one for tag encryption */ + sctx->ccm.s390x.blocks += (((len + 15) >> 4) << 1) + 1; + if (sctx->ccm.s390x.blocks > (1ULL << 61)) + return 0; /* too much data */ + } + + num = 0; + rem = len & 0xf; + len &= ~(size_t)0xf; + + if (enc) { + /* mac-then-encrypt */ + if (len) + s390x_kmac(in, len, sctx->ccm.s390x.fc, &sctx->ccm.s390x.kmac); + if (rem) { + for (i = 0; i < rem; i++) + sctx->ccm.s390x.kmac.icv.b[i] ^= in[len + i]; + + s390x_km(sctx->ccm.s390x.kmac.icv.b, 16, + sctx->ccm.s390x.kmac.icv.b, + sctx->ccm.s390x.fc, sctx->ccm.s390x.kmac.k); + } + + CRYPTO_ctr128_encrypt_ctr32(in, out, len + rem, &sctx->ccm.ks.ks, + sctx->ccm.s390x.nonce.b, sctx->ccm.s390x.buf.b, + &num, (ctr128_f)AES_ctr32_encrypt); + } else { + /* decrypt-then-mac */ + CRYPTO_ctr128_encrypt_ctr32(in, out, len + rem, &sctx->ccm.ks.ks, + sctx->ccm.s390x.nonce.b, sctx->ccm.s390x.buf.b, + &num, (ctr128_f)AES_ctr32_encrypt); + + if (len) + s390x_kmac(out, len, sctx->ccm.s390x.fc, &sctx->ccm.s390x.kmac); + if (rem) { + for (i = 0; i < rem; i++) + sctx->ccm.s390x.kmac.icv.b[i] ^= out[len + i]; + + s390x_km(sctx->ccm.s390x.kmac.icv.b, 16, + sctx->ccm.s390x.kmac.icv.b, + sctx->ccm.s390x.fc, sctx->ccm.s390x.kmac.k); + } + } + /* encrypt tag */ + for (i = 15 - l; i < 16; i++) + sctx->ccm.s390x.nonce.b[i] = 0; + + s390x_km(sctx->ccm.s390x.nonce.b, 16, sctx->ccm.s390x.buf.b, + sctx->ccm.s390x.fc, sctx->ccm.s390x.kmac.k); + sctx->ccm.s390x.kmac.icv.g[0] ^= sctx->ccm.s390x.buf.g[0]; + sctx->ccm.s390x.kmac.icv.g[1] ^= sctx->ccm.s390x.buf.g[1]; + + sctx->ccm.s390x.nonce.b[0] = flags; /* restore flags field */ + return 1; +} + + +static int s390x_aes_ccm_gettag(PROV_CCM_CTX *ctx, + unsigned char *tag, size_t tlen) +{ + PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx; + + if (tlen > ctx->m) + return 0; + memcpy(tag, sctx->ccm.s390x.kmac.icv.b, tlen); + return 1; +} + +static int s390x_aes_ccm_auth_encrypt(PROV_CCM_CTX *ctx, + const unsigned char *in, + unsigned char *out, size_t len, + unsigned char *tag, size_t taglen) +{ + int rv; + + rv = s390x_aes_ccm_auth_encdec(ctx, in, out, len, 1); + if (rv && tag != NULL) + rv = s390x_aes_ccm_gettag(ctx, tag, taglen); + return rv; +} + +static int s390x_aes_ccm_auth_decrypt(PROV_CCM_CTX *ctx, + const unsigned char *in, + unsigned char *out, size_t len, + unsigned char *expected_tag, + size_t taglen) +{ + int rv = 0; + PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx; + + rv = s390x_aes_ccm_auth_encdec(ctx, in, out, len, 0); + if (rv) { + if (CRYPTO_memcmp(sctx->ccm.s390x.kmac.icv.b, expected_tag, ctx->m) != 0) + rv = 0; + } + if (rv == 0) + OPENSSL_cleanse(out, len); + return rv; +} + +static const PROV_CCM_HW s390x_aes_ccm = { + s390x_aes_ccm_initkey, + s390x_aes_ccm_setiv, + s390x_aes_ccm_setaad, + s390x_aes_ccm_auth_encrypt, + s390x_aes_ccm_auth_decrypt, + s390x_aes_ccm_gettag +}; + +const PROV_CCM_HW *ossl_prov_aes_hw_ccm(size_t keybits) +{ + if ((keybits == 128 && S390X_aes_128_ccm_CAPABLE) + || (keybits == 192 && S390X_aes_192_ccm_CAPABLE) + || (keybits == 256 && S390X_aes_256_ccm_CAPABLE)) + return &s390x_aes_ccm; + return &aes_ccm; +} diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_t4.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_t4.inc new file mode 100644 index 000000000000..a676d411b598 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_t4.inc @@ -0,0 +1,36 @@ +/* + * Copyright 2001-2021 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 + */ + +/*- + * Fujitsu SPARC64 X support for AES CCM. + * This file is included by cipher_aes_ccm_hw.c + */ + +static int ccm_t4_aes_initkey(PROV_CCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + PROV_AES_CCM_CTX *actx = (PROV_AES_CCM_CTX *)ctx; + + AES_HW_CCM_SET_KEY_FN(aes_t4_set_encrypt_key, aes_t4_encrypt, NULL, NULL); + return 1; +} + +static const PROV_CCM_HW t4_aes_ccm = { + ccm_t4_aes_initkey, + ossl_ccm_generic_setiv, + ossl_ccm_generic_setaad, + ossl_ccm_generic_auth_encrypt, + ossl_ccm_generic_auth_decrypt, + ossl_ccm_generic_gettag +}; + +const PROV_CCM_HW *ossl_prov_aes_hw_ccm(size_t keybits) +{ + return SPARC_AES_CAPABLE ? &t4_aes_ccm : &aes_ccm; +} diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_cts.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_cts.inc new file mode 100644 index 000000000000..1fb5ec3553f9 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_cts.inc @@ -0,0 +1,94 @@ +/* + * Copyright 2020-2021 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 + */ + +/* Dispatch functions for AES CBC CTS ciphers */ + +#include <openssl/proverr.h> +#include "cipher_cts.h" + +#define CTS_FLAGS PROV_CIPHER_FLAG_CTS + +static OSSL_FUNC_cipher_encrypt_init_fn aes_cbc_cts_einit; +static OSSL_FUNC_cipher_decrypt_init_fn aes_cbc_cts_dinit; +static OSSL_FUNC_cipher_get_ctx_params_fn aes_cbc_cts_get_ctx_params; +static OSSL_FUNC_cipher_set_ctx_params_fn aes_cbc_cts_set_ctx_params; +static OSSL_FUNC_cipher_gettable_ctx_params_fn aes_cbc_cts_gettable_ctx_params; +static OSSL_FUNC_cipher_settable_ctx_params_fn aes_cbc_cts_settable_ctx_params; + +CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_START(aes_cbc_cts) +OSSL_PARAM_utf8_string(OSSL_CIPHER_PARAM_CTS_MODE, NULL, 0), +CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_END(aes_cbc_cts) + +static int aes_cbc_cts_einit(void *ctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + if (!ossl_cipher_generic_einit(ctx, key, keylen, iv, ivlen, NULL)) + return 0; + return aes_cbc_cts_set_ctx_params(ctx, params); +} + +static int aes_cbc_cts_dinit(void *ctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + if (!ossl_cipher_generic_dinit(ctx, key, keylen, iv, ivlen, NULL)) + return 0; + return aes_cbc_cts_set_ctx_params(ctx, params); +} + +static int aes_cbc_cts_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + OSSL_PARAM *p; + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_CTS_MODE); + if (p != NULL) { + const char *name = ossl_cipher_cbc_cts_mode_id2name(ctx->cts_mode); + + if (name == NULL || !OSSL_PARAM_set_utf8_string(p, name)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + } + return ossl_cipher_generic_get_ctx_params(vctx, params); +} + +CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_START(aes_cbc_cts) +OSSL_PARAM_utf8_string(OSSL_CIPHER_PARAM_CTS_MODE, NULL, 0), +CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_END(aes_cbc_cts) + +static int aes_cbc_cts_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + const OSSL_PARAM *p; + int id; + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_CTS_MODE); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_UTF8_STRING) + goto err; + id = ossl_cipher_cbc_cts_mode_name2id(p->data); + if (id < 0) + goto err; + + ctx->cts_mode = (unsigned int)id; + } + return ossl_cipher_generic_set_ctx_params(vctx, params); +err: + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; +} + +/* ossl_aes256cbc_cts_functions */ +IMPLEMENT_cts_cipher(aes, AES, cbc, CBC, CTS_FLAGS, 256, 128, 128, block) +/* ossl_aes192cbc_cts_functions */ +IMPLEMENT_cts_cipher(aes, AES, cbc, CBC, CTS_FLAGS, 192, 128, 128, block) +/* ossl_aes128cbc_cts_functions */ +IMPLEMENT_cts_cipher(aes, AES, cbc, CBC, CTS_FLAGS, 128, 128, 128, block) diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm.c new file mode 100644 index 000000000000..c14c1e32fe35 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm.c @@ -0,0 +1,68 @@ +/* + * Copyright 2019-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 + */ + +/* + * AES low level APIs are deprecated for public use, but still ok for internal + * use where we're using them to implement the higher level EVP interface, as is + * the case here. + */ +#include "internal/deprecated.h" + +/* Dispatch functions for AES GCM mode */ + +#include "cipher_aes_gcm.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" + +static void *aes_gcm_newctx(void *provctx, size_t keybits) +{ + PROV_AES_GCM_CTX *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + ctx = OPENSSL_zalloc(sizeof(*ctx)); + if (ctx != NULL) + ossl_gcm_initctx(provctx, &ctx->base, keybits, + ossl_prov_aes_hw_gcm(keybits)); + return ctx; +} + +static void *aes_gcm_dupctx(void *provctx) +{ + PROV_AES_GCM_CTX *ctx = provctx; + PROV_AES_GCM_CTX *dctx = NULL; + + if (!ossl_prov_is_running()) + return NULL; + + if (ctx == NULL) + return NULL; + + dctx = OPENSSL_memdup(ctx, sizeof(*ctx)); + if (dctx != NULL && dctx->base.gcm.key != NULL) + dctx->base.gcm.key = &dctx->ks.ks; + + return dctx; +} + +static OSSL_FUNC_cipher_freectx_fn aes_gcm_freectx; +static void aes_gcm_freectx(void *vctx) +{ + PROV_AES_GCM_CTX *ctx = (PROV_AES_GCM_CTX *)vctx; + + OPENSSL_clear_free(ctx, sizeof(*ctx)); +} + +/* ossl_aes128gcm_functions */ +IMPLEMENT_aead_cipher(aes, gcm, GCM, AEAD_FLAGS, 128, 8, 96); +/* ossl_aes192gcm_functions */ +IMPLEMENT_aead_cipher(aes, gcm, GCM, AEAD_FLAGS, 192, 8, 96); +/* ossl_aes256gcm_functions */ +IMPLEMENT_aead_cipher(aes, gcm, GCM, AEAD_FLAGS, 256, 8, 96); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm.h b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm.h new file mode 100644 index 000000000000..5e88ccca7b4d --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm.h @@ -0,0 +1,45 @@ +/* + * Copyright 2019-2021 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 + */ + +#include <openssl/aes.h> +#include "prov/ciphercommon.h" +#include "prov/ciphercommon_gcm.h" +#include "crypto/aes_platform.h" + +typedef struct prov_aes_gcm_ctx_st { + PROV_GCM_CTX base; /* must be first entry in struct */ + union { + OSSL_UNION_ALIGN; + AES_KEY ks; + } ks; /* AES key schedule to use */ + + /* Platform specific data */ + union { + int dummy; +#if defined(OPENSSL_CPUID_OBJ) && defined(__s390__) + struct { + union { + OSSL_UNION_ALIGN; + S390X_KMA_PARAMS kma; + } param; + unsigned int fc; + unsigned int hsflag; /* hash subkey set flag */ + unsigned char ares[16]; + unsigned char mres[16]; + unsigned char kres[16]; + int areslen; + int mreslen; + int kreslen; + int res; + } s390x; +#endif /* defined(OPENSSL_CPUID_OBJ) && defined(__s390__) */ + } plat; +} PROV_AES_GCM_CTX; + +const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw.c new file mode 100644 index 000000000000..207a16bc70e7 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw.c @@ -0,0 +1,155 @@ +/* + * Copyright 2019-2024 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 + */ + +/* Dispatch functions for AES GCM mode */ + +/* + * This file uses the low level AES functions (which are deprecated for + * non-internal use) in order to implement provider AES ciphers. + */ +#include "internal/deprecated.h" + +#include "cipher_aes_gcm.h" + +static int aes_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; + AES_KEY *ks = &actx->ks.ks; + +# ifdef HWAES_CAPABLE + if (HWAES_CAPABLE) { +# ifdef HWAES_ctr32_encrypt_blocks + GCM_HW_SET_KEY_CTR_FN(ks, HWAES_set_encrypt_key, HWAES_encrypt, + HWAES_ctr32_encrypt_blocks); +# else + GCM_HW_SET_KEY_CTR_FN(ks, HWAES_set_encrypt_key, HWAES_encrypt, NULL); +# endif /* HWAES_ctr32_encrypt_blocks */ + } else +# endif /* HWAES_CAPABLE */ + +# ifdef BSAES_CAPABLE + if (BSAES_CAPABLE) { + GCM_HW_SET_KEY_CTR_FN(ks, AES_set_encrypt_key, AES_encrypt, + ossl_bsaes_ctr32_encrypt_blocks); + } else +# endif /* BSAES_CAPABLE */ + +# ifdef VPAES_CAPABLE + if (VPAES_CAPABLE) { + GCM_HW_SET_KEY_CTR_FN(ks, vpaes_set_encrypt_key, vpaes_encrypt, NULL); + } else +# endif /* VPAES_CAPABLE */ + + { +# ifdef AES_CTR_ASM + GCM_HW_SET_KEY_CTR_FN(ks, AES_set_encrypt_key, AES_encrypt, + AES_ctr32_encrypt); +# else + GCM_HW_SET_KEY_CTR_FN(ks, AES_set_encrypt_key, AES_encrypt, NULL); +# endif /* AES_CTR_ASM */ + } + return 1; +} + +static int generic_aes_gcm_cipher_update(PROV_GCM_CTX *ctx, const unsigned char *in, + size_t len, unsigned char *out) +{ + if (ctx->enc) { + if (ctx->ctr != NULL) { +#if defined(AES_GCM_ASM) + size_t bulk = 0; + + if (len >= AES_GCM_ENC_BYTES && AES_GCM_ASM(ctx)) { + size_t res = (16 - ctx->gcm.mres) % 16; + + if (CRYPTO_gcm128_encrypt(&ctx->gcm, in, out, res)) + return 0; + + bulk = AES_gcm_encrypt(in + res, out + res, len - res, + ctx->gcm.key, + ctx->gcm.Yi.c, ctx->gcm.Xi.u); + + ctx->gcm.len.u[1] += bulk; + bulk += res; + } + if (CRYPTO_gcm128_encrypt_ctr32(&ctx->gcm, in + bulk, out + bulk, + len - bulk, ctx->ctr)) + return 0; +#else + if (CRYPTO_gcm128_encrypt_ctr32(&ctx->gcm, in, out, len, ctx->ctr)) + return 0; +#endif /* AES_GCM_ASM */ + } else { + if (CRYPTO_gcm128_encrypt(&ctx->gcm, in, out, len)) + return 0; + } + } else { + if (ctx->ctr != NULL) { +#if defined(AES_GCM_ASM) + size_t bulk = 0; + + if (len >= AES_GCM_DEC_BYTES && AES_GCM_ASM(ctx)) { + size_t res = (16 - ctx->gcm.mres) % 16; + + if (CRYPTO_gcm128_decrypt(&ctx->gcm, in, out, res)) + return 0; + + bulk = AES_gcm_decrypt(in + res, out + res, len - res, + ctx->gcm.key, + ctx->gcm.Yi.c, ctx->gcm.Xi.u); + + ctx->gcm.len.u[1] += bulk; + bulk += res; + } + if (CRYPTO_gcm128_decrypt_ctr32(&ctx->gcm, in + bulk, out + bulk, + len - bulk, ctx->ctr)) + return 0; +#else + if (CRYPTO_gcm128_decrypt_ctr32(&ctx->gcm, in, out, len, ctx->ctr)) + return 0; +#endif /* AES_GCM_ASM */ + } else { + if (CRYPTO_gcm128_decrypt(&ctx->gcm, in, out, len)) + return 0; + } + } + return 1; +} + +static const PROV_GCM_HW aes_gcm = { + aes_gcm_initkey, + ossl_gcm_setiv, + ossl_gcm_aad_update, + generic_aes_gcm_cipher_update, + ossl_gcm_cipher_final, + ossl_gcm_one_shot +}; + +#if defined(S390X_aes_128_CAPABLE) +# include "cipher_aes_gcm_hw_s390x.inc" +#elif defined(AESNI_CAPABLE) +# include "cipher_aes_gcm_hw_aesni.inc" +#elif defined(SPARC_AES_CAPABLE) +# include "cipher_aes_gcm_hw_t4.inc" +#elif defined(AES_PMULL_CAPABLE) && defined(AES_GCM_ASM) +# include "cipher_aes_gcm_hw_armv8.inc" +#elif defined(PPC_AES_GCM_CAPABLE) && defined(_ARCH_PPC64) +# include "cipher_aes_gcm_hw_ppc.inc" +#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 +# include "cipher_aes_gcm_hw_rv64i.inc" +#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 +# include "cipher_aes_gcm_hw_rv32i.inc" +#else +const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits) +{ + return &aes_gcm; +} +#endif + diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_aesni.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_aesni.inc new file mode 100644 index 000000000000..92f41b8cd6e1 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_aesni.inc @@ -0,0 +1,47 @@ +/* + * Copyright 2001-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 + */ + +/*- + * AES-NI support for AES GCM. + * This file is included by cipher_aes_gcm_hw.c + */ + +static int aesni_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; + AES_KEY *ks = &actx->ks.ks; + GCM_HW_SET_KEY_CTR_FN(ks, aesni_set_encrypt_key, aesni_encrypt, + aesni_ctr32_encrypt_blocks); + return 1; +} + +static const PROV_GCM_HW aesni_gcm = { + aesni_gcm_initkey, + ossl_gcm_setiv, + ossl_gcm_aad_update, + generic_aes_gcm_cipher_update, + ossl_gcm_cipher_final, + ossl_gcm_one_shot +}; + +#include "cipher_aes_gcm_hw_vaes_avx512.inc" + +const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits) +{ +#ifdef VAES_GCM_ENABLED + if (ossl_vaes_vpclmulqdq_capable()) + return &vaes_gcm; + else +#endif + if (AESNI_CAPABLE) + return &aesni_gcm; + else + return &aes_gcm; +} diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_armv8.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_armv8.inc new file mode 100644 index 000000000000..60fff493d809 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_armv8.inc @@ -0,0 +1,108 @@ +/* + * Copyright 2019-2025 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 + */ + +/* + * Crypto extension support for AES GCM. + * This file is included by cipher_aes_gcm_hw.c + */ + +size_t armv8_aes_gcm_encrypt(const unsigned char *in, unsigned char *out, size_t len, + const void *key, unsigned char ivec[16], u64 *Xi) +{ + AES_KEY *aes_key = (AES_KEY *)key; + size_t align_bytes = len - len % 16; + + switch(aes_key->rounds) { + case 10: + if (IS_CPU_SUPPORT_UNROLL8_EOR3()) { + unroll8_eor3_aes_gcm_enc_128_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } else { + aes_gcm_enc_128_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } + break; + case 12: + if (IS_CPU_SUPPORT_UNROLL8_EOR3()) { + unroll8_eor3_aes_gcm_enc_192_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } else { + aes_gcm_enc_192_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } + break; + case 14: + if (IS_CPU_SUPPORT_UNROLL8_EOR3()) { + unroll8_eor3_aes_gcm_enc_256_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } else { + aes_gcm_enc_256_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } + break; + } + return align_bytes; +} + +size_t armv8_aes_gcm_decrypt(const unsigned char *in, unsigned char *out, size_t len, + const void *key, unsigned char ivec[16], u64 *Xi) +{ + AES_KEY *aes_key = (AES_KEY *)key; + size_t align_bytes = len - len % 16; + + switch(aes_key->rounds) { + case 10: + if (IS_CPU_SUPPORT_UNROLL8_EOR3()) { + unroll8_eor3_aes_gcm_dec_128_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } else { + aes_gcm_dec_128_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } + break; + case 12: + if (IS_CPU_SUPPORT_UNROLL8_EOR3()) { + unroll8_eor3_aes_gcm_dec_192_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } else { + aes_gcm_dec_192_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } + break; + case 14: + if (IS_CPU_SUPPORT_UNROLL8_EOR3()) { + unroll8_eor3_aes_gcm_dec_256_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } else { + aes_gcm_dec_256_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key); + } + break; + } + return align_bytes; +} + +static int armv8_aes_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; + AES_KEY *ks = &actx->ks.ks; + + if (AES_UNROLL12_EOR3_CAPABLE) { + GCM_HW_SET_KEY_CTR_FN(ks, aes_v8_set_encrypt_key, aes_v8_encrypt, + aes_v8_ctr32_encrypt_blocks_unroll12_eor3); + } else { + GCM_HW_SET_KEY_CTR_FN(ks, aes_v8_set_encrypt_key, aes_v8_encrypt, + aes_v8_ctr32_encrypt_blocks); + } + return 1; +} + + +static const PROV_GCM_HW armv8_aes_gcm = { + armv8_aes_gcm_initkey, + ossl_gcm_setiv, + ossl_gcm_aad_update, + generic_aes_gcm_cipher_update, + ossl_gcm_cipher_final, + ossl_gcm_one_shot +}; + +const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits) +{ + return AES_PMULL_CAPABLE ? &armv8_aes_gcm : &aes_gcm; +} diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_ppc.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_ppc.inc new file mode 100644 index 000000000000..153eb7989171 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_ppc.inc @@ -0,0 +1,155 @@ +/* + * Copyright 2001-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 + */ + +/*- + * PPC support for AES GCM. + * This file is included by cipher_aes_gcm_hw.c + */ + +static int aes_ppc_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; + AES_KEY *ks = &actx->ks.ks; + + GCM_HW_SET_KEY_CTR_FN(ks, aes_p8_set_encrypt_key, aes_p8_encrypt, + aes_p8_ctr32_encrypt_blocks); + return 1; +} + +static inline u32 UTO32(unsigned char *buf) +{ + return ((u32) buf[0] << 24) | ((u32) buf[1] << 16) | ((u32) buf[2] << 8) | ((u32) buf[3]); +} + +static inline u32 add32TOU(unsigned char buf[4], u32 n) +{ + u32 r; + + r = UTO32(buf); + r += n; + buf[0] = (unsigned char) (r >> 24) & 0xFF; + buf[1] = (unsigned char) (r >> 16) & 0xFF; + buf[2] = (unsigned char) (r >> 8) & 0xFF; + buf[3] = (unsigned char) r & 0xFF; + return r; +} + +static size_t ppc_aes_gcm_crypt(const unsigned char *in, unsigned char *out, size_t len, + const void *key, unsigned char ivec[16], u64 *Xi, int encrypt) +{ + int s = 0; + int ndone = 0; + int ctr_reset = 0; + u64 blocks_unused; + u64 nb = len / 16; + u64 next_ctr = 0; + unsigned char ctr_saved[12]; + + memcpy(ctr_saved, ivec, 12); + + while (nb) { + blocks_unused = (u64) 0xffffffffU + 1 - (u64) UTO32 (ivec + 12); + if (nb > blocks_unused) { + len = blocks_unused * 16; + nb -= blocks_unused; + next_ctr = blocks_unused; + ctr_reset = 1; + } else { + len = nb * 16; + next_ctr = nb; + nb = 0; + } + + s = encrypt ? ppc_aes_gcm_encrypt(in, out, len, key, ivec, Xi) + : ppc_aes_gcm_decrypt(in, out, len, key, ivec, Xi); + + /* add counter to ivec */ + add32TOU(ivec + 12, (u32) next_ctr); + if (ctr_reset) { + ctr_reset = 0; + in += len; + out += len; + } + memcpy(ivec, ctr_saved, 12); + ndone += s; + } + + return ndone; +} + +static int ppc_aes_gcm_cipher_update(PROV_GCM_CTX *ctx, const unsigned char *in, + size_t len, unsigned char *out) +{ + if (ctx->enc) { + if (ctx->ctr != NULL) { + size_t bulk = 0; + + if (len >= AES_GCM_ENC_BYTES && AES_GCM_ASM_PPC(ctx)) { + size_t res = (16 - ctx->gcm.mres) % 16; + + if (CRYPTO_gcm128_encrypt(&ctx->gcm, in, out, res)) + return 0; + + bulk = ppc_aes_gcm_crypt(in + res, out + res, len - res, + ctx->gcm.key, + ctx->gcm.Yi.c, ctx->gcm.Xi.u, 1); + + ctx->gcm.len.u[1] += bulk; + bulk += res; + } + if (CRYPTO_gcm128_encrypt_ctr32(&ctx->gcm, in + bulk, out + bulk, + len - bulk, ctx->ctr)) + return 0; + } else { + if (CRYPTO_gcm128_encrypt(&ctx->gcm, in, out, len)) + return 0; + } + } else { + if (ctx->ctr != NULL) { + size_t bulk = 0; + + if (len >= AES_GCM_DEC_BYTES && AES_GCM_ASM_PPC(ctx)) { + size_t res = (16 - ctx->gcm.mres) % 16; + + if (CRYPTO_gcm128_decrypt(&ctx->gcm, in, out, res)) + return -1; + + bulk = ppc_aes_gcm_crypt(in + res, out + res, len - res, + ctx->gcm.key, + ctx->gcm.Yi.c, ctx->gcm.Xi.u, 0); + + ctx->gcm.len.u[1] += bulk; + bulk += res; + } + if (CRYPTO_gcm128_decrypt_ctr32(&ctx->gcm, in + bulk, out + bulk, + len - bulk, ctx->ctr)) + return 0; + } else { + if (CRYPTO_gcm128_decrypt(&ctx->gcm, in, out, len)) + return 0; + } + } + return 1; +} + +static const PROV_GCM_HW aes_ppc_gcm = { + aes_ppc_gcm_initkey, + ossl_gcm_setiv, + ossl_gcm_aad_update, + ppc_aes_gcm_cipher_update, + ossl_gcm_cipher_final, + ossl_gcm_one_shot +}; + +const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits) +{ + return PPC_AES_GCM_CAPABLE ? &aes_ppc_gcm : &aes_gcm; +} + diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_rv32i.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_rv32i.inc new file mode 100644 index 000000000000..bf3f98df1631 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_rv32i.inc @@ -0,0 +1,63 @@ +/* + * 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 + */ + +/*- + * RISC-V 32 ZKND ZKNE support for AES GCM. + * This file is included by cipher_aes_gcm_hw.c + */ + +static int rv32i_zknd_zkne_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; + AES_KEY *ks = &actx->ks.ks; + + GCM_HW_SET_KEY_CTR_FN(ks, rv32i_zkne_set_encrypt_key, rv32i_zkne_encrypt, + NULL); + return 1; +} + +static int rv32i_zbkb_zknd_zkne_gcm_initkey(PROV_GCM_CTX *ctx, + const unsigned char *key, + size_t keylen) +{ + PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; + AES_KEY *ks = &actx->ks.ks; + + GCM_HW_SET_KEY_CTR_FN(ks, rv32i_zbkb_zkne_set_encrypt_key, rv32i_zkne_encrypt, + NULL); + return 1; +} + +static const PROV_GCM_HW rv32i_zknd_zkne_gcm = { + rv32i_zknd_zkne_gcm_initkey, + ossl_gcm_setiv, + ossl_gcm_aad_update, + generic_aes_gcm_cipher_update, + ossl_gcm_cipher_final, + ossl_gcm_one_shot +}; + +static const PROV_GCM_HW rv32i_zbkb_zknd_zkne_gcm = { + rv32i_zbkb_zknd_zkne_gcm_initkey, + ossl_gcm_setiv, + ossl_gcm_aad_update, + generic_aes_gcm_cipher_update, + ossl_gcm_cipher_final, + ossl_gcm_one_shot +}; + +const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits) +{ + if (RISCV_HAS_ZBKB_AND_ZKND_AND_ZKNE()) + return &rv32i_zbkb_zknd_zkne_gcm; + if (RISCV_HAS_ZKND_AND_ZKNE()) + return &rv32i_zknd_zkne_gcm; + return &aes_gcm; +} diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_rv64i.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_rv64i.inc new file mode 100644 index 000000000000..105ca58fd324 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_rv64i.inc @@ -0,0 +1,118 @@ +/* + * 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 + */ + +/*- + * RISC-V 64 support for AES GCM. + * This file is included by cipher_aes_gcm_hw.c + */ + +/*- + * RISC-V 64 ZKND and ZKNE support for AES GCM. + */ +static int rv64i_zknd_zkne_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; + AES_KEY *ks = &actx->ks.ks; + GCM_HW_SET_KEY_CTR_FN(ks, rv64i_zkne_set_encrypt_key, rv64i_zkne_encrypt, + NULL); + return 1; +} + +static const PROV_GCM_HW rv64i_zknd_zkne_gcm = { + rv64i_zknd_zkne_gcm_initkey, + ossl_gcm_setiv, + ossl_gcm_aad_update, + generic_aes_gcm_cipher_update, + ossl_gcm_cipher_final, + ossl_gcm_one_shot +}; + +/*- + * RISC-V RV64 ZVKNED support for AES GCM. + */ +static int rv64i_zvkned_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; + AES_KEY *ks = &actx->ks.ks; + + /* + * Zvkned only supports 128 and 256 bit keys for key schedule generation. + * For AES-192 case, we could fallback to `AES_set_encrypt_key`. + */ + if (keylen * 8 == 128 || keylen * 8 == 256) { + GCM_HW_SET_KEY_CTR_FN(ks, rv64i_zvkned_set_encrypt_key, + rv64i_zvkned_encrypt, NULL); + } else { + GCM_HW_SET_KEY_CTR_FN(ks, AES_set_encrypt_key, + rv64i_zvkned_encrypt, NULL); + } + + return 1; +} + +static const PROV_GCM_HW rv64i_zvkned_gcm = { + rv64i_zvkned_gcm_initkey, + ossl_gcm_setiv, + ossl_gcm_aad_update, + generic_aes_gcm_cipher_update, + ossl_gcm_cipher_final, + ossl_gcm_one_shot +}; + +/*- + * RISC-V RV64 ZVKB, ZVKG and ZVKNED support for AES GCM. + */ +static int rv64i_zvkb_zvkg_zvkned_gcm_initkey(PROV_GCM_CTX *ctx, + const unsigned char *key, + size_t keylen) { + PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; + AES_KEY *ks = &actx->ks.ks; + + /* + * Zvkned only supports 128 and 256 bit keys for key schedule generation. + * For AES-192 case, we could fallback to `AES_set_encrypt_key`. + */ + if (keylen * 8 == 128 || keylen * 8 == 256) { + GCM_HW_SET_KEY_CTR_FN(ks, rv64i_zvkned_set_encrypt_key, + rv64i_zvkned_encrypt, + rv64i_zvkb_zvkned_ctr32_encrypt_blocks); + } else { + GCM_HW_SET_KEY_CTR_FN(ks, AES_set_encrypt_key, + rv64i_zvkned_encrypt, + rv64i_zvkb_zvkned_ctr32_encrypt_blocks); + } + + return 1; +} + +static const PROV_GCM_HW rv64i_zvkb_zvkg_zvkned_gcm = { + rv64i_zvkb_zvkg_zvkned_gcm_initkey, + ossl_gcm_setiv, + ossl_gcm_aad_update, + generic_aes_gcm_cipher_update, + ossl_gcm_cipher_final, + ossl_gcm_one_shot +}; + +const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits) { + if (RISCV_HAS_ZVKNED()) { + if (RISCV_HAS_ZVKB() && RISCV_HAS_ZVKG() && riscv_vlen() >= 128) { + return &rv64i_zvkb_zvkg_zvkned_gcm; + } + return &rv64i_zvkned_gcm; + } + + if (RISCV_HAS_ZKND_AND_ZKNE()) { + return &rv64i_zknd_zkne_gcm; + } + + return &aes_gcm; +} diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_s390x.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_s390x.inc new file mode 100644 index 000000000000..a36c48e3ec49 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_s390x.inc @@ -0,0 +1,312 @@ +/* + * Copyright 2001-2021 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 + */ + +/*- + * IBM S390X support for AES GCM. + * This file is included by cipher_aes_gcm_hw.c + */ + +/* iv + padding length for iv lengths != 12 */ +#define S390X_gcm_ivpadlen(i) ((((i) + 15) >> 4 << 4) + 16) + +/* Additional flag or'ed to fc for decryption */ +#define S390X_gcm_decrypt_flag(ctx) (((ctx)->enc) ? 0 : S390X_DECRYPT) + +#define S390X_gcm_fc(A,C) ((A)->plat.s390x.fc | (A)->plat.s390x.hsflag |\ + S390X_gcm_decrypt_flag((C))) + +static int s390x_aes_gcm_initkey(PROV_GCM_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; + + ctx->key_set = 1; + memcpy(&actx->plat.s390x.param.kma.k, key, keylen); + actx->plat.s390x.fc = S390X_AES_FC(keylen); + return 1; +} + +static int s390x_aes_gcm_setiv(PROV_GCM_CTX *ctx, const unsigned char *iv, + size_t ivlen) +{ + PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; + S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma; + + kma->t.g[0] = 0; + kma->t.g[1] = 0; + kma->tpcl = 0; + kma->taadl = 0; + actx->plat.s390x.mreslen = 0; + actx->plat.s390x.areslen = 0; + actx->plat.s390x.kreslen = 0; + + if (ivlen == GCM_IV_DEFAULT_SIZE) { + memcpy(&kma->j0, iv, ivlen); + kma->j0.w[3] = 1; + kma->cv.w = 1; + actx->plat.s390x.hsflag = 0; + } else { + unsigned long long ivbits = ivlen << 3; + size_t len = S390X_gcm_ivpadlen(ivlen); + unsigned char iv_zero_pad[S390X_gcm_ivpadlen(GCM_IV_MAX_SIZE)]; + /* + * The IV length needs to be zero padded to be a multiple of 16 bytes + * followed by 8 bytes of zeros and 8 bytes for the IV length. + * The GHASH of this value can then be calculated. + */ + memcpy(iv_zero_pad, iv, ivlen); + memset(iv_zero_pad + ivlen, 0, len - ivlen); + memcpy(iv_zero_pad + len - sizeof(ivbits), &ivbits, sizeof(ivbits)); + /* + * Calculate the ghash of the iv - the result is stored into the tag + * param. + */ + s390x_kma(iv_zero_pad, len, NULL, 0, NULL, actx->plat.s390x.fc, kma); + actx->plat.s390x.hsflag = S390X_KMA_HS; /* The hash subkey is set */ + + /* Copy the 128 bit GHASH result into J0 and clear the tag */ + kma->j0.g[0] = kma->t.g[0]; + kma->j0.g[1] = kma->t.g[1]; + kma->t.g[0] = 0; + kma->t.g[1] = 0; + /* Set the 32 bit counter */ + kma->cv.w = kma->j0.w[3]; + } + return 1; +} + +static int s390x_aes_gcm_cipher_final(PROV_GCM_CTX *ctx, unsigned char *tag) +{ + PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; + S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma; + unsigned char out[AES_BLOCK_SIZE]; + unsigned int fc; + int rc; + + kma->taadl <<= 3; + kma->tpcl <<= 3; + fc = S390X_gcm_fc(actx, ctx) | S390X_KMA_LAAD | S390X_KMA_LPC; + s390x_kma(actx->plat.s390x.ares, actx->plat.s390x.areslen, + actx->plat.s390x.mres, actx->plat.s390x.mreslen, out, + fc, kma); + + /* gctx->mres already returned to the caller */ + OPENSSL_cleanse(out, actx->plat.s390x.mreslen); + + if (ctx->enc) { + ctx->taglen = GCM_TAG_MAX_SIZE; + memcpy(tag, kma->t.b, ctx->taglen); + rc = 1; + } else { + rc = (CRYPTO_memcmp(tag, kma->t.b, ctx->taglen) == 0); + } + return rc; +} + +static int s390x_aes_gcm_one_shot(PROV_GCM_CTX *ctx, + unsigned char *aad, size_t aad_len, + const unsigned char *in, size_t in_len, + unsigned char *out, + unsigned char *tag, size_t taglen) +{ + PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; + S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma; + unsigned int fc; + int rc; + + kma->taadl = aad_len << 3; + kma->tpcl = in_len << 3; + fc = S390X_gcm_fc(actx, ctx) | S390X_KMA_LAAD | S390X_KMA_LPC; + s390x_kma(aad, aad_len, in, in_len, out, fc, kma); + + if (ctx->enc) { + memcpy(tag, kma->t.b, taglen); + rc = 1; + } else { + rc = (CRYPTO_memcmp(tag, kma->t.b, taglen) == 0); + } + return rc; +} + +/* + * Process additional authenticated data. Returns 1 on success. Code is + * big-endian. + */ +static int s390x_aes_gcm_aad_update(PROV_GCM_CTX *ctx, + const unsigned char *aad, size_t len) +{ + PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; + S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma; + unsigned long long alen; + unsigned int fc; + int n, rem; + + /* If already processed pt/ct then error */ + if (kma->tpcl != 0) + return 0; + + /* update the total aad length */ + alen = kma->taadl + len; + if (alen > (U64(1) << 61) || (sizeof(len) == 8 && alen < len)) + return 0; + kma->taadl = alen; + + /* check if there is any existing aad data from a previous add */ + n = actx->plat.s390x.areslen; + if (n) { + /* add additional data to a buffer until it has 16 bytes */ + while (n && len) { + actx->plat.s390x.ares[n] = *aad; + ++aad; + --len; + n = (n + 1) & 0xf; + } + /* ctx->ares contains a complete block if offset has wrapped around */ + if (!n) { + fc = S390X_gcm_fc(actx, ctx); + s390x_kma(actx->plat.s390x.ares, 16, NULL, 0, NULL, fc, kma); + actx->plat.s390x.hsflag = S390X_KMA_HS; + } + actx->plat.s390x.areslen = n; + } + + /* If there are leftover bytes (< 128 bits) save them for next time */ + rem = len & 0xf; + /* Add any remaining 16 byte blocks (128 bit each) */ + len &= ~(size_t)0xf; + if (len) { + fc = S390X_gcm_fc(actx, ctx); + s390x_kma(aad, len, NULL, 0, NULL, fc, kma); + actx->plat.s390x.hsflag = S390X_KMA_HS; + aad += len; + } + + if (rem) { + actx->plat.s390x.areslen = rem; + + do { + --rem; + actx->plat.s390x.ares[rem] = aad[rem]; + } while (rem); + } + return 1; +} + +/*- + * En/de-crypt plain/cipher-text and authenticate ciphertext. Returns 1 for + * success. Code is big-endian. + */ +static int s390x_aes_gcm_cipher_update(PROV_GCM_CTX *ctx, + const unsigned char *in, size_t len, + unsigned char *out) +{ + PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; + S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma; + const unsigned char *inptr; + unsigned long long mlen; + unsigned int fc; + union { + unsigned int w[4]; + unsigned char b[16]; + } buf; + size_t inlen; + int n, rem, i; + + mlen = kma->tpcl + len; + if (mlen > ((U64(1) << 36) - 32) || (sizeof(len) == 8 && mlen < len)) + return 0; + kma->tpcl = mlen; + + fc = S390X_gcm_fc(actx, ctx) | S390X_KMA_LAAD; + n = actx->plat.s390x.mreslen; + if (n) { + inptr = in; + inlen = len; + while (n && inlen) { + actx->plat.s390x.mres[n] = *inptr; + n = (n + 1) & 0xf; + ++inptr; + --inlen; + } + /* ctx->mres contains a complete block if offset has wrapped around */ + if (!n) { + s390x_kma(actx->plat.s390x.ares, actx->plat.s390x.areslen, + actx->plat.s390x.mres, 16, buf.b, fc, kma); + actx->plat.s390x.hsflag = S390X_KMA_HS; + fc |= S390X_KMA_HS; + actx->plat.s390x.areslen = 0; + + /* previous call already encrypted/decrypted its remainder, + * see comment below */ + n = actx->plat.s390x.mreslen; + while (n) { + *out = buf.b[n]; + n = (n + 1) & 0xf; + ++out; + ++in; + --len; + } + actx->plat.s390x.mreslen = 0; + } + } + + rem = len & 0xf; + + len &= ~(size_t)0xf; + if (len) { + s390x_kma(actx->plat.s390x.ares, actx->plat.s390x.areslen, in, len, out, + fc, kma); + in += len; + out += len; + actx->plat.s390x.hsflag = S390X_KMA_HS; + actx->plat.s390x.areslen = 0; + } + + /*- + * If there is a remainder, it has to be saved such that it can be + * processed by kma later. However, we also have to do the for-now + * unauthenticated encryption/decryption part here and now... + */ + if (rem) { + if (!actx->plat.s390x.mreslen) { + buf.w[0] = kma->j0.w[0]; + buf.w[1] = kma->j0.w[1]; + buf.w[2] = kma->j0.w[2]; + buf.w[3] = kma->cv.w + 1; + s390x_km(buf.b, 16, actx->plat.s390x.kres, + fc & 0x1f, &kma->k); + } + + n = actx->plat.s390x.mreslen; + for (i = 0; i < rem; i++) { + actx->plat.s390x.mres[n + i] = in[i]; + out[i] = in[i] ^ actx->plat.s390x.kres[n + i]; + } + actx->plat.s390x.mreslen += rem; + } + return 1; +} + +static const PROV_GCM_HW s390x_aes_gcm = { + s390x_aes_gcm_initkey, + s390x_aes_gcm_setiv, + s390x_aes_gcm_aad_update, + s390x_aes_gcm_cipher_update, + s390x_aes_gcm_cipher_final, + s390x_aes_gcm_one_shot +}; + +const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits) +{ + if ((keybits == 128 && S390X_aes_128_gcm_CAPABLE) + || (keybits == 192 && S390X_aes_192_gcm_CAPABLE) + || (keybits == 256 && S390X_aes_256_gcm_CAPABLE)) + return &s390x_aes_gcm; + return &aes_gcm; +} diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_t4.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_t4.inc new file mode 100644 index 000000000000..2b3a6d1d5ea2 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_t4.inc @@ -0,0 +1,52 @@ +/* + * Copyright 2001-2021 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 + */ + +/*- + * Fujitsu SPARC64 X support for AES GCM. + * This file is included by cipher_aes_gcm_hw.c + */ + +static int t4_aes_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + ctr128_f ctr; + PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; + AES_KEY *ks = &actx->ks.ks; + + + switch (keylen) { + case 16: + ctr = (ctr128_f)aes128_t4_ctr32_encrypt; + break; + case 24: + ctr = (ctr128_f)aes192_t4_ctr32_encrypt; + break; + case 32: + ctr = (ctr128_f)aes256_t4_ctr32_encrypt; + break; + default: + return 0; + } + + GCM_HW_SET_KEY_CTR_FN(ks, aes_t4_set_encrypt_key, aes_t4_encrypt, ctr); + return 1; +} + +static const PROV_GCM_HW t4_aes_gcm = { + t4_aes_gcm_initkey, + ossl_gcm_setiv, + ossl_gcm_aad_update, + generic_aes_gcm_cipher_update, + ossl_gcm_cipher_final, + ossl_gcm_one_shot +}; +const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits) +{ + return SPARC_AES_CAPABLE ? &t4_aes_gcm : &aes_gcm; +} diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_vaes_avx512.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_vaes_avx512.inc new file mode 100644 index 000000000000..c892c0754e8d --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_vaes_avx512.inc @@ -0,0 +1,204 @@ +/* + * Copyright 2021-2022 The OpenSSL Project Authors. All Rights Reserved. + * Copyright (c) 2021, Intel Corporation. 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 + */ + +/*- + * AVX512 VAES + VPCLMULDQD support for AES GCM. + * This file is included by cipher_aes_gcm_hw_aesni.inc + */ + +#undef VAES_GCM_ENABLED +#if (defined(__x86_64) || defined(__x86_64__) || \ + defined(_M_AMD64) || defined(_M_X64)) +# define VAES_GCM_ENABLED + +/* Returns non-zero when AVX512F + VAES + VPCLMULDQD combination is available */ +int ossl_vaes_vpclmulqdq_capable(void); + +# define OSSL_AES_GCM_UPDATE(direction) \ + void ossl_aes_gcm_ ## direction ## _avx512(const void *ks, \ + void *gcm128ctx, \ + unsigned int *pblocklen, \ + const unsigned char *in, \ + size_t len, \ + unsigned char *out); + +OSSL_AES_GCM_UPDATE(encrypt) +OSSL_AES_GCM_UPDATE(decrypt) + +void ossl_aes_gcm_init_avx512(const void *ks, void *gcm128ctx); +void ossl_aes_gcm_setiv_avx512(const void *ks, void *gcm128ctx, + const unsigned char *iv, size_t ivlen); +void ossl_aes_gcm_update_aad_avx512(void *gcm128ctx, const unsigned char *aad, + size_t aadlen); +void ossl_aes_gcm_finalize_avx512(void *gcm128ctx, unsigned int pblocklen); + +void ossl_gcm_gmult_avx512(u64 Xi[2], const void *gcm128ctx); + +static int vaes_gcm_setkey(PROV_GCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + GCM128_CONTEXT *gcmctx = &ctx->gcm; + PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx; + AES_KEY *ks = &actx->ks.ks; + + aesni_set_encrypt_key(key, keylen * 8, ks); + memset(gcmctx, 0, sizeof(*gcmctx)); + gcmctx->key = ks; + ctx->key_set = 1; + + ossl_aes_gcm_init_avx512(ks, gcmctx); + + return 1; +} + +static int vaes_gcm_setiv(PROV_GCM_CTX *ctx, const unsigned char *iv, + size_t ivlen) +{ + GCM128_CONTEXT *gcmctx = &ctx->gcm; + + gcmctx->Yi.u[0] = 0; /* Current counter */ + gcmctx->Yi.u[1] = 0; + gcmctx->Xi.u[0] = 0; /* AAD hash */ + gcmctx->Xi.u[1] = 0; + gcmctx->len.u[0] = 0; /* AAD length */ + gcmctx->len.u[1] = 0; /* Message length */ + gcmctx->ares = 0; + gcmctx->mres = 0; + + /* IV is limited by 2^64 bits, thus 2^61 bytes */ + if (ivlen > (U64(1) << 61)) + return 0; + + ossl_aes_gcm_setiv_avx512(gcmctx->key, gcmctx, iv, ivlen); + + return 1; +} + +static int vaes_gcm_aadupdate(PROV_GCM_CTX *ctx, + const unsigned char *aad, + size_t aad_len) +{ + GCM128_CONTEXT *gcmctx = &ctx->gcm; + u64 alen = gcmctx->len.u[0]; + unsigned int ares; + size_t i, lenBlks; + + /* Bad sequence: call of AAD update after message processing */ + if (gcmctx->len.u[1] > 0) + return 0; + + alen += aad_len; + /* AAD is limited by 2^64 bits, thus 2^61 bytes */ + if ((alen > (U64(1) << 61)) || (alen < aad_len)) + return 0; + + gcmctx->len.u[0] = alen; + + ares = gcmctx->ares; + /* Partial AAD block left from previous AAD update calls */ + if (ares > 0) { + /* + * Fill partial block buffer till full block + * (note, the hash is stored reflected) + */ + while (ares > 0 && aad_len > 0) { + gcmctx->Xi.c[15 - ares] ^= *(aad++); + --aad_len; + ares = (ares + 1) % AES_BLOCK_SIZE; + } + /* Full block gathered */ + if (ares == 0) { + ossl_gcm_gmult_avx512(gcmctx->Xi.u, gcmctx); + } else { /* no more AAD */ + gcmctx->ares = ares; + return 1; + } + } + + /* Bulk AAD processing */ + lenBlks = aad_len & ((size_t)(-AES_BLOCK_SIZE)); + if (lenBlks > 0) { + ossl_aes_gcm_update_aad_avx512(gcmctx, aad, lenBlks); + aad += lenBlks; + aad_len -= lenBlks; + } + + /* Add remaining AAD to the hash (note, the hash is stored reflected) */ + if (aad_len > 0) { + ares = aad_len; + for (i = 0; i < aad_len; i++) + gcmctx->Xi.c[15 - i] ^= aad[i]; + } + + gcmctx->ares = ares; + + return 1; +} + +static int vaes_gcm_cipherupdate(PROV_GCM_CTX *ctx, const unsigned char *in, + size_t len, unsigned char *out) +{ + GCM128_CONTEXT *gcmctx = &ctx->gcm; + u64 mlen = gcmctx->len.u[1]; + + mlen += len; + if (mlen > ((U64(1) << 36) - 32) || (mlen < len)) + return 0; + + gcmctx->len.u[1] = mlen; + + /* Finalize GHASH(AAD) if AAD partial blocks left unprocessed */ + if (gcmctx->ares > 0) { + ossl_gcm_gmult_avx512(gcmctx->Xi.u, gcmctx); + gcmctx->ares = 0; + } + + if (ctx->enc) + ossl_aes_gcm_encrypt_avx512(gcmctx->key, gcmctx, &gcmctx->mres, in, len, out); + else + ossl_aes_gcm_decrypt_avx512(gcmctx->key, gcmctx, &gcmctx->mres, in, len, out); + + return 1; +} + +static int vaes_gcm_cipherfinal(PROV_GCM_CTX *ctx, unsigned char *tag) +{ + GCM128_CONTEXT *gcmctx = &ctx->gcm; + unsigned int *res = &gcmctx->mres; + + /* Finalize AAD processing */ + if (gcmctx->ares > 0) + res = &gcmctx->ares; + + ossl_aes_gcm_finalize_avx512(gcmctx, *res); + + if (ctx->enc) { + ctx->taglen = GCM_TAG_MAX_SIZE; + memcpy(tag, gcmctx->Xi.c, + ctx->taglen <= sizeof(gcmctx->Xi.c) ? ctx->taglen : + sizeof(gcmctx->Xi.c)); + *res = 0; + } else { + return !CRYPTO_memcmp(gcmctx->Xi.c, tag, ctx->taglen); + } + + return 1; +} + +static const PROV_GCM_HW vaes_gcm = { + vaes_gcm_setkey, + vaes_gcm_setiv, + vaes_gcm_aadupdate, + vaes_gcm_cipherupdate, + vaes_gcm_cipherfinal, + ossl_gcm_one_shot +}; + +#endif diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv.c new file mode 100644 index 000000000000..c9afeddef67a --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv.c @@ -0,0 +1,316 @@ +/* + * Copyright 2019-2025 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 + */ + +/* Dispatch functions for AES SIV mode */ + +/* + * This file uses the low level AES functions (which are deprecated for + * non-internal use) in order to implement provider AES ciphers. + */ +#include "internal/deprecated.h" + +#include <openssl/proverr.h> +#include "prov/implementations.h" +#include "prov/providercommon.h" +#include "prov/ciphercommon_aead.h" +#include "prov/provider_ctx.h" +#include "cipher_aes_gcm_siv.h" + +static int ossl_aes_gcm_siv_set_ctx_params(void *vctx, const OSSL_PARAM params[]); + +static void *ossl_aes_gcm_siv_newctx(void *provctx, size_t keybits) +{ + PROV_AES_GCM_SIV_CTX *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + ctx = OPENSSL_zalloc(sizeof(*ctx)); + if (ctx != NULL) { + ctx->key_len = keybits / 8; + ctx->hw = ossl_prov_cipher_hw_aes_gcm_siv(keybits); + ctx->libctx = PROV_LIBCTX_OF(provctx); + ctx->provctx = provctx; + } + return ctx; +} + +static void ossl_aes_gcm_siv_freectx(void *vctx) +{ + PROV_AES_GCM_SIV_CTX *ctx = (PROV_AES_GCM_SIV_CTX *)vctx; + + if (ctx == NULL) + return; + + OPENSSL_clear_free(ctx->aad, ctx->aad_len); + ctx->hw->clean_ctx(ctx); + OPENSSL_clear_free(ctx, sizeof(*ctx)); +} + +static void *ossl_aes_gcm_siv_dupctx(void *vctx) +{ + PROV_AES_GCM_SIV_CTX *in = (PROV_AES_GCM_SIV_CTX *)vctx; + PROV_AES_GCM_SIV_CTX *ret; + + if (!ossl_prov_is_running()) + return NULL; + + if (in->hw == NULL) + return NULL; + + ret = OPENSSL_memdup(in, sizeof(*in)); + if (ret == NULL) + return NULL; + /* NULL-out these things we create later */ + ret->aad = NULL; + ret->ecb_ctx = NULL; + + if (in->aad != NULL) { + if ((ret->aad = OPENSSL_memdup(in->aad, UP16(ret->aad_len))) == NULL) + goto err; + } + + if (!in->hw->dup_ctx(ret, in)) + goto err; + + return ret; + err: + if (ret != NULL) { + OPENSSL_clear_free(ret->aad, ret->aad_len); + OPENSSL_free(ret); + } + return NULL; +} + +static int ossl_aes_gcm_siv_init(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[], int enc) +{ + PROV_AES_GCM_SIV_CTX *ctx = (PROV_AES_GCM_SIV_CTX *)vctx; + + if (!ossl_prov_is_running()) + return 0; + + ctx->enc = enc; + + if (key != NULL) { + if (keylen != ctx->key_len) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + memcpy(ctx->key_gen_key, key, ctx->key_len); + } + if (iv != NULL) { + if (ivlen != sizeof(ctx->nonce)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); + return 0; + } + memcpy(ctx->nonce, iv, sizeof(ctx->nonce)); + } + + if (!ctx->hw->initkey(ctx)) + return 0; + + return ossl_aes_gcm_siv_set_ctx_params(ctx, params); +} + +static int ossl_aes_gcm_siv_einit(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + return ossl_aes_gcm_siv_init(vctx, key, keylen, iv, ivlen, params, 1); +} + +static int ossl_aes_gcm_siv_dinit(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + return ossl_aes_gcm_siv_init(vctx, key, keylen, iv, ivlen, params, 0); +} + +#define ossl_aes_gcm_siv_stream_update ossl_aes_gcm_siv_cipher +static int ossl_aes_gcm_siv_cipher(void *vctx, unsigned char *out, size_t *outl, + size_t outsize, const unsigned char *in, size_t inl) +{ + PROV_AES_GCM_SIV_CTX *ctx = (PROV_AES_GCM_SIV_CTX *)vctx; + int error = 0; + + if (!ossl_prov_is_running()) + return 0; + + if (outsize < inl) { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + return 0; + } + + error |= !ctx->hw->cipher(ctx, out, in, inl); + + if (outl != NULL && !error) + *outl = inl; + return !error; +} + +static int ossl_aes_gcm_siv_stream_final(void *vctx, unsigned char *out, size_t *outl, + size_t outsize) +{ + PROV_AES_GCM_SIV_CTX *ctx = (PROV_AES_GCM_SIV_CTX *)vctx; + int error = 0; + + if (!ossl_prov_is_running()) + return 0; + + error |= !ctx->hw->cipher(vctx, out, NULL, 0); + + if (outl != NULL && !error) + *outl = 0; + return !error; +} + +static int ossl_aes_gcm_siv_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + PROV_AES_GCM_SIV_CTX *ctx = (PROV_AES_GCM_SIV_CTX *)vctx; + OSSL_PARAM *p; + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TAG); + if (p != NULL && p->data_type == OSSL_PARAM_OCTET_STRING) { + if (!ctx->enc || !ctx->generated_tag + || p->data_size != sizeof(ctx->tag) + || !OSSL_PARAM_set_octet_string(p, ctx->tag, sizeof(ctx->tag))) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TAGLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, sizeof(ctx->tag))) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->key_len)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + return 1; +} + +static const OSSL_PARAM aes_gcm_siv_known_gettable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_TAGLEN, NULL), + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, NULL, 0), + OSSL_PARAM_END +}; + +static const OSSL_PARAM *ossl_aes_gcm_siv_gettable_ctx_params(ossl_unused void *cctx, + ossl_unused void *provctx) +{ + return aes_gcm_siv_known_gettable_ctx_params; +} + +static int ossl_aes_gcm_siv_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_AES_GCM_SIV_CTX *ctx = (PROV_AES_GCM_SIV_CTX *)vctx; + const OSSL_PARAM *p; + unsigned int speed = 0; + + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TAG); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_OCTET_STRING + || p->data_size != sizeof(ctx->user_tag)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + if (!ctx->enc) { + memcpy(ctx->user_tag, p->data, sizeof(ctx->tag)); + ctx->have_user_tag = 1; + } + } + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_SPEED); + if (p != NULL) { + if (!OSSL_PARAM_get_uint(p, &speed)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + ctx->speed = !!speed; + } + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_KEYLEN); + if (p != NULL) { + size_t key_len; + + if (!OSSL_PARAM_get_size_t(p, &key_len)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + /* The key length can not be modified */ + if (key_len != ctx->key_len) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + } + return 1; +} + +static const OSSL_PARAM aes_gcm_siv_known_settable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), + OSSL_PARAM_uint(OSSL_CIPHER_PARAM_SPEED, NULL), + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, NULL, 0), + OSSL_PARAM_END +}; +static const OSSL_PARAM *ossl_aes_gcm_siv_settable_ctx_params(ossl_unused void *cctx, + ossl_unused void *provctx) +{ + return aes_gcm_siv_known_settable_ctx_params; +} + +#define IMPLEMENT_cipher(alg, lc, UCMODE, flags, kbits, blkbits, ivbits) \ +static OSSL_FUNC_cipher_newctx_fn ossl_##alg##kbits##_##lc##_newctx; \ +static OSSL_FUNC_cipher_freectx_fn ossl_##alg##_##lc##_freectx; \ +static OSSL_FUNC_cipher_dupctx_fn ossl_##alg##_##lc##_dupctx; \ +static OSSL_FUNC_cipher_encrypt_init_fn ossl_##alg##_##lc##_einit; \ +static OSSL_FUNC_cipher_decrypt_init_fn ossl_##alg##_##lc##_dinit; \ +static OSSL_FUNC_cipher_update_fn ossl_##alg##_##lc##_stream_update; \ +static OSSL_FUNC_cipher_final_fn ossl_##alg##_##lc##_stream_final; \ +static OSSL_FUNC_cipher_cipher_fn ossl_##alg##_##lc##_cipher; \ +static OSSL_FUNC_cipher_get_params_fn ossl_##alg##_##kbits##_##lc##_get_params; \ +static OSSL_FUNC_cipher_get_ctx_params_fn ossl_##alg##_##lc##_get_ctx_params; \ +static OSSL_FUNC_cipher_gettable_ctx_params_fn ossl_##alg##_##lc##_gettable_ctx_params; \ +static OSSL_FUNC_cipher_set_ctx_params_fn ossl_##alg##_##lc##_set_ctx_params; \ +static OSSL_FUNC_cipher_settable_ctx_params_fn ossl_##alg##_##lc##_settable_ctx_params; \ +static int ossl_##alg##_##kbits##_##lc##_get_params(OSSL_PARAM params[]) \ +{ \ + return ossl_cipher_generic_get_params(params, EVP_CIPH_##UCMODE##_MODE, \ + flags, kbits, blkbits, ivbits); \ +} \ +static void *ossl_##alg##kbits##_##lc##_newctx(void *provctx) \ +{ \ + return ossl_##alg##_##lc##_newctx(provctx, kbits); \ +} \ +const OSSL_DISPATCH ossl_##alg##kbits##lc##_functions[] = { \ + { OSSL_FUNC_CIPHER_NEWCTX, (void (*)(void))ossl_##alg##kbits##_##lc##_newctx }, \ + { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))ossl_##alg##_##lc##_freectx }, \ + { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void))ossl_##alg##_##lc##_dupctx }, \ + { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))ossl_##alg##_##lc##_einit }, \ + { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))ossl_##alg##_##lc##_dinit }, \ + { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))ossl_##alg##_##lc##_stream_update }, \ + { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))ossl_##alg##_##lc##_stream_final }, \ + { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))ossl_##alg##_##lc##_cipher }, \ + { OSSL_FUNC_CIPHER_GET_PARAMS, (void (*)(void))ossl_##alg##_##kbits##_##lc##_get_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, (void (*)(void))ossl_cipher_generic_gettable_params }, \ + { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, (void (*)(void))ossl_##alg##_##lc##_get_ctx_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, (void (*)(void))ossl_##alg##_##lc##_gettable_ctx_params }, \ + { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, (void (*)(void))ossl_##alg##_##lc##_set_ctx_params }, \ + { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, (void (*)(void))ossl_##alg##_##lc##_settable_ctx_params }, \ + OSSL_DISPATCH_END \ +} + +IMPLEMENT_cipher(aes, gcm_siv, GCM_SIV, AEAD_FLAGS, 128, 8, 96); +IMPLEMENT_cipher(aes, gcm_siv, GCM_SIV, AEAD_FLAGS, 192, 8, 96); +IMPLEMENT_cipher(aes, gcm_siv, GCM_SIV, AEAD_FLAGS, 256, 8, 96); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv.h b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv.h new file mode 100644 index 000000000000..37d1e3326b18 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv.h @@ -0,0 +1,76 @@ +/* + * Copyright 2019-2021 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 + */ + +#include <openssl/aes.h> +#include "prov/ciphercommon.h" +#include "crypto/aes_platform.h" + +#define BLOCK_SIZE 16 +#define NONCE_SIZE 12 +#define TAG_SIZE 16 + +/* AAD manipulation macros */ +#define UP16(x) (((x) + 15) & ~0x0F) +#define DOWN16(x) ((x) & ~0x0F) +#define REMAINDER16(x) ((x) & 0x0F) +#define IS16(x) (((x) & 0x0F) == 0) + +typedef struct prov_cipher_hw_aes_gcm_siv_st { + int (*initkey)(void *vctx); + int (*cipher)(void *vctx, unsigned char *out, const unsigned char *in, + size_t len); + int (*dup_ctx)(void *vdst, void *vsrc); + void (*clean_ctx)(void *vctx); +} PROV_CIPHER_HW_AES_GCM_SIV; + +/* Arranged for alignment purposes */ +typedef struct prov_aes_gcm_siv_ctx_st { + EVP_CIPHER_CTX *ecb_ctx; + const PROV_CIPHER_HW_AES_GCM_SIV *hw; /* maybe not used, yet? */ + uint8_t *aad; /* Allocated, rounded up to 16 bytes, from user */ + OSSL_LIB_CTX *libctx; + OSSL_PROVIDER *provctx; + size_t aad_len; /* actual AAD length */ + size_t key_len; + uint8_t key_gen_key[32]; /* from user */ + uint8_t msg_enc_key[32]; /* depends on key size */ + uint8_t msg_auth_key[BLOCK_SIZE]; + uint8_t tag[TAG_SIZE]; /* generated tag, given to user or compared to user */ + uint8_t user_tag[TAG_SIZE]; /* from user */ + uint8_t nonce[NONCE_SIZE]; /* from user */ + u128 Htable[16]; /* Polyval calculations via ghash */ + unsigned int enc : 1; /* Set to 1 if we are encrypting or 0 otherwise */ + unsigned int have_user_tag : 1; + unsigned int generated_tag : 1; + unsigned int used_enc : 1; + unsigned int used_dec : 1; + unsigned int speed : 1; +} PROV_AES_GCM_SIV_CTX; + +const PROV_CIPHER_HW_AES_GCM_SIV *ossl_prov_cipher_hw_aes_gcm_siv(size_t keybits); + +void ossl_polyval_ghash_init(u128 Htable[16], const uint64_t H[2]); +void ossl_polyval_ghash_hash(const u128 Htable[16], uint8_t *tag, const uint8_t *inp, size_t len); + +/* Define GSWAP8/GSWAP4 - used for BOTH little and big endian architectures */ +static ossl_inline uint32_t GSWAP4(uint32_t n) +{ + return (((n & 0x000000FF) << 24) + | ((n & 0x0000FF00) << 8) + | ((n & 0x00FF0000) >> 8) + | ((n & 0xFF000000) >> 24)); +} +static ossl_inline uint64_t GSWAP8(uint64_t n) +{ + uint64_t result; + + result = GSWAP4(n & 0x0FFFFFFFF); + result <<= 32; + return result | GSWAP4(n >> 32); +} diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c new file mode 100644 index 000000000000..c60d4e048769 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c @@ -0,0 +1,372 @@ +/* + * Copyright 2019-2025 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 + */ + +/* + * AES low level APIs are deprecated for public use, but still ok for internal + * use where we're using them to implement the higher level EVP interface, as is + * the case here. + */ +#include "internal/deprecated.h" + +#include <openssl/evp.h> +#include <internal/endian.h> +#include <prov/implementations.h> +#include "cipher_aes_gcm_siv.h" + +static int aes_gcm_siv_ctr32(PROV_AES_GCM_SIV_CTX *ctx, const unsigned char *init_counter, + unsigned char *out, const unsigned char *in, size_t len); + +static int aes_gcm_siv_initkey(void *vctx) +{ + PROV_AES_GCM_SIV_CTX *ctx = (PROV_AES_GCM_SIV_CTX *)vctx; + uint8_t output[BLOCK_SIZE]; + uint32_t counter = 0x0; + size_t i; + union { + uint32_t counter; + uint8_t block[BLOCK_SIZE]; + } data; + int out_len; + EVP_CIPHER *ecb = NULL; + DECLARE_IS_ENDIAN; + + switch (ctx->key_len) { + case 16: + ecb = EVP_CIPHER_fetch(ctx->libctx, "AES-128-ECB", NULL); + break; + case 24: + ecb = EVP_CIPHER_fetch(ctx->libctx, "AES-192-ECB", NULL); + break; + case 32: + ecb = EVP_CIPHER_fetch(ctx->libctx, "AES-256-ECB", NULL); + break; + default: + goto err; + } + + if (ctx->ecb_ctx == NULL && (ctx->ecb_ctx = EVP_CIPHER_CTX_new()) == NULL) + goto err; + if (!EVP_EncryptInit_ex2(ctx->ecb_ctx, ecb, ctx->key_gen_key, NULL, NULL)) + goto err; + + memset(&data, 0, sizeof(data)); + memcpy(&data.block[sizeof(data.counter)], ctx->nonce, NONCE_SIZE); + + /* msg_auth_key is always 16 bytes in size, regardless of AES128/AES256 */ + /* counter is stored little-endian */ + for (i = 0; i < BLOCK_SIZE; i += 8) { + if (IS_LITTLE_ENDIAN) { + data.counter = counter; + } else { + data.counter = GSWAP4(counter); + } + /* Block size is 16 (128 bits), but only 8 bytes are used */ + out_len = BLOCK_SIZE; + if (!EVP_EncryptUpdate(ctx->ecb_ctx, output, &out_len, data.block, BLOCK_SIZE)) + goto err; + memcpy(&ctx->msg_auth_key[i], output, 8); + counter++; + } + + /* msg_enc_key length is directly tied to key length AES128/AES256 */ + for (i = 0; i < ctx->key_len; i += 8) { + if (IS_LITTLE_ENDIAN) { + data.counter = counter; + } else { + data.counter = GSWAP4(counter); + } + /* Block size is 16 bytes (128 bits), but only 8 bytes are used */ + out_len = BLOCK_SIZE; + if (!EVP_EncryptUpdate(ctx->ecb_ctx, output, &out_len, data.block, BLOCK_SIZE)) + goto err; + memcpy(&ctx->msg_enc_key[i], output, 8); + counter++; + } + + if (!EVP_EncryptInit_ex2(ctx->ecb_ctx, ecb, ctx->msg_enc_key, NULL, NULL)) + goto err; + + /* Freshen up the state */ + ctx->used_enc = 0; + ctx->used_dec = 0; + EVP_CIPHER_free(ecb); + return 1; + err: + EVP_CIPHER_CTX_free(ctx->ecb_ctx); + EVP_CIPHER_free(ecb); + ctx->ecb_ctx = NULL; + return 0; +} + +static int aes_gcm_siv_aad(PROV_AES_GCM_SIV_CTX *ctx, + const unsigned char *aad, size_t len) +{ + size_t to_alloc; + uint8_t *ptr; + uint64_t len64; + + /* length of 0 resets the AAD */ + if (len == 0) { + OPENSSL_free(ctx->aad); + ctx->aad = NULL; + ctx->aad_len = 0; + return 1; + } + to_alloc = UP16(ctx->aad_len + len); + /* need to check the size of the AAD per RFC8452 */ + len64 = to_alloc; + if (len64 > ((uint64_t)1 << 36)) + return 0; + ptr = OPENSSL_realloc(ctx->aad, to_alloc); + if (ptr == NULL) + return 0; + ctx->aad = ptr; + memcpy(&ctx->aad[ctx->aad_len], aad, len); + ctx->aad_len += len; + if (to_alloc > ctx->aad_len) + memset(&ctx->aad[ctx->aad_len], 0, to_alloc - ctx->aad_len); + return 1; +} + +static int aes_gcm_siv_finish(PROV_AES_GCM_SIV_CTX *ctx) +{ + int ret = 0; + + if (ctx->enc) + return ctx->generated_tag; + ret = !CRYPTO_memcmp(ctx->tag, ctx->user_tag, sizeof(ctx->tag)); + ret &= ctx->have_user_tag; + return ret; +} + +static int aes_gcm_siv_encrypt(PROV_AES_GCM_SIV_CTX *ctx, const unsigned char *in, + unsigned char *out, size_t len) +{ + uint64_t len_blk[2]; + uint8_t S_s[TAG_SIZE]; + uint8_t counter_block[TAG_SIZE]; + uint8_t padding[BLOCK_SIZE]; + size_t i; + int64_t len64 = len; + int out_len; + int error = 0; + DECLARE_IS_ENDIAN; + + ctx->generated_tag = 0; + if (!ctx->speed && ctx->used_enc) + return 0; + /* need to check the size of the input! */ + if (len64 > ((int64_t)1 << 36)) + return 0; + + if (IS_LITTLE_ENDIAN) { + len_blk[0] = (uint64_t)ctx->aad_len * 8; + len_blk[1] = (uint64_t)len * 8; + } else { + len_blk[0] = GSWAP8((uint64_t)ctx->aad_len * 8); + len_blk[1] = GSWAP8((uint64_t)len * 8); + } + memset(S_s, 0, TAG_SIZE); + ossl_polyval_ghash_init(ctx->Htable, (const uint64_t*)ctx->msg_auth_key); + + if (ctx->aad != NULL) { + /* AAD is allocated with padding, but need to adjust length */ + ossl_polyval_ghash_hash(ctx->Htable, S_s, ctx->aad, UP16(ctx->aad_len)); + } + if (DOWN16(len) > 0) + ossl_polyval_ghash_hash(ctx->Htable, S_s, (uint8_t *) in, DOWN16(len)); + if (!IS16(len)) { + /* deal with padding - probably easier to memset the padding first rather than calculate */ + memset(padding, 0, sizeof(padding)); + memcpy(padding, &in[DOWN16(len)], REMAINDER16(len)); + ossl_polyval_ghash_hash(ctx->Htable, S_s, padding, sizeof(padding)); + } + ossl_polyval_ghash_hash(ctx->Htable, S_s, (uint8_t *) len_blk, sizeof(len_blk)); + + for (i = 0; i < NONCE_SIZE; i++) + S_s[i] ^= ctx->nonce[i]; + + S_s[TAG_SIZE - 1] &= 0x7f; + out_len = sizeof(ctx->tag); + error |= !EVP_EncryptUpdate(ctx->ecb_ctx, ctx->tag, &out_len, S_s, sizeof(S_s)); + memcpy(counter_block, ctx->tag, TAG_SIZE); + counter_block[TAG_SIZE - 1] |= 0x80; + + error |= !aes_gcm_siv_ctr32(ctx, counter_block, out, in, len); + + ctx->generated_tag = !error; + /* Regardless of error */ + ctx->used_enc = 1; + return !error; +} + +static int aes_gcm_siv_decrypt(PROV_AES_GCM_SIV_CTX *ctx, const unsigned char *in, + unsigned char *out, size_t len) +{ + uint8_t counter_block[TAG_SIZE]; + uint64_t len_blk[2]; + uint8_t S_s[TAG_SIZE]; + size_t i; + uint64_t padding[2]; + int64_t len64 = len; + int out_len; + int error = 0; + DECLARE_IS_ENDIAN; + + ctx->generated_tag = 0; + if (!ctx->speed && ctx->used_dec) + return 0; + /* need to check the size of the input! */ + if (len64 > ((int64_t)1 << 36)) + return 0; + + memcpy(counter_block, ctx->user_tag, sizeof(counter_block)); + counter_block[TAG_SIZE - 1] |= 0x80; + + error |= !aes_gcm_siv_ctr32(ctx, counter_block, out, in, len); + + if (IS_LITTLE_ENDIAN) { + len_blk[0] = (uint64_t)ctx->aad_len * 8; + len_blk[1] = (uint64_t)len * 8; + } else { + len_blk[0] = GSWAP8((uint64_t)ctx->aad_len * 8); + len_blk[1] = GSWAP8((uint64_t)len * 8); + } + memset(S_s, 0, TAG_SIZE); + ossl_polyval_ghash_init(ctx->Htable, (const uint64_t*)ctx->msg_auth_key); + if (ctx->aad != NULL) { + /* AAD allocated with padding, but need to adjust length */ + ossl_polyval_ghash_hash(ctx->Htable, S_s, ctx->aad, UP16(ctx->aad_len)); + } + if (DOWN16(len) > 0) + ossl_polyval_ghash_hash(ctx->Htable, S_s, out, DOWN16(len)); + if (!IS16(len)) { + /* deal with padding - probably easier to "memset" the padding first rather than calculate */ + padding[0] = padding[1] = 0; + memcpy(padding, &out[DOWN16(len)], REMAINDER16(len)); + ossl_polyval_ghash_hash(ctx->Htable, S_s, (uint8_t *)padding, sizeof(padding)); + } + ossl_polyval_ghash_hash(ctx->Htable, S_s, (uint8_t *)len_blk, TAG_SIZE); + + for (i = 0; i < NONCE_SIZE; i++) + S_s[i] ^= ctx->nonce[i]; + + S_s[TAG_SIZE - 1] &= 0x7f; + + /* + * In the ctx, user_tag is the one received/set by the user, + * and tag is generated from the input + */ + out_len = sizeof(ctx->tag); + error |= !EVP_EncryptUpdate(ctx->ecb_ctx, ctx->tag, &out_len, S_s, sizeof(S_s)); + ctx->generated_tag = !error; + /* Regardless of error */ + ctx->used_dec = 1; + return !error; +} + +static int aes_gcm_siv_cipher(void *vctx, unsigned char *out, + const unsigned char *in, size_t len) +{ + PROV_AES_GCM_SIV_CTX *ctx = (PROV_AES_GCM_SIV_CTX *)vctx; + + /* EncryptFinal or DecryptFinal */ + if (in == NULL) + return aes_gcm_siv_finish(ctx); + + /* Deal with associated data */ + if (out == NULL) + return aes_gcm_siv_aad(ctx, in, len); + + if (ctx->enc) + return aes_gcm_siv_encrypt(ctx, in, out, len); + + return aes_gcm_siv_decrypt(ctx, in, out, len); +} + +static void aes_gcm_siv_clean_ctx(void *vctx) +{ + PROV_AES_GCM_SIV_CTX *ctx = (PROV_AES_GCM_SIV_CTX *)vctx; + + EVP_CIPHER_CTX_free(ctx->ecb_ctx); + ctx->ecb_ctx = NULL; +} + +static int aes_gcm_siv_dup_ctx(void *vdst, void *vsrc) +{ + PROV_AES_GCM_SIV_CTX *dst = (PROV_AES_GCM_SIV_CTX *)vdst; + PROV_AES_GCM_SIV_CTX *src = (PROV_AES_GCM_SIV_CTX *)vsrc; + + dst->ecb_ctx = NULL; + if (src->ecb_ctx != NULL) { + if ((dst->ecb_ctx = EVP_CIPHER_CTX_new()) == NULL) + goto err; + if (!EVP_CIPHER_CTX_copy(dst->ecb_ctx, src->ecb_ctx)) + goto err; + } + return 1; + + err: + EVP_CIPHER_CTX_free(dst->ecb_ctx); + dst->ecb_ctx = NULL; + return 0; +} + +static const PROV_CIPHER_HW_AES_GCM_SIV aes_gcm_siv_hw = { + aes_gcm_siv_initkey, + aes_gcm_siv_cipher, + aes_gcm_siv_dup_ctx, + aes_gcm_siv_clean_ctx, +}; + +const PROV_CIPHER_HW_AES_GCM_SIV *ossl_prov_cipher_hw_aes_gcm_siv(size_t keybits) +{ + return &aes_gcm_siv_hw; +} + +/* AES-GCM-SIV needs AES-CTR32, which is different than the AES-CTR implementation */ +static int aes_gcm_siv_ctr32(PROV_AES_GCM_SIV_CTX *ctx, const unsigned char *init_counter, + unsigned char *out, const unsigned char *in, size_t len) +{ + uint8_t keystream[BLOCK_SIZE]; + int out_len; + size_t i; + size_t j; + size_t todo; + uint32_t counter; + int error = 0; + union { + uint32_t x32[BLOCK_SIZE / sizeof(uint32_t)]; + uint8_t x8[BLOCK_SIZE]; + } block; + DECLARE_IS_ENDIAN; + + memcpy(&block, init_counter, sizeof(block)); + if (IS_BIG_ENDIAN) { + counter = GSWAP4(block.x32[0]); + } + + for (i = 0; i < len; i += sizeof(block)) { + out_len = BLOCK_SIZE; + error |= !EVP_EncryptUpdate(ctx->ecb_ctx, keystream, &out_len, (uint8_t*)&block, sizeof(block)); + if (IS_LITTLE_ENDIAN) { + block.x32[0]++; + } else { + counter++; + block.x32[0] = GSWAP4(counter); + } + todo = len - i; + if (todo > sizeof(keystream)) + todo = sizeof(keystream); + /* Non optimal, but avoids alignment issues */ + for (j = 0; j < todo; j++) + out[i + j] = in[i + j] ^ keystream[j]; + } + return !error; +} diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv_polyval.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv_polyval.c new file mode 100644 index 000000000000..fead51dd36f7 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv_polyval.c @@ -0,0 +1,95 @@ +/* + * Copyright 2019-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 + */ + +/* + * AES low level APIs are deprecated for public use, but still ok for internal + * use where we're using them to implement the higher level EVP interface, as is + * the case here. + */ +#include "internal/deprecated.h" + +#include <openssl/evp.h> +#include <internal/endian.h> +#include <prov/implementations.h> +#include "cipher_aes_gcm_siv.h" + +static ossl_inline void mulx_ghash(uint64_t *a) +{ + uint64_t t[2], mask; + DECLARE_IS_ENDIAN; + + if (IS_LITTLE_ENDIAN) { + t[0] = GSWAP8(a[0]); + t[1] = GSWAP8(a[1]); + } else { + t[0] = a[0]; + t[1] = a[1]; + } + mask = -(int64_t)(t[1] & 1) & 0xe1; + mask <<= 56; + + if (IS_LITTLE_ENDIAN) { + a[1] = GSWAP8((t[1] >> 1) ^ (t[0] << 63)); + a[0] = GSWAP8((t[0] >> 1) ^ mask); + } else { + a[1] = (t[1] >> 1) ^ (t[0] << 63); + a[0] = (t[0] >> 1) ^ mask; + } +} + +#define aligned64(p) (((uintptr_t)p & 0x07) == 0) +static ossl_inline void byte_reverse16(uint8_t *out, const uint8_t *in) +{ + if (aligned64(out) && aligned64(in)) { + ((uint64_t *)out)[0] = GSWAP8(((uint64_t *)in)[1]); + ((uint64_t *)out)[1] = GSWAP8(((uint64_t *)in)[0]); + } else { + int i; + + for (i = 0; i < 16; i++) + out[i] = in[15 - i]; + } +} + +/* Initialization of POLYVAL via existing GHASH implementation */ +void ossl_polyval_ghash_init(u128 Htable[16], const uint64_t H[2]) +{ + uint64_t tmp[2]; + DECLARE_IS_ENDIAN; + + byte_reverse16((uint8_t *)tmp, (const uint8_t *)H); + mulx_ghash(tmp); + if (IS_LITTLE_ENDIAN) { + /* "H is stored in host byte order" */ + tmp[0] = GSWAP8(tmp[0]); + tmp[1] = GSWAP8(tmp[1]); + } + + ossl_gcm_init_4bit(Htable, (u64*)tmp); +} + +/* Implementation of POLYVAL via existing GHASH implementation */ +void ossl_polyval_ghash_hash(const u128 Htable[16], uint8_t *tag, const uint8_t *inp, size_t len) +{ + uint64_t out[2]; + uint64_t tmp[2]; + size_t i; + + byte_reverse16((uint8_t *)out, (uint8_t *)tag); + + /* + * This implementation doesn't deal with partials, callers do, + * so, len is a multiple of 16 + */ + for (i = 0; i < len; i += 16) { + byte_reverse16((uint8_t *)tmp, &inp[i]); + ossl_gcm_ghash_4bit((u64*)out, Htable, (uint8_t *)tmp, 16); + } + byte_reverse16(tag, (uint8_t *)out); +} diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw.c new file mode 100644 index 000000000000..a3b72d9f728a --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw.c @@ -0,0 +1,163 @@ +/* + * Copyright 2001-2024 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 + */ + +/* + * This file uses the low level AES functions (which are deprecated for + * non-internal use) in order to implement provider AES ciphers. + */ +#include "internal/deprecated.h" + +#include <openssl/proverr.h> +#include "cipher_aes.h" + +static int cipher_hw_aes_initkey(PROV_CIPHER_CTX *dat, + const unsigned char *key, size_t keylen) +{ + int ret; + PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; + AES_KEY *ks = &adat->ks.ks; + + dat->ks = ks; + + if ((dat->mode == EVP_CIPH_ECB_MODE || dat->mode == EVP_CIPH_CBC_MODE) + && !dat->enc) { +#ifdef HWAES_CAPABLE + if (HWAES_CAPABLE) { + ret = HWAES_set_decrypt_key(key, keylen * 8, ks); + dat->block = (block128_f)HWAES_decrypt; + dat->stream.cbc = NULL; +# ifdef HWAES_cbc_encrypt + if (dat->mode == EVP_CIPH_CBC_MODE) + dat->stream.cbc = (cbc128_f)HWAES_cbc_encrypt; +# endif +# ifdef HWAES_ecb_encrypt + if (dat->mode == EVP_CIPH_ECB_MODE) + dat->stream.ecb = (ecb128_f)HWAES_ecb_encrypt; +# endif + } else +#endif +#ifdef BSAES_CAPABLE + if (BSAES_CAPABLE && dat->mode == EVP_CIPH_CBC_MODE) { + ret = AES_set_decrypt_key(key, keylen * 8, ks); + dat->block = (block128_f)AES_decrypt; + dat->stream.cbc = (cbc128_f)ossl_bsaes_cbc_encrypt; + } else +#endif +#ifdef VPAES_CAPABLE + if (VPAES_CAPABLE) { + ret = vpaes_set_decrypt_key(key, keylen * 8, ks); + dat->block = (block128_f)vpaes_decrypt; + dat->stream.cbc = (dat->mode == EVP_CIPH_CBC_MODE) + ?(cbc128_f)vpaes_cbc_encrypt : NULL; + } else +#endif + { + ret = AES_set_decrypt_key(key, keylen * 8, ks); + dat->block = (block128_f)AES_decrypt; + dat->stream.cbc = (dat->mode == EVP_CIPH_CBC_MODE) + ? (cbc128_f)AES_cbc_encrypt : NULL; + } + } else +#ifdef HWAES_CAPABLE + if (HWAES_CAPABLE) { + ret = HWAES_set_encrypt_key(key, keylen * 8, ks); + dat->block = (block128_f)HWAES_encrypt; + dat->stream.cbc = NULL; +# ifdef HWAES_cbc_encrypt + if (dat->mode == EVP_CIPH_CBC_MODE) + dat->stream.cbc = (cbc128_f)HWAES_cbc_encrypt; + else +# endif +# ifdef HWAES_ecb_encrypt + if (dat->mode == EVP_CIPH_ECB_MODE) + dat->stream.ecb = (ecb128_f)HWAES_ecb_encrypt; + else +# endif +# ifdef HWAES_ctr32_encrypt_blocks + if (dat->mode == EVP_CIPH_CTR_MODE) + dat->stream.ctr = (ctr128_f)HWAES_ctr32_encrypt_blocks; + else +# endif + (void)0; /* terminate potentially open 'else' */ + } else +#endif +#ifdef BSAES_CAPABLE + if (BSAES_CAPABLE && dat->mode == EVP_CIPH_CTR_MODE) { + ret = AES_set_encrypt_key(key, keylen * 8, ks); + dat->block = (block128_f)AES_encrypt; + dat->stream.ctr = (ctr128_f)ossl_bsaes_ctr32_encrypt_blocks; + } else +#endif +#ifdef VPAES_CAPABLE + if (VPAES_CAPABLE) { + ret = vpaes_set_encrypt_key(key, keylen * 8, ks); + dat->block = (block128_f)vpaes_encrypt; + dat->stream.cbc = (dat->mode == EVP_CIPH_CBC_MODE) + ? (cbc128_f)vpaes_cbc_encrypt : NULL; + } else +#endif + { + ret = AES_set_encrypt_key(key, keylen * 8, ks); + dat->block = (block128_f)AES_encrypt; + dat->stream.cbc = (dat->mode == EVP_CIPH_CBC_MODE) + ? (cbc128_f)AES_cbc_encrypt : NULL; +#ifdef AES_CTR_ASM + if (dat->mode == EVP_CIPH_CTR_MODE) + dat->stream.ctr = (ctr128_f)AES_ctr32_encrypt; +#endif + } + + if (ret < 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED); + return 0; + } + + return 1; +} + +IMPLEMENT_CIPHER_HW_COPYCTX(cipher_hw_aes_copyctx, PROV_AES_CTX) + +#define PROV_CIPHER_HW_aes_mode(mode) \ +static const PROV_CIPHER_HW aes_##mode = { \ + cipher_hw_aes_initkey, \ + ossl_cipher_hw_generic_##mode, \ + cipher_hw_aes_copyctx \ +}; \ +PROV_CIPHER_HW_declare(mode) \ +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_##mode(size_t keybits) \ +{ \ + PROV_CIPHER_HW_select(mode) \ + return &aes_##mode; \ +} + +#if defined(AESNI_CAPABLE) +# include "cipher_aes_hw_aesni.inc" +#elif defined(SPARC_AES_CAPABLE) +# include "cipher_aes_hw_t4.inc" +#elif defined(S390X_aes_128_CAPABLE) +# include "cipher_aes_hw_s390x.inc" +#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 +# include "cipher_aes_hw_rv64i.inc" +#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 +# include "cipher_aes_hw_rv32i.inc" +#elif defined (ARMv8_HWAES_CAPABLE) +# include "cipher_aes_hw_armv8.inc" +#else +/* The generic case */ +# define PROV_CIPHER_HW_declare(mode) +# define PROV_CIPHER_HW_select(mode) +#endif + +PROV_CIPHER_HW_aes_mode(cbc) +PROV_CIPHER_HW_aes_mode(ecb) +PROV_CIPHER_HW_aes_mode(ofb128) +PROV_CIPHER_HW_aes_mode(cfb128) +PROV_CIPHER_HW_aes_mode(cfb1) +PROV_CIPHER_HW_aes_mode(cfb8) +PROV_CIPHER_HW_aes_mode(ctr) diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_aesni.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_aesni.inc new file mode 100644 index 000000000000..33b9046054e9 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_aesni.inc @@ -0,0 +1,84 @@ +/* + * Copyright 2001-2021 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 + */ + +/*- + * AES-NI support for AES modes ecb, cbc, ofb, cfb, ctr. + * This file is included by cipher_aes_hw.c + */ + +#define cipher_hw_aesni_ofb128 ossl_cipher_hw_generic_ofb128 +#define cipher_hw_aesni_cfb128 ossl_cipher_hw_generic_cfb128 +#define cipher_hw_aesni_cfb8 ossl_cipher_hw_generic_cfb8 +#define cipher_hw_aesni_cfb1 ossl_cipher_hw_generic_cfb1 +#define cipher_hw_aesni_ctr ossl_cipher_hw_generic_ctr + +static int cipher_hw_aesni_initkey(PROV_CIPHER_CTX *dat, + const unsigned char *key, size_t keylen) +{ + int ret; + PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; + AES_KEY *ks = &adat->ks.ks; + + dat->ks = ks; + + if ((dat->mode == EVP_CIPH_ECB_MODE || dat->mode == EVP_CIPH_CBC_MODE) + && !dat->enc) { + ret = aesni_set_decrypt_key(key, keylen * 8, ks); + dat->block = (block128_f) aesni_decrypt; + dat->stream.cbc = dat->mode == EVP_CIPH_CBC_MODE ? + (cbc128_f) aesni_cbc_encrypt : NULL; + } else { + ret = aesni_set_encrypt_key(key, keylen * 8, ks); + dat->block = (block128_f) aesni_encrypt; + if (dat->mode == EVP_CIPH_CBC_MODE) + dat->stream.cbc = (cbc128_f) aesni_cbc_encrypt; + else if (dat->mode == EVP_CIPH_CTR_MODE) + dat->stream.ctr = (ctr128_f) aesni_ctr32_encrypt_blocks; + else + dat->stream.cbc = NULL; + } + + if (ret < 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED); + return 0; + } + + return 1; +} + +static int cipher_hw_aesni_cbc(PROV_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t len) +{ + const AES_KEY *ks = ctx->ks; + + aesni_cbc_encrypt(in, out, len, ks, ctx->iv, ctx->enc); + + return 1; +} + +static int cipher_hw_aesni_ecb(PROV_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t len) +{ + if (len < ctx->blocksize) + return 1; + + aesni_ecb_encrypt(in, out, len, ctx->ks, ctx->enc); + + return 1; +} + +#define PROV_CIPHER_HW_declare(mode) \ +static const PROV_CIPHER_HW aesni_##mode = { \ + cipher_hw_aesni_initkey, \ + cipher_hw_aesni_##mode, \ + cipher_hw_aes_copyctx \ +}; +#define PROV_CIPHER_HW_select(mode) \ +if (AESNI_CAPABLE) \ + return &aesni_##mode; diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_armv8.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_armv8.inc new file mode 100644 index 000000000000..3f73c79290a8 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_armv8.inc @@ -0,0 +1,34 @@ +/* + * Copyright 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 + */ + +/* + * Crypto extension support for AES modes ecb, cbc, ofb, cfb, ctr. + * This file is included by cipher_aes_hw.c + */ + +static int cipher_hw_aes_arm_initkey(PROV_CIPHER_CTX *dat, + const unsigned char *key, + size_t keylen) +{ + int ret = cipher_hw_aes_initkey(dat, key, keylen); + if (AES_UNROLL12_EOR3_CAPABLE && dat->mode == EVP_CIPH_CTR_MODE) + dat->stream.ctr = (ctr128_f)HWAES_ctr32_encrypt_blocks_unroll12_eor3; + + return ret; +} + +#define PROV_CIPHER_HW_declare(mode) \ +static const PROV_CIPHER_HW aes_arm_##mode = { \ + cipher_hw_aes_arm_initkey, \ + ossl_cipher_hw_generic_##mode, \ + cipher_hw_aes_copyctx \ +}; +#define PROV_CIPHER_HW_select(mode) \ +if (ARMv8_HWAES_CAPABLE) \ + return &aes_arm_##mode; diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_rv32i.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_rv32i.inc new file mode 100644 index 000000000000..f6c652c32d9b --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_rv32i.inc @@ -0,0 +1,102 @@ +/* + * 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 + */ + +/*- + * RISC-V 32 ZKND ZKNE support for AES modes ecb, cbc, ofb, cfb, ctr. + * This file is included by cipher_aes_hw.c + */ + +#define cipher_hw_rv32i_zknd_zkne_cbc ossl_cipher_hw_generic_cbc +#define cipher_hw_rv32i_zknd_zkne_ecb ossl_cipher_hw_generic_ecb +#define cipher_hw_rv32i_zknd_zkne_ofb128 ossl_cipher_hw_generic_ofb128 +#define cipher_hw_rv32i_zknd_zkne_cfb128 ossl_cipher_hw_generic_cfb128 +#define cipher_hw_rv32i_zknd_zkne_cfb8 ossl_cipher_hw_generic_cfb8 +#define cipher_hw_rv32i_zknd_zkne_cfb1 ossl_cipher_hw_generic_cfb1 +#define cipher_hw_rv32i_zknd_zkne_ctr ossl_cipher_hw_generic_ctr + +#define cipher_hw_rv32i_zbkb_zknd_zkne_cbc ossl_cipher_hw_generic_cbc +#define cipher_hw_rv32i_zbkb_zknd_zkne_ecb ossl_cipher_hw_generic_ecb +#define cipher_hw_rv32i_zbkb_zknd_zkne_ofb128 ossl_cipher_hw_generic_ofb128 +#define cipher_hw_rv32i_zbkb_zknd_zkne_cfb128 ossl_cipher_hw_generic_cfb128 +#define cipher_hw_rv32i_zbkb_zknd_zkne_cfb8 ossl_cipher_hw_generic_cfb8 +#define cipher_hw_rv32i_zbkb_zknd_zkne_cfb1 ossl_cipher_hw_generic_cfb1 +#define cipher_hw_rv32i_zbkb_zknd_zkne_ctr ossl_cipher_hw_generic_ctr + +static int cipher_hw_rv32i_zknd_zkne_initkey(PROV_CIPHER_CTX *dat, + const unsigned char *key, size_t keylen) +{ + int ret; + PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; + AES_KEY *ks = &adat->ks.ks; + + dat->ks = ks; + + if ((dat->mode == EVP_CIPH_ECB_MODE || dat->mode == EVP_CIPH_CBC_MODE) + && !dat->enc) { + ret = rv32i_zknd_zkne_set_decrypt_key(key, keylen * 8, ks); + dat->block = (block128_f) rv32i_zknd_decrypt; + dat->stream.cbc = NULL; + } else { + ret = rv32i_zkne_set_encrypt_key(key, keylen * 8, ks); + dat->block = (block128_f) rv32i_zkne_encrypt; + dat->stream.cbc = NULL; + } + + if (ret < 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED); + return 0; + } + + return 1; +} + +static int cipher_hw_rv32i_zbkb_zknd_zkne_initkey(PROV_CIPHER_CTX *dat, + const unsigned char *key, size_t keylen) +{ + int ret; + PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; + AES_KEY *ks = &adat->ks.ks; + + dat->ks = ks; + + if ((dat->mode == EVP_CIPH_ECB_MODE || dat->mode == EVP_CIPH_CBC_MODE) + && !dat->enc) { + ret = rv32i_zbkb_zknd_zkne_set_decrypt_key(key, keylen * 8, ks); + dat->block = (block128_f) rv32i_zknd_decrypt; + dat->stream.cbc = NULL; + } else { + ret = rv32i_zbkb_zkne_set_encrypt_key(key, keylen * 8, ks); + dat->block = (block128_f) rv32i_zkne_encrypt; + dat->stream.cbc = NULL; + } + + if (ret < 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED); + return 0; + } + + return 1; +} + +#define PROV_CIPHER_HW_declare(mode) \ +static const PROV_CIPHER_HW rv32i_zknd_zkne_##mode = { \ + cipher_hw_rv32i_zknd_zkne_initkey, \ + cipher_hw_rv32i_zknd_zkne_##mode, \ + cipher_hw_aes_copyctx \ +}; \ +static const PROV_CIPHER_HW rv32i_zbkb_zknd_zkne_##mode = { \ + cipher_hw_rv32i_zbkb_zknd_zkne_initkey, \ + cipher_hw_rv32i_zbkb_zknd_zkne_##mode, \ + cipher_hw_aes_copyctx \ +}; +#define PROV_CIPHER_HW_select(mode) \ +if (RISCV_HAS_ZBKB_AND_ZKND_AND_ZKNE()) \ + return &rv32i_zbkb_zknd_zkne_##mode; \ +if (RISCV_HAS_ZKND_AND_ZKNE()) \ + return &rv32i_zknd_zkne_##mode; diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_rv64i.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_rv64i.inc new file mode 100644 index 000000000000..07d479303d90 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_rv64i.inc @@ -0,0 +1,135 @@ +/* + * 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 + */ + +/*- + * RISC-V 64 ZKND ZKNE support for AES modes ecb, cbc, ofb, cfb, ctr. + * This file is included by cipher_aes_hw.c + */ + +#define cipher_hw_rv64i_zknd_zkne_cbc ossl_cipher_hw_generic_cbc +#define cipher_hw_rv64i_zknd_zkne_ecb ossl_cipher_hw_generic_ecb +#define cipher_hw_rv64i_zknd_zkne_ofb128 ossl_cipher_hw_generic_ofb128 +#define cipher_hw_rv64i_zknd_zkne_cfb128 ossl_cipher_hw_generic_cfb128 +#define cipher_hw_rv64i_zknd_zkne_cfb8 ossl_cipher_hw_generic_cfb8 +#define cipher_hw_rv64i_zknd_zkne_cfb1 ossl_cipher_hw_generic_cfb1 +#define cipher_hw_rv64i_zknd_zkne_ctr ossl_cipher_hw_generic_ctr + +static int cipher_hw_rv64i_zknd_zkne_initkey(PROV_CIPHER_CTX *dat, + const unsigned char *key, size_t keylen) +{ + int ret; + PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; + AES_KEY *ks = &adat->ks.ks; + + dat->ks = ks; + + if ((dat->mode == EVP_CIPH_ECB_MODE || dat->mode == EVP_CIPH_CBC_MODE) + && !dat->enc) { + ret = rv64i_zknd_set_decrypt_key(key, keylen * 8, ks); + dat->block = (block128_f) rv64i_zknd_decrypt; + dat->stream.cbc = NULL; + } else { + ret = rv64i_zkne_set_encrypt_key(key, keylen * 8, ks); + dat->block = (block128_f) rv64i_zkne_encrypt; + dat->stream.cbc = NULL; + } + + if (ret < 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED); + return 0; + } + + return 1; +} + +/*- + * RISC-V RV64 ZVKNED support for AES modes ecb, cbc, ofb, cfb, ctr. + * This file is included by cipher_aes_hw.c + */ + +#define cipher_hw_rv64i_zvkned_cbc ossl_cipher_hw_generic_cbc +#define cipher_hw_rv64i_zvkned_ecb ossl_cipher_hw_generic_ecb +#define cipher_hw_rv64i_zvkned_ofb128 ossl_cipher_hw_generic_ofb128 +#define cipher_hw_rv64i_zvkned_cfb128 ossl_cipher_hw_generic_cfb128 +#define cipher_hw_rv64i_zvkned_cfb8 ossl_cipher_hw_generic_cfb8 +#define cipher_hw_rv64i_zvkned_cfb1 ossl_cipher_hw_generic_cfb1 +#define cipher_hw_rv64i_zvkned_ctr ossl_cipher_hw_generic_ctr + +static int cipher_hw_rv64i_zvkned_initkey(PROV_CIPHER_CTX *dat, + const unsigned char *key, + size_t keylen) +{ + int ret; + PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; + AES_KEY *ks = &adat->ks.ks; + + dat->ks = ks; + + /* + * Zvkned only supports 128 and 256 bit keys for key schedule generation. + * For AES-192 case, we could fallback to `AES_set_encrypt_key`. + * All Zvkned-based implementations use the same `encrypt-key` scheduling + * for both encryption and decryption. + */ + if (keylen * 8 == 128 || keylen * 8 == 256) { + ret = rv64i_zvkned_set_encrypt_key(key, keylen * 8, ks); + } else { + ret = AES_set_encrypt_key(key, keylen * 8, ks); + } + + if (dat->mode == EVP_CIPH_CBC_MODE) { + if (dat->enc) { + dat->stream.cbc = (cbc128_f) rv64i_zvkned_cbc_encrypt; + } else { + dat->stream.cbc = (cbc128_f) rv64i_zvkned_cbc_decrypt; + } + } else if (dat->mode == EVP_CIPH_CTR_MODE) { + if (RISCV_HAS_ZVKB()) { + dat->stream.ctr = (ctr128_f) rv64i_zvkb_zvkned_ctr32_encrypt_blocks; + } + } else if (dat->mode == EVP_CIPH_ECB_MODE) { + if (dat->enc) { + dat->stream.ecb = (ecb128_f) rv64i_zvkned_ecb_encrypt; + } else { + dat->stream.ecb = (ecb128_f) rv64i_zvkned_ecb_decrypt; + } + } + + /* Zvkned supports aes-128/192/256 encryption and decryption. */ + if ((dat->mode == EVP_CIPH_ECB_MODE || dat->mode == EVP_CIPH_CBC_MODE) && + !dat->enc) { + dat->block = (block128_f) rv64i_zvkned_decrypt; + } else { + dat->block = (block128_f) rv64i_zvkned_encrypt; + } + + if (ret < 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED); + return 0; + } + + return 1; +} + +#define PROV_CIPHER_HW_declare(mode) \ +static const PROV_CIPHER_HW rv64i_zknd_zkne_##mode = { \ + cipher_hw_rv64i_zknd_zkne_initkey, \ + cipher_hw_rv64i_zknd_zkne_##mode, \ + cipher_hw_aes_copyctx \ +}; \ +static const PROV_CIPHER_HW rv64i_zvkned_##mode = { \ + cipher_hw_rv64i_zvkned_initkey, \ + cipher_hw_rv64i_zvkned_##mode, \ + cipher_hw_aes_copyctx \ +}; +#define PROV_CIPHER_HW_select(mode) \ +if (RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) \ + return &rv64i_zvkned_##mode; \ +else if (RISCV_HAS_ZKND_AND_ZKNE()) \ + return &rv64i_zknd_zkne_##mode; diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_s390x.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_s390x.inc new file mode 100644 index 000000000000..6c4a4cc99511 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_s390x.inc @@ -0,0 +1,204 @@ +/* + * Copyright 2001-2024 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 + */ + +/* + * IBM S390X support for AES modes ecb, cbc, ofb, cfb, ctr. + * This file is included by cipher_aes_hw.c + */ + +#include "s390x_arch.h" + +#include <stdio.h> + +#define s390x_aes_cbc_initkey cipher_hw_aes_initkey +#define s390x_aes_cfb1_initkey cipher_hw_aes_initkey +#define s390x_aes_ctr_initkey cipher_hw_aes_initkey +#define s390x_aes_cbc_cipher_hw ossl_cipher_hw_generic_cbc +#define s390x_aes_cfb1_cipher_hw ossl_cipher_hw_generic_cfb1 +#define s390x_aes_ctr_cipher_hw ossl_cipher_hw_generic_ctr + +#define S390X_aes_128_ofb128_CAPABLE S390X_aes_128_ofb_CAPABLE +#define S390X_aes_192_ofb128_CAPABLE S390X_aes_192_ofb_CAPABLE +#define S390X_aes_256_ofb128_CAPABLE S390X_aes_256_ofb_CAPABLE +#define S390X_aes_128_cfb128_CAPABLE S390X_aes_128_cfb_CAPABLE +#define S390X_aes_192_cfb128_CAPABLE S390X_aes_192_cfb_CAPABLE +#define S390X_aes_256_cfb128_CAPABLE S390X_aes_256_cfb_CAPABLE + +static int s390x_aes_ecb_initkey(PROV_CIPHER_CTX *dat, + const unsigned char *key, size_t keylen) +{ + PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; + + adat->plat.s390x.fc = S390X_AES_FC(keylen); + memcpy(adat->plat.s390x.param.km.k, key, keylen); + return 1; +} + +static int s390x_aes_ecb_cipher_hw(PROV_CIPHER_CTX *dat, unsigned char *out, + const unsigned char *in, size_t len) +{ + PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; + unsigned int modifier = adat->base.enc ? 0 : S390X_DECRYPT; + + s390x_km(in, len, out, adat->plat.s390x.fc | modifier, + &adat->plat.s390x.param.km); + return 1; +} + +static int s390x_aes_ofb128_initkey(PROV_CIPHER_CTX *dat, + const unsigned char *key, size_t keylen) +{ + PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; + + memcpy(adat->plat.s390x.param.kmo_kmf.k, key, keylen); + adat->plat.s390x.fc = S390X_AES_FC(keylen); + return 1; +} + +static int s390x_aes_ofb128_cipher_hw(PROV_CIPHER_CTX *dat, unsigned char *out, + const unsigned char *in, size_t len) +{ + PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; + int n = dat->num; + int rem; + + memcpy(adat->plat.s390x.param.kmo_kmf.cv, dat->iv, dat->ivlen); + while (n && len) { + *out = *in ^ adat->plat.s390x.param.kmo_kmf.cv[n]; + n = (n + 1) & 0xf; + --len; + ++in; + ++out; + } + + rem = len & 0xf; + + len &= ~(size_t)0xf; + if (len) { + s390x_kmo(in, len, out, adat->plat.s390x.fc, + &adat->plat.s390x.param.kmo_kmf); + + out += len; + in += len; + } + + if (rem) { + s390x_km(adat->plat.s390x.param.kmo_kmf.cv, 16, + adat->plat.s390x.param.kmo_kmf.cv, + adat->plat.s390x.fc, + adat->plat.s390x.param.kmo_kmf.k); + + while (rem--) { + out[n] = in[n] ^ adat->plat.s390x.param.kmo_kmf.cv[n]; + ++n; + } + } + + memcpy(dat->iv, adat->plat.s390x.param.kmo_kmf.cv, dat->ivlen); + dat->num = n; + return 1; +} + +static int s390x_aes_cfb128_initkey(PROV_CIPHER_CTX *dat, + const unsigned char *key, size_t keylen) +{ + PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; + + adat->plat.s390x.fc = S390X_AES_FC(keylen); + adat->plat.s390x.fc |= 16 << 24; /* 16 bytes cipher feedback */ + memcpy(adat->plat.s390x.param.kmo_kmf.k, key, keylen); + return 1; +} + +static int s390x_aes_cfb128_cipher_hw(PROV_CIPHER_CTX *dat, unsigned char *out, + const unsigned char *in, size_t len) +{ + PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; + unsigned int modifier = adat->base.enc ? 0 : S390X_DECRYPT; + int n = dat->num; + int rem; + unsigned char tmp; + + memcpy(adat->plat.s390x.param.kmo_kmf.cv, dat->iv, dat->ivlen); + while (n && len) { + tmp = *in; + *out = adat->plat.s390x.param.kmo_kmf.cv[n] ^ tmp; + adat->plat.s390x.param.kmo_kmf.cv[n] = dat->enc ? *out : tmp; + n = (n + 1) & 0xf; + --len; + ++in; + ++out; + } + + rem = len & 0xf; + + len &= ~(size_t)0xf; + if (len) { + s390x_kmf(in, len, out, adat->plat.s390x.fc | modifier, + &adat->plat.s390x.param.kmo_kmf); + + out += len; + in += len; + } + + if (rem) { + s390x_km(adat->plat.s390x.param.kmo_kmf.cv, 16, + adat->plat.s390x.param.kmo_kmf.cv, + S390X_AES_FC(dat->keylen), + adat->plat.s390x.param.kmo_kmf.k); + + while (rem--) { + tmp = in[n]; + out[n] = adat->plat.s390x.param.kmo_kmf.cv[n] ^ tmp; + adat->plat.s390x.param.kmo_kmf.cv[n] = dat->enc ? out[n] : tmp; + ++n; + } + } + + memcpy(dat->iv, adat->plat.s390x.param.kmo_kmf.cv, dat->ivlen); + dat->num = n; + return 1; +} + +static int s390x_aes_cfb8_initkey(PROV_CIPHER_CTX *dat, + const unsigned char *key, size_t keylen) +{ + PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; + + adat->plat.s390x.fc = S390X_AES_FC(keylen); + adat->plat.s390x.fc |= 1 << 24; /* 1 byte cipher feedback */ + memcpy(adat->plat.s390x.param.kmo_kmf.k, key, keylen); + return 1; +} + +static int s390x_aes_cfb8_cipher_hw(PROV_CIPHER_CTX *dat, unsigned char *out, + const unsigned char *in, size_t len) +{ + PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; + unsigned int modifier = adat->base.enc ? 0 : S390X_DECRYPT; + + memcpy(adat->plat.s390x.param.kmo_kmf.cv, dat->iv, dat->ivlen); + s390x_kmf(in, len, out, adat->plat.s390x.fc | modifier, + &adat->plat.s390x.param.kmo_kmf); + memcpy(dat->iv, adat->plat.s390x.param.kmo_kmf.cv, dat->ivlen); + return 1; +} + +#define PROV_CIPHER_HW_declare(mode) \ +static const PROV_CIPHER_HW s390x_aes_##mode = { \ + s390x_aes_##mode##_initkey, \ + s390x_aes_##mode##_cipher_hw, \ + cipher_hw_aes_copyctx \ +}; +#define PROV_CIPHER_HW_select(mode) \ +if ((keybits == 128 && S390X_aes_128_##mode##_CAPABLE) \ + || (keybits == 192 && S390X_aes_192_##mode##_CAPABLE) \ + || (keybits == 256 && S390X_aes_256_##mode##_CAPABLE)) \ + return &s390x_aes_##mode; + diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_t4.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_t4.inc new file mode 100644 index 000000000000..28454fc5089b --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_t4.inc @@ -0,0 +1,96 @@ +/* + * Copyright 2001-2021 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 + */ + +/*- + * Sparc t4 support for AES modes ecb, cbc, ofb, cfb, ctr. + * This file is included by cipher_aes_hw.c + */ + +static int cipher_hw_aes_t4_initkey(PROV_CIPHER_CTX *dat, + const unsigned char *key, size_t keylen) +{ + int ret, bits; + PROV_AES_CTX *adat = (PROV_AES_CTX *)dat; + AES_KEY *ks = &adat->ks.ks; + + dat->ks = (const void *)ks; /* used by cipher_hw_generic_XXX */ + + bits = keylen * 8; + if ((dat->mode == EVP_CIPH_ECB_MODE || dat->mode == EVP_CIPH_CBC_MODE) + && !dat->enc) { + ret = 0; + aes_t4_set_decrypt_key(key, bits, ks); + dat->block = (block128_f)aes_t4_decrypt; + switch (bits) { + case 128: + dat->stream.cbc = dat->mode == EVP_CIPH_CBC_MODE ? + (cbc128_f)aes128_t4_cbc_decrypt : NULL; + break; + case 192: + dat->stream.cbc = dat->mode == EVP_CIPH_CBC_MODE ? + (cbc128_f)aes192_t4_cbc_decrypt : NULL; + break; + case 256: + dat->stream.cbc = dat->mode == EVP_CIPH_CBC_MODE ? + (cbc128_f)aes256_t4_cbc_decrypt : NULL; + break; + default: + ret = -1; + } + } else { + ret = 0; + aes_t4_set_encrypt_key(key, bits, ks); + dat->block = (block128_f)aes_t4_encrypt; + switch (bits) { + case 128: + if (dat->mode == EVP_CIPH_CBC_MODE) + dat->stream.cbc = (cbc128_f)aes128_t4_cbc_encrypt; + else if (dat->mode == EVP_CIPH_CTR_MODE) + dat->stream.ctr = (ctr128_f)aes128_t4_ctr32_encrypt; + else + dat->stream.cbc = NULL; + break; + case 192: + if (dat->mode == EVP_CIPH_CBC_MODE) + dat->stream.cbc = (cbc128_f)aes192_t4_cbc_encrypt; + else if (dat->mode == EVP_CIPH_CTR_MODE) + dat->stream.ctr = (ctr128_f)aes192_t4_ctr32_encrypt; + else + dat->stream.cbc = NULL; + break; + case 256: + if (dat->mode == EVP_CIPH_CBC_MODE) + dat->stream.cbc = (cbc128_f)aes256_t4_cbc_encrypt; + else if (dat->mode == EVP_CIPH_CTR_MODE) + dat->stream.ctr = (ctr128_f)aes256_t4_ctr32_encrypt; + else + dat->stream.cbc = NULL; + break; + default: + ret = -1; + } + } + + if (ret < 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED); + return 0; + } + + return 1; +} + +#define PROV_CIPHER_HW_declare(mode) \ +static const PROV_CIPHER_HW aes_t4_##mode = { \ + cipher_hw_aes_t4_initkey, \ + ossl_cipher_hw_generic_##mode, \ + cipher_hw_aes_copyctx \ +}; +#define PROV_CIPHER_HW_select(mode) \ + if (SPARC_AES_CAPABLE) \ + return &aes_t4_##mode; diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb.c new file mode 100644 index 000000000000..876ff294246c --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb.c @@ -0,0 +1,568 @@ +/* + * Copyright 2019-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 + */ + +/* + * AES low level APIs are deprecated for public use, but still ok for internal + * use where we're using them to implement the higher level EVP interface, as is + * the case here. + */ +#include "internal/deprecated.h" + +#include <openssl/proverr.h> +#include "cipher_aes_ocb.h" +#include "prov/providercommon.h" +#include "prov/ciphercommon_aead.h" +#include "prov/implementations.h" + +#define AES_OCB_FLAGS AEAD_FLAGS + +#define OCB_DEFAULT_TAG_LEN 16 +#define OCB_DEFAULT_IV_LEN 12 +#define OCB_MIN_IV_LEN 1 +#define OCB_MAX_IV_LEN 15 + +PROV_CIPHER_FUNC(int, ocb_cipher, (PROV_AES_OCB_CTX *ctx, + const unsigned char *in, unsigned char *out, + size_t nextblock)); +/* forward declarations */ +static OSSL_FUNC_cipher_encrypt_init_fn aes_ocb_einit; +static OSSL_FUNC_cipher_decrypt_init_fn aes_ocb_dinit; +static OSSL_FUNC_cipher_update_fn aes_ocb_block_update; +static OSSL_FUNC_cipher_final_fn aes_ocb_block_final; +static OSSL_FUNC_cipher_cipher_fn aes_ocb_cipher; +static OSSL_FUNC_cipher_freectx_fn aes_ocb_freectx; +static OSSL_FUNC_cipher_dupctx_fn aes_ocb_dupctx; +static OSSL_FUNC_cipher_get_ctx_params_fn aes_ocb_get_ctx_params; +static OSSL_FUNC_cipher_set_ctx_params_fn aes_ocb_set_ctx_params; +static OSSL_FUNC_cipher_gettable_ctx_params_fn cipher_ocb_gettable_ctx_params; +static OSSL_FUNC_cipher_settable_ctx_params_fn cipher_ocb_settable_ctx_params; + +/* + * The following methods could be moved into PROV_AES_OCB_HW if + * multiple hardware implementations are ever needed. + */ +static ossl_inline int aes_generic_ocb_setiv(PROV_AES_OCB_CTX *ctx, + const unsigned char *iv, + size_t ivlen, size_t taglen) +{ + return (CRYPTO_ocb128_setiv(&ctx->ocb, iv, ivlen, taglen) == 1); +} + +static ossl_inline int aes_generic_ocb_setaad(PROV_AES_OCB_CTX *ctx, + const unsigned char *aad, + size_t alen) +{ + return CRYPTO_ocb128_aad(&ctx->ocb, aad, alen) == 1; +} + +static ossl_inline int aes_generic_ocb_gettag(PROV_AES_OCB_CTX *ctx, + unsigned char *tag, size_t tlen) +{ + return CRYPTO_ocb128_tag(&ctx->ocb, tag, tlen) > 0; +} + +static ossl_inline int aes_generic_ocb_final(PROV_AES_OCB_CTX *ctx) +{ + return (CRYPTO_ocb128_finish(&ctx->ocb, ctx->tag, ctx->taglen) == 0); +} + +static ossl_inline void aes_generic_ocb_cleanup(PROV_AES_OCB_CTX *ctx) +{ + CRYPTO_ocb128_cleanup(&ctx->ocb); +} + +static ossl_inline int aes_generic_ocb_cipher(PROV_AES_OCB_CTX *ctx, + const unsigned char *in, + unsigned char *out, size_t len) +{ + if (ctx->base.enc) { + if (!CRYPTO_ocb128_encrypt(&ctx->ocb, in, out, len)) + return 0; + } else { + if (!CRYPTO_ocb128_decrypt(&ctx->ocb, in, out, len)) + return 0; + } + return 1; +} + +static ossl_inline int aes_generic_ocb_copy_ctx(PROV_AES_OCB_CTX *dst, + PROV_AES_OCB_CTX *src) +{ + return CRYPTO_ocb128_copy_ctx(&dst->ocb, &src->ocb, + &dst->ksenc.ks, &dst->ksdec.ks); +} + +/*- + * Provider dispatch functions + */ +static int aes_ocb_init(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[], int enc) +{ + PROV_AES_OCB_CTX *ctx = (PROV_AES_OCB_CTX *)vctx; + + if (!ossl_prov_is_running()) + return 0; + + ctx->aad_buf_len = 0; + ctx->data_buf_len = 0; + ctx->base.enc = enc; + + if (iv != NULL) { + if (ivlen != ctx->base.ivlen) { + /* IV len must be 1 to 15 */ + if (ivlen < OCB_MIN_IV_LEN || ivlen > OCB_MAX_IV_LEN) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); + return 0; + } + ctx->base.ivlen = ivlen; + } + if (!ossl_cipher_generic_initiv(&ctx->base, iv, ivlen)) + return 0; + ctx->iv_state = IV_STATE_BUFFERED; + } + if (key != NULL) { + if (keylen != ctx->base.keylen) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + if (!ctx->base.hw->init(&ctx->base, key, keylen)) + return 0; + } + return aes_ocb_set_ctx_params(ctx, params); +} + +static int aes_ocb_einit(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + return aes_ocb_init(vctx, key, keylen, iv, ivlen, params, 1); +} + +static int aes_ocb_dinit(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + return aes_ocb_init(vctx, key, keylen, iv, ivlen, params, 0); +} + +/* + * Because of the way OCB works, both the AAD and data are buffered in the + * same way. Only the last block can be a partial block. + */ +static int aes_ocb_block_update_internal(PROV_AES_OCB_CTX *ctx, + unsigned char *buf, size_t *bufsz, + unsigned char *out, size_t *outl, + size_t outsize, const unsigned char *in, + size_t inl, OSSL_ocb_cipher_fn ciph) +{ + size_t nextblocks; + size_t outlint = 0; + + if (*bufsz != 0) + nextblocks = ossl_cipher_fillblock(buf, bufsz, AES_BLOCK_SIZE, &in, &inl); + else + nextblocks = inl & ~(AES_BLOCK_SIZE-1); + + if (*bufsz == AES_BLOCK_SIZE) { + if (outsize < AES_BLOCK_SIZE) { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + return 0; + } + if (!ciph(ctx, buf, out, AES_BLOCK_SIZE)) { + ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); + return 0; + } + *bufsz = 0; + outlint = AES_BLOCK_SIZE; + if (out != NULL) + out += AES_BLOCK_SIZE; + } + if (nextblocks > 0) { + outlint += nextblocks; + if (outsize < outlint) { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + return 0; + } + if (!ciph(ctx, in, out, nextblocks)) { + ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); + return 0; + } + in += nextblocks; + inl -= nextblocks; + } + if (inl != 0 + && !ossl_cipher_trailingdata(buf, bufsz, AES_BLOCK_SIZE, &in, &inl)) { + /* PROVerr already called */ + return 0; + } + + *outl = outlint; + return inl == 0; +} + +/* A wrapper function that has the same signature as cipher */ +static int cipher_updateaad(PROV_AES_OCB_CTX *ctx, const unsigned char *in, + unsigned char *out, size_t len) +{ + return aes_generic_ocb_setaad(ctx, in, len); +} + +static int update_iv(PROV_AES_OCB_CTX *ctx) +{ + if (ctx->iv_state == IV_STATE_FINISHED + || ctx->iv_state == IV_STATE_UNINITIALISED) + return 0; + if (ctx->iv_state == IV_STATE_BUFFERED) { + if (!aes_generic_ocb_setiv(ctx, ctx->base.iv, ctx->base.ivlen, + ctx->taglen)) + return 0; + ctx->iv_state = IV_STATE_COPIED; + } + return 1; +} + +static int aes_ocb_block_update(void *vctx, unsigned char *out, size_t *outl, + size_t outsize, const unsigned char *in, + size_t inl) +{ + PROV_AES_OCB_CTX *ctx = (PROV_AES_OCB_CTX *)vctx; + unsigned char *buf; + size_t *buflen; + OSSL_ocb_cipher_fn fn; + + if (!ctx->key_set || !update_iv(ctx)) + return 0; + + if (inl == 0) { + *outl = 0; + return 1; + } + + /* Are we dealing with AAD or normal data here? */ + if (out == NULL) { + buf = ctx->aad_buf; + buflen = &ctx->aad_buf_len; + fn = cipher_updateaad; + } else { + buf = ctx->data_buf; + buflen = &ctx->data_buf_len; + fn = aes_generic_ocb_cipher; + } + return aes_ocb_block_update_internal(ctx, buf, buflen, out, outl, outsize, + in, inl, fn); +} + +static int aes_ocb_block_final(void *vctx, unsigned char *out, size_t *outl, + size_t outsize) +{ + PROV_AES_OCB_CTX *ctx = (PROV_AES_OCB_CTX *)vctx; + + if (!ossl_prov_is_running()) + return 0; + + /* If no block_update has run then the iv still needs to be set */ + if (!ctx->key_set || !update_iv(ctx)) + return 0; + + /* + * Empty the buffer of any partial block that we might have been provided, + * both for data and AAD + */ + *outl = 0; + if (ctx->data_buf_len > 0) { + if (!aes_generic_ocb_cipher(ctx, ctx->data_buf, out, ctx->data_buf_len)) + return 0; + *outl = ctx->data_buf_len; + ctx->data_buf_len = 0; + } + if (ctx->aad_buf_len > 0) { + if (!aes_generic_ocb_setaad(ctx, ctx->aad_buf, ctx->aad_buf_len)) + return 0; + ctx->aad_buf_len = 0; + } + if (ctx->base.enc) { + /* If encrypting then just get the tag */ + if (!aes_generic_ocb_gettag(ctx, ctx->tag, ctx->taglen)) + return 0; + } else { + /* If decrypting then verify */ + if (ctx->taglen == 0) + return 0; + if (!aes_generic_ocb_final(ctx)) + return 0; + } + /* Don't reuse the IV */ + ctx->iv_state = IV_STATE_FINISHED; + return 1; +} + +static void *aes_ocb_newctx(void *provctx, size_t kbits, size_t blkbits, + size_t ivbits, unsigned int mode, uint64_t flags) +{ + PROV_AES_OCB_CTX *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + ctx = OPENSSL_zalloc(sizeof(*ctx)); + if (ctx != NULL) { + ossl_cipher_generic_initkey(ctx, kbits, blkbits, ivbits, mode, flags, + ossl_prov_cipher_hw_aes_ocb(kbits), NULL); + ctx->taglen = OCB_DEFAULT_TAG_LEN; + } + return ctx; +} + +static void aes_ocb_freectx(void *vctx) +{ + PROV_AES_OCB_CTX *ctx = (PROV_AES_OCB_CTX *)vctx; + + if (ctx != NULL) { + aes_generic_ocb_cleanup(ctx); + ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx); + OPENSSL_clear_free(ctx, sizeof(*ctx)); + } +} + +static void *aes_ocb_dupctx(void *vctx) +{ + PROV_AES_OCB_CTX *in = (PROV_AES_OCB_CTX *)vctx; + PROV_AES_OCB_CTX *ret; + + if (!ossl_prov_is_running()) + return NULL; + + ret = OPENSSL_malloc(sizeof(*ret)); + if (ret == NULL) + return NULL; + *ret = *in; + if (!aes_generic_ocb_copy_ctx(ret, in)) { + OPENSSL_free(ret); + ret = NULL; + } + return ret; +} + +static int aes_ocb_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_AES_OCB_CTX *ctx = (PROV_AES_OCB_CTX *)vctx; + const OSSL_PARAM *p; + size_t sz; + + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TAG); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_OCTET_STRING) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + if (p->data == NULL) { + /* Tag len must be 0 to 16 */ + if (p->data_size > OCB_MAX_TAG_LEN) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_TAG_LENGTH); + return 0; + } + ctx->taglen = p->data_size; + } else { + if (ctx->base.enc) { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } + if (p->data_size != ctx->taglen) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_TAG_LENGTH); + return 0; + } + memcpy(ctx->tag, p->data, p->data_size); + } + } + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_IVLEN); + if (p != NULL) { + if (!OSSL_PARAM_get_size_t(p, &sz)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + /* IV len must be 1 to 15 */ + if (sz < OCB_MIN_IV_LEN || sz > OCB_MAX_IV_LEN) + return 0; + if (ctx->base.ivlen != sz) { + ctx->base.ivlen = sz; + ctx->iv_state = IV_STATE_UNINITIALISED; + } + } + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_KEYLEN); + if (p != NULL) { + size_t keylen; + + if (!OSSL_PARAM_get_size_t(p, &keylen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + if (ctx->base.keylen != keylen) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + } + return 1; +} + +static int aes_ocb_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + PROV_AES_OCB_CTX *ctx = (PROV_AES_OCB_CTX *)vctx; + OSSL_PARAM *p; + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->base.ivlen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->base.keylen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TAGLEN); + if (p != NULL) { + if (!OSSL_PARAM_set_size_t(p, ctx->taglen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + } + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IV); + if (p != NULL) { + if (ctx->base.ivlen > p->data_size) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); + return 0; + } + if (!OSSL_PARAM_set_octet_string(p, ctx->base.oiv, ctx->base.ivlen) + && !OSSL_PARAM_set_octet_ptr(p, &ctx->base.oiv, ctx->base.ivlen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_UPDATED_IV); + if (p != NULL) { + if (ctx->base.ivlen > p->data_size) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); + return 0; + } + if (!OSSL_PARAM_set_octet_string(p, ctx->base.iv, ctx->base.ivlen) + && !OSSL_PARAM_set_octet_ptr(p, &ctx->base.iv, ctx->base.ivlen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TAG); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_OCTET_STRING) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + if (!ctx->base.enc || p->data_size != ctx->taglen) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_TAG_LENGTH); + return 0; + } + memcpy(p->data, ctx->tag, ctx->taglen); + } + return 1; +} + +static const OSSL_PARAM cipher_ocb_known_gettable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_TAGLEN, NULL), + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_IV, NULL, 0), + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_UPDATED_IV, NULL, 0), + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, NULL, 0), + OSSL_PARAM_END +}; +static const OSSL_PARAM *cipher_ocb_gettable_ctx_params(ossl_unused void *cctx, + ossl_unused void *p_ctx) +{ + return cipher_ocb_known_gettable_ctx_params; +} + +static const OSSL_PARAM cipher_ocb_known_settable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_IVLEN, NULL), + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, NULL, 0), + OSSL_PARAM_END +}; +static const OSSL_PARAM *cipher_ocb_settable_ctx_params(ossl_unused void *cctx, + ossl_unused void *p_ctx) +{ + return cipher_ocb_known_settable_ctx_params; +} + +static int aes_ocb_cipher(void *vctx, unsigned char *out, size_t *outl, + size_t outsize, const unsigned char *in, size_t inl) +{ + PROV_AES_OCB_CTX *ctx = (PROV_AES_OCB_CTX *)vctx; + + if (!ossl_prov_is_running()) + return 0; + + if (outsize < inl) { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + return 0; + } + + if (!aes_generic_ocb_cipher(ctx, in, out, inl)) { + ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); + return 0; + } + + *outl = inl; + return 1; +} + +#define IMPLEMENT_cipher(mode, UCMODE, flags, kbits, blkbits, ivbits) \ +static OSSL_FUNC_cipher_get_params_fn aes_##kbits##_##mode##_get_params; \ +static int aes_##kbits##_##mode##_get_params(OSSL_PARAM params[]) \ +{ \ + return ossl_cipher_generic_get_params(params, EVP_CIPH_##UCMODE##_MODE, \ + flags, kbits, blkbits, ivbits); \ +} \ +static OSSL_FUNC_cipher_newctx_fn aes_##kbits##_##mode##_newctx; \ +static void *aes_##kbits##_##mode##_newctx(void *provctx) \ +{ \ + return aes_##mode##_newctx(provctx, kbits, blkbits, ivbits, \ + EVP_CIPH_##UCMODE##_MODE, flags); \ +} \ +const OSSL_DISPATCH ossl_##aes##kbits##mode##_functions[] = { \ + { OSSL_FUNC_CIPHER_NEWCTX, \ + (void (*)(void))aes_##kbits##_##mode##_newctx }, \ + { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))aes_##mode##_einit }, \ + { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))aes_##mode##_dinit }, \ + { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))aes_##mode##_block_update }, \ + { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))aes_##mode##_block_final }, \ + { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))aes_ocb_cipher }, \ + { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))aes_##mode##_freectx }, \ + { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void))aes_##mode##_dupctx }, \ + { OSSL_FUNC_CIPHER_GET_PARAMS, \ + (void (*)(void))aes_##kbits##_##mode##_get_params }, \ + { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, \ + (void (*)(void))aes_##mode##_get_ctx_params }, \ + { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \ + (void (*)(void))aes_##mode##_set_ctx_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \ + (void (*)(void))ossl_cipher_generic_gettable_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \ + (void (*)(void))cipher_ocb_gettable_ctx_params }, \ + { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \ + (void (*)(void))cipher_ocb_settable_ctx_params }, \ + OSSL_DISPATCH_END \ +} + +IMPLEMENT_cipher(ocb, OCB, AES_OCB_FLAGS, 256, 128, OCB_DEFAULT_IV_LEN * 8); +IMPLEMENT_cipher(ocb, OCB, AES_OCB_FLAGS, 192, 128, OCB_DEFAULT_IV_LEN * 8); +IMPLEMENT_cipher(ocb, OCB, AES_OCB_FLAGS, 128, 128, OCB_DEFAULT_IV_LEN * 8); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb.h b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb.h new file mode 100644 index 000000000000..370717b43614 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb.h @@ -0,0 +1,39 @@ +/* + * Copyright 2019-2020 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 + */ + +#include <openssl/aes.h> +#include "prov/ciphercommon.h" +#include "crypto/aes_platform.h" + +#define OCB_MAX_TAG_LEN AES_BLOCK_SIZE +#define OCB_MAX_DATA_LEN AES_BLOCK_SIZE +#define OCB_MAX_AAD_LEN AES_BLOCK_SIZE + +typedef struct prov_aes_ocb_ctx_st { + PROV_CIPHER_CTX base; /* Must be first */ + union { + OSSL_UNION_ALIGN; + AES_KEY ks; + } ksenc; /* AES key schedule to use for encryption/aad */ + union { + OSSL_UNION_ALIGN; + AES_KEY ks; + } ksdec; /* AES key schedule to use for decryption */ + OCB128_CONTEXT ocb; + unsigned int iv_state; /* set to one of IV_STATE_XXX */ + unsigned int key_set : 1; + size_t taglen; + size_t data_buf_len; + size_t aad_buf_len; + unsigned char tag[OCB_MAX_TAG_LEN]; + unsigned char data_buf[OCB_MAX_DATA_LEN]; /* Store partial data blocks */ + unsigned char aad_buf[OCB_MAX_AAD_LEN]; /* Store partial AAD blocks */ +} PROV_AES_OCB_CTX; + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_ocb(size_t keybits); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb_hw.c new file mode 100644 index 000000000000..00920408b918 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb_hw.c @@ -0,0 +1,209 @@ +/* + * Copyright 2019-2024 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 + */ + +/* + * This file uses the low level AES functions (which are deprecated for + * non-internal use) in order to implement provider AES ciphers. + */ +#include "internal/deprecated.h" + +#include "cipher_aes_ocb.h" + +#define OCB_SET_KEY_FN(fn_set_enc_key, fn_set_dec_key, \ + fn_block_enc, fn_block_dec, \ + fn_stream_enc, fn_stream_dec) \ +CRYPTO_ocb128_cleanup(&ctx->ocb); \ +fn_set_enc_key(key, keylen * 8, &ctx->ksenc.ks); \ +fn_set_dec_key(key, keylen * 8, &ctx->ksdec.ks); \ +if (!CRYPTO_ocb128_init(&ctx->ocb, &ctx->ksenc.ks, &ctx->ksdec.ks, \ + (block128_f)fn_block_enc, (block128_f)fn_block_dec, \ + ctx->base.enc ? (ocb128_f)fn_stream_enc : \ + (ocb128_f)fn_stream_dec)) \ + return 0; \ +ctx->key_set = 1 + + +static int cipher_hw_aes_ocb_generic_initkey(PROV_CIPHER_CTX *vctx, + const unsigned char *key, + size_t keylen) +{ + PROV_AES_OCB_CTX *ctx = (PROV_AES_OCB_CTX *)vctx; + +/* + * We set both the encrypt and decrypt key here because decrypt + * needs both. (i.e- AAD uses encrypt). + */ +# ifdef HWAES_CAPABLE + if (HWAES_CAPABLE) { + OCB_SET_KEY_FN(HWAES_set_encrypt_key, HWAES_set_decrypt_key, + HWAES_encrypt, HWAES_decrypt, + HWAES_ocb_encrypt, HWAES_ocb_decrypt); + } else +# endif +# ifdef VPAES_CAPABLE + if (VPAES_CAPABLE) { + OCB_SET_KEY_FN(vpaes_set_encrypt_key, vpaes_set_decrypt_key, + vpaes_encrypt, vpaes_decrypt, NULL, NULL); + } else +# endif + { + OCB_SET_KEY_FN(AES_set_encrypt_key, AES_set_decrypt_key, + AES_encrypt, AES_decrypt, NULL, NULL); + } + return 1; +} + +# if defined(AESNI_CAPABLE) + +static int cipher_hw_aes_ocb_aesni_initkey(PROV_CIPHER_CTX *vctx, + const unsigned char *key, + size_t keylen) +{ + PROV_AES_OCB_CTX *ctx = (PROV_AES_OCB_CTX *)vctx; + + OCB_SET_KEY_FN(aesni_set_encrypt_key, aesni_set_decrypt_key, + aesni_encrypt, aesni_decrypt, + aesni_ocb_encrypt, aesni_ocb_decrypt); + return 1; +} + +# define PROV_CIPHER_HW_declare() \ +static const PROV_CIPHER_HW aesni_ocb = { \ + cipher_hw_aes_ocb_aesni_initkey, \ + NULL \ +}; +# define PROV_CIPHER_HW_select() \ + if (AESNI_CAPABLE) \ + return &aesni_ocb; + +#elif defined(SPARC_AES_CAPABLE) + +static int cipher_hw_aes_ocb_t4_initkey(PROV_CIPHER_CTX *vctx, + const unsigned char *key, + size_t keylen) +{ + PROV_AES_OCB_CTX *ctx = (PROV_AES_OCB_CTX *)vctx; + + OCB_SET_KEY_FN(aes_t4_set_encrypt_key, aes_t4_set_decrypt_key, + aes_t4_encrypt, aes_t4_decrypt, NULL, NULL); + return 1; +} + +# define PROV_CIPHER_HW_declare() \ +static const PROV_CIPHER_HW aes_t4_ocb = { \ + cipher_hw_aes_ocb_t4_initkey, \ + NULL \ +}; +# define PROV_CIPHER_HW_select() \ + if (SPARC_AES_CAPABLE) \ + return &aes_t4_ocb; + +#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 + +static int cipher_hw_aes_ocb_rv64i_zknd_zkne_initkey(PROV_CIPHER_CTX *vctx, + const unsigned char *key, + size_t keylen) +{ + PROV_AES_OCB_CTX *ctx = (PROV_AES_OCB_CTX *)vctx; + + OCB_SET_KEY_FN(rv64i_zkne_set_encrypt_key, rv64i_zknd_set_decrypt_key, + rv64i_zkne_encrypt, rv64i_zknd_decrypt, NULL, NULL); + return 1; +} + +static int cipher_hw_aes_ocb_rv64i_zvkned_initkey(PROV_CIPHER_CTX *vctx, + const unsigned char *key, + size_t keylen) +{ + PROV_AES_OCB_CTX *ctx = (PROV_AES_OCB_CTX *)vctx; + + /* Zvkned only supports 128 and 256 bit keys. */ + if (keylen * 8 == 128 || keylen * 8 == 256) { + OCB_SET_KEY_FN(rv64i_zvkned_set_encrypt_key, + rv64i_zvkned_set_decrypt_key, + rv64i_zvkned_encrypt, rv64i_zvkned_decrypt, + NULL, NULL); + } else { + OCB_SET_KEY_FN(AES_set_encrypt_key, AES_set_encrypt_key, + rv64i_zvkned_encrypt, rv64i_zvkned_decrypt, + NULL, NULL); + } + return 1; +} + +# define PROV_CIPHER_HW_declare() \ +static const PROV_CIPHER_HW aes_rv64i_zknd_zkne_ocb = { \ + cipher_hw_aes_ocb_rv64i_zknd_zkne_initkey, \ + NULL \ +}; \ +static const PROV_CIPHER_HW aes_rv64i_zvkned_ocb = { \ + cipher_hw_aes_ocb_rv64i_zvkned_initkey, \ + NULL \ +}; +# define PROV_CIPHER_HW_select() \ + if (RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) \ + return &aes_rv64i_zvkned_ocb; \ + else if (RISCV_HAS_ZKND_AND_ZKNE()) \ + return &aes_rv64i_zknd_zkne_ocb; + +#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 + +static int cipher_hw_aes_ocb_rv32i_zknd_zkne_initkey(PROV_CIPHER_CTX *vctx, + const unsigned char *key, + size_t keylen) +{ + PROV_AES_OCB_CTX *ctx = (PROV_AES_OCB_CTX *)vctx; + + OCB_SET_KEY_FN(rv32i_zkne_set_encrypt_key, rv32i_zknd_zkne_set_decrypt_key, + rv32i_zkne_encrypt, rv32i_zknd_decrypt, NULL, NULL); + return 1; +} + +static int cipher_hw_aes_ocb_rv32i_zbkb_zknd_zkne_initkey(PROV_CIPHER_CTX *vctx, + const unsigned char *key, + size_t keylen) +{ + PROV_AES_OCB_CTX *ctx = (PROV_AES_OCB_CTX *)vctx; + + OCB_SET_KEY_FN(rv32i_zbkb_zkne_set_encrypt_key, rv32i_zbkb_zknd_zkne_set_decrypt_key, + rv32i_zkne_encrypt, rv32i_zknd_decrypt, NULL, NULL); + return 1; +} + +# define PROV_CIPHER_HW_declare() \ +static const PROV_CIPHER_HW aes_rv32i_zknd_zkne_ocb = { \ + cipher_hw_aes_ocb_rv32i_zknd_zkne_initkey, \ + NULL \ +}; \ +static const PROV_CIPHER_HW aes_rv32i_zbkb_zknd_zkne_ocb = { \ + cipher_hw_aes_ocb_rv32i_zbkb_zknd_zkne_initkey, \ + NULL \ +}; +# define PROV_CIPHER_HW_select() \ + if (RISCV_HAS_ZBKB_AND_ZKND_AND_ZKNE()) \ + return &aes_rv32i_zbkb_zknd_zkne_ocb; \ + if (RISCV_HAS_ZKND_AND_ZKNE()) \ + return &aes_rv32i_zknd_zkne_ocb; +#else +# define PROV_CIPHER_HW_declare() +# define PROV_CIPHER_HW_select() +# endif + +static const PROV_CIPHER_HW aes_generic_ocb = { + cipher_hw_aes_ocb_generic_initkey, + NULL +}; +PROV_CIPHER_HW_declare() +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_ocb(size_t keybits) +{ + PROV_CIPHER_HW_select() + return &aes_generic_ocb; +} + + diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_siv.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_siv.c new file mode 100644 index 000000000000..ccac2bbe9663 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_siv.c @@ -0,0 +1,296 @@ +/* + * Copyright 2019-2025 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 + */ + +/* Dispatch functions for AES SIV mode */ + +/* + * This file uses the low level AES functions (which are deprecated for + * non-internal use) in order to implement provider AES ciphers. + */ +#include "internal/deprecated.h" + +#include <openssl/proverr.h> +#include "cipher_aes_siv.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" +#include "prov/ciphercommon_aead.h" +#include "prov/provider_ctx.h" + +#define siv_stream_update siv_cipher +#define SIV_FLAGS AEAD_FLAGS + +static OSSL_FUNC_cipher_set_ctx_params_fn aes_siv_set_ctx_params; + +static void *aes_siv_newctx(void *provctx, size_t keybits, unsigned int mode, + uint64_t flags) +{ + PROV_AES_SIV_CTX *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + ctx = OPENSSL_zalloc(sizeof(*ctx)); + if (ctx != NULL) { + ctx->taglen = SIV_LEN; + ctx->mode = mode; + ctx->keylen = keybits / 8; + ctx->hw = ossl_prov_cipher_hw_aes_siv(keybits); + ctx->libctx = PROV_LIBCTX_OF(provctx); + } + return ctx; +} + +static void aes_siv_freectx(void *vctx) +{ + PROV_AES_SIV_CTX *ctx = (PROV_AES_SIV_CTX *)vctx; + + if (ctx != NULL) { + ctx->hw->cleanup(ctx); + OPENSSL_clear_free(ctx, sizeof(*ctx)); + } +} + +static void *siv_dupctx(void *vctx) +{ + PROV_AES_SIV_CTX *in = (PROV_AES_SIV_CTX *)vctx; + PROV_AES_SIV_CTX *ret; + + if (!ossl_prov_is_running()) + return NULL; + + ret = OPENSSL_malloc(sizeof(*ret)); + if (ret == NULL) + return NULL; + if (!in->hw->dupctx(in, ret)) { + OPENSSL_free(ret); + ret = NULL; + } + return ret; +} + +static int siv_init(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[], int enc) +{ + PROV_AES_SIV_CTX *ctx = (PROV_AES_SIV_CTX *)vctx; + + if (!ossl_prov_is_running()) + return 0; + + ctx->enc = enc; + + if (key != NULL) { + if (keylen != ctx->keylen) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + if (!ctx->hw->initkey(ctx, key, ctx->keylen)) + return 0; + } + return aes_siv_set_ctx_params(ctx, params); +} + +static int siv_einit(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + return siv_init(vctx, key, keylen, iv, ivlen, params, 1); +} + +static int siv_dinit(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + return siv_init(vctx, key, keylen, iv, ivlen, params, 0); +} + +static int siv_cipher(void *vctx, unsigned char *out, size_t *outl, + size_t outsize, const unsigned char *in, size_t inl) +{ + PROV_AES_SIV_CTX *ctx = (PROV_AES_SIV_CTX *)vctx; + + if (!ossl_prov_is_running()) + return 0; + + if (out != NULL && outsize < inl) { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + return 0; + } + + if (ctx->hw->cipher(ctx, out, in, inl) <= 0) + return 0; + + if (outl != NULL) + *outl = inl; + return 1; +} + +static int siv_stream_final(void *vctx, unsigned char *out, size_t *outl, + size_t outsize) +{ + PROV_AES_SIV_CTX *ctx = (PROV_AES_SIV_CTX *)vctx; + + if (!ossl_prov_is_running()) + return 0; + + if (!ctx->hw->cipher(vctx, out, NULL, 0)) + return 0; + + if (outl != NULL) + *outl = 0; + return 1; +} + +static int aes_siv_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + PROV_AES_SIV_CTX *ctx = (PROV_AES_SIV_CTX *)vctx; + SIV128_CONTEXT *sctx = &ctx->siv; + OSSL_PARAM *p; + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TAG); + if (p != NULL && p->data_type == OSSL_PARAM_OCTET_STRING) { + if (!ctx->enc + || p->data_size != ctx->taglen + || !OSSL_PARAM_set_octet_string(p, &sctx->tag.byte, ctx->taglen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TAGLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->taglen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->keylen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + return 1; +} + +static const OSSL_PARAM aes_siv_known_gettable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_TAGLEN, NULL), + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, NULL, 0), + OSSL_PARAM_END +}; +static const OSSL_PARAM *aes_siv_gettable_ctx_params(ossl_unused void *cctx, + ossl_unused void *provctx) +{ + return aes_siv_known_gettable_ctx_params; +} + +static int aes_siv_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_AES_SIV_CTX *ctx = (PROV_AES_SIV_CTX *)vctx; + const OSSL_PARAM *p; + unsigned int speed = 0; + + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TAG); + if (p != NULL) { + if (ctx->enc) + return 1; + if (p->data_type != OSSL_PARAM_OCTET_STRING + || !ctx->hw->settag(ctx, p->data, p->data_size)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + } + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_SPEED); + if (p != NULL) { + if (!OSSL_PARAM_get_uint(p, &speed)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + ctx->hw->setspeed(ctx, (int)speed); + } + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_KEYLEN); + if (p != NULL) { + size_t keylen; + + if (!OSSL_PARAM_get_size_t(p, &keylen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + /* The key length can not be modified */ + if (keylen != ctx->keylen) + return 0; + } + return 1; +} + +static const OSSL_PARAM aes_siv_known_settable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), + OSSL_PARAM_uint(OSSL_CIPHER_PARAM_SPEED, NULL), + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, NULL, 0), + OSSL_PARAM_END +}; +static const OSSL_PARAM *aes_siv_settable_ctx_params(ossl_unused void *cctx, + ossl_unused void *provctx) +{ + return aes_siv_known_settable_ctx_params; +} + +#define IMPLEMENT_cipher(alg, lc, UCMODE, flags, kbits, blkbits, ivbits) \ +static OSSL_FUNC_cipher_newctx_fn alg##kbits##lc##_newctx; \ +static OSSL_FUNC_cipher_freectx_fn alg##_##lc##_freectx; \ +static OSSL_FUNC_cipher_dupctx_fn lc##_dupctx; \ +static OSSL_FUNC_cipher_encrypt_init_fn lc##_einit; \ +static OSSL_FUNC_cipher_decrypt_init_fn lc##_dinit; \ +static OSSL_FUNC_cipher_update_fn lc##_stream_update; \ +static OSSL_FUNC_cipher_final_fn lc##_stream_final; \ +static OSSL_FUNC_cipher_cipher_fn lc##_cipher; \ +static OSSL_FUNC_cipher_get_params_fn alg##_##kbits##_##lc##_get_params; \ +static OSSL_FUNC_cipher_get_ctx_params_fn alg##_##lc##_get_ctx_params; \ +static OSSL_FUNC_cipher_gettable_ctx_params_fn \ + alg##_##lc##_gettable_ctx_params; \ +static OSSL_FUNC_cipher_set_ctx_params_fn alg##_##lc##_set_ctx_params; \ +static OSSL_FUNC_cipher_settable_ctx_params_fn \ + alg##_##lc##_settable_ctx_params; \ +static int alg##_##kbits##_##lc##_get_params(OSSL_PARAM params[]) \ +{ \ + return ossl_cipher_generic_get_params(params, EVP_CIPH_##UCMODE##_MODE, \ + flags, 2*kbits, blkbits, ivbits); \ +} \ +static void *alg##kbits##lc##_newctx(void *provctx) \ +{ \ + return alg##_##lc##_newctx(provctx, 2*kbits, EVP_CIPH_##UCMODE##_MODE, \ + flags); \ +} \ +const OSSL_DISPATCH ossl_##alg##kbits##lc##_functions[] = { \ + { OSSL_FUNC_CIPHER_NEWCTX, (void (*)(void))alg##kbits##lc##_newctx }, \ + { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))alg##_##lc##_freectx }, \ + { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void)) lc##_dupctx }, \ + { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void)) lc##_einit }, \ + { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void)) lc##_dinit }, \ + { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void)) lc##_stream_update }, \ + { OSSL_FUNC_CIPHER_FINAL, (void (*)(void)) lc##_stream_final }, \ + { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void)) lc##_cipher }, \ + { OSSL_FUNC_CIPHER_GET_PARAMS, \ + (void (*)(void)) alg##_##kbits##_##lc##_get_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \ + (void (*)(void))ossl_cipher_generic_gettable_params }, \ + { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, \ + (void (*)(void)) alg##_##lc##_get_ctx_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \ + (void (*)(void)) alg##_##lc##_gettable_ctx_params }, \ + { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \ + (void (*)(void)) alg##_##lc##_set_ctx_params }, \ + { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \ + (void (*)(void)) alg##_##lc##_settable_ctx_params }, \ + OSSL_DISPATCH_END \ +}; + +IMPLEMENT_cipher(aes, siv, SIV, SIV_FLAGS, 128, 8, 0) +IMPLEMENT_cipher(aes, siv, SIV, SIV_FLAGS, 192, 8, 0) +IMPLEMENT_cipher(aes, siv, SIV, SIV_FLAGS, 256, 8, 0) diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_siv.h b/crypto/openssl/providers/implementations/ciphers/cipher_aes_siv.h new file mode 100644 index 000000000000..4a682b77c440 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_siv.h @@ -0,0 +1,36 @@ +/* + * Copyright 2019-2021 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 + */ + +#include "prov/ciphercommon.h" +#include "crypto/aes_platform.h" +#include "crypto/siv.h" + +typedef struct prov_cipher_hw_aes_siv_st { + int (*initkey)(void *ctx, const uint8_t *key, size_t keylen); + int (*cipher)(void *ctx, unsigned char *out, const unsigned char *in, + size_t len); + void (*setspeed)(void *ctx, int speed); + int (*settag)(void *ctx, const unsigned char *tag, size_t tagl); + void (*cleanup)(void *ctx); + int (*dupctx)(void *src, void *dst); +} PROV_CIPHER_HW_AES_SIV; + +typedef struct prov_siv_ctx_st { + unsigned int mode; /* The mode that we are using */ + unsigned int enc : 1; /* Set to 1 if we are encrypting or 0 otherwise */ + size_t keylen; /* The input keylength (twice the alg key length) */ + size_t taglen; /* the taglen is the same as the sivlen */ + SIV128_CONTEXT siv; + EVP_CIPHER *ctr; /* These are fetched - so we need to free them */ + EVP_CIPHER *cbc; + const PROV_CIPHER_HW_AES_SIV *hw; + OSSL_LIB_CTX *libctx; +} PROV_AES_SIV_CTX; + +const PROV_CIPHER_HW_AES_SIV *ossl_prov_cipher_hw_aes_siv(size_t keybits); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_siv_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_siv_hw.c new file mode 100644 index 000000000000..75fd54829c4b --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_siv_hw.c @@ -0,0 +1,139 @@ +/* + * Copyright 2019-2025 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 + */ + +/* + * This file uses the low level AES functions (which are deprecated for + * non-internal use) in order to implement provider AES ciphers. + */ +#include "internal/deprecated.h" + +#include "cipher_aes_siv.h" + +static void aes_siv_cleanup(void *vctx); + +static int aes_siv_initkey(void *vctx, const unsigned char *key, size_t keylen) +{ + PROV_AES_SIV_CTX *ctx = (PROV_AES_SIV_CTX *)vctx; + SIV128_CONTEXT *sctx = &ctx->siv; + size_t klen = keylen / 2; + OSSL_LIB_CTX *libctx = ctx->libctx; + const char *propq = NULL; + + EVP_CIPHER_free(ctx->cbc); + EVP_CIPHER_free(ctx->ctr); + ctx->cbc = NULL; + ctx->ctr = NULL; + + switch (klen) { + case 16: + ctx->cbc = EVP_CIPHER_fetch(libctx, "AES-128-CBC", propq); + ctx->ctr = EVP_CIPHER_fetch(libctx, "AES-128-CTR", propq); + break; + case 24: + ctx->cbc = EVP_CIPHER_fetch(libctx, "AES-192-CBC", propq); + ctx->ctr = EVP_CIPHER_fetch(libctx, "AES-192-CTR", propq); + break; + case 32: + ctx->cbc = EVP_CIPHER_fetch(libctx, "AES-256-CBC", propq); + ctx->ctr = EVP_CIPHER_fetch(libctx, "AES-256-CTR", propq); + break; + default: + break; + } + if (ctx->cbc == NULL || ctx->ctr == NULL) + return 0; + /* + * klen is the length of the underlying cipher, not the input key, + * which should be twice as long + */ + return ossl_siv128_init(sctx, key, klen, ctx->cbc, ctx->ctr, libctx, + propq); +} + +static int aes_siv_dupctx(void *in_vctx, void *out_vctx) +{ + PROV_AES_SIV_CTX *in = (PROV_AES_SIV_CTX *)in_vctx; + PROV_AES_SIV_CTX *out = (PROV_AES_SIV_CTX *)out_vctx; + + if (in->cbc != NULL && !EVP_CIPHER_up_ref(in->cbc)) + return 0; + if (in->ctr != NULL && !EVP_CIPHER_up_ref(in->ctr)) { + EVP_CIPHER_free(in->cbc); + return 0; + } + + *out = *in; + out->siv.cipher_ctx = NULL; + out->siv.mac_ctx_init = NULL; + out->siv.mac = NULL; + if (!ossl_siv128_copy_ctx(&out->siv, &in->siv)) + return 0; + + return 1; +} + +static int aes_siv_settag(void *vctx, const unsigned char *tag, size_t tagl) +{ + PROV_AES_SIV_CTX *ctx = (PROV_AES_SIV_CTX *)vctx; + SIV128_CONTEXT *sctx = &ctx->siv; + + return ossl_siv128_set_tag(sctx, tag, tagl); +} + +static void aes_siv_setspeed(void *vctx, int speed) +{ + PROV_AES_SIV_CTX *ctx = (PROV_AES_SIV_CTX *)vctx; + SIV128_CONTEXT *sctx = &ctx->siv; + + ossl_siv128_speed(sctx, (int)speed); +} + +static void aes_siv_cleanup(void *vctx) +{ + PROV_AES_SIV_CTX *ctx = (PROV_AES_SIV_CTX *)vctx; + SIV128_CONTEXT *sctx = &ctx->siv; + + ossl_siv128_cleanup(sctx); + EVP_CIPHER_free(ctx->cbc); + EVP_CIPHER_free(ctx->ctr); +} + +static int aes_siv_cipher(void *vctx, unsigned char *out, + const unsigned char *in, size_t len) +{ + PROV_AES_SIV_CTX *ctx = (PROV_AES_SIV_CTX *)vctx; + SIV128_CONTEXT *sctx = &ctx->siv; + + /* EncryptFinal or DecryptFinal */ + if (in == NULL) + return ossl_siv128_finish(sctx) == 0; + + /* Deal with associated data */ + if (out == NULL) + return (ossl_siv128_aad(sctx, in, len) == 1); + + if (ctx->enc) + return ossl_siv128_encrypt(sctx, in, out, len) > 0; + + return ossl_siv128_decrypt(sctx, in, out, len) > 0; +} + +static const PROV_CIPHER_HW_AES_SIV aes_siv_hw = { + aes_siv_initkey, + aes_siv_cipher, + aes_siv_setspeed, + aes_siv_settag, + aes_siv_cleanup, + aes_siv_dupctx, +}; + +const PROV_CIPHER_HW_AES_SIV *ossl_prov_cipher_hw_aes_siv(size_t keybits) +{ + return &aes_siv_hw; +} diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_wrp.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_wrp.c new file mode 100644 index 000000000000..a3bac9b23d0a --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_wrp.c @@ -0,0 +1,335 @@ +/* + * Copyright 2019-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 + */ + +/* + * This file uses the low level AES functions (which are deprecated for + * non-internal use) in order to implement provider AES ciphers. + */ +#include "internal/deprecated.h" + +#include <openssl/proverr.h> +#include "cipher_aes.h" +#include "prov/providercommon.h" +#include "prov/implementations.h" + +/* AES wrap with padding has IV length of 4, without padding 8 */ +#define AES_WRAP_PAD_IVLEN 4 +#define AES_WRAP_NOPAD_IVLEN 8 + +#define WRAP_FLAGS (PROV_CIPHER_FLAG_CUSTOM_IV) +#define WRAP_FLAGS_INV (WRAP_FLAGS | PROV_CIPHER_FLAG_INVERSE_CIPHER) + +typedef size_t (*aeswrap_fn)(void *key, const unsigned char *iv, + unsigned char *out, const unsigned char *in, + size_t inlen, block128_f block); + +static OSSL_FUNC_cipher_encrypt_init_fn aes_wrap_einit; +static OSSL_FUNC_cipher_decrypt_init_fn aes_wrap_dinit; +static OSSL_FUNC_cipher_update_fn aes_wrap_cipher; +static OSSL_FUNC_cipher_final_fn aes_wrap_final; +static OSSL_FUNC_cipher_freectx_fn aes_wrap_freectx; +static OSSL_FUNC_cipher_set_ctx_params_fn aes_wrap_set_ctx_params; + +typedef struct prov_aes_wrap_ctx_st { + PROV_CIPHER_CTX base; + union { + OSSL_UNION_ALIGN; + AES_KEY ks; + } ks; + aeswrap_fn wrapfn; + +} PROV_AES_WRAP_CTX; + + +static void *aes_wrap_newctx(size_t kbits, size_t blkbits, + size_t ivbits, unsigned int mode, uint64_t flags) +{ + PROV_AES_WRAP_CTX *wctx; + PROV_CIPHER_CTX *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + wctx = OPENSSL_zalloc(sizeof(*wctx)); + ctx = (PROV_CIPHER_CTX *)wctx; + if (ctx != NULL) { + ossl_cipher_generic_initkey(ctx, kbits, blkbits, ivbits, mode, flags, + NULL, NULL); + ctx->pad = (ctx->ivlen == AES_WRAP_PAD_IVLEN); + } + return wctx; +} + +static void *aes_wrap_dupctx(void *wctx) +{ + PROV_AES_WRAP_CTX *ctx = wctx; + PROV_AES_WRAP_CTX *dctx = wctx; + + if (!ossl_prov_is_running()) + return NULL; + + if (ctx == NULL) + return NULL; + dctx = OPENSSL_memdup(ctx, sizeof(*ctx)); + + if (dctx != NULL && dctx->base.tlsmac != NULL && dctx->base.alloced) { + dctx->base.tlsmac = OPENSSL_memdup(dctx->base.tlsmac, + dctx->base.tlsmacsize); + if (dctx->base.tlsmac == NULL) { + OPENSSL_free(dctx); + dctx = NULL; + } + } + return dctx; +} + +static void aes_wrap_freectx(void *vctx) +{ + PROV_AES_WRAP_CTX *wctx = (PROV_AES_WRAP_CTX *)vctx; + + ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx); + OPENSSL_clear_free(wctx, sizeof(*wctx)); +} + +static int aes_wrap_init(void *vctx, const unsigned char *key, + size_t keylen, const unsigned char *iv, + size_t ivlen, const OSSL_PARAM params[], int enc) +{ + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + PROV_AES_WRAP_CTX *wctx = (PROV_AES_WRAP_CTX *)vctx; + + if (!ossl_prov_is_running()) + return 0; + + ctx->enc = enc; + if (ctx->pad) + wctx->wrapfn = enc ? CRYPTO_128_wrap_pad : CRYPTO_128_unwrap_pad; + else + wctx->wrapfn = enc ? CRYPTO_128_wrap : CRYPTO_128_unwrap; + + if (iv != NULL) { + if (!ossl_cipher_generic_initiv(ctx, iv, ivlen)) + return 0; + } + if (key != NULL) { + int use_forward_transform; + + if (keylen != ctx->keylen) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + /* + * See SP800-38F : Section 5.1 + * The forward and inverse transformations for the AES block + * cipher—called “cipher” and “inverse cipher” are informally known as + * the AES encryption and AES decryption functions, respectively. + * If the designated cipher function for a key-wrap algorithm is chosen + * to be the AES decryption function, then CIPH-1K will be the AES + * encryption function. + */ + if (ctx->inverse_cipher == 0) + use_forward_transform = ctx->enc; + else + use_forward_transform = !ctx->enc; + if (use_forward_transform) { + AES_set_encrypt_key(key, keylen * 8, &wctx->ks.ks); + ctx->block = (block128_f)AES_encrypt; + } else { + AES_set_decrypt_key(key, keylen * 8, &wctx->ks.ks); + ctx->block = (block128_f)AES_decrypt; + } + } + return aes_wrap_set_ctx_params(ctx, params); +} + +static int aes_wrap_einit(void *ctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + return aes_wrap_init(ctx, key, keylen, iv, ivlen, params, 1); +} + +static int aes_wrap_dinit(void *ctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + return aes_wrap_init(ctx, key, keylen, iv, ivlen, params, 0); +} + +static int aes_wrap_cipher_internal(void *vctx, unsigned char *out, + const unsigned char *in, size_t inlen) +{ + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + PROV_AES_WRAP_CTX *wctx = (PROV_AES_WRAP_CTX *)vctx; + size_t rv; + int pad = ctx->pad; + + /* No final operation so always return zero length */ + if (in == NULL) + return 0; + + /* Input length must always be non-zero */ + if (inlen == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_INPUT_LENGTH); + return -1; + } + + /* If decrypting need at least 16 bytes and multiple of 8 */ + if (!ctx->enc && (inlen < 16 || inlen & 0x7)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_INPUT_LENGTH); + return -1; + } + + /* If not padding input must be multiple of 8 */ + if (!pad && inlen & 0x7) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_INPUT_LENGTH); + return -1; + } + + if (out == NULL) { + if (ctx->enc) { + /* If padding round up to multiple of 8 */ + if (pad) + inlen = (inlen + 7) / 8 * 8; + /* 8 byte prefix */ + return inlen + 8; + } else { + /* + * If not padding output will be exactly 8 bytes smaller than + * input. If padding it will be at least 8 bytes smaller but we + * don't know how much. + */ + return inlen - 8; + } + } + + rv = wctx->wrapfn(&wctx->ks.ks, ctx->iv_set ? ctx->iv : NULL, out, in, + inlen, ctx->block); + if (!rv) { + ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); + return -1; + } + if (rv > INT_MAX) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_OUTPUT_LENGTH); + return -1; + } + return (int)rv; +} + +static int aes_wrap_final(void *vctx, unsigned char *out, size_t *outl, + size_t outsize) +{ + if (!ossl_prov_is_running()) + return 0; + + *outl = 0; + return 1; +} + +static int aes_wrap_cipher(void *vctx, + unsigned char *out, size_t *outl, size_t outsize, + const unsigned char *in, size_t inl) +{ + PROV_AES_WRAP_CTX *ctx = (PROV_AES_WRAP_CTX *)vctx; + size_t len; + + if (!ossl_prov_is_running()) + return 0; + + if (inl == 0) { + *outl = 0; + return 1; + } + + if (outsize < inl) { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + return 0; + } + + len = aes_wrap_cipher_internal(ctx, out, in, inl); + if (len <= 0) + return 0; + + *outl = len; + return 1; +} + +static int aes_wrap_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + const OSSL_PARAM *p; + size_t keylen = 0; + + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_KEYLEN); + if (p != NULL) { + if (!OSSL_PARAM_get_size_t(p, &keylen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + if (ctx->keylen != keylen) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + } + return 1; +} + +#define IMPLEMENT_cipher(mode, fname, UCMODE, flags, kbits, blkbits, ivbits) \ + static OSSL_FUNC_cipher_get_params_fn aes_##kbits##_##fname##_get_params; \ + static int aes_##kbits##_##fname##_get_params(OSSL_PARAM params[]) \ + { \ + return ossl_cipher_generic_get_params(params, EVP_CIPH_##UCMODE##_MODE,\ + flags, kbits, blkbits, ivbits); \ + } \ + static OSSL_FUNC_cipher_newctx_fn aes_##kbits##fname##_newctx; \ + static void *aes_##kbits##fname##_newctx(void *provctx) \ + { \ + return aes_##mode##_newctx(kbits, blkbits, ivbits, \ + EVP_CIPH_##UCMODE##_MODE, flags); \ + } \ + const OSSL_DISPATCH ossl_##aes##kbits##fname##_functions[] = { \ + { OSSL_FUNC_CIPHER_NEWCTX, \ + (void (*)(void))aes_##kbits##fname##_newctx }, \ + { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))aes_##mode##_einit }, \ + { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))aes_##mode##_dinit }, \ + { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))aes_##mode##_cipher }, \ + { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))aes_##mode##_final }, \ + { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))aes_##mode##_freectx }, \ + { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void))aes_##mode##_dupctx }, \ + { OSSL_FUNC_CIPHER_GET_PARAMS, \ + (void (*)(void))aes_##kbits##_##fname##_get_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \ + (void (*)(void))ossl_cipher_generic_gettable_params }, \ + { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, \ + (void (*)(void))ossl_cipher_generic_get_ctx_params }, \ + { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \ + (void (*)(void))aes_wrap_set_ctx_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \ + (void (*)(void))ossl_cipher_generic_gettable_ctx_params }, \ + { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \ + (void (*)(void))ossl_cipher_generic_settable_ctx_params }, \ + OSSL_DISPATCH_END \ + } + +IMPLEMENT_cipher(wrap, wrap, WRAP, WRAP_FLAGS, 256, 64, AES_WRAP_NOPAD_IVLEN * 8); +IMPLEMENT_cipher(wrap, wrap, WRAP, WRAP_FLAGS, 192, 64, AES_WRAP_NOPAD_IVLEN * 8); +IMPLEMENT_cipher(wrap, wrap, WRAP, WRAP_FLAGS, 128, 64, AES_WRAP_NOPAD_IVLEN * 8); +IMPLEMENT_cipher(wrap, wrappad, WRAP, WRAP_FLAGS, 256, 64, AES_WRAP_PAD_IVLEN * 8); +IMPLEMENT_cipher(wrap, wrappad, WRAP, WRAP_FLAGS, 192, 64, AES_WRAP_PAD_IVLEN * 8); +IMPLEMENT_cipher(wrap, wrappad, WRAP, WRAP_FLAGS, 128, 64, AES_WRAP_PAD_IVLEN * 8); + +IMPLEMENT_cipher(wrap, wrapinv, WRAP, WRAP_FLAGS_INV, 256, 64, AES_WRAP_NOPAD_IVLEN * 8); +IMPLEMENT_cipher(wrap, wrapinv, WRAP, WRAP_FLAGS_INV, 192, 64, AES_WRAP_NOPAD_IVLEN * 8); +IMPLEMENT_cipher(wrap, wrapinv, WRAP, WRAP_FLAGS_INV, 128, 64, AES_WRAP_NOPAD_IVLEN * 8); +IMPLEMENT_cipher(wrap, wrappadinv, WRAP, WRAP_FLAGS_INV, 256, 64, AES_WRAP_PAD_IVLEN * 8); +IMPLEMENT_cipher(wrap, wrappadinv, WRAP, WRAP_FLAGS_INV, 192, 64, AES_WRAP_PAD_IVLEN * 8); +IMPLEMENT_cipher(wrap, wrappadinv, WRAP, WRAP_FLAGS_INV, 128, 64, AES_WRAP_PAD_IVLEN * 8); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_xts.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_xts.c new file mode 100644 index 000000000000..1e0081da94ec --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_xts.c @@ -0,0 +1,318 @@ + +/* + * Copyright 2019-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 + */ + +/* + * AES low level APIs are deprecated for public use, but still ok for internal + * use where we're using them to implement the higher level EVP interface, as is + * the case here. + */ +#include "internal/deprecated.h" + +#include <openssl/proverr.h> +#include "cipher_aes_xts.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" + +#define AES_XTS_FLAGS PROV_CIPHER_FLAG_CUSTOM_IV +#define AES_XTS_IV_BITS 128 +#define AES_XTS_BLOCK_BITS 8 + +/* forward declarations */ +static OSSL_FUNC_cipher_encrypt_init_fn aes_xts_einit; +static OSSL_FUNC_cipher_decrypt_init_fn aes_xts_dinit; +static OSSL_FUNC_cipher_update_fn aes_xts_stream_update; +static OSSL_FUNC_cipher_final_fn aes_xts_stream_final; +static OSSL_FUNC_cipher_cipher_fn aes_xts_cipher; +static OSSL_FUNC_cipher_freectx_fn aes_xts_freectx; +static OSSL_FUNC_cipher_dupctx_fn aes_xts_dupctx; +static OSSL_FUNC_cipher_set_ctx_params_fn aes_xts_set_ctx_params; +static OSSL_FUNC_cipher_settable_ctx_params_fn aes_xts_settable_ctx_params; + +/* + * Verify that the two keys are different. + * + * This addresses the vulnerability described in Rogaway's + * September 2004 paper: + * + * "Efficient Instantiations of Tweakable Blockciphers and + * Refinements to Modes OCB and PMAC". + * (http://web.cs.ucdavis.edu/~rogaway/papers/offsets.pdf) + * + * FIPS 140-2 IG A.9 XTS-AES Key Generation Requirements states + * that: + * "The check for Key_1 != Key_2 shall be done at any place + * BEFORE using the keys in the XTS-AES algorithm to process + * data with them." + */ +static int aes_xts_check_keys_differ(const unsigned char *key, size_t bytes, + int enc) +{ + if ((!ossl_aes_xts_allow_insecure_decrypt || enc) + && CRYPTO_memcmp(key, key + bytes, bytes) == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_XTS_DUPLICATED_KEYS); + return 0; + } + return 1; +} + +#ifdef AES_XTS_S390X +# include "cipher_aes_xts_s390x.inc" +#endif + +/*- + * Provider dispatch functions + */ +static int aes_xts_init(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[], int enc) +{ + PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)vctx; + PROV_CIPHER_CTX *ctx = &xctx->base; + + if (!ossl_prov_is_running()) + return 0; + + ctx->enc = enc; + + if (iv != NULL) { + if (!ossl_cipher_generic_initiv(vctx, iv, ivlen)) + return 0; + } + if (key != NULL) { + if (keylen != ctx->keylen) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + if (!aes_xts_check_keys_differ(key, keylen / 2, enc)) + return 0; + if (!ctx->hw->init(ctx, key, keylen)) + return 0; + } + return aes_xts_set_ctx_params(ctx, params); +} + +static int aes_xts_einit(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ +#ifdef AES_XTS_S390X + if (s390x_aes_xts_einit(vctx, key, keylen, iv, ivlen, params) == 1) + return 1; +#endif + return aes_xts_init(vctx, key, keylen, iv, ivlen, params, 1); +} + +static int aes_xts_dinit(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ +#ifdef AES_XTS_S390X + if (s390x_aes_xts_dinit(vctx, key, keylen, iv, ivlen, params) == 1) + return 1; +#endif + return aes_xts_init(vctx, key, keylen, iv, ivlen, params, 0); +} + +static void *aes_xts_newctx(void *provctx, unsigned int mode, uint64_t flags, + size_t kbits, size_t blkbits, size_t ivbits) +{ + PROV_AES_XTS_CTX *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + ctx = OPENSSL_zalloc(sizeof(*ctx)); + if (ctx != NULL) { + ossl_cipher_generic_initkey(&ctx->base, kbits, blkbits, ivbits, mode, + flags, ossl_prov_cipher_hw_aes_xts(kbits), + NULL); + } + return ctx; +} + +static void aes_xts_freectx(void *vctx) +{ + PROV_AES_XTS_CTX *ctx = (PROV_AES_XTS_CTX *)vctx; + + ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx); + OPENSSL_clear_free(ctx, sizeof(*ctx)); +} + +static void *aes_xts_dupctx(void *vctx) +{ + PROV_AES_XTS_CTX *in = (PROV_AES_XTS_CTX *)vctx; + PROV_AES_XTS_CTX *ret = NULL; + + if (!ossl_prov_is_running()) + return NULL; + +#ifdef AES_XTS_S390X + if (in->plat.s390x.fc) + return s390x_aes_xts_dupctx(vctx); +#endif + + if (in->xts.key1 != NULL) { + if (in->xts.key1 != &in->ks1) + return NULL; + } + if (in->xts.key2 != NULL) { + if (in->xts.key2 != &in->ks2) + return NULL; + } + ret = OPENSSL_malloc(sizeof(*ret)); + if (ret == NULL) + return NULL; + in->base.hw->copyctx(&ret->base, &in->base); + return ret; +} + +static int aes_xts_cipher(void *vctx, unsigned char *out, size_t *outl, + size_t outsize, const unsigned char *in, size_t inl) +{ + PROV_AES_XTS_CTX *ctx = (PROV_AES_XTS_CTX *)vctx; + +#ifdef AES_XTS_S390X + if (ctx->plat.s390x.fc) + return s390x_aes_xts_cipher(vctx, out, outl, outsize, in, inl); +#endif + + if (!ossl_prov_is_running() + || ctx->xts.key1 == NULL + || ctx->xts.key2 == NULL + || !ctx->base.iv_set + || out == NULL + || in == NULL + || inl < AES_BLOCK_SIZE) + return 0; + + /* + * Impose a limit of 2^20 blocks per data unit as specified by + * IEEE Std 1619-2018. The earlier and obsolete IEEE Std 1619-2007 + * indicated that this was a SHOULD NOT rather than a MUST NOT. + * NIST SP 800-38E mandates the same limit. + */ + if (inl > XTS_MAX_BLOCKS_PER_DATA_UNIT * AES_BLOCK_SIZE) { + ERR_raise(ERR_LIB_PROV, PROV_R_XTS_DATA_UNIT_IS_TOO_LARGE); + return 0; + } + + if (ctx->stream != NULL) + (*ctx->stream)(in, out, inl, ctx->xts.key1, ctx->xts.key2, ctx->base.iv); + else if (CRYPTO_xts128_encrypt(&ctx->xts, ctx->base.iv, in, out, inl, + ctx->base.enc)) + return 0; + + *outl = inl; + return 1; +} + +static int aes_xts_stream_update(void *vctx, unsigned char *out, size_t *outl, + size_t outsize, const unsigned char *in, + size_t inl) +{ + PROV_AES_XTS_CTX *ctx = (PROV_AES_XTS_CTX *)vctx; + + if (outsize < inl) { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + return 0; + } + + if (!aes_xts_cipher(ctx, out, outl, outsize, in, inl)) { + ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); + return 0; + } + + return 1; +} + +static int aes_xts_stream_final(void *vctx, unsigned char *out, size_t *outl, + size_t outsize) +{ + if (!ossl_prov_is_running()) + return 0; + *outl = 0; + return 1; +} + +static const OSSL_PARAM aes_xts_known_settable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), + OSSL_PARAM_END +}; + +static const OSSL_PARAM *aes_xts_settable_ctx_params(ossl_unused void *cctx, + ossl_unused void *provctx) +{ + return aes_xts_known_settable_ctx_params; +} + +static int aes_xts_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + const OSSL_PARAM *p; + + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_KEYLEN); + if (p != NULL) { + size_t keylen; + + if (!OSSL_PARAM_get_size_t(p, &keylen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + /* The key length can not be modified for xts mode */ + if (keylen != ctx->keylen) + return 0; + } + + return 1; +} + +#define IMPLEMENT_cipher(lcmode, UCMODE, kbits, flags) \ +static OSSL_FUNC_cipher_get_params_fn aes_##kbits##_##lcmode##_get_params; \ +static int aes_##kbits##_##lcmode##_get_params(OSSL_PARAM params[]) \ +{ \ + return ossl_cipher_generic_get_params(params, EVP_CIPH_##UCMODE##_MODE, \ + flags, 2 * kbits, AES_XTS_BLOCK_BITS, \ + AES_XTS_IV_BITS); \ +} \ +static OSSL_FUNC_cipher_newctx_fn aes_##kbits##_xts_newctx; \ +static void *aes_##kbits##_xts_newctx(void *provctx) \ +{ \ + return aes_xts_newctx(provctx, EVP_CIPH_##UCMODE##_MODE, flags, 2 * kbits, \ + AES_XTS_BLOCK_BITS, AES_XTS_IV_BITS); \ +} \ +const OSSL_DISPATCH ossl_aes##kbits##xts_functions[] = { \ + { OSSL_FUNC_CIPHER_NEWCTX, (void (*)(void))aes_##kbits##_xts_newctx }, \ + { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))aes_xts_einit }, \ + { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))aes_xts_dinit }, \ + { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))aes_xts_stream_update }, \ + { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))aes_xts_stream_final }, \ + { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))aes_xts_cipher }, \ + { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))aes_xts_freectx }, \ + { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void))aes_xts_dupctx }, \ + { OSSL_FUNC_CIPHER_GET_PARAMS, \ + (void (*)(void))aes_##kbits##_##lcmode##_get_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \ + (void (*)(void))ossl_cipher_generic_gettable_params }, \ + { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, \ + (void (*)(void))ossl_cipher_generic_get_ctx_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \ + (void (*)(void))ossl_cipher_generic_gettable_ctx_params }, \ + { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \ + (void (*)(void))aes_xts_set_ctx_params }, \ + { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \ + (void (*)(void))aes_xts_settable_ctx_params }, \ + OSSL_DISPATCH_END \ +} + +IMPLEMENT_cipher(xts, XTS, 256, AES_XTS_FLAGS); +IMPLEMENT_cipher(xts, XTS, 128, AES_XTS_FLAGS); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_xts.h b/crypto/openssl/providers/implementations/ciphers/cipher_aes_xts.h new file mode 100644 index 000000000000..56891ca98c42 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_xts.h @@ -0,0 +1,60 @@ +/* + * Copyright 2019-2021 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 + */ + +#include <openssl/aes.h> +#include "prov/ciphercommon.h" +#include "crypto/aes_platform.h" + +/* + * Available in cipher_fips.c, and compiled with different values depending + * on we're in the FIPS module or not. + */ +extern const int ossl_aes_xts_allow_insecure_decrypt; + +PROV_CIPHER_FUNC(void, xts_stream, + (const unsigned char *in, unsigned char *out, size_t len, + const AES_KEY *key1, const AES_KEY *key2, + const unsigned char iv[16])); + +#if defined(OPENSSL_CPUID_OBJ) && defined(__s390__) +typedef struct S390X_km_xts_params_st { + unsigned char key[64]; + unsigned char tweak[16]; + unsigned char nap[16]; +} S390X_KM_XTS_PARAMS; +#endif + +typedef struct prov_aes_xts_ctx_st { + PROV_CIPHER_CTX base; /* Must be first */ + union { + OSSL_UNION_ALIGN; + AES_KEY ks; + } ks1, ks2; /* AES key schedules to use */ + XTS128_CONTEXT xts; + OSSL_xts_stream_fn stream; + + /* Platform specific data */ + union { + int dummy; +#if defined(OPENSSL_CPUID_OBJ) && defined(__s390__) + struct { + union { + OSSL_UNION_ALIGN; + S390X_KM_XTS_PARAMS km; + } param; + size_t offset; + unsigned int fc; + unsigned int iv_set : 1; + unsigned int key_set : 1; + } s390x; +#endif + } plat; +} PROV_AES_XTS_CTX; + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts(size_t keybits); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_xts_fips.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_xts_fips.c new file mode 100644 index 000000000000..777d928bf96f --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_xts_fips.c @@ -0,0 +1,23 @@ +/* + * Copyright 2019-2021 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 + */ + +/* + * AES low level APIs are deprecated for public use, but still ok for internal + * use where we're using them to implement the higher level EVP interface, as is + * the case here. + */ +#include "internal/deprecated.h" + +#include "cipher_aes_xts.h" + +#ifdef FIPS_MODULE +const int ossl_aes_xts_allow_insecure_decrypt = 0; +#else +const int ossl_aes_xts_allow_insecure_decrypt = 1; +#endif /* FIPS_MODULE */ diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_xts_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_xts_hw.c new file mode 100644 index 000000000000..b6353f18456b --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_xts_hw.c @@ -0,0 +1,333 @@ +/* + * Copyright 2019-2025 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 + */ + +/* + * This file uses the low level AES functions (which are deprecated for + * non-internal use) in order to implement provider AES ciphers. + */ +#include "internal/deprecated.h" + +#include "cipher_aes_xts.h" + +#define XTS_SET_KEY_FN(fn_set_enc_key, fn_set_dec_key, \ + fn_block_enc, fn_block_dec, \ + fn_stream_enc, fn_stream_dec) { \ + size_t bytes = keylen / 2; \ + size_t bits = bytes * 8; \ + \ + if (ctx->enc) { \ + fn_set_enc_key(key, bits, &xctx->ks1.ks); \ + xctx->xts.block1 = (block128_f)fn_block_enc; \ + } else { \ + fn_set_dec_key(key, bits, &xctx->ks1.ks); \ + xctx->xts.block1 = (block128_f)fn_block_dec; \ + } \ + fn_set_enc_key(key + bytes, bits, &xctx->ks2.ks); \ + xctx->xts.block2 = (block128_f)fn_block_enc; \ + xctx->xts.key1 = &xctx->ks1; \ + xctx->xts.key2 = &xctx->ks2; \ + xctx->stream = ctx->enc ? fn_stream_enc : fn_stream_dec; \ +} + +static int cipher_hw_aes_xts_generic_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, + size_t keylen) +{ + PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)ctx; + OSSL_xts_stream_fn stream_enc = NULL; + OSSL_xts_stream_fn stream_dec = NULL; + +#ifdef AES_XTS_ASM + stream_enc = AES_xts_encrypt; + stream_dec = AES_xts_decrypt; +#endif /* AES_XTS_ASM */ + +#ifdef HWAES_CAPABLE + if (HWAES_CAPABLE) { +# ifdef HWAES_xts_encrypt + stream_enc = HWAES_xts_encrypt; +# endif /* HWAES_xts_encrypt */ +# ifdef HWAES_xts_decrypt + stream_dec = HWAES_xts_decrypt; +# endif /* HWAES_xts_decrypt */ + XTS_SET_KEY_FN(HWAES_set_encrypt_key, HWAES_set_decrypt_key, + HWAES_encrypt, HWAES_decrypt, + stream_enc, stream_dec); + return 1; + } else +#endif /* HWAES_CAPABLE */ + +#ifdef BSAES_CAPABLE + if (BSAES_CAPABLE) { + stream_enc = ossl_bsaes_xts_encrypt; + stream_dec = ossl_bsaes_xts_decrypt; + } else +#endif /* BSAES_CAPABLE */ +#ifdef VPAES_CAPABLE + if (VPAES_CAPABLE) { + XTS_SET_KEY_FN(vpaes_set_encrypt_key, vpaes_set_decrypt_key, + vpaes_encrypt, vpaes_decrypt, stream_enc, stream_dec); + return 1; + } else +#endif /* VPAES_CAPABLE */ + { + (void)0; + } + { + XTS_SET_KEY_FN(AES_set_encrypt_key, AES_set_decrypt_key, + AES_encrypt, AES_decrypt, stream_enc, stream_dec); + } + return 1; +} + +static void cipher_hw_aes_xts_copyctx(PROV_CIPHER_CTX *dst, + const PROV_CIPHER_CTX *src) +{ + PROV_AES_XTS_CTX *sctx = (PROV_AES_XTS_CTX *)src; + PROV_AES_XTS_CTX *dctx = (PROV_AES_XTS_CTX *)dst; + + *dctx = *sctx; + dctx->xts.key1 = &dctx->ks1.ks; + dctx->xts.key2 = &dctx->ks2.ks; +} + +#if defined(AESNI_CAPABLE) + +static int cipher_hw_aesni_xts_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)ctx; + + void (*aesni_xts_enc)(const unsigned char *in, + unsigned char *out, + size_t length, + const AES_KEY *key1, const AES_KEY *key2, + const unsigned char iv[16]); + void (*aesni_xts_dec)(const unsigned char *in, + unsigned char *out, + size_t length, + const AES_KEY *key1, const AES_KEY *key2, + const unsigned char iv[16]); + + aesni_xts_enc = aesni_xts_encrypt; + aesni_xts_dec = aesni_xts_decrypt; + +# if (defined(__x86_64) || defined(__x86_64__) || \ + defined(_M_AMD64) || defined(_M_X64)) + if (aesni_xts_avx512_eligible()) { + if (keylen == 64) { + aesni_xts_enc = aesni_xts_256_encrypt_avx512; + aesni_xts_dec = aesni_xts_256_decrypt_avx512; + } else if (keylen == 32) { + aesni_xts_enc = aesni_xts_128_encrypt_avx512; + aesni_xts_dec = aesni_xts_128_decrypt_avx512; + } + } +# endif + + XTS_SET_KEY_FN(aesni_set_encrypt_key, aesni_set_decrypt_key, + aesni_encrypt, aesni_decrypt, + aesni_xts_enc, aesni_xts_dec); + return 1; +} + +# define PROV_CIPHER_HW_declare_xts() \ +static const PROV_CIPHER_HW aesni_xts = { \ + cipher_hw_aesni_xts_initkey, \ + NULL, \ + cipher_hw_aes_xts_copyctx \ +}; +# define PROV_CIPHER_HW_select_xts() \ +if (AESNI_CAPABLE) \ + return &aesni_xts; + +# elif defined(SPARC_AES_CAPABLE) + +static int cipher_hw_aes_xts_t4_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)ctx; + OSSL_xts_stream_fn stream_enc = NULL; + OSSL_xts_stream_fn stream_dec = NULL; + + /* Note: keylen is the size of 2 keys */ + switch (keylen) { + case 32: + stream_enc = aes128_t4_xts_encrypt; + stream_dec = aes128_t4_xts_decrypt; + break; + case 64: + stream_enc = aes256_t4_xts_encrypt; + stream_dec = aes256_t4_xts_decrypt; + break; + default: + return 0; + } + + XTS_SET_KEY_FN(aes_t4_set_encrypt_key, aes_t4_set_decrypt_key, + aes_t4_encrypt, aes_t4_decrypt, + stream_enc, stream_dec); + return 1; +} + +# define PROV_CIPHER_HW_declare_xts() \ +static const PROV_CIPHER_HW aes_xts_t4 = { \ + cipher_hw_aes_xts_t4_initkey, \ + NULL, \ + cipher_hw_aes_xts_copyctx \ +}; +# define PROV_CIPHER_HW_select_xts() \ +if (SPARC_AES_CAPABLE) \ + return &aes_xts_t4; + +#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 + +static int cipher_hw_aes_xts_rv64i_zknd_zkne_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, + size_t keylen) +{ + PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)ctx; + OSSL_xts_stream_fn stream_enc = NULL; + OSSL_xts_stream_fn stream_dec = NULL; + + XTS_SET_KEY_FN(rv64i_zkne_set_encrypt_key, rv64i_zknd_set_decrypt_key, + rv64i_zkne_encrypt, rv64i_zknd_decrypt, + stream_enc, stream_dec); + return 1; +} + +static int cipher_hw_aes_xts_rv64i_zvbb_zvkg_zvkned_initkey( + PROV_CIPHER_CTX *ctx, const unsigned char *key, size_t keylen) +{ + PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)ctx; + OSSL_xts_stream_fn stream_enc = NULL; + OSSL_xts_stream_fn stream_dec = NULL; + + /* Zvkned only supports 128 and 256 bit keys. */ + if (keylen * 8 == 128 * 2 || keylen * 8 == 256 * 2) { + XTS_SET_KEY_FN(rv64i_zvkned_set_encrypt_key, + rv64i_zvkned_set_decrypt_key, rv64i_zvkned_encrypt, + rv64i_zvkned_decrypt, + rv64i_zvbb_zvkg_zvkned_aes_xts_encrypt, + rv64i_zvbb_zvkg_zvkned_aes_xts_decrypt); + } else { + XTS_SET_KEY_FN(AES_set_encrypt_key, AES_set_encrypt_key, + rv64i_zvkned_encrypt, rv64i_zvkned_decrypt, + stream_enc, stream_dec); + } + return 1; +} + +static int cipher_hw_aes_xts_rv64i_zvkned_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, + size_t keylen) +{ + PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)ctx; + OSSL_xts_stream_fn stream_enc = NULL; + OSSL_xts_stream_fn stream_dec = NULL; + + /* Zvkned only supports 128 and 256 bit keys. */ + if (keylen * 8 == 128 * 2 || keylen * 8 == 256 * 2) { + XTS_SET_KEY_FN(rv64i_zvkned_set_encrypt_key, + rv64i_zvkned_set_decrypt_key, + rv64i_zvkned_encrypt, rv64i_zvkned_decrypt, + stream_enc, stream_dec); + } else { + XTS_SET_KEY_FN(AES_set_encrypt_key, AES_set_encrypt_key, + rv64i_zvkned_encrypt, rv64i_zvkned_decrypt, + stream_enc, stream_dec); + } + return 1; +} + +# define PROV_CIPHER_HW_declare_xts() \ +static const PROV_CIPHER_HW aes_xts_rv64i_zknd_zkne = { \ + cipher_hw_aes_xts_rv64i_zknd_zkne_initkey, \ + NULL, \ + cipher_hw_aes_xts_copyctx \ +}; \ +static const PROV_CIPHER_HW aes_xts_rv64i_zvkned = { \ + cipher_hw_aes_xts_rv64i_zvkned_initkey, \ + NULL, \ + cipher_hw_aes_xts_copyctx \ +}; \ +static const PROV_CIPHER_HW aes_xts_rv64i_zvbb_zvkg_zvkned = { \ + cipher_hw_aes_xts_rv64i_zvbb_zvkg_zvkned_initkey, \ + NULL, \ + cipher_hw_aes_xts_copyctx \ +}; + +# define PROV_CIPHER_HW_select_xts() \ +if (RISCV_HAS_ZVBB() && RISCV_HAS_ZVKG() && RISCV_HAS_ZVKNED() && \ + riscv_vlen() >= 128) \ + return &aes_xts_rv64i_zvbb_zvkg_zvkned; \ +if (RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) \ + return &aes_xts_rv64i_zvkned; \ +else if (RISCV_HAS_ZKND_AND_ZKNE()) \ + return &aes_xts_rv64i_zknd_zkne; + +#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32 + +static int cipher_hw_aes_xts_rv32i_zknd_zkne_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, + size_t keylen) +{ + PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)ctx; + + XTS_SET_KEY_FN(rv32i_zkne_set_encrypt_key, rv32i_zknd_zkne_set_decrypt_key, + rv32i_zkne_encrypt, rv32i_zknd_decrypt, + NULL, NULL); + return 1; +} + +static int cipher_hw_aes_xts_rv32i_zbkb_zknd_zkne_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, + size_t keylen) +{ + PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)ctx; + + XTS_SET_KEY_FN(rv32i_zbkb_zkne_set_encrypt_key, rv32i_zbkb_zknd_zkne_set_decrypt_key, + rv32i_zkne_encrypt, rv32i_zknd_decrypt, + NULL, NULL); + return 1; +} + +# define PROV_CIPHER_HW_declare_xts() \ +static const PROV_CIPHER_HW aes_xts_rv32i_zknd_zkne = { \ + cipher_hw_aes_xts_rv32i_zknd_zkne_initkey, \ + NULL, \ + cipher_hw_aes_xts_copyctx \ +}; \ +static const PROV_CIPHER_HW aes_xts_rv32i_zbkb_zknd_zkne = { \ + cipher_hw_aes_xts_rv32i_zbkb_zknd_zkne_initkey, \ + NULL, \ + cipher_hw_aes_xts_copyctx \ +}; +# define PROV_CIPHER_HW_select_xts() \ +if (RISCV_HAS_ZBKB_AND_ZKND_AND_ZKNE()) \ + return &aes_xts_rv32i_zbkb_zknd_zkne; \ +if (RISCV_HAS_ZKND_AND_ZKNE()) \ + return &aes_xts_rv32i_zknd_zkne; +# else +/* The generic case */ +# define PROV_CIPHER_HW_declare_xts() +# define PROV_CIPHER_HW_select_xts() +#endif + +static const PROV_CIPHER_HW aes_generic_xts = { + cipher_hw_aes_xts_generic_initkey, + NULL, + cipher_hw_aes_xts_copyctx +}; +PROV_CIPHER_HW_declare_xts() +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts(size_t keybits) +{ + PROV_CIPHER_HW_select_xts() + return &aes_generic_xts; +} diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_xts_s390x.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_xts_s390x.inc new file mode 100644 index 000000000000..77341b3bbd4d --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_xts_s390x.inc @@ -0,0 +1,167 @@ +/* + * Copyright 2024 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 + */ + +#include "crypto/s390x_arch.h" + +static OSSL_FUNC_cipher_encrypt_init_fn s390x_aes_xts_einit; +static OSSL_FUNC_cipher_decrypt_init_fn s390x_aes_xts_dinit; +static OSSL_FUNC_cipher_cipher_fn s390x_aes_xts_cipher; +static OSSL_FUNC_cipher_dupctx_fn s390x_aes_xts_dupctx; + +static int s390x_aes_xts_init(void *vctx, const unsigned char *key, + size_t keylen, const unsigned char *iv, + size_t ivlen, const OSSL_PARAM params[], + unsigned int dec) +{ + PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)vctx; + S390X_KM_XTS_PARAMS *km = &xctx->plat.s390x.param.km; + unsigned int fc, offs; + + switch (xctx->base.keylen) { + case 128 / 8 * 2: + fc = S390X_XTS_AES_128_MSA10; + offs = 32; + break; + case 256 / 8 * 2: + fc = S390X_XTS_AES_256_MSA10; + offs = 0; + break; + default: + goto not_supported; + } + + if (!(OPENSSL_s390xcap_P.km[1] && S390X_CAPBIT(fc))) + goto not_supported; + + if (iv != NULL) { + if (ivlen != xctx->base.ivlen + || ivlen > sizeof(km->tweak)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); + return 0; + } + memcpy(km->tweak, iv, ivlen); + xctx->plat.s390x.iv_set = 1; + } + + if (key != NULL) { + if (keylen != xctx->base.keylen) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + if (!aes_xts_check_keys_differ(key, keylen / 2, !dec)) + return 0; + + memcpy(km->key + offs, key, keylen); + xctx->plat.s390x.key_set = 1; + } + + xctx->plat.s390x.fc = fc | dec; + xctx->plat.s390x.offset = offs; + + memset(km->nap, 0, sizeof(km->nap)); + km->nap[0] = 0x1; + + return aes_xts_set_ctx_params(xctx, params); + +not_supported: + xctx->plat.s390x.fc = 0; + xctx->plat.s390x.offset = 0; + return 0; +} + +static int s390x_aes_xts_einit(void *vctx, const unsigned char *key, + size_t keylen, const unsigned char *iv, + size_t ivlen, const OSSL_PARAM params[]) +{ + return s390x_aes_xts_init(vctx, key, keylen, iv, ivlen, params, 0); +} + +static int s390x_aes_xts_dinit(void *vctx, const unsigned char *key, + size_t keylen, const unsigned char *iv, + size_t ivlen, const OSSL_PARAM params[]) +{ + return s390x_aes_xts_init(vctx, key, keylen, iv, ivlen, params, + S390X_DECRYPT); +} + +static void *s390x_aes_xts_dupctx(void *vctx) +{ + PROV_AES_XTS_CTX *in = (PROV_AES_XTS_CTX *)vctx; + PROV_AES_XTS_CTX *ret = OPENSSL_zalloc(sizeof(*in)); + + if (ret != NULL) + *ret = *in; + + return ret; +} + +static int s390x_aes_xts_cipher(void *vctx, unsigned char *out, size_t *outl, + size_t outsize, const unsigned char *in, + size_t inl) +{ + PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)vctx; + S390X_KM_XTS_PARAMS *km = &xctx->plat.s390x.param.km; + unsigned char *param = (unsigned char *)km + xctx->plat.s390x.offset; + unsigned int fc = xctx->plat.s390x.fc; + unsigned char tmp[2][AES_BLOCK_SIZE]; + unsigned char nap_n1[AES_BLOCK_SIZE]; + unsigned char drop[AES_BLOCK_SIZE]; + size_t len_incomplete, len_complete; + + if (!ossl_prov_is_running() + || inl < AES_BLOCK_SIZE + || in == NULL + || out == NULL + || !xctx->plat.s390x.iv_set + || !xctx->plat.s390x.key_set) + return 0; + + /* + * Impose a limit of 2^20 blocks per data unit as specified by + * IEEE Std 1619-2018. The earlier and obsolete IEEE Std 1619-2007 + * indicated that this was a SHOULD NOT rather than a MUST NOT. + * NIST SP 800-38E mandates the same limit. + */ + if (inl > XTS_MAX_BLOCKS_PER_DATA_UNIT * AES_BLOCK_SIZE) { + ERR_raise(ERR_LIB_PROV, PROV_R_XTS_DATA_UNIT_IS_TOO_LARGE); + return 0; + } + + len_incomplete = inl % AES_BLOCK_SIZE; + len_complete = (len_incomplete == 0) ? inl : + (inl / AES_BLOCK_SIZE - 1) * AES_BLOCK_SIZE; + + if (len_complete > 0) + s390x_km(in, len_complete, out, fc, param); + if (len_incomplete == 0) + goto out; + + memcpy(tmp, in + len_complete, AES_BLOCK_SIZE + len_incomplete); + /* swap NAP for decrypt */ + if (fc & S390X_DECRYPT) { + memcpy(nap_n1, km->nap, AES_BLOCK_SIZE); + s390x_km(tmp[0], AES_BLOCK_SIZE, drop, fc, param); + } + s390x_km(tmp[0], AES_BLOCK_SIZE, tmp[0], fc, param); + if (fc & S390X_DECRYPT) + memcpy(km->nap, nap_n1, AES_BLOCK_SIZE); + + memcpy(tmp[1] + len_incomplete, tmp[0] + len_incomplete, + AES_BLOCK_SIZE - len_incomplete); + s390x_km(tmp[1], AES_BLOCK_SIZE, out + len_complete, fc, param); + memcpy(out + len_complete + AES_BLOCK_SIZE, tmp[0], len_incomplete); + + /* do not expose temporary data */ + OPENSSL_cleanse(tmp, sizeof(tmp)); +out: + memcpy(xctx->base.iv, km->tweak, AES_BLOCK_SIZE); + *outl = inl; + + return 1; +} diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aria.c b/crypto/openssl/providers/implementations/ciphers/cipher_aria.c new file mode 100644 index 000000000000..ce4938d44a9a --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aria.c @@ -0,0 +1,84 @@ +/* + * Copyright 2019-2020 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 + */ + +/* Dispatch functions for ARIA cipher modes ecb, cbc, ofb, cfb, ctr */ + +#include "cipher_aria.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" + +static OSSL_FUNC_cipher_freectx_fn aria_freectx; +static OSSL_FUNC_cipher_dupctx_fn aria_dupctx; + +static void aria_freectx(void *vctx) +{ + PROV_ARIA_CTX *ctx = (PROV_ARIA_CTX *)vctx; + + ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx); + OPENSSL_clear_free(ctx, sizeof(*ctx)); +} + +static void *aria_dupctx(void *ctx) +{ + PROV_ARIA_CTX *in = (PROV_ARIA_CTX *)ctx; + PROV_ARIA_CTX *ret; + + if (!ossl_prov_is_running()) + return NULL; + + ret = OPENSSL_malloc(sizeof(*ret)); + if (ret == NULL) + return NULL; + in->base.hw->copyctx(&ret->base, &in->base); + + return ret; +} + +/* ossl_aria256ecb_functions */ +IMPLEMENT_generic_cipher(aria, ARIA, ecb, ECB, 0, 256, 128, 0, block) +/* ossl_aria192ecb_functions */ +IMPLEMENT_generic_cipher(aria, ARIA, ecb, ECB, 0, 192, 128, 0, block) +/* ossl_aria128ecb_functions */ +IMPLEMENT_generic_cipher(aria, ARIA, ecb, ECB, 0, 128, 128, 0, block) +/* ossl_aria256cbc_functions */ +IMPLEMENT_generic_cipher(aria, ARIA, cbc, CBC, 0, 256, 128, 128, block) +/* ossl_aria192cbc_functions */ +IMPLEMENT_generic_cipher(aria, ARIA, cbc, CBC, 0, 192, 128, 128, block) +/* ossl_aria128cbc_functions */ +IMPLEMENT_generic_cipher(aria, ARIA, cbc, CBC, 0, 128, 128, 128, block) +/* ossl_aria256ofb_functions */ +IMPLEMENT_generic_cipher(aria, ARIA, ofb, OFB, 0, 256, 8, 128, stream) +/* ossl_aria192ofb_functions */ +IMPLEMENT_generic_cipher(aria, ARIA, ofb, OFB, 0, 192, 8, 128, stream) +/* ossl_aria128ofb_functions */ +IMPLEMENT_generic_cipher(aria, ARIA, ofb, OFB, 0, 128, 8, 128, stream) +/* ossl_aria256cfb_functions */ +IMPLEMENT_generic_cipher(aria, ARIA, cfb, CFB, 0, 256, 8, 128, stream) +/* ossl_aria192cfb_functions */ +IMPLEMENT_generic_cipher(aria, ARIA, cfb, CFB, 0, 192, 8, 128, stream) +/* ossl_aria128cfb_functions */ +IMPLEMENT_generic_cipher(aria, ARIA, cfb, CFB, 0, 128, 8, 128, stream) +/* ossl_aria256cfb1_functions */ +IMPLEMENT_generic_cipher(aria, ARIA, cfb1, CFB, 0, 256, 8, 128, stream) +/* ossl_aria192cfb1_functions */ +IMPLEMENT_generic_cipher(aria, ARIA, cfb1, CFB, 0, 192, 8, 128, stream) +/* ossl_aria128cfb1_functions */ +IMPLEMENT_generic_cipher(aria, ARIA, cfb1, CFB, 0, 128, 8, 128, stream) +/* ossl_aria256cfb8_functions */ +IMPLEMENT_generic_cipher(aria, ARIA, cfb8, CFB, 0, 256, 8, 128, stream) +/* ossl_aria192cfb8_functions */ +IMPLEMENT_generic_cipher(aria, ARIA, cfb8, CFB, 0, 192, 8, 128, stream) +/* ossl_aria128cfb8_functions */ +IMPLEMENT_generic_cipher(aria, ARIA, cfb8, CFB, 0, 128, 8, 128, stream) +/* ossl_aria256ctr_functions */ +IMPLEMENT_generic_cipher(aria, ARIA, ctr, CTR, 0, 256, 8, 128, stream) +/* ossl_aria192ctr_functions */ +IMPLEMENT_generic_cipher(aria, ARIA, ctr, CTR, 0, 192, 8, 128, stream) +/* ossl_aria128ctr_functions */ +IMPLEMENT_generic_cipher(aria, ARIA, ctr, CTR, 0, 128, 8, 128, stream) diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aria.h b/crypto/openssl/providers/implementations/ciphers/cipher_aria.h new file mode 100644 index 000000000000..39f84d3b4321 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aria.h @@ -0,0 +1,30 @@ +/* + * Copyright 2019-2020 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 + */ + +#include "crypto/aria.h" +#include "prov/ciphercommon.h" + +typedef struct prov_aria_ctx_st { + PROV_CIPHER_CTX base; /* Must be first */ + union { + OSSL_UNION_ALIGN; + ARIA_KEY ks; + } ks; +} PROV_ARIA_CTX; + + +#define ossl_prov_cipher_hw_aria_ofb ossl_prov_cipher_hw_aria_ofb128 +#define ossl_prov_cipher_hw_aria_cfb ossl_prov_cipher_hw_aria_cfb128 +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aria_ecb(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aria_cbc(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aria_ofb128(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aria_cfb128(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aria_cfb1(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aria_cfb8(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aria_ctr(size_t keybits); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aria_ccm.c b/crypto/openssl/providers/implementations/ciphers/cipher_aria_ccm.c new file mode 100644 index 000000000000..0a0f52cdcc1b --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aria_ccm.c @@ -0,0 +1,59 @@ +/* + * Copyright 2019-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 + */ + +/* Dispatch functions for ARIA CCM mode */ + +#include "cipher_aria_ccm.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" + +static OSSL_FUNC_cipher_freectx_fn aria_ccm_freectx; + +static void *aria_ccm_newctx(void *provctx, size_t keybits) +{ + PROV_ARIA_CCM_CTX *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + ctx = OPENSSL_zalloc(sizeof(*ctx)); + if (ctx != NULL) + ossl_ccm_initctx(&ctx->base, keybits, ossl_prov_aria_hw_ccm(keybits)); + return ctx; +} + +static void *aria_ccm_dupctx(void *provctx) +{ + PROV_ARIA_CCM_CTX *ctx = provctx; + PROV_ARIA_CCM_CTX *dctx = NULL; + + if (ctx == NULL) + return NULL; + + dctx = OPENSSL_memdup(ctx, sizeof(*ctx)); + if (dctx != NULL && dctx->base.ccm_ctx.key != NULL) + dctx->base.ccm_ctx.key = &dctx->ks.ks; + + return dctx; +} + +static void aria_ccm_freectx(void *vctx) +{ + PROV_ARIA_CCM_CTX *ctx = (PROV_ARIA_CCM_CTX *)vctx; + + OPENSSL_clear_free(ctx, sizeof(*ctx)); +} + +/* aria128ccm functions */ +IMPLEMENT_aead_cipher(aria, ccm, CCM, AEAD_FLAGS, 128, 8, 96); +/* aria192ccm functions */ +IMPLEMENT_aead_cipher(aria, ccm, CCM, AEAD_FLAGS, 192, 8, 96); +/* aria256ccm functions */ +IMPLEMENT_aead_cipher(aria, ccm, CCM, AEAD_FLAGS, 256, 8, 96); + diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aria_ccm.h b/crypto/openssl/providers/implementations/ciphers/cipher_aria_ccm.h new file mode 100644 index 000000000000..558da4973fa2 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aria_ccm.h @@ -0,0 +1,22 @@ +/* + * Copyright 2019-2020 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 + */ + +#include "crypto/aria.h" +#include "prov/ciphercommon.h" +#include "prov/ciphercommon_ccm.h" + +typedef struct prov_aria_ccm_ctx_st { + PROV_CCM_CTX base; /* Must be first */ + union { + OSSL_UNION_ALIGN; + ARIA_KEY ks; + } ks; /* ARIA key schedule to use */ +} PROV_ARIA_CCM_CTX; + +const PROV_CCM_HW *ossl_prov_aria_hw_ccm(size_t keylen); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aria_ccm_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_aria_ccm_hw.c new file mode 100644 index 000000000000..e56ec8fb0865 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aria_ccm_hw.c @@ -0,0 +1,40 @@ +/* + * Copyright 2019-2021 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 + */ + +/*- + * Generic support for ARIA CCM. + */ + +#include "cipher_aria_ccm.h" + +static int ccm_aria_initkey(PROV_CCM_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + PROV_ARIA_CCM_CTX *actx = (PROV_ARIA_CCM_CTX *)ctx; + + ossl_aria_set_encrypt_key(key, keylen * 8, &actx->ks.ks); + CRYPTO_ccm128_init(&ctx->ccm_ctx, ctx->m, ctx->l, &actx->ks.ks, + (block128_f)ossl_aria_encrypt); + ctx->str = NULL; + ctx->key_set = 1; + return 1; +} + +static const PROV_CCM_HW ccm_aria = { + ccm_aria_initkey, + ossl_ccm_generic_setiv, + ossl_ccm_generic_setaad, + ossl_ccm_generic_auth_encrypt, + ossl_ccm_generic_auth_decrypt, + ossl_ccm_generic_gettag +}; +const PROV_CCM_HW *ossl_prov_aria_hw_ccm(size_t keybits) +{ + return &ccm_aria; +} diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aria_gcm.c b/crypto/openssl/providers/implementations/ciphers/cipher_aria_gcm.c new file mode 100644 index 000000000000..e794a80a00a4 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aria_gcm.c @@ -0,0 +1,59 @@ +/* + * Copyright 2019-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 + */ + +/* Dispatch functions for ARIA GCM mode */ + +#include "cipher_aria_gcm.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" + +static void *aria_gcm_newctx(void *provctx, size_t keybits) +{ + PROV_ARIA_GCM_CTX *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + ctx = OPENSSL_zalloc(sizeof(*ctx)); + if (ctx != NULL) + ossl_gcm_initctx(provctx, &ctx->base, keybits, + ossl_prov_aria_hw_gcm(keybits)); + return ctx; +} + +static void *aria_gcm_dupctx(void *provctx) +{ + PROV_ARIA_GCM_CTX *ctx = provctx; + PROV_ARIA_GCM_CTX *dctx = NULL; + + if (ctx == NULL) + return NULL; + + dctx = OPENSSL_memdup(ctx, sizeof(*ctx)); + if (dctx != NULL && dctx->base.gcm.key != NULL) + dctx->base.gcm.key = &dctx->ks.ks; + + return dctx; +} + +static OSSL_FUNC_cipher_freectx_fn aria_gcm_freectx; +static void aria_gcm_freectx(void *vctx) +{ + PROV_ARIA_GCM_CTX *ctx = (PROV_ARIA_GCM_CTX *)vctx; + + OPENSSL_clear_free(ctx, sizeof(*ctx)); +} + +/* ossl_aria128gcm_functions */ +IMPLEMENT_aead_cipher(aria, gcm, GCM, AEAD_FLAGS, 128, 8, 96); +/* ossl_aria192gcm_functions */ +IMPLEMENT_aead_cipher(aria, gcm, GCM, AEAD_FLAGS, 192, 8, 96); +/* ossl_aria256gcm_functions */ +IMPLEMENT_aead_cipher(aria, gcm, GCM, AEAD_FLAGS, 256, 8, 96); + diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aria_gcm.h b/crypto/openssl/providers/implementations/ciphers/cipher_aria_gcm.h new file mode 100644 index 000000000000..6251e8322f36 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aria_gcm.h @@ -0,0 +1,22 @@ +/* + * Copyright 2019-2020 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 + */ + +#include "crypto/aria.h" +#include "prov/ciphercommon.h" +#include "prov/ciphercommon_gcm.h" + +typedef struct prov_aria_gcm_ctx_st { + PROV_GCM_CTX base; /* must be first entry in struct */ + union { + OSSL_UNION_ALIGN; + ARIA_KEY ks; + } ks; +} PROV_ARIA_GCM_CTX; + +const PROV_GCM_HW *ossl_prov_aria_hw_gcm(size_t keybits); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aria_gcm_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_aria_gcm_hw.c new file mode 100644 index 000000000000..927327c29c77 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aria_gcm_hw.c @@ -0,0 +1,37 @@ +/* + * Copyright 2019-2021 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 + */ + +/*- + * Generic support for ARIA GCM. + */ + +#include "cipher_aria_gcm.h" + +static int aria_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + PROV_ARIA_GCM_CTX *actx = (PROV_ARIA_GCM_CTX *)ctx; + ARIA_KEY *ks = &actx->ks.ks; + + GCM_HW_SET_KEY_CTR_FN(ks, ossl_aria_set_encrypt_key, ossl_aria_encrypt, NULL); + return 1; +} + +static const PROV_GCM_HW aria_gcm = { + aria_gcm_initkey, + ossl_gcm_setiv, + ossl_gcm_aad_update, + ossl_gcm_cipher_update, + ossl_gcm_cipher_final, + ossl_gcm_one_shot +}; +const PROV_GCM_HW *ossl_prov_aria_hw_gcm(size_t keybits) +{ + return &aria_gcm; +} diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aria_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_aria_hw.c new file mode 100644 index 000000000000..425d87a65eca --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_aria_hw.c @@ -0,0 +1,52 @@ +/* + * Copyright 2001-2021 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 + */ + +#include <openssl/proverr.h> +#include "cipher_aria.h" + +static int cipher_hw_aria_initkey(PROV_CIPHER_CTX *dat, + const unsigned char *key, size_t keylen) +{ + int ret, mode = dat->mode; + PROV_ARIA_CTX *adat = (PROV_ARIA_CTX *)dat; + ARIA_KEY *ks = &adat->ks.ks; + + if (dat->enc || (mode != EVP_CIPH_ECB_MODE && mode != EVP_CIPH_CBC_MODE)) + ret = ossl_aria_set_encrypt_key(key, keylen * 8, ks); + else + ret = ossl_aria_set_decrypt_key(key, keylen * 8, ks); + if (ret < 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED); + return 0; + } + dat->ks = ks; + dat->block = (block128_f)ossl_aria_encrypt; + return 1; +} + +IMPLEMENT_CIPHER_HW_COPYCTX(cipher_hw_aria_copyctx, PROV_ARIA_CTX) + +# define PROV_CIPHER_HW_aria_mode(mode) \ +static const PROV_CIPHER_HW aria_##mode = { \ + cipher_hw_aria_initkey, \ + ossl_cipher_hw_chunked_##mode, \ + cipher_hw_aria_copyctx \ +}; \ +const PROV_CIPHER_HW *ossl_prov_cipher_hw_aria_##mode(size_t keybits) \ +{ \ + return &aria_##mode; \ +} + +PROV_CIPHER_HW_aria_mode(cbc) +PROV_CIPHER_HW_aria_mode(ecb) +PROV_CIPHER_HW_aria_mode(ofb128) +PROV_CIPHER_HW_aria_mode(cfb128) +PROV_CIPHER_HW_aria_mode(cfb1) +PROV_CIPHER_HW_aria_mode(cfb8) +PROV_CIPHER_HW_aria_mode(ctr) diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_blowfish.c b/crypto/openssl/providers/implementations/ciphers/cipher_blowfish.c new file mode 100644 index 000000000000..9f17f1200dfd --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_blowfish.c @@ -0,0 +1,58 @@ +/* + * Copyright 2019-2021 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 + */ + +/* Dispatch functions for Blowfish cipher modes ecb, cbc, ofb, cfb */ + +/* + * BF low level APIs are deprecated for public use, but still ok for internal + * use. + */ +#include "internal/deprecated.h" + +#include "cipher_blowfish.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" + +#define BF_FLAGS PROV_CIPHER_FLAG_VARIABLE_LENGTH + +static OSSL_FUNC_cipher_freectx_fn blowfish_freectx; +static OSSL_FUNC_cipher_dupctx_fn blowfish_dupctx; + +static void blowfish_freectx(void *vctx) +{ + PROV_BLOWFISH_CTX *ctx = (PROV_BLOWFISH_CTX *)vctx; + + ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx); + OPENSSL_clear_free(ctx, sizeof(*ctx)); +} + +static void *blowfish_dupctx(void *ctx) +{ + PROV_BLOWFISH_CTX *in = (PROV_BLOWFISH_CTX *)ctx; + PROV_BLOWFISH_CTX *ret; + + if (!ossl_prov_is_running()) + return NULL; + + ret = OPENSSL_malloc(sizeof(*ret)); + if (ret == NULL) + return NULL; + *ret = *in; + + return ret; +} + +/* bf_ecb_functions */ +IMPLEMENT_var_keylen_cipher(blowfish, BLOWFISH, ecb, ECB, BF_FLAGS, 128, 64, 0, block) +/* bf_cbc_functions */ +IMPLEMENT_var_keylen_cipher(blowfish, BLOWFISH, cbc, CBC, BF_FLAGS, 128, 64, 64, block) +/* bf_ofb_functions */ +IMPLEMENT_var_keylen_cipher(blowfish, BLOWFISH, ofb64, OFB, BF_FLAGS, 128, 8, 64, stream) +/* bf_cfb_functions */ +IMPLEMENT_var_keylen_cipher(blowfish, BLOWFISH, cfb64, CFB, BF_FLAGS, 128, 8, 64, stream) diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_blowfish.h b/crypto/openssl/providers/implementations/ciphers/cipher_blowfish.h new file mode 100644 index 000000000000..bbdc9da3789d --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_blowfish.h @@ -0,0 +1,24 @@ +/* + * Copyright 2019-2020 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 + */ + +#include <openssl/blowfish.h> +#include "prov/ciphercommon.h" + +typedef struct prov_blowfish_ctx_st { + PROV_CIPHER_CTX base; /* Must be first */ + union { + OSSL_UNION_ALIGN; + BF_KEY ks; + } ks; +} PROV_BLOWFISH_CTX; + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_blowfish_cbc(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_blowfish_ecb(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_blowfish_ofb64(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_blowfish_cfb64(size_t keybits); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_blowfish_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_blowfish_hw.c new file mode 100644 index 000000000000..4855a71f6871 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_blowfish_hw.c @@ -0,0 +1,42 @@ +/* + * Copyright 2019-2020 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 + */ + +/* + * BF low level APIs are deprecated for public use, but still ok for internal + * use. + */ +#include "internal/deprecated.h" + +#include "cipher_blowfish.h" + +static int cipher_hw_blowfish_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + PROV_BLOWFISH_CTX *bctx = (PROV_BLOWFISH_CTX *)ctx; + + BF_set_key(&bctx->ks.ks, keylen, key); + return 1; +} + +# define PROV_CIPHER_HW_blowfish_mode(mode, UCMODE) \ +IMPLEMENT_CIPHER_HW_##UCMODE(mode, blowfish, PROV_BLOWFISH_CTX, BF_KEY, \ + BF_##mode) \ +static const PROV_CIPHER_HW bf_##mode = { \ + cipher_hw_blowfish_initkey, \ + cipher_hw_blowfish_##mode##_cipher \ +}; \ +const PROV_CIPHER_HW *ossl_prov_cipher_hw_blowfish_##mode(size_t keybits) \ +{ \ + return &bf_##mode; \ +} + +PROV_CIPHER_HW_blowfish_mode(cbc, CBC) +PROV_CIPHER_HW_blowfish_mode(ecb, ECB) +PROV_CIPHER_HW_blowfish_mode(ofb64, OFB) +PROV_CIPHER_HW_blowfish_mode(cfb64, CFB) diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_camellia.c b/crypto/openssl/providers/implementations/ciphers/cipher_camellia.c new file mode 100644 index 000000000000..c550af3f8330 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_camellia.c @@ -0,0 +1,92 @@ +/* + * Copyright 2019-2021 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 + */ + +/* + * Camellia low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +/* Dispatch functions for CAMELLIA cipher modes ecb, cbc, ofb, cfb, ctr */ + +#include "cipher_camellia.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" + +static OSSL_FUNC_cipher_freectx_fn camellia_freectx; +static OSSL_FUNC_cipher_dupctx_fn camellia_dupctx; + +static void camellia_freectx(void *vctx) +{ + PROV_CAMELLIA_CTX *ctx = (PROV_CAMELLIA_CTX *)vctx; + + ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx); + OPENSSL_clear_free(ctx, sizeof(*ctx)); +} + +static void *camellia_dupctx(void *ctx) +{ + PROV_CAMELLIA_CTX *in = (PROV_CAMELLIA_CTX *)ctx; + PROV_CAMELLIA_CTX *ret; + + if (!ossl_prov_is_running()) + return NULL; + + ret = OPENSSL_malloc(sizeof(*ret)); + if (ret == NULL) + return NULL; + in->base.hw->copyctx(&ret->base, &in->base); + + return ret; +} + +/* ossl_camellia256ecb_functions */ +IMPLEMENT_generic_cipher(camellia, CAMELLIA, ecb, ECB, 0, 256, 128, 0, block) +/* ossl_camellia192ecb_functions */ +IMPLEMENT_generic_cipher(camellia, CAMELLIA, ecb, ECB, 0, 192, 128, 0, block) +/* ossl_camellia128ecb_functions */ +IMPLEMENT_generic_cipher(camellia, CAMELLIA, ecb, ECB, 0, 128, 128, 0, block) +/* ossl_camellia256cbc_functions */ +IMPLEMENT_generic_cipher(camellia, CAMELLIA, cbc, CBC, 0, 256, 128, 128, block) +/* ossl_camellia192cbc_functions */ +IMPLEMENT_generic_cipher(camellia, CAMELLIA, cbc, CBC, 0, 192, 128, 128, block) +/* ossl_camellia128cbc_functions */ +IMPLEMENT_generic_cipher(camellia, CAMELLIA, cbc, CBC, 0, 128, 128, 128, block) +/* ossl_camellia256ofb_functions */ +IMPLEMENT_generic_cipher(camellia, CAMELLIA, ofb, OFB, 0, 256, 8, 128, stream) +/* ossl_camellia192ofb_functions */ +IMPLEMENT_generic_cipher(camellia, CAMELLIA, ofb, OFB, 0, 192, 8, 128, stream) +/* ossl_camellia128ofb_functions */ +IMPLEMENT_generic_cipher(camellia, CAMELLIA, ofb, OFB, 0, 128, 8, 128, stream) +/* ossl_camellia256cfb_functions */ +IMPLEMENT_generic_cipher(camellia, CAMELLIA, cfb, CFB, 0, 256, 8, 128, stream) +/* ossl_camellia192cfb_functions */ +IMPLEMENT_generic_cipher(camellia, CAMELLIA, cfb, CFB, 0, 192, 8, 128, stream) +/* ossl_camellia128cfb_functions */ +IMPLEMENT_generic_cipher(camellia, CAMELLIA, cfb, CFB, 0, 128, 8, 128, stream) +/* ossl_camellia256cfb1_functions */ +IMPLEMENT_generic_cipher(camellia, CAMELLIA, cfb1, CFB, 0, 256, 8, 128, stream) +/* ossl_camellia192cfb1_functions */ +IMPLEMENT_generic_cipher(camellia, CAMELLIA, cfb1, CFB, 0, 192, 8, 128, stream) +/* ossl_camellia128cfb1_functions */ +IMPLEMENT_generic_cipher(camellia, CAMELLIA, cfb1, CFB, 0, 128, 8, 128, stream) +/* ossl_camellia256cfb8_functions */ +IMPLEMENT_generic_cipher(camellia, CAMELLIA, cfb8, CFB, 0, 256, 8, 128, stream) +/* ossl_camellia192cfb8_functions */ +IMPLEMENT_generic_cipher(camellia, CAMELLIA, cfb8, CFB, 0, 192, 8, 128, stream) +/* ossl_camellia128cfb8_functions */ +IMPLEMENT_generic_cipher(camellia, CAMELLIA, cfb8, CFB, 0, 128, 8, 128, stream) +/* ossl_camellia256ctr_functions */ +IMPLEMENT_generic_cipher(camellia, CAMELLIA, ctr, CTR, 0, 256, 8, 128, stream) +/* ossl_camellia192ctr_functions */ +IMPLEMENT_generic_cipher(camellia, CAMELLIA, ctr, CTR, 0, 192, 8, 128, stream) +/* ossl_camellia128ctr_functions */ +IMPLEMENT_generic_cipher(camellia, CAMELLIA, ctr, CTR, 0, 128, 8, 128, stream) + +#include "cipher_camellia_cts.inc" diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_camellia.h b/crypto/openssl/providers/implementations/ciphers/cipher_camellia.h new file mode 100644 index 000000000000..953ea74c0b5d --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_camellia.h @@ -0,0 +1,30 @@ +/* + * Copyright 2019-2021 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 + */ + +#include <openssl/camellia.h> +#include "prov/ciphercommon.h" +#include "crypto/cmll_platform.h" + +typedef struct prov_camellia_ctx_st { + PROV_CIPHER_CTX base; /* Must be first */ + union { + OSSL_UNION_ALIGN; + CAMELLIA_KEY ks; + } ks; +} PROV_CAMELLIA_CTX; + +#define ossl_prov_cipher_hw_camellia_ofb ossl_prov_cipher_hw_camellia_ofb128 +#define ossl_prov_cipher_hw_camellia_cfb ossl_prov_cipher_hw_camellia_cfb128 +const PROV_CIPHER_HW *ossl_prov_cipher_hw_camellia_ecb(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_camellia_cbc(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_camellia_ofb128(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_camellia_cfb128(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_camellia_cfb1(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_camellia_cfb8(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_camellia_ctr(size_t keybits); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_camellia_cts.inc b/crypto/openssl/providers/implementations/ciphers/cipher_camellia_cts.inc new file mode 100644 index 000000000000..84ea992b8da9 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_camellia_cts.inc @@ -0,0 +1,94 @@ +/* + * Copyright 2021 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 + */ + +/* Dispatch functions for CAMELLIA CBC CTS ciphers */ + +#include <openssl/proverr.h> +#include "cipher_cts.h" + +#define CTS_FLAGS PROV_CIPHER_FLAG_CTS + +static OSSL_FUNC_cipher_encrypt_init_fn camellia_cbc_cts_einit; +static OSSL_FUNC_cipher_decrypt_init_fn camellia_cbc_cts_dinit; +static OSSL_FUNC_cipher_get_ctx_params_fn camellia_cbc_cts_get_ctx_params; +static OSSL_FUNC_cipher_set_ctx_params_fn camellia_cbc_cts_set_ctx_params; +static OSSL_FUNC_cipher_gettable_ctx_params_fn camellia_cbc_cts_gettable_ctx_params; +static OSSL_FUNC_cipher_settable_ctx_params_fn camellia_cbc_cts_settable_ctx_params; + +CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_START(camellia_cbc_cts) +OSSL_PARAM_utf8_string(OSSL_CIPHER_PARAM_CTS_MODE, NULL, 0), +CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_END(camellia_cbc_cts) + +static int camellia_cbc_cts_einit(void *ctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + if (!ossl_cipher_generic_einit(ctx, key, keylen, iv, ivlen, NULL)) + return 0; + return camellia_cbc_cts_set_ctx_params(ctx, params); +} + +static int camellia_cbc_cts_dinit(void *ctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + if (!ossl_cipher_generic_dinit(ctx, key, keylen, iv, ivlen, NULL)) + return 0; + return camellia_cbc_cts_set_ctx_params(ctx, params); +} + +static int camellia_cbc_cts_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + OSSL_PARAM *p; + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_CTS_MODE); + if (p != NULL) { + const char *name = ossl_cipher_cbc_cts_mode_id2name(ctx->cts_mode); + + if (name == NULL || !OSSL_PARAM_set_utf8_string(p, name)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + } + return ossl_cipher_generic_get_ctx_params(vctx, params); +} + +CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_START(camellia_cbc_cts) +OSSL_PARAM_utf8_string(OSSL_CIPHER_PARAM_CTS_MODE, NULL, 0), +CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_END(camellia_cbc_cts) + +static int camellia_cbc_cts_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + const OSSL_PARAM *p; + int id; + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_CTS_MODE); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_UTF8_STRING) + goto err; + id = ossl_cipher_cbc_cts_mode_name2id(p->data); + if (id < 0) + goto err; + + ctx->cts_mode = (unsigned int)id; + } + return ossl_cipher_generic_set_ctx_params(vctx, params); +err: + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; +} + +/* ossl_camellia256cbc_cts_functions */ +IMPLEMENT_cts_cipher(camellia, CAMELLIA, cbc, CBC, CTS_FLAGS, 256, 128, 128, block) +/* ossl_camellia192cbc_cts_functions */ +IMPLEMENT_cts_cipher(camellia, CAMELLIA, cbc, CBC, CTS_FLAGS, 192, 128, 128, block) +/* ossl_camellia128cbc_cts_functions */ +IMPLEMENT_cts_cipher(camellia, CAMELLIA, cbc, CBC, CTS_FLAGS, 128, 128, 128, block) diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_camellia_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_camellia_hw.c new file mode 100644 index 000000000000..3ebf5b8d4612 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_camellia_hw.c @@ -0,0 +1,74 @@ +/* + * Copyright 2001-2021 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 + */ + +/* + * Camellia low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include <openssl/camellia.h> +#include <openssl/proverr.h> +#include "cipher_camellia.h" + +static int cipher_hw_camellia_initkey(PROV_CIPHER_CTX *dat, + const unsigned char *key, size_t keylen) +{ + int ret, mode = dat->mode; + PROV_CAMELLIA_CTX *adat = (PROV_CAMELLIA_CTX *)dat; + CAMELLIA_KEY *ks = &adat->ks.ks; + + dat->ks = ks; + ret = Camellia_set_key(key, keylen * 8, ks); + if (ret < 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED); + return 0; + } + if (dat->enc || (mode != EVP_CIPH_ECB_MODE && mode != EVP_CIPH_CBC_MODE)) { + dat->block = (block128_f) Camellia_encrypt; + dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ? + (cbc128_f) Camellia_cbc_encrypt : NULL; + } else { + dat->block = (block128_f) Camellia_decrypt; + dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ? + (cbc128_f) Camellia_cbc_encrypt : NULL; + } + return 1; +} + +IMPLEMENT_CIPHER_HW_COPYCTX(cipher_hw_camellia_copyctx, PROV_CAMELLIA_CTX) + +# if defined(SPARC_CMLL_CAPABLE) +# include "cipher_camellia_hw_t4.inc" +# else +/* The generic case */ +# define PROV_CIPHER_HW_declare(mode) +# define PROV_CIPHER_HW_select(mode) +# endif /* SPARC_CMLL_CAPABLE */ + +#define PROV_CIPHER_HW_camellia_mode(mode) \ +static const PROV_CIPHER_HW camellia_##mode = { \ + cipher_hw_camellia_initkey, \ + ossl_cipher_hw_generic_##mode, \ + cipher_hw_camellia_copyctx \ +}; \ +PROV_CIPHER_HW_declare(mode) \ +const PROV_CIPHER_HW *ossl_prov_cipher_hw_camellia_##mode(size_t keybits) \ +{ \ + PROV_CIPHER_HW_select(mode) \ + return &camellia_##mode; \ +} + +PROV_CIPHER_HW_camellia_mode(cbc) +PROV_CIPHER_HW_camellia_mode(ecb) +PROV_CIPHER_HW_camellia_mode(ofb128) +PROV_CIPHER_HW_camellia_mode(cfb128) +PROV_CIPHER_HW_camellia_mode(cfb1) +PROV_CIPHER_HW_camellia_mode(cfb8) +PROV_CIPHER_HW_camellia_mode(ctr) diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_camellia_hw_t4.inc b/crypto/openssl/providers/implementations/ciphers/cipher_camellia_hw_t4.inc new file mode 100644 index 000000000000..2dcf3fa18eb5 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_camellia_hw_t4.inc @@ -0,0 +1,84 @@ +/* + * Copyright 2001-2021 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 + */ + +/*- + * Fujitsu SPARC64 X support for camellia modes. + * This file is included by cipher_camellia_hw.c + */ + +static int cipher_hw_camellia_t4_initkey(PROV_CIPHER_CTX *dat, + const unsigned char *key, + size_t keylen) +{ + int ret = 0, bits, mode = dat->mode; + PROV_CAMELLIA_CTX *adat = (PROV_CAMELLIA_CTX *)dat; + CAMELLIA_KEY *ks = &adat->ks.ks; + + dat->ks = ks; + bits = keylen * 8; + + cmll_t4_set_key(key, bits, ks); + + if (dat->enc || (mode != EVP_CIPH_ECB_MODE && mode != EVP_CIPH_CBC_MODE)) { + dat->block = (block128_f) cmll_t4_encrypt; + switch (bits) { + case 128: + if (mode == EVP_CIPH_CBC_MODE) + dat->stream.cbc = (cbc128_f) cmll128_t4_cbc_encrypt; + else if (mode == EVP_CIPH_CTR_MODE) + dat->stream.ctr = (ctr128_f) cmll128_t4_ctr32_encrypt; + else + dat->stream.cbc = NULL; + break; + case 192: + case 256: + if (mode == EVP_CIPH_CBC_MODE) + dat->stream.cbc = (cbc128_f) cmll256_t4_cbc_encrypt; + else if (mode == EVP_CIPH_CTR_MODE) + dat->stream.ctr = (ctr128_f) cmll256_t4_ctr32_encrypt; + else + dat->stream.cbc = NULL; + break; + default: + ret = -1; + break; + } + } else { + dat->block = (block128_f) cmll_t4_decrypt; + switch (bits) { + case 128: + dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ? + (cbc128_f) cmll128_t4_cbc_decrypt : NULL; + break; + case 192: + case 256: + dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ? + (cbc128_f) cmll256_t4_cbc_decrypt : NULL; + break; + default: + ret = -1; + break; + } + } + if (ret < 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED); + return 0; + } + return 1; +} + +#define PROV_CIPHER_HW_declare(mode) \ +static const PROV_CIPHER_HW t4_camellia_##mode = { \ + cipher_hw_camellia_t4_initkey, \ + ossl_cipher_hw_generic_##mode, \ + cipher_hw_camellia_copyctx \ +}; +#define PROV_CIPHER_HW_select(mode) \ +if (SPARC_CMLL_CAPABLE) \ + return &t4_camellia_##mode; diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_cast.h b/crypto/openssl/providers/implementations/ciphers/cipher_cast.h new file mode 100644 index 000000000000..84b58621c1bc --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_cast.h @@ -0,0 +1,24 @@ +/* + * Copyright 2019-2020 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 + */ + +#include <openssl/cast.h> +#include "prov/ciphercommon.h" + +typedef struct prov_cast_ctx_st { + PROV_CIPHER_CTX base; /* Must be first */ + union { + OSSL_UNION_ALIGN; + CAST_KEY ks; + } ks; +} PROV_CAST_CTX; + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_cast5_cbc(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_cast5_ecb(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_cast5_ofb64(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_cast5_cfb64(size_t keybits); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_cast5.c b/crypto/openssl/providers/implementations/ciphers/cipher_cast5.c new file mode 100644 index 000000000000..84c88793b05e --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_cast5.c @@ -0,0 +1,59 @@ +/* + * Copyright 2019-2021 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 + */ + +/* + * CAST low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +/* Dispatch functions for cast cipher modes ecb, cbc, ofb, cfb */ + +#include <openssl/proverr.h> +#include "cipher_cast.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" + +#define CAST5_FLAGS PROV_CIPHER_FLAG_VARIABLE_LENGTH + +static OSSL_FUNC_cipher_freectx_fn cast5_freectx; +static OSSL_FUNC_cipher_dupctx_fn cast5_dupctx; + +static void cast5_freectx(void *vctx) +{ + PROV_CAST_CTX *ctx = (PROV_CAST_CTX *)vctx; + + ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx); + OPENSSL_clear_free(ctx, sizeof(*ctx)); +} + +static void *cast5_dupctx(void *ctx) +{ + PROV_CAST_CTX *in = (PROV_CAST_CTX *)ctx; + PROV_CAST_CTX *ret; + + if (!ossl_prov_is_running()) + return NULL; + + ret = OPENSSL_malloc(sizeof(*ret)); + if (ret == NULL) + return NULL; + *ret = *in; + + return ret; +} + +/* ossl_cast5128ecb_functions */ +IMPLEMENT_var_keylen_cipher(cast5, CAST, ecb, ECB, CAST5_FLAGS, 128, 64, 0, block) +/* ossl_cast5128cbc_functions */ +IMPLEMENT_var_keylen_cipher(cast5, CAST, cbc, CBC, CAST5_FLAGS, 128, 64, 64, block) +/* ossl_cast5128ofb64_functions */ +IMPLEMENT_var_keylen_cipher(cast5, CAST, ofb64, OFB, CAST5_FLAGS, 128, 8, 64, stream) +/* ossl_cast5128cfb64_functions */ +IMPLEMENT_var_keylen_cipher(cast5, CAST, cfb64, CFB, CAST5_FLAGS, 128, 8, 64, stream) diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_cast5_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_cast5_hw.c new file mode 100644 index 000000000000..73f0628e578b --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_cast5_hw.c @@ -0,0 +1,42 @@ +/* + * Copyright 2019-2020 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 + */ + +/* + * CAST low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include "cipher_cast.h" + +static int cipher_hw_cast5_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + PROV_CAST_CTX *bctx = (PROV_CAST_CTX *)ctx; + + CAST_set_key(&(bctx->ks.ks), keylen, key); + return 1; +} + +# define PROV_CIPHER_HW_cast_mode(mode, UCMODE) \ +IMPLEMENT_CIPHER_HW_##UCMODE(mode, cast5, PROV_CAST_CTX, CAST_KEY, \ + CAST_##mode) \ +static const PROV_CIPHER_HW cast5_##mode = { \ + cipher_hw_cast5_initkey, \ + cipher_hw_cast5_##mode##_cipher \ +}; \ +const PROV_CIPHER_HW *ossl_prov_cipher_hw_cast5_##mode(size_t keybits) \ +{ \ + return &cast5_##mode; \ +} + +PROV_CIPHER_HW_cast_mode(cbc, CBC) +PROV_CIPHER_HW_cast_mode(ecb, ECB) +PROV_CIPHER_HW_cast_mode(ofb64, OFB) +PROV_CIPHER_HW_cast_mode(cfb64, CFB) diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_chacha20.c b/crypto/openssl/providers/implementations/ciphers/cipher_chacha20.c new file mode 100644 index 000000000000..3d03e6b09266 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_chacha20.c @@ -0,0 +1,226 @@ +/* + * Copyright 2019-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 + */ + +/* Dispatch functions for chacha20 cipher */ + +#include <openssl/proverr.h> +#include "cipher_chacha20.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" + +#define CHACHA20_KEYLEN (CHACHA_KEY_SIZE) +#define CHACHA20_BLKLEN (1) +#define CHACHA20_IVLEN (CHACHA_CTR_SIZE) +#define CHACHA20_FLAGS (PROV_CIPHER_FLAG_CUSTOM_IV) + +static OSSL_FUNC_cipher_newctx_fn chacha20_newctx; +static OSSL_FUNC_cipher_freectx_fn chacha20_freectx; +static OSSL_FUNC_cipher_dupctx_fn chacha20_dupctx; +static OSSL_FUNC_cipher_get_params_fn chacha20_get_params; +static OSSL_FUNC_cipher_get_ctx_params_fn chacha20_get_ctx_params; +static OSSL_FUNC_cipher_set_ctx_params_fn chacha20_set_ctx_params; +static OSSL_FUNC_cipher_gettable_ctx_params_fn chacha20_gettable_ctx_params; +static OSSL_FUNC_cipher_settable_ctx_params_fn chacha20_settable_ctx_params; +#define chacha20_cipher ossl_cipher_generic_cipher +#define chacha20_update ossl_cipher_generic_stream_update +#define chacha20_final ossl_cipher_generic_stream_final +#define chacha20_gettable_params ossl_cipher_generic_gettable_params + +void ossl_chacha20_initctx(PROV_CHACHA20_CTX *ctx) +{ + ossl_cipher_generic_initkey(ctx, CHACHA20_KEYLEN * 8, + CHACHA20_BLKLEN * 8, + CHACHA20_IVLEN * 8, + 0, CHACHA20_FLAGS, + ossl_prov_cipher_hw_chacha20(CHACHA20_KEYLEN * 8), + NULL); +} + +static void *chacha20_newctx(void *provctx) +{ + PROV_CHACHA20_CTX *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + ctx = OPENSSL_zalloc(sizeof(*ctx)); + if (ctx != NULL) + ossl_chacha20_initctx(ctx); + return ctx; +} + +static void chacha20_freectx(void *vctx) +{ + PROV_CHACHA20_CTX *ctx = (PROV_CHACHA20_CTX *)vctx; + + if (ctx != NULL) { + ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx); + OPENSSL_clear_free(ctx, sizeof(*ctx)); + } +} + +static void *chacha20_dupctx(void *vctx) +{ + PROV_CHACHA20_CTX *ctx = (PROV_CHACHA20_CTX *)vctx; + PROV_CHACHA20_CTX *dupctx = NULL; + + if (ctx != NULL) { + dupctx = OPENSSL_memdup(ctx, sizeof(*dupctx)); + if (dupctx != NULL && dupctx->base.tlsmac != NULL && dupctx->base.alloced) { + dupctx->base.tlsmac = OPENSSL_memdup(dupctx->base.tlsmac, + dupctx->base.tlsmacsize); + if (dupctx->base.tlsmac == NULL) { + OPENSSL_free(dupctx); + dupctx = NULL; + } + } + } + return dupctx; +} + +static int chacha20_get_params(OSSL_PARAM params[]) +{ + return ossl_cipher_generic_get_params(params, 0, CHACHA20_FLAGS, + CHACHA20_KEYLEN * 8, + CHACHA20_BLKLEN * 8, + CHACHA20_IVLEN * 8); +} + +static int chacha20_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + OSSL_PARAM *p; + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, CHACHA20_IVLEN)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, CHACHA20_KEYLEN)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + + return 1; +} + +static const OSSL_PARAM chacha20_known_gettable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL), + OSSL_PARAM_END +}; +const OSSL_PARAM *chacha20_gettable_ctx_params(ossl_unused void *cctx, + ossl_unused void *provctx) +{ + return chacha20_known_gettable_ctx_params; +} + +static int chacha20_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p; + size_t len; + + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_KEYLEN); + if (p != NULL) { + if (!OSSL_PARAM_get_size_t(p, &len)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + if (len != CHACHA20_KEYLEN) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + } + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_IVLEN); + if (p != NULL) { + if (!OSSL_PARAM_get_size_t(p, &len)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + if (len != CHACHA20_IVLEN) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); + return 0; + } + } + return 1; +} + +static const OSSL_PARAM chacha20_known_settable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL), + OSSL_PARAM_END +}; +const OSSL_PARAM *chacha20_settable_ctx_params(ossl_unused void *cctx, + ossl_unused void *provctx) +{ + return chacha20_known_settable_ctx_params; +} + +int ossl_chacha20_einit(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + int ret; + + /* The generic function checks for ossl_prov_is_running() */ + ret = ossl_cipher_generic_einit(vctx, key, keylen, iv, ivlen, NULL); + if (ret && iv != NULL) { + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + PROV_CIPHER_HW_CHACHA20 *hw = (PROV_CIPHER_HW_CHACHA20 *)ctx->hw; + + hw->initiv(ctx); + } + if (ret && !chacha20_set_ctx_params(vctx, params)) + ret = 0; + return ret; +} + +int ossl_chacha20_dinit(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + int ret; + + /* The generic function checks for ossl_prov_is_running() */ + ret = ossl_cipher_generic_dinit(vctx, key, keylen, iv, ivlen, NULL); + if (ret && iv != NULL) { + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + PROV_CIPHER_HW_CHACHA20 *hw = (PROV_CIPHER_HW_CHACHA20 *)ctx->hw; + + hw->initiv(ctx); + } + if (ret && !chacha20_set_ctx_params(vctx, params)) + ret = 0; + return ret; +} + +/* ossl_chacha20_functions */ +const OSSL_DISPATCH ossl_chacha20_functions[] = { + { OSSL_FUNC_CIPHER_NEWCTX, (void (*)(void))chacha20_newctx }, + { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))chacha20_freectx }, + { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void))chacha20_dupctx }, + { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))ossl_chacha20_einit }, + { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))ossl_chacha20_dinit }, + { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))chacha20_update }, + { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))chacha20_final }, + { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))chacha20_cipher}, + { OSSL_FUNC_CIPHER_GET_PARAMS, (void (*)(void))chacha20_get_params }, + { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, (void (*)(void))chacha20_gettable_params }, + { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, (void (*)(void))chacha20_get_ctx_params }, + { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, + (void (*)(void))chacha20_gettable_ctx_params }, + { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, (void (*)(void))chacha20_set_ctx_params }, + { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, + (void (*)(void))chacha20_settable_ctx_params }, + OSSL_DISPATCH_END +}; + diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_chacha20.h b/crypto/openssl/providers/implementations/ciphers/cipher_chacha20.h new file mode 100644 index 000000000000..9db8ed9cb424 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_chacha20.h @@ -0,0 +1,34 @@ +/* + * Copyright 2019-2021 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 + */ + +#include "include/crypto/chacha.h" +#include "prov/ciphercommon.h" + +typedef struct { + PROV_CIPHER_CTX base; /* must be first */ + union { + OSSL_UNION_ALIGN; + unsigned int d[CHACHA_KEY_SIZE / 4]; + } key; + unsigned int counter[CHACHA_CTR_SIZE / 4]; + unsigned char buf[CHACHA_BLK_SIZE]; + unsigned int partial_len; +} PROV_CHACHA20_CTX; + +typedef struct prov_cipher_hw_chacha20_st { + PROV_CIPHER_HW base; /* must be first */ + int (*initiv)(PROV_CIPHER_CTX *ctx); + +} PROV_CIPHER_HW_CHACHA20; + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_chacha20(size_t keybits); + +OSSL_FUNC_cipher_encrypt_init_fn ossl_chacha20_einit; +OSSL_FUNC_cipher_decrypt_init_fn ossl_chacha20_dinit; +void ossl_chacha20_initctx(PROV_CHACHA20_CTX *ctx); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_chacha20_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_chacha20_hw.c new file mode 100644 index 000000000000..3b03bc81ed5a --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_chacha20_hw.c @@ -0,0 +1,122 @@ +/* + * Copyright 2019-2021 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 + */ + +/* chacha20 cipher implementation */ + +#include "cipher_chacha20.h" + +static int chacha20_initkey(PROV_CIPHER_CTX *bctx, const uint8_t *key, + size_t keylen) +{ + PROV_CHACHA20_CTX *ctx = (PROV_CHACHA20_CTX *)bctx; + unsigned int i; + + if (key != NULL) { + for (i = 0; i < CHACHA_KEY_SIZE; i += 4) + ctx->key.d[i / 4] = CHACHA_U8TOU32(key + i); + } + ctx->partial_len = 0; + return 1; +} + +static int chacha20_initiv(PROV_CIPHER_CTX *bctx) +{ + PROV_CHACHA20_CTX *ctx = (PROV_CHACHA20_CTX *)bctx; + unsigned int i; + + if (bctx->iv_set) { + for (i = 0; i < CHACHA_CTR_SIZE; i += 4) + ctx->counter[i / 4] = CHACHA_U8TOU32(bctx->oiv + i); + } + ctx->partial_len = 0; + return 1; +} + +static int chacha20_cipher(PROV_CIPHER_CTX *bctx, unsigned char *out, + const unsigned char *in, size_t inl) +{ + PROV_CHACHA20_CTX *ctx = (PROV_CHACHA20_CTX *)bctx; + unsigned int n, rem, ctr32; + + n = ctx->partial_len; + if (n > 0) { + while (inl > 0 && n < CHACHA_BLK_SIZE) { + *out++ = *in++ ^ ctx->buf[n++]; + inl--; + } + ctx->partial_len = n; + + if (inl == 0) + return 1; + + if (n == CHACHA_BLK_SIZE) { + ctx->partial_len = 0; + ctx->counter[0]++; + if (ctx->counter[0] == 0) + ctx->counter[1]++; + } + } + + rem = (unsigned int)(inl % CHACHA_BLK_SIZE); + inl -= rem; + ctr32 = ctx->counter[0]; + while (inl >= CHACHA_BLK_SIZE) { + size_t blocks = inl / CHACHA_BLK_SIZE; + + /* + * 1<<28 is just a not-so-small yet not-so-large number... + * Below condition is practically never met, but it has to + * be checked for code correctness. + */ + if (sizeof(size_t) > sizeof(unsigned int) && blocks > (1U << 28)) + blocks = (1U << 28); + + /* + * As ChaCha20_ctr32 operates on 32-bit counter, caller + * has to handle overflow. 'if' below detects the + * overflow, which is then handled by limiting the + * amount of blocks to the exact overflow point... + */ + ctr32 += (unsigned int)blocks; + if (ctr32 < blocks) { + blocks -= ctr32; + ctr32 = 0; + } + blocks *= CHACHA_BLK_SIZE; + ChaCha20_ctr32(out, in, blocks, ctx->key.d, ctx->counter); + inl -= blocks; + in += blocks; + out += blocks; + + ctx->counter[0] = ctr32; + if (ctr32 == 0) ctx->counter[1]++; + } + + if (rem > 0) { + memset(ctx->buf, 0, sizeof(ctx->buf)); + ChaCha20_ctr32(ctx->buf, ctx->buf, CHACHA_BLK_SIZE, + ctx->key.d, ctx->counter); + for (n = 0; n < rem; n++) + out[n] = in[n] ^ ctx->buf[n]; + ctx->partial_len = rem; + } + + return 1; +} + +static const PROV_CIPHER_HW_CHACHA20 chacha20_hw = { + { chacha20_initkey, chacha20_cipher }, + chacha20_initiv +}; + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_chacha20(size_t keybits) +{ + return (PROV_CIPHER_HW *)&chacha20_hw; +} + diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_chacha20_poly1305.c b/crypto/openssl/providers/implementations/ciphers/cipher_chacha20_poly1305.c new file mode 100644 index 000000000000..3724da23bcd3 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_chacha20_poly1305.c @@ -0,0 +1,367 @@ +/* + * Copyright 2019-2025 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 + */ + +/* Dispatch functions for chacha20_poly1305 cipher */ + +#include <openssl/proverr.h> +#include "cipher_chacha20_poly1305.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" + +#define CHACHA20_POLY1305_KEYLEN CHACHA_KEY_SIZE +#define CHACHA20_POLY1305_BLKLEN 1 +#define CHACHA20_POLY1305_MAX_IVLEN 12 +#define CHACHA20_POLY1305_MODE 0 +#define CHACHA20_POLY1305_FLAGS (PROV_CIPHER_FLAG_AEAD \ + | PROV_CIPHER_FLAG_CUSTOM_IV) + +static OSSL_FUNC_cipher_newctx_fn chacha20_poly1305_newctx; +static OSSL_FUNC_cipher_freectx_fn chacha20_poly1305_freectx; +static OSSL_FUNC_cipher_dupctx_fn chacha20_poly1305_dupctx; +static OSSL_FUNC_cipher_encrypt_init_fn chacha20_poly1305_einit; +static OSSL_FUNC_cipher_decrypt_init_fn chacha20_poly1305_dinit; +static OSSL_FUNC_cipher_get_params_fn chacha20_poly1305_get_params; +static OSSL_FUNC_cipher_get_ctx_params_fn chacha20_poly1305_get_ctx_params; +static OSSL_FUNC_cipher_set_ctx_params_fn chacha20_poly1305_set_ctx_params; +static OSSL_FUNC_cipher_cipher_fn chacha20_poly1305_cipher; +static OSSL_FUNC_cipher_final_fn chacha20_poly1305_final; +static OSSL_FUNC_cipher_gettable_ctx_params_fn chacha20_poly1305_gettable_ctx_params; +static OSSL_FUNC_cipher_settable_ctx_params_fn chacha20_poly1305_settable_ctx_params; +#define chacha20_poly1305_gettable_params ossl_cipher_generic_gettable_params +#define chacha20_poly1305_update chacha20_poly1305_cipher + +static void *chacha20_poly1305_newctx(void *provctx) +{ + PROV_CHACHA20_POLY1305_CTX *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + ctx = OPENSSL_zalloc(sizeof(*ctx)); + if (ctx != NULL) { + ossl_cipher_generic_initkey(&ctx->base, CHACHA20_POLY1305_KEYLEN * 8, + CHACHA20_POLY1305_BLKLEN * 8, + CHACHA20_POLY1305_IVLEN * 8, + CHACHA20_POLY1305_MODE, + CHACHA20_POLY1305_FLAGS, + ossl_prov_cipher_hw_chacha20_poly1305( + CHACHA20_POLY1305_KEYLEN * 8), + NULL); + ctx->tls_payload_length = NO_TLS_PAYLOAD_LENGTH; + ossl_chacha20_initctx(&ctx->chacha); + } + return ctx; +} + +static void *chacha20_poly1305_dupctx(void *provctx) +{ + PROV_CHACHA20_POLY1305_CTX *ctx = provctx; + PROV_CHACHA20_POLY1305_CTX *dctx = NULL; + + if (ctx == NULL) + return NULL; + dctx = OPENSSL_memdup(ctx, sizeof(*ctx)); + if (dctx != NULL && dctx->base.tlsmac != NULL && dctx->base.alloced) { + dctx->base.tlsmac = OPENSSL_memdup(dctx->base.tlsmac, + dctx->base.tlsmacsize); + if (dctx->base.tlsmac == NULL) { + OPENSSL_free(dctx); + dctx = NULL; + } + } + return dctx; +} + +static void chacha20_poly1305_freectx(void *vctx) +{ + PROV_CHACHA20_POLY1305_CTX *ctx = (PROV_CHACHA20_POLY1305_CTX *)vctx; + + if (ctx != NULL) { + ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx); + OPENSSL_clear_free(ctx, sizeof(*ctx)); + } +} + +static int chacha20_poly1305_get_params(OSSL_PARAM params[]) +{ + return ossl_cipher_generic_get_params(params, 0, CHACHA20_POLY1305_FLAGS, + CHACHA20_POLY1305_KEYLEN * 8, + CHACHA20_POLY1305_BLKLEN * 8, + CHACHA20_POLY1305_IVLEN * 8); +} + +static int chacha20_poly1305_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + PROV_CHACHA20_POLY1305_CTX *ctx = (PROV_CHACHA20_POLY1305_CTX *)vctx; + OSSL_PARAM *p; + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN); + if (p != NULL) { + if (!OSSL_PARAM_set_size_t(p, CHACHA20_POLY1305_IVLEN)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, CHACHA20_POLY1305_KEYLEN)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TAGLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->tag_len)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TLS1_AAD_PAD); + if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->tls_aad_pad_sz)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TAG); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_OCTET_STRING) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + if (!ctx->base.enc) { + ERR_raise(ERR_LIB_PROV, PROV_R_TAG_NOT_SET); + return 0; + } + if (p->data_size == 0 || p->data_size > POLY1305_BLOCK_SIZE) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_TAG_LENGTH); + return 0; + } + memcpy(p->data, ctx->tag, p->data_size); + } + + return 1; +} + +static const OSSL_PARAM chacha20_poly1305_known_gettable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_TAGLEN, NULL), + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, NULL, 0), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_TLS1_AAD_PAD, NULL), + OSSL_PARAM_END +}; +static const OSSL_PARAM *chacha20_poly1305_gettable_ctx_params + (ossl_unused void *cctx, ossl_unused void *provctx) +{ + return chacha20_poly1305_known_gettable_ctx_params; +} + +static const OSSL_PARAM chacha20_poly1305_known_settable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL), + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, NULL, 0), + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TLS1_AAD, NULL, 0), + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TLS1_IV_FIXED, NULL, 0), + OSSL_PARAM_END +}; +static const OSSL_PARAM *chacha20_poly1305_settable_ctx_params( + ossl_unused void *cctx, ossl_unused void *provctx + ) +{ + return chacha20_poly1305_known_settable_ctx_params; +} + +static int chacha20_poly1305_set_ctx_params(void *vctx, + const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p; + size_t len; + PROV_CHACHA20_POLY1305_CTX *ctx = (PROV_CHACHA20_POLY1305_CTX *)vctx; + PROV_CIPHER_HW_CHACHA20_POLY1305 *hw = + (PROV_CIPHER_HW_CHACHA20_POLY1305 *)ctx->base.hw; + + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_KEYLEN); + if (p != NULL) { + if (!OSSL_PARAM_get_size_t(p, &len)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + if (len != CHACHA20_POLY1305_KEYLEN) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + } + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_IVLEN); + if (p != NULL) { + if (!OSSL_PARAM_get_size_t(p, &len)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + if (len != CHACHA20_POLY1305_MAX_IVLEN) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); + return 0; + } + } + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TAG); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_OCTET_STRING) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + if (p->data_size == 0 || p->data_size > POLY1305_BLOCK_SIZE) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_TAG_LENGTH); + return 0; + } + if (p->data != NULL) { + if (ctx->base.enc) { + ERR_raise(ERR_LIB_PROV, PROV_R_TAG_NOT_NEEDED); + return 0; + } + memcpy(ctx->tag, p->data, p->data_size); + } + ctx->tag_len = p->data_size; + } + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TLS1_AAD); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_OCTET_STRING) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + len = hw->tls_init(&ctx->base, p->data, p->data_size); + if (len == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DATA); + return 0; + } + ctx->tls_aad_pad_sz = len; + } + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TLS1_IV_FIXED); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_OCTET_STRING) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + if (hw->tls_iv_set_fixed(&ctx->base, p->data, p->data_size) == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); + return 0; + } + } + return 1; +} + +static int chacha20_poly1305_einit(void *vctx, const unsigned char *key, + size_t keylen, const unsigned char *iv, + size_t ivlen, const OSSL_PARAM params[]) +{ + int ret; + + /* The generic function checks for ossl_prov_is_running() */ + ret = ossl_cipher_generic_einit(vctx, key, keylen, iv, ivlen, NULL); + if (ret && iv != NULL) { + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + PROV_CIPHER_HW_CHACHA20_POLY1305 *hw = + (PROV_CIPHER_HW_CHACHA20_POLY1305 *)ctx->hw; + + hw->initiv(ctx); + } + if (ret && !chacha20_poly1305_set_ctx_params(vctx, params)) + ret = 0; + return ret; +} + +static int chacha20_poly1305_dinit(void *vctx, const unsigned char *key, + size_t keylen, const unsigned char *iv, + size_t ivlen, const OSSL_PARAM params[]) +{ + int ret; + + /* The generic function checks for ossl_prov_is_running() */ + ret = ossl_cipher_generic_dinit(vctx, key, keylen, iv, ivlen, NULL); + if (ret && iv != NULL) { + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + PROV_CIPHER_HW_CHACHA20_POLY1305 *hw = + (PROV_CIPHER_HW_CHACHA20_POLY1305 *)ctx->hw; + + hw->initiv(ctx); + } + if (ret && !chacha20_poly1305_set_ctx_params(vctx, params)) + ret = 0; + return ret; +} + +static int chacha20_poly1305_cipher(void *vctx, unsigned char *out, + size_t *outl, size_t outsize, + const unsigned char *in, size_t inl) +{ + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + PROV_CIPHER_HW_CHACHA20_POLY1305 *hw = + (PROV_CIPHER_HW_CHACHA20_POLY1305 *)ctx->hw; + + if (!ossl_prov_is_running()) + return 0; + + if (inl == 0) { + *outl = 0; + return 1; + } + + if (outsize < inl) { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + return 0; + } + + if (!hw->aead_cipher(ctx, out, outl, in, inl)) + return 0; + + return 1; +} + +static int chacha20_poly1305_final(void *vctx, unsigned char *out, size_t *outl, + size_t outsize) +{ + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + PROV_CIPHER_HW_CHACHA20_POLY1305 *hw = + (PROV_CIPHER_HW_CHACHA20_POLY1305 *)ctx->hw; + + if (!ossl_prov_is_running()) + return 0; + + if (hw->aead_cipher(ctx, out, outl, NULL, 0) <= 0) + return 0; + + *outl = 0; + return 1; +} + +/* ossl_chacha20_ossl_poly1305_functions */ +const OSSL_DISPATCH ossl_chacha20_ossl_poly1305_functions[] = { + { OSSL_FUNC_CIPHER_NEWCTX, (void (*)(void))chacha20_poly1305_newctx }, + { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))chacha20_poly1305_freectx }, + { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void))chacha20_poly1305_dupctx }, + { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))chacha20_poly1305_einit }, + { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))chacha20_poly1305_dinit }, + { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))chacha20_poly1305_update }, + { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))chacha20_poly1305_final }, + { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))chacha20_poly1305_cipher }, + { OSSL_FUNC_CIPHER_GET_PARAMS, + (void (*)(void))chacha20_poly1305_get_params }, + { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, + (void (*)(void))chacha20_poly1305_gettable_params }, + { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, + (void (*)(void))chacha20_poly1305_get_ctx_params }, + { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, + (void (*)(void))chacha20_poly1305_gettable_ctx_params }, + { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, + (void (*)(void))chacha20_poly1305_set_ctx_params }, + { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, + (void (*)(void))chacha20_poly1305_settable_ctx_params }, + OSSL_DISPATCH_END +}; + diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_chacha20_poly1305.h b/crypto/openssl/providers/implementations/ciphers/cipher_chacha20_poly1305.h new file mode 100644 index 000000000000..f2ea26a77f3a --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_chacha20_poly1305.h @@ -0,0 +1,43 @@ +/* + * Copyright 2019-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 + */ + +/* Dispatch functions for chacha20_poly1305 cipher */ + +#include "include/crypto/poly1305.h" +#include "cipher_chacha20.h" + +#define NO_TLS_PAYLOAD_LENGTH ((size_t)-1) +#define CHACHA20_POLY1305_IVLEN 12 + +typedef struct { + PROV_CIPHER_CTX base; /* must be first */ + PROV_CHACHA20_CTX chacha; + POLY1305 poly1305; + unsigned int nonce[12 / 4]; + unsigned char tag[POLY1305_BLOCK_SIZE]; + unsigned char tls_aad[POLY1305_BLOCK_SIZE]; + struct { uint64_t aad, text; } len; + unsigned int aad : 1; + unsigned int mac_inited : 1; + size_t tag_len; + size_t tls_payload_length; + size_t tls_aad_pad_sz; +} PROV_CHACHA20_POLY1305_CTX; + +typedef struct prov_cipher_hw_chacha_aead_st { + PROV_CIPHER_HW base; /* must be first */ + int (*aead_cipher)(PROV_CIPHER_CTX *dat, unsigned char *out, size_t *outl, + const unsigned char *in, size_t len); + int (*initiv)(PROV_CIPHER_CTX *ctx); + int (*tls_init)(PROV_CIPHER_CTX *ctx, unsigned char *aad, size_t alen); + int (*tls_iv_set_fixed)(PROV_CIPHER_CTX *ctx, unsigned char *fixed, + size_t flen); +} PROV_CIPHER_HW_CHACHA20_POLY1305; + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_chacha20_poly1305(size_t keybits); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_chacha20_poly1305_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_chacha20_poly1305_hw.c new file mode 100644 index 000000000000..29533efe0eb5 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_chacha20_poly1305_hw.c @@ -0,0 +1,412 @@ +/* + * Copyright 2019-2024 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 + */ + +/* chacha20_poly1305 cipher implementation */ + +#include "internal/endian.h" +#include "cipher_chacha20_poly1305.h" + +static int chacha_poly1305_tls_init(PROV_CIPHER_CTX *bctx, + unsigned char *aad, size_t alen) +{ + unsigned int len; + PROV_CHACHA20_POLY1305_CTX *ctx = (PROV_CHACHA20_POLY1305_CTX *)bctx; + + if (alen != EVP_AEAD_TLS1_AAD_LEN) + return 0; + + memcpy(ctx->tls_aad, aad, EVP_AEAD_TLS1_AAD_LEN); + len = aad[EVP_AEAD_TLS1_AAD_LEN - 2] << 8 | aad[EVP_AEAD_TLS1_AAD_LEN - 1]; + aad = ctx->tls_aad; + if (!bctx->enc) { + if (len < POLY1305_BLOCK_SIZE) + return 0; + len -= POLY1305_BLOCK_SIZE; /* discount attached tag */ + aad[EVP_AEAD_TLS1_AAD_LEN - 2] = (unsigned char)(len >> 8); + aad[EVP_AEAD_TLS1_AAD_LEN - 1] = (unsigned char)len; + } + ctx->tls_payload_length = len; + + /* merge record sequence number as per RFC7905 */ + ctx->chacha.counter[1] = ctx->nonce[0]; + ctx->chacha.counter[2] = ctx->nonce[1] ^ CHACHA_U8TOU32(aad); + ctx->chacha.counter[3] = ctx->nonce[2] ^ CHACHA_U8TOU32(aad+4); + ctx->mac_inited = 0; + + return POLY1305_BLOCK_SIZE; /* tag length */ +} + +static int chacha_poly1305_tls_iv_set_fixed(PROV_CIPHER_CTX *bctx, + unsigned char *fixed, size_t flen) +{ + PROV_CHACHA20_POLY1305_CTX *ctx = (PROV_CHACHA20_POLY1305_CTX *)bctx; + + if (flen != CHACHA20_POLY1305_IVLEN) + return 0; + ctx->nonce[0] = ctx->chacha.counter[1] = CHACHA_U8TOU32(fixed); + ctx->nonce[1] = ctx->chacha.counter[2] = CHACHA_U8TOU32(fixed + 4); + ctx->nonce[2] = ctx->chacha.counter[3] = CHACHA_U8TOU32(fixed + 8); + return 1; +} + +static int chacha20_poly1305_initkey(PROV_CIPHER_CTX *bctx, + const unsigned char *key, size_t keylen) +{ + PROV_CHACHA20_POLY1305_CTX *ctx = (PROV_CHACHA20_POLY1305_CTX *)bctx; + + ctx->len.aad = 0; + ctx->len.text = 0; + ctx->aad = 0; + ctx->mac_inited = 0; + ctx->tls_payload_length = NO_TLS_PAYLOAD_LENGTH; + + if (bctx->enc) + return ossl_chacha20_einit(&ctx->chacha, key, keylen, NULL, 0, NULL); + else + return ossl_chacha20_dinit(&ctx->chacha, key, keylen, NULL, 0, NULL); +} + +static int chacha20_poly1305_initiv(PROV_CIPHER_CTX *bctx) +{ + PROV_CHACHA20_POLY1305_CTX *ctx = (PROV_CHACHA20_POLY1305_CTX *)bctx; + unsigned char tempiv[CHACHA_CTR_SIZE] = { 0 }; + int ret = 1; + size_t noncelen = CHACHA20_POLY1305_IVLEN; + + ctx->len.aad = 0; + ctx->len.text = 0; + ctx->aad = 0; + ctx->mac_inited = 0; + ctx->tls_payload_length = NO_TLS_PAYLOAD_LENGTH; + + /* pad on the left */ + memcpy(tempiv + CHACHA_CTR_SIZE - noncelen, bctx->oiv, + noncelen); + + if (bctx->enc) + ret = ossl_chacha20_einit(&ctx->chacha, NULL, 0, + tempiv, sizeof(tempiv), NULL); + else + ret = ossl_chacha20_dinit(&ctx->chacha, NULL, 0, + tempiv, sizeof(tempiv), NULL); + ctx->nonce[0] = ctx->chacha.counter[1]; + ctx->nonce[1] = ctx->chacha.counter[2]; + ctx->nonce[2] = ctx->chacha.counter[3]; + bctx->iv_set = 1; + return ret; +} + +#if !defined(OPENSSL_SMALL_FOOTPRINT) + +# if defined(POLY1305_ASM) && (defined(__x86_64) || defined(__x86_64__) \ + || defined(_M_AMD64) || defined(_M_X64)) +# define XOR128_HELPERS +void *xor128_encrypt_n_pad(void *out, const void *inp, void *otp, size_t len); +void *xor128_decrypt_n_pad(void *out, const void *inp, void *otp, size_t len); +static const unsigned char zero[4 * CHACHA_BLK_SIZE] = { 0 }; +# else +static const unsigned char zero[2 * CHACHA_BLK_SIZE] = { 0 }; +# endif + +static int chacha20_poly1305_tls_cipher(PROV_CIPHER_CTX *bctx, + unsigned char *out, + size_t *out_padlen, + const unsigned char *in, size_t len) +{ + PROV_CHACHA20_POLY1305_CTX *ctx = (PROV_CHACHA20_POLY1305_CTX *)bctx; + POLY1305 *poly = &ctx->poly1305; + size_t tail, tohash_len, buf_len, plen = ctx->tls_payload_length; + unsigned char *buf, *tohash, *ctr, storage[sizeof(zero) + 32]; + + DECLARE_IS_ENDIAN; + + buf = storage + ((0 - (size_t)storage) & 15); /* align */ + ctr = buf + CHACHA_BLK_SIZE; + tohash = buf + CHACHA_BLK_SIZE - POLY1305_BLOCK_SIZE; + +# ifdef XOR128_HELPERS + if (plen <= 3 * CHACHA_BLK_SIZE) { + ctx->chacha.counter[0] = 0; + buf_len = (plen + 2 * CHACHA_BLK_SIZE - 1) & (0 - CHACHA_BLK_SIZE); + ChaCha20_ctr32(buf, zero, buf_len, ctx->chacha.key.d, ctx->chacha.counter); + Poly1305_Init(poly, buf); + ctx->chacha.partial_len = 0; + memcpy(tohash, ctx->tls_aad, POLY1305_BLOCK_SIZE); + tohash_len = POLY1305_BLOCK_SIZE; + ctx->len.aad = EVP_AEAD_TLS1_AAD_LEN; + ctx->len.text = plen; + + if (plen) { + if (bctx->enc) + ctr = xor128_encrypt_n_pad(out, in, ctr, plen); + else + ctr = xor128_decrypt_n_pad(out, in, ctr, plen); + + in += plen; + out += plen; + tohash_len = (size_t)(ctr - tohash); + } + } +# else + if (plen <= CHACHA_BLK_SIZE) { + size_t i; + + ctx->chacha.counter[0] = 0; + ChaCha20_ctr32(buf, zero, (buf_len = 2 * CHACHA_BLK_SIZE), + ctx->chacha.key.d, ctx->chacha.counter); + Poly1305_Init(poly, buf); + ctx->chacha.partial_len = 0; + memcpy(tohash, ctx->tls_aad, POLY1305_BLOCK_SIZE); + tohash_len = POLY1305_BLOCK_SIZE; + ctx->len.aad = EVP_AEAD_TLS1_AAD_LEN; + ctx->len.text = plen; + + if (bctx->enc) { + for (i = 0; i < plen; i++) + out[i] = ctr[i] ^= in[i]; + } else { + for (i = 0; i < plen; i++) { + unsigned char c = in[i]; + + out[i] = ctr[i] ^ c; + ctr[i] = c; + } + } + + in += i; + out += i; + + tail = (0 - i) & (POLY1305_BLOCK_SIZE - 1); + memset(ctr + i, 0, tail); + ctr += i + tail; + tohash_len += i + tail; + } +# endif + else { + ctx->chacha.counter[0] = 0; + ChaCha20_ctr32(buf, zero, (buf_len = CHACHA_BLK_SIZE), + ctx->chacha.key.d, ctx->chacha.counter); + Poly1305_Init(poly, buf); + ctx->chacha.counter[0] = 1; + ctx->chacha.partial_len = 0; + Poly1305_Update(poly, ctx->tls_aad, POLY1305_BLOCK_SIZE); + tohash = ctr; + tohash_len = 0; + ctx->len.aad = EVP_AEAD_TLS1_AAD_LEN; + ctx->len.text = plen; + + if (bctx->enc) { + ChaCha20_ctr32(out, in, plen, ctx->chacha.key.d, ctx->chacha.counter); + Poly1305_Update(poly, out, plen); + } else { + Poly1305_Update(poly, in, plen); + ChaCha20_ctr32(out, in, plen, ctx->chacha.key.d, ctx->chacha.counter); + } + + in += plen; + out += plen; + tail = (0 - plen) & (POLY1305_BLOCK_SIZE - 1); + Poly1305_Update(poly, zero, tail); + } + + if (IS_LITTLE_ENDIAN) { + memcpy(ctr, (unsigned char *)&ctx->len, POLY1305_BLOCK_SIZE); + } else { + ctr[0] = (unsigned char)(ctx->len.aad); + ctr[1] = (unsigned char)(ctx->len.aad>>8); + ctr[2] = (unsigned char)(ctx->len.aad>>16); + ctr[3] = (unsigned char)(ctx->len.aad>>24); + ctr[4] = (unsigned char)(ctx->len.aad>>32); + ctr[5] = (unsigned char)(ctx->len.aad>>40); + ctr[6] = (unsigned char)(ctx->len.aad>>48); + ctr[7] = (unsigned char)(ctx->len.aad>>56); + + ctr[8] = (unsigned char)(ctx->len.text); + ctr[9] = (unsigned char)(ctx->len.text>>8); + ctr[10] = (unsigned char)(ctx->len.text>>16); + ctr[11] = (unsigned char)(ctx->len.text>>24); + ctr[12] = (unsigned char)(ctx->len.text>>32); + ctr[13] = (unsigned char)(ctx->len.text>>40); + ctr[14] = (unsigned char)(ctx->len.text>>48); + ctr[15] = (unsigned char)(ctx->len.text>>56); + } + tohash_len += POLY1305_BLOCK_SIZE; + + Poly1305_Update(poly, tohash, tohash_len); + OPENSSL_cleanse(buf, buf_len); + Poly1305_Final(poly, bctx->enc ? ctx->tag : tohash); + + ctx->tls_payload_length = NO_TLS_PAYLOAD_LENGTH; + + if (bctx->enc) { + memcpy(out, ctx->tag, POLY1305_BLOCK_SIZE); + } else { + if (CRYPTO_memcmp(tohash, in, POLY1305_BLOCK_SIZE)) { + if (len > POLY1305_BLOCK_SIZE) + memset(out - (len - POLY1305_BLOCK_SIZE), 0, + len - POLY1305_BLOCK_SIZE); + return 0; + } + /* Strip the tag */ + len -= POLY1305_BLOCK_SIZE; + } + + *out_padlen = len; + return 1; +} +#else +static const unsigned char zero[CHACHA_BLK_SIZE] = { 0 }; +#endif /* OPENSSL_SMALL_FOOTPRINT */ + +static int chacha20_poly1305_aead_cipher(PROV_CIPHER_CTX *bctx, + unsigned char *out, size_t *outl, + const unsigned char *in, size_t inl) +{ + PROV_CHACHA20_POLY1305_CTX *ctx = (PROV_CHACHA20_POLY1305_CTX *)bctx; + POLY1305 *poly = &ctx->poly1305; + size_t rem, plen = ctx->tls_payload_length; + size_t olen = 0; + int rv = 0; + + DECLARE_IS_ENDIAN; + + if (!ctx->mac_inited) { + if (plen != NO_TLS_PAYLOAD_LENGTH && out != NULL) { + if (inl != plen + POLY1305_BLOCK_SIZE) + return 0; +#if !defined(OPENSSL_SMALL_FOOTPRINT) + return chacha20_poly1305_tls_cipher(bctx, out, outl, in, inl); +#endif + } + + ctx->chacha.counter[0] = 0; + ChaCha20_ctr32(ctx->chacha.buf, zero, CHACHA_BLK_SIZE, + ctx->chacha.key.d, ctx->chacha.counter); + Poly1305_Init(poly, ctx->chacha.buf); + ctx->chacha.counter[0] = 1; + ctx->chacha.partial_len = 0; + ctx->len.aad = ctx->len.text = 0; + ctx->mac_inited = 1; + if (plen != NO_TLS_PAYLOAD_LENGTH) { + Poly1305_Update(poly, ctx->tls_aad, EVP_AEAD_TLS1_AAD_LEN); + ctx->len.aad = EVP_AEAD_TLS1_AAD_LEN; + ctx->aad = 1; + } + } + + if (in != NULL) { /* aad or text */ + if (out == NULL) { /* aad */ + Poly1305_Update(poly, in, inl); + ctx->len.aad += inl; + ctx->aad = 1; + goto finish; + } else { /* plain- or ciphertext */ + if (ctx->aad) { /* wrap up aad */ + if ((rem = (size_t)ctx->len.aad % POLY1305_BLOCK_SIZE)) + Poly1305_Update(poly, zero, POLY1305_BLOCK_SIZE - rem); + ctx->aad = 0; + } + + ctx->tls_payload_length = NO_TLS_PAYLOAD_LENGTH; + if (plen == NO_TLS_PAYLOAD_LENGTH) + plen = inl; + else if (inl != plen + POLY1305_BLOCK_SIZE) + goto err; + + if (bctx->enc) { /* plaintext */ + ctx->chacha.base.hw->cipher(&ctx->chacha.base, out, in, plen); + Poly1305_Update(poly, out, plen); + in += plen; + out += plen; + ctx->len.text += plen; + } else { /* ciphertext */ + Poly1305_Update(poly, in, plen); + ctx->chacha.base.hw->cipher(&ctx->chacha.base, out, in, plen); + in += plen; + out += plen; + ctx->len.text += plen; + } + } + } + /* explicit final, or tls mode */ + if (in == NULL || inl != plen) { + + unsigned char temp[POLY1305_BLOCK_SIZE]; + + if (ctx->aad) { /* wrap up aad */ + if ((rem = (size_t)ctx->len.aad % POLY1305_BLOCK_SIZE)) + Poly1305_Update(poly, zero, POLY1305_BLOCK_SIZE - rem); + ctx->aad = 0; + } + + if ((rem = (size_t)ctx->len.text % POLY1305_BLOCK_SIZE)) + Poly1305_Update(poly, zero, POLY1305_BLOCK_SIZE - rem); + + if (IS_LITTLE_ENDIAN) { + Poly1305_Update(poly, (unsigned char *)&ctx->len, + POLY1305_BLOCK_SIZE); + } else { + temp[0] = (unsigned char)(ctx->len.aad); + temp[1] = (unsigned char)(ctx->len.aad>>8); + temp[2] = (unsigned char)(ctx->len.aad>>16); + temp[3] = (unsigned char)(ctx->len.aad>>24); + temp[4] = (unsigned char)(ctx->len.aad>>32); + temp[5] = (unsigned char)(ctx->len.aad>>40); + temp[6] = (unsigned char)(ctx->len.aad>>48); + temp[7] = (unsigned char)(ctx->len.aad>>56); + temp[8] = (unsigned char)(ctx->len.text); + temp[9] = (unsigned char)(ctx->len.text>>8); + temp[10] = (unsigned char)(ctx->len.text>>16); + temp[11] = (unsigned char)(ctx->len.text>>24); + temp[12] = (unsigned char)(ctx->len.text>>32); + temp[13] = (unsigned char)(ctx->len.text>>40); + temp[14] = (unsigned char)(ctx->len.text>>48); + temp[15] = (unsigned char)(ctx->len.text>>56); + Poly1305_Update(poly, temp, POLY1305_BLOCK_SIZE); + } + Poly1305_Final(poly, bctx->enc ? ctx->tag : temp); + ctx->mac_inited = 0; + + if (in != NULL && inl != plen) { + if (bctx->enc) { + memcpy(out, ctx->tag, POLY1305_BLOCK_SIZE); + } else { + if (CRYPTO_memcmp(temp, in, POLY1305_BLOCK_SIZE)) { + memset(out - plen, 0, plen); + goto err; + } + /* Strip the tag */ + inl -= POLY1305_BLOCK_SIZE; + } + } + else if (!bctx->enc) { + if (CRYPTO_memcmp(temp, ctx->tag, ctx->tag_len)) + goto err; + } + } +finish: + olen = inl; + rv = 1; +err: + *outl = olen; + return rv; +} + +static const PROV_CIPHER_HW_CHACHA20_POLY1305 chacha20poly1305_hw = { + { chacha20_poly1305_initkey, NULL }, + chacha20_poly1305_aead_cipher, + chacha20_poly1305_initiv, + chacha_poly1305_tls_init, + chacha_poly1305_tls_iv_set_fixed +}; + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_chacha20_poly1305(size_t keybits) +{ + return (PROV_CIPHER_HW *)&chacha20poly1305_hw; +} diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_cts.c b/crypto/openssl/providers/implementations/ciphers/cipher_cts.c new file mode 100644 index 000000000000..9f58b06c4c96 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_cts.c @@ -0,0 +1,377 @@ +/* + * Copyright 2020-2024 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 + */ + +/* + * Helper functions for 128 bit CBC CTS ciphers (Currently AES and Camellia). + * + * The function dispatch tables are embedded into cipher_aes.c + * and cipher_camellia.c using cipher_aes_cts.inc and cipher_camellia_cts.inc + */ + +/* + * Refer to SP800-38A-Addendum + * + * Ciphertext stealing encrypts plaintext using a block cipher, without padding + * the message to a multiple of the block size, so the ciphertext is the same + * size as the plaintext. + * It does this by altering processing of the last two blocks of the message. + * The processing of all but the last two blocks is unchanged, but a portion of + * the second-last block's ciphertext is "stolen" to pad the last plaintext + * block. The padded final block is then encrypted as usual. + * The final ciphertext for the last two blocks, consists of the partial block + * (with the "stolen" portion omitted) plus the full final block, + * which are the same size as the original plaintext. + * Decryption requires decrypting the final block first, then restoring the + * stolen ciphertext to the partial block, which can then be decrypted as usual. + + * AES_CBC_CTS has 3 variants: + * (1) CS1 The NIST variant. + * If the length is a multiple of the blocksize it is the same as CBC mode. + * otherwise it produces C1||C2||(C(n-1))*||Cn. + * Where C(n-1)* is a partial block. + * (2) CS2 + * If the length is a multiple of the blocksize it is the same as CBC mode. + * otherwise it produces C1||C2||Cn||(C(n-1))*. + * Where C(n-1)* is a partial block. + * (3) CS3 The Kerberos5 variant. + * Produces C1||C2||Cn||(C(n-1))* regardless of the length. + * If the length is a multiple of the blocksize it looks similar to CBC mode + * with the last 2 blocks swapped. + * Otherwise it is the same as CS2. + */ + +#include <openssl/core_names.h> +#include "prov/ciphercommon.h" +#include "internal/nelem.h" +#include "cipher_cts.h" + +/* The value assigned to 0 is the default */ +#define CTS_CS1 0 +#define CTS_CS2 1 +#define CTS_CS3 2 + +#define CTS_BLOCK_SIZE 16 + +typedef union { + size_t align; + unsigned char c[CTS_BLOCK_SIZE]; +} aligned_16bytes; + +typedef struct cts_mode_name2id_st { + unsigned int id; + const char *name; +} CTS_MODE_NAME2ID; + +static CTS_MODE_NAME2ID cts_modes[] = { + { CTS_CS1, OSSL_CIPHER_CTS_MODE_CS1 }, + { CTS_CS2, OSSL_CIPHER_CTS_MODE_CS2 }, + { CTS_CS3, OSSL_CIPHER_CTS_MODE_CS3 }, +}; + +const char *ossl_cipher_cbc_cts_mode_id2name(unsigned int id) +{ + size_t i; + + for (i = 0; i < OSSL_NELEM(cts_modes); ++i) { + if (cts_modes[i].id == id) + return cts_modes[i].name; + } + return NULL; +} + +int ossl_cipher_cbc_cts_mode_name2id(const char *name) +{ + size_t i; + + for (i = 0; i < OSSL_NELEM(cts_modes); ++i) { + if (OPENSSL_strcasecmp(name, cts_modes[i].name) == 0) + return (int)cts_modes[i].id; + } + return -1; +} + +static size_t cts128_cs1_encrypt(PROV_CIPHER_CTX *ctx, const unsigned char *in, + unsigned char *out, size_t len) +{ + aligned_16bytes tmp_in; + size_t residue; + + residue = len % CTS_BLOCK_SIZE; + len -= residue; + if (!ctx->hw->cipher(ctx, out, in, len)) + return 0; + + if (residue == 0) + return len; + + in += len; + out += len; + + memset(tmp_in.c, 0, sizeof(tmp_in)); + memcpy(tmp_in.c, in, residue); + if (!ctx->hw->cipher(ctx, out - CTS_BLOCK_SIZE + residue, tmp_in.c, + CTS_BLOCK_SIZE)) + return 0; + return len + residue; +} + +static void do_xor(const unsigned char *in1, const unsigned char *in2, + size_t len, unsigned char *out) +{ + size_t i; + + for (i = 0; i < len; ++i) + out[i] = in1[i] ^ in2[i]; +} + +static size_t cts128_cs1_decrypt(PROV_CIPHER_CTX *ctx, const unsigned char *in, + unsigned char *out, size_t len) +{ + aligned_16bytes mid_iv, ct_mid, cn, pt_last; + size_t residue; + + residue = len % CTS_BLOCK_SIZE; + if (residue == 0) { + /* If there are no partial blocks then it is the same as CBC mode */ + if (!ctx->hw->cipher(ctx, out, in, len)) + return 0; + return len; + } + /* Process blocks at the start - but leave the last 2 blocks */ + len -= CTS_BLOCK_SIZE + residue; + if (len > 0) { + if (!ctx->hw->cipher(ctx, out, in, len)) + return 0; + in += len; + out += len; + } + /* Save the iv that will be used by the second last block */ + memcpy(mid_iv.c, ctx->iv, CTS_BLOCK_SIZE); + /* Save the C(n) block */ + memcpy(cn.c, in + residue, CTS_BLOCK_SIZE); + + /* Decrypt the last block first using an iv of zero */ + memset(ctx->iv, 0, CTS_BLOCK_SIZE); + if (!ctx->hw->cipher(ctx, pt_last.c, in + residue, CTS_BLOCK_SIZE)) + return 0; + + /* + * Rebuild the ciphertext of the second last block as a combination of + * the decrypted last block + replace the start with the ciphertext bytes + * of the partial second last block. + */ + memcpy(ct_mid.c, in, residue); + memcpy(ct_mid.c + residue, pt_last.c + residue, CTS_BLOCK_SIZE - residue); + /* + * Restore the last partial ciphertext block. + * Now that we have the cipher text of the second last block, apply + * that to the partial plaintext end block. We have already decrypted the + * block using an IV of zero. For decryption the IV is just XORed after + * doing an Cipher CBC block - so just XOR in the cipher text. + */ + do_xor(ct_mid.c, pt_last.c, residue, out + CTS_BLOCK_SIZE); + + /* Restore the iv needed by the second last block */ + memcpy(ctx->iv, mid_iv.c, CTS_BLOCK_SIZE); + + /* + * Decrypt the second last plaintext block now that we have rebuilt the + * ciphertext. + */ + if (!ctx->hw->cipher(ctx, out, ct_mid.c, CTS_BLOCK_SIZE)) + return 0; + + /* The returned iv is the C(n) block */ + memcpy(ctx->iv, cn.c, CTS_BLOCK_SIZE); + return len + CTS_BLOCK_SIZE + residue; +} + +static size_t cts128_cs3_encrypt(PROV_CIPHER_CTX *ctx, const unsigned char *in, + unsigned char *out, size_t len) +{ + aligned_16bytes tmp_in; + size_t residue; + + if (len < CTS_BLOCK_SIZE) /* CS3 requires at least one block */ + return 0; + + /* If we only have one block then just process the aligned block */ + if (len == CTS_BLOCK_SIZE) + return ctx->hw->cipher(ctx, out, in, len) ? len : 0; + + residue = len % CTS_BLOCK_SIZE; + if (residue == 0) + residue = CTS_BLOCK_SIZE; + len -= residue; + + if (!ctx->hw->cipher(ctx, out, in, len)) + return 0; + + in += len; + out += len; + + memset(tmp_in.c, 0, sizeof(tmp_in)); + memcpy(tmp_in.c, in, residue); + memcpy(out, out - CTS_BLOCK_SIZE, residue); + if (!ctx->hw->cipher(ctx, out - CTS_BLOCK_SIZE, tmp_in.c, CTS_BLOCK_SIZE)) + return 0; + return len + residue; +} + +/* + * Note: + * The cipher text (in) is of the form C(0), C(1), ., C(n), C(n-1)* where + * C(n) is a full block and C(n-1)* can be a partial block + * (but could be a full block). + * This means that the output plaintext (out) needs to swap the plaintext of + * the last two decoded ciphertext blocks. + */ +static size_t cts128_cs3_decrypt(PROV_CIPHER_CTX *ctx, const unsigned char *in, + unsigned char *out, size_t len) +{ + aligned_16bytes mid_iv, ct_mid, cn, pt_last; + size_t residue; + + if (len < CTS_BLOCK_SIZE) /* CS3 requires at least one block */ + return 0; + + /* If we only have one block then just process the aligned block */ + if (len == CTS_BLOCK_SIZE) + return ctx->hw->cipher(ctx, out, in, len) ? len : 0; + + /* Process blocks at the start - but leave the last 2 blocks */ + residue = len % CTS_BLOCK_SIZE; + if (residue == 0) + residue = CTS_BLOCK_SIZE; + len -= CTS_BLOCK_SIZE + residue; + + if (len > 0) { + if (!ctx->hw->cipher(ctx, out, in, len)) + return 0; + in += len; + out += len; + } + /* Save the iv that will be used by the second last block */ + memcpy(mid_iv.c, ctx->iv, CTS_BLOCK_SIZE); + /* Save the C(n) block : For CS3 it is C(1)||...||C(n-2)||C(n)||C(n-1)* */ + memcpy(cn.c, in, CTS_BLOCK_SIZE); + + /* Decrypt the C(n) block first using an iv of zero */ + memset(ctx->iv, 0, CTS_BLOCK_SIZE); + if (!ctx->hw->cipher(ctx, pt_last.c, in, CTS_BLOCK_SIZE)) + return 0; + + /* + * Rebuild the ciphertext of C(n-1) as a combination of + * the decrypted C(n) block + replace the start with the ciphertext bytes + * of the partial last block. + */ + memcpy(ct_mid.c, in + CTS_BLOCK_SIZE, residue); + if (residue != CTS_BLOCK_SIZE) + memcpy(ct_mid.c + residue, pt_last.c + residue, CTS_BLOCK_SIZE - residue); + /* + * Restore the last partial ciphertext block. + * Now that we have the cipher text of the second last block, apply + * that to the partial plaintext end block. We have already decrypted the + * block using an IV of zero. For decryption the IV is just XORed after + * doing an AES block - so just XOR in the ciphertext. + */ + do_xor(ct_mid.c, pt_last.c, residue, out + CTS_BLOCK_SIZE); + + /* Restore the iv needed by the second last block */ + memcpy(ctx->iv, mid_iv.c, CTS_BLOCK_SIZE); + /* + * Decrypt the second last plaintext block now that we have rebuilt the + * ciphertext. + */ + if (!ctx->hw->cipher(ctx, out, ct_mid.c, CTS_BLOCK_SIZE)) + return 0; + + /* The returned iv is the C(n) block */ + memcpy(ctx->iv, cn.c, CTS_BLOCK_SIZE); + return len + CTS_BLOCK_SIZE + residue; +} + +static size_t cts128_cs2_encrypt(PROV_CIPHER_CTX *ctx, const unsigned char *in, + unsigned char *out, size_t len) +{ + if (len % CTS_BLOCK_SIZE == 0) { + /* If there are no partial blocks then it is the same as CBC mode */ + if (!ctx->hw->cipher(ctx, out, in, len)) + return 0; + return len; + } + /* For partial blocks CS2 is equivalent to CS3 */ + return cts128_cs3_encrypt(ctx, in, out, len); +} + +static size_t cts128_cs2_decrypt(PROV_CIPHER_CTX *ctx, const unsigned char *in, + unsigned char *out, size_t len) +{ + if (len % CTS_BLOCK_SIZE == 0) { + /* If there are no partial blocks then it is the same as CBC mode */ + if (!ctx->hw->cipher(ctx, out, in, len)) + return 0; + return len; + } + /* For partial blocks CS2 is equivalent to CS3 */ + return cts128_cs3_decrypt(ctx, in, out, len); +} + +int ossl_cipher_cbc_cts_block_update(void *vctx, unsigned char *out, size_t *outl, + size_t outsize, const unsigned char *in, + size_t inl) +{ + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + size_t sz = 0; + + if (inl < CTS_BLOCK_SIZE) /* There must be at least one block for CTS mode */ + return 0; + if (outsize < inl) + return 0; + if (out == NULL) { + *outl = inl; + return 1; + } + + /* + * Return an error if the update is called multiple times, only one shot + * is supported. + */ + if (ctx->updated == 1) + return 0; + + if (ctx->enc) { + if (ctx->cts_mode == CTS_CS1) + sz = cts128_cs1_encrypt(ctx, in, out, inl); + else if (ctx->cts_mode == CTS_CS2) + sz = cts128_cs2_encrypt(ctx, in, out, inl); + else if (ctx->cts_mode == CTS_CS3) + sz = cts128_cs3_encrypt(ctx, in, out, inl); + } else { + if (ctx->cts_mode == CTS_CS1) + sz = cts128_cs1_decrypt(ctx, in, out, inl); + else if (ctx->cts_mode == CTS_CS2) + sz = cts128_cs2_decrypt(ctx, in, out, inl); + else if (ctx->cts_mode == CTS_CS3) + sz = cts128_cs3_decrypt(ctx, in, out, inl); + } + if (sz == 0) + return 0; + ctx->updated = 1; /* Stop multiple updates being allowed */ + *outl = sz; + return 1; +} + +int ossl_cipher_cbc_cts_block_final(void *vctx, unsigned char *out, size_t *outl, + size_t outsize) +{ + *outl = 0; + return 1; +} diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_cts.h b/crypto/openssl/providers/implementations/ciphers/cipher_cts.h new file mode 100644 index 000000000000..a26e5a9e0719 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_cts.h @@ -0,0 +1,52 @@ +/* + * Copyright 2020-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 + */ + +#include "crypto/evp.h" + +/* NOTE: The underlying block cipher is CBC so we reuse most of the code */ +#define IMPLEMENT_cts_cipher(alg, UCALG, lcmode, UCMODE, flags, kbits, \ + blkbits, ivbits, typ) \ +static OSSL_FUNC_cipher_get_params_fn alg##_##kbits##_##lcmode##_get_params; \ +static int alg##_cts_##kbits##_##lcmode##_get_params(OSSL_PARAM params[]) \ +{ \ + return ossl_cipher_generic_get_params(params, EVP_CIPH_##UCMODE##_MODE, \ + flags, kbits, blkbits, ivbits); \ +} \ +const OSSL_DISPATCH ossl_##alg##kbits##lcmode##_cts_functions[] = { \ + { OSSL_FUNC_CIPHER_NEWCTX, \ + (void (*)(void)) alg##_##kbits##_##lcmode##_newctx }, \ + { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void)) alg##_freectx }, \ + { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void)) alg##_dupctx }, \ + { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void)) alg##_cbc_cts_einit }, \ + { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void)) alg##_cbc_cts_dinit }, \ + { OSSL_FUNC_CIPHER_UPDATE, \ + (void (*)(void)) ossl_cipher_cbc_cts_block_update }, \ + { OSSL_FUNC_CIPHER_FINAL, \ + (void (*)(void)) ossl_cipher_cbc_cts_block_final }, \ + { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))ossl_cipher_generic_cipher }, \ + { OSSL_FUNC_CIPHER_GET_PARAMS, \ + (void (*)(void)) alg##_cts_##kbits##_##lcmode##_get_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \ + (void (*)(void))ossl_cipher_generic_gettable_params }, \ + { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, \ + (void (*)(void)) alg##_cbc_cts_get_ctx_params }, \ + { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \ + (void (*)(void)) alg##_cbc_cts_set_ctx_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \ + (void (*)(void)) alg##_cbc_cts_gettable_ctx_params }, \ + { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \ + (void (*)(void)) alg##_cbc_cts_settable_ctx_params }, \ + OSSL_DISPATCH_END \ +}; + +OSSL_FUNC_cipher_update_fn ossl_cipher_cbc_cts_block_update; +OSSL_FUNC_cipher_final_fn ossl_cipher_cbc_cts_block_final; + +const char *ossl_cipher_cbc_cts_mode_id2name(unsigned int id); +int ossl_cipher_cbc_cts_mode_name2id(const char *name); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_des.c b/crypto/openssl/providers/implementations/ciphers/cipher_des.c new file mode 100644 index 000000000000..e2c890979ea4 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_des.c @@ -0,0 +1,201 @@ +/* + * Copyright 2019-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 + */ + +/* + * DES low level APIs are deprecated for public use, but still ok for internal + * use. + */ +#include "internal/deprecated.h" + +#include <openssl/rand.h> +#include <openssl/proverr.h> +#include "prov/ciphercommon.h" +#include "cipher_des.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" + +#define DES_FLAGS PROV_CIPHER_FLAG_RAND_KEY + +static OSSL_FUNC_cipher_freectx_fn des_freectx; +static OSSL_FUNC_cipher_encrypt_init_fn des_einit; +static OSSL_FUNC_cipher_decrypt_init_fn des_dinit; +static OSSL_FUNC_cipher_get_ctx_params_fn des_get_ctx_params; +static OSSL_FUNC_cipher_gettable_ctx_params_fn des_gettable_ctx_params; + +static void *des_newctx(void *provctx, size_t kbits, size_t blkbits, + size_t ivbits, unsigned int mode, uint64_t flags, + const PROV_CIPHER_HW *hw) +{ + PROV_DES_CTX *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + ctx = OPENSSL_zalloc(sizeof(*ctx)); + if (ctx != NULL) + ossl_cipher_generic_initkey(ctx, kbits, blkbits, ivbits, mode, flags, + hw, provctx); + return ctx; +} + +static void *des_dupctx(void *ctx) +{ + PROV_DES_CTX *in = (PROV_DES_CTX *)ctx; + PROV_DES_CTX *ret; + + if (!ossl_prov_is_running()) + return NULL; + + ret = OPENSSL_malloc(sizeof(*ret)); + if (ret == NULL) + return NULL; + in->base.hw->copyctx(&ret->base, &in->base); + + return ret; +} + +static void des_freectx(void *vctx) +{ + PROV_DES_CTX *ctx = (PROV_DES_CTX *)vctx; + + ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx); + OPENSSL_clear_free(ctx, sizeof(*ctx)); +} + +static int des_init(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[], int enc) +{ + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + + if (!ossl_prov_is_running()) + return 0; + + ctx->num = 0; + ctx->bufsz = 0; + ctx->enc = enc; + + if (iv != NULL) { + if (!ossl_cipher_generic_initiv(ctx, iv, ivlen)) + return 0; + } else if (ctx->iv_set) { + /* reset IV to keep compatibility with 1.1.1 */ + memcpy(ctx->iv, ctx->oiv, ctx->ivlen); + } + + if (key != NULL) { + if (keylen != ctx->keylen) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + if (!ctx->hw->init(ctx, key, keylen)) + return 0; + ctx->key_set = 1; + } + return ossl_cipher_generic_set_ctx_params(ctx, params); +} + +static int des_einit(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + return des_init(vctx, key, keylen, iv, ivlen, params, 1); +} + +static int des_dinit(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + return des_init(vctx, key, keylen, iv, ivlen, params, 0); +} + +static int des_generatekey(PROV_CIPHER_CTX *ctx, void *ptr) +{ + + DES_cblock *deskey = ptr; + size_t kl = ctx->keylen; + + if (kl == 0 || RAND_priv_bytes_ex(ctx->libctx, ptr, kl, 0) <= 0) + return 0; + DES_set_odd_parity(deskey); + return 1; +} + +CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_START(des) + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_RANDOM_KEY, NULL, 0), +CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_END(des) + +static int des_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + OSSL_PARAM *p; + + if (!ossl_cipher_generic_get_ctx_params(vctx, params)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_RANDOM_KEY); + if (p != NULL && !des_generatekey(ctx, p->data)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GENERATE_KEY); + return 0; + } + return 1; +} + +#define IMPLEMENT_des_cipher(type, lcmode, UCMODE, flags, \ + kbits, blkbits, ivbits, block) \ +static OSSL_FUNC_cipher_newctx_fn type##_##lcmode##_newctx; \ +static void *des_##lcmode##_newctx(void *provctx) \ +{ \ + return des_newctx(provctx, kbits, blkbits, ivbits, \ + EVP_CIPH_##UCMODE##_MODE, flags, \ + ossl_prov_cipher_hw_des_##lcmode()); \ +} \ +static OSSL_FUNC_cipher_get_params_fn des_##lcmode##_get_params; \ +static int des_##lcmode##_get_params(OSSL_PARAM params[]) \ +{ \ + return ossl_cipher_generic_get_params(params, EVP_CIPH_##UCMODE##_MODE, \ + flags, kbits, blkbits, ivbits); \ +} \ +const OSSL_DISPATCH ossl_##des_##lcmode##_functions[] = { \ + { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))des_einit }, \ + { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))des_dinit }, \ + { OSSL_FUNC_CIPHER_UPDATE, \ + (void (*)(void))ossl_cipher_generic_##block##_update }, \ + { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))ossl_cipher_generic_##block##_final },\ + { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))ossl_cipher_generic_cipher }, \ + { OSSL_FUNC_CIPHER_NEWCTX, \ + (void (*)(void))des_##lcmode##_newctx }, \ + { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void))des_dupctx }, \ + { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))des_freectx }, \ + { OSSL_FUNC_CIPHER_GET_PARAMS, \ + (void (*)(void))des_##lcmode##_get_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \ + (void (*)(void))ossl_cipher_generic_gettable_params }, \ + { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, (void (*)(void))des_get_ctx_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \ + (void (*)(void))des_gettable_ctx_params }, \ + { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \ + (void (*)(void))ossl_cipher_generic_set_ctx_params }, \ + { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \ + (void (*)(void))ossl_cipher_generic_settable_ctx_params }, \ + OSSL_DISPATCH_END \ +} + +/* ossl_des_ecb_functions */ +IMPLEMENT_des_cipher(des, ecb, ECB, DES_FLAGS, 64, 64, 0, block); +/* ossl_des_cbc_functions */ +IMPLEMENT_des_cipher(des, cbc, CBC, DES_FLAGS, 64, 64, 64, block); +/* ossl_des_ofb64_functions */ +IMPLEMENT_des_cipher(des, ofb64, OFB, DES_FLAGS, 64, 8, 64, stream); +/* ossl_des_cfb64_functions */ +IMPLEMENT_des_cipher(des, cfb64, CFB, DES_FLAGS, 64, 8, 64, stream); +/* ossl_des_cfb1_functions */ +IMPLEMENT_des_cipher(des, cfb1, CFB, DES_FLAGS, 64, 8, 64, stream); +/* ossl_des_cfb8_functions */ +IMPLEMENT_des_cipher(des, cfb8, CFB, DES_FLAGS, 64, 8, 64, stream); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_des.h b/crypto/openssl/providers/implementations/ciphers/cipher_des.h new file mode 100644 index 000000000000..ad10f63d8b16 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_des.h @@ -0,0 +1,33 @@ +/* + * Copyright 2019-2021 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 + */ + +#include <openssl/des.h> +#include "crypto/des_platform.h" + +#define TDES_FLAGS 0 + +typedef struct prov_des_ctx_st { + PROV_CIPHER_CTX base; /* Must be first */ + union { + OSSL_UNION_ALIGN; + DES_key_schedule ks; + } dks; + union { + void (*cbc) (const void *, void *, size_t, + const DES_key_schedule *, unsigned char *); + } dstream; + +} PROV_DES_CTX; + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_des_cbc(void); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_des_ecb(void); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_des_ofb64(void); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_des_cfb64(void); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_des_cfb1(void); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_des_cfb8(void); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_des_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_des_hw.c new file mode 100644 index 000000000000..a2d54b46ba11 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_des_hw.c @@ -0,0 +1,197 @@ +/* + * Copyright 1995-2021 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 + */ + +/* + * DES low level APIs are deprecated for public use, but still ok for internal + * use. + */ +#include "internal/deprecated.h" + +#include "prov/ciphercommon.h" +#include "cipher_des.h" + +static int cipher_hw_des_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + PROV_DES_CTX *dctx = (PROV_DES_CTX *)ctx; + DES_cblock *deskey = (DES_cblock *)key; + DES_key_schedule *ks = &dctx->dks.ks; + + dctx->dstream.cbc = NULL; +#if defined(SPARC_DES_CAPABLE) + if (SPARC_DES_CAPABLE) { + if (ctx->mode == EVP_CIPH_CBC_MODE) { + des_t4_key_expand(&deskey[0], ks); + dctx->dstream.cbc = ctx->enc ? des_t4_cbc_encrypt : + des_t4_cbc_decrypt; + return 1; + } + } +#endif + DES_set_key_unchecked(deskey, ks); + return 1; +} + +static void cipher_hw_des_copyctx(PROV_CIPHER_CTX *dst, + const PROV_CIPHER_CTX *src) +{ + PROV_DES_CTX *sctx = (PROV_DES_CTX *)src; + PROV_DES_CTX *dctx = (PROV_DES_CTX *)dst; + + *dctx = *sctx; + dst->ks = &dctx->dks.ks; +} + +static int cipher_hw_des_ecb_cipher(PROV_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t len) +{ + size_t i, bl = ctx->blocksize; + DES_key_schedule *key = &(((PROV_DES_CTX *)ctx)->dks.ks); + + if (len < bl) + return 1; + for (i = 0, len -= bl; i <= len; i += bl) + DES_ecb_encrypt((const_DES_cblock *)(in + i), + (const_DES_cblock *)(out + i), key, ctx->enc); + return 1; +} + +static int cipher_hw_des_cbc_cipher(PROV_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t len) +{ + PROV_DES_CTX *dctx = (PROV_DES_CTX *)ctx; + DES_key_schedule *key = &(dctx->dks.ks); + + if (dctx->dstream.cbc != NULL) { + (*dctx->dstream.cbc) (in, out, len, key, ctx->iv); + return 1; + } + + while (len >= MAXCHUNK) { + DES_ncbc_encrypt(in, out, MAXCHUNK, key, (DES_cblock *)ctx->iv, + ctx->enc); + len -= MAXCHUNK; + in += MAXCHUNK; + out += MAXCHUNK; + } + if (len > 0) + DES_ncbc_encrypt(in, out, (long)len, key, (DES_cblock *)ctx->iv, + ctx->enc); + return 1; +} + +static int cipher_hw_des_ofb64_cipher(PROV_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t len) +{ + int num = ctx->num; + DES_key_schedule *key = &(((PROV_DES_CTX *)ctx)->dks.ks); + + while (len >= MAXCHUNK) { + DES_ofb64_encrypt(in, out, MAXCHUNK, key, (DES_cblock *)ctx->iv, &num); + len -= MAXCHUNK; + in += MAXCHUNK; + out += MAXCHUNK; + } + if (len > 0) { + DES_ofb64_encrypt(in, out, (long)len, key, (DES_cblock *)ctx->iv, &num); + } + ctx->num = num; + return 1; +} + +static int cipher_hw_des_cfb64_cipher(PROV_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t len) +{ + size_t chunk = MAXCHUNK; + DES_key_schedule *key = &(((PROV_DES_CTX *)ctx)->dks.ks); + int num = ctx->num; + + if (len < chunk) + chunk = len; + while (len > 0 && len >= chunk) { + DES_cfb64_encrypt(in, out, (long)chunk, key, (DES_cblock *)ctx->iv, + &num, ctx->enc); + len -= chunk; + in += chunk; + out += chunk; + if (len < chunk) + chunk = len; + } + ctx->num = num; + return 1; +} + +/* + * Although we have a CFB-r implementation for DES, it doesn't pack the right + * way, so wrap it here + */ +static int cipher_hw_des_cfb1_cipher(PROV_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t inl) +{ + size_t n, chunk = MAXCHUNK / 8; + DES_key_schedule *key = &(((PROV_DES_CTX *)ctx)->dks.ks); + unsigned char c[1]; + unsigned char d[1] = { 0 }; + + if (inl < chunk) + chunk = inl; + + while (inl && inl >= chunk) { + for (n = 0; n < chunk * 8; ++n) { + c[0] = (in[n / 8] & (1 << (7 - n % 8))) ? 0x80 : 0; + DES_cfb_encrypt(c, d, 1, 1, key, (DES_cblock *)ctx->iv, ctx->enc); + out[n / 8] = + (out[n / 8] & ~(0x80 >> (unsigned int)(n % 8))) | + ((d[0] & 0x80) >> (unsigned int)(n % 8)); + } + inl -= chunk; + in += chunk; + out += chunk; + if (inl < chunk) + chunk = inl; + } + + return 1; +} + +static int cipher_hw_des_cfb8_cipher(PROV_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t inl) +{ + DES_key_schedule *key = &(((PROV_DES_CTX *)ctx)->dks.ks); + + while (inl >= MAXCHUNK) { + DES_cfb_encrypt(in, out, 8, (long)MAXCHUNK, key, + (DES_cblock *)ctx->iv, ctx->enc); + inl -= MAXCHUNK; + in += MAXCHUNK; + out += MAXCHUNK; + } + if (inl > 0) + DES_cfb_encrypt(in, out, 8, (long)inl, key, + (DES_cblock *)ctx->iv, ctx->enc); + return 1; +} + +#define PROV_CIPHER_HW_des_mode(mode) \ +static const PROV_CIPHER_HW des_##mode = { \ + cipher_hw_des_initkey, \ + cipher_hw_des_##mode##_cipher, \ + cipher_hw_des_copyctx \ +}; \ +const PROV_CIPHER_HW *ossl_prov_cipher_hw_des_##mode(void) \ +{ \ + return &des_##mode; \ +} + +PROV_CIPHER_HW_des_mode(ecb) +PROV_CIPHER_HW_des_mode(cbc) +PROV_CIPHER_HW_des_mode(ofb64) +PROV_CIPHER_HW_des_mode(cfb64) +PROV_CIPHER_HW_des_mode(cfb1) +PROV_CIPHER_HW_des_mode(cfb8) diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_desx.c b/crypto/openssl/providers/implementations/ciphers/cipher_desx.c new file mode 100644 index 000000000000..41596554435e --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_desx.c @@ -0,0 +1,21 @@ +/* + * Copyright 2019-2020 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 + */ + +/* + * DES low level APIs are deprecated for public use, but still ok for internal + * use. + */ +#include "internal/deprecated.h" + +#include "cipher_tdes_default.h" +#include "prov/implementations.h" + +/* desx_cbc_functions */ +IMPLEMENT_tdes_cipher(desx, DESX, cbc, CBC, TDES_FLAGS, 64*3, 64, 64, block); + diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_desx_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_desx_hw.c new file mode 100644 index 000000000000..31fd18e54ce5 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_desx_hw.c @@ -0,0 +1,79 @@ +/* + * Copyright 1995-2024 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 + */ + +/* + * DES low level APIs are deprecated for public use, but still ok for internal + * use. + */ +#include "internal/deprecated.h" + +#include <openssl/des.h> +#include "cipher_tdes_default.h" + +/* + * Note the PROV_TDES_CTX has been used for the DESX cipher, just to reduce + * code size. + */ +#define ks1 tks.ks[0] +#define ks2 tks.ks[1].ks[0].cblock +#define ks3 tks.ks[2].ks[0].cblock + +static int cipher_hw_desx_cbc_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + PROV_TDES_CTX *tctx = (PROV_TDES_CTX *)ctx; + DES_cblock *deskey = (DES_cblock *)key; + + DES_set_key_unchecked(deskey, &tctx->ks1); + memcpy(&tctx->ks2, &key[8], 8); + memcpy(&tctx->ks3, &key[16], 8); + + return 1; +} + +static void cipher_hw_desx_copyctx(PROV_CIPHER_CTX *dst, + const PROV_CIPHER_CTX *src) +{ + PROV_TDES_CTX *sctx = (PROV_TDES_CTX *)src; + PROV_TDES_CTX *dctx = (PROV_TDES_CTX *)dst; + + *dctx = *sctx; + dst->ks = &dctx->tks.ks; +} + +static int cipher_hw_desx_cbc(PROV_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t inl) +{ + PROV_TDES_CTX *tctx = (PROV_TDES_CTX *)ctx; + + while (inl >= MAXCHUNK) { + DES_xcbc_encrypt(in, out, (long)MAXCHUNK, &tctx->ks1, + (DES_cblock *)ctx->iv, &tctx->ks2, &tctx->ks3, + ctx->enc); + inl -= MAXCHUNK; + in += MAXCHUNK; + out += MAXCHUNK; + } + if (inl > 0) + DES_xcbc_encrypt(in, out, (long)inl, &tctx->ks1, + (DES_cblock *)ctx->iv, &tctx->ks2, &tctx->ks3, + ctx->enc); + return 1; +} + +static const PROV_CIPHER_HW desx_cbc = { + cipher_hw_desx_cbc_initkey, + cipher_hw_desx_cbc, + cipher_hw_desx_copyctx +}; + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_tdes_desx_cbc(void) +{ + return &desx_cbc; +} diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_idea.c b/crypto/openssl/providers/implementations/ciphers/cipher_idea.c new file mode 100644 index 000000000000..c69c6ac092b4 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_idea.c @@ -0,0 +1,57 @@ +/* + * Copyright 2019-2020 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 + */ + +/* + * IDEA low level APIs are deprecated for public use, but still ok for internal + * use where we're using them to implement the higher level EVP interface, as is + * the case here. + */ +#include "internal/deprecated.h" + +/* Dispatch functions for Idea cipher modes ecb, cbc, ofb, cfb */ + +#include "cipher_idea.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" + +static OSSL_FUNC_cipher_freectx_fn idea_freectx; +static OSSL_FUNC_cipher_dupctx_fn idea_dupctx; + +static void idea_freectx(void *vctx) +{ + PROV_IDEA_CTX *ctx = (PROV_IDEA_CTX *)vctx; + + ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx); + OPENSSL_clear_free(ctx, sizeof(*ctx)); +} + +static void *idea_dupctx(void *ctx) +{ + PROV_IDEA_CTX *in = (PROV_IDEA_CTX *)ctx; + PROV_IDEA_CTX *ret; + + if (!ossl_prov_is_running()) + return NULL; + + ret = OPENSSL_malloc(sizeof(*ret)); + if (ret == NULL) + return NULL; + *ret = *in; + + return ret; +} + +/* ossl_idea128ecb_functions */ +IMPLEMENT_generic_cipher(idea, IDEA, ecb, ECB, 0, 128, 64, 0, block) +/* ossl_idea128cbc_functions */ +IMPLEMENT_generic_cipher(idea, IDEA, cbc, CBC, 0, 128, 64, 64, block) +/* ossl_idea128ofb64_functions */ +IMPLEMENT_generic_cipher(idea, IDEA, ofb64, OFB, 0, 128, 8, 64, stream) +/* ossl_idea128cfb64_functions */ +IMPLEMENT_generic_cipher(idea, IDEA, cfb64, CFB, 0, 128, 8, 64, stream) diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_idea.h b/crypto/openssl/providers/implementations/ciphers/cipher_idea.h new file mode 100644 index 000000000000..212efa8af575 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_idea.h @@ -0,0 +1,24 @@ +/* + * Copyright 2019-2020 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 + */ + +#include <openssl/idea.h> +#include "prov/ciphercommon.h" + +typedef struct prov_idea_ctx_st { + PROV_CIPHER_CTX base; /* Must be first */ + union { + OSSL_UNION_ALIGN; + IDEA_KEY_SCHEDULE ks; + } ks; +} PROV_IDEA_CTX; + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_idea_cbc(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_idea_ecb(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_idea_ofb64(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_idea_cfb64(size_t keybits); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_idea_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_idea_hw.c new file mode 100644 index 000000000000..1c451b77edc4 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_idea_hw.c @@ -0,0 +1,63 @@ +/* + * Copyright 2019-2020 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 + */ + +/* + * IDEA low level APIs are deprecated for public use, but still ok for internal + * use where we're using them to implement the higher level EVP interface, as is + * the case here. + */ +#include "internal/deprecated.h" + +#include "cipher_idea.h" + +static int cipher_hw_idea_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + PROV_IDEA_CTX *ictx = (PROV_IDEA_CTX *)ctx; + IDEA_KEY_SCHEDULE *ks = &(ictx->ks.ks); + + if (ctx->enc + || ctx->mode == EVP_CIPH_OFB_MODE + || ctx->mode == EVP_CIPH_CFB_MODE) { + IDEA_set_encrypt_key(key, ks); + } else { + IDEA_KEY_SCHEDULE tmp; + + IDEA_set_encrypt_key(key, &tmp); + IDEA_set_decrypt_key(&tmp, ks); + OPENSSL_cleanse((unsigned char *)&tmp, sizeof(IDEA_KEY_SCHEDULE)); + } + return 1; +} + +# define PROV_CIPHER_HW_idea_mode_ex(mode, UCMODE, fname) \ +IMPLEMENT_CIPHER_HW_##UCMODE(mode, idea, PROV_IDEA_CTX, IDEA_KEY_SCHEDULE, \ + fname) \ +static const PROV_CIPHER_HW idea_##mode = { \ + cipher_hw_idea_initkey, \ + cipher_hw_idea_##mode##_cipher \ +}; \ +const PROV_CIPHER_HW *ossl_prov_cipher_hw_idea_##mode(size_t keybits) \ +{ \ + return &idea_##mode; \ +} + +# define PROV_CIPHER_HW_idea_mode(mode, UCMODE) \ + PROV_CIPHER_HW_idea_mode_ex(mode, UCMODE, IDEA_##mode) + +PROV_CIPHER_HW_idea_mode(cbc, CBC) +PROV_CIPHER_HW_idea_mode(ofb64, OFB) +PROV_CIPHER_HW_idea_mode(cfb64, CFB) +/* + * IDEA_ecb_encrypt() does not have a enc parameter - so we create a macro + * that ignores this parameter when IMPLEMENT_CIPHER_HW_ecb() is called. + */ +#define IDEA2_ecb_encrypt(in, out, ks, enc) IDEA_ecb_encrypt(in, out, ks) + +PROV_CIPHER_HW_idea_mode_ex(ecb, ECB, IDEA2_ecb) diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_null.c b/crypto/openssl/providers/implementations/ciphers/cipher_null.c new file mode 100644 index 000000000000..7e934093ce5a --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_null.c @@ -0,0 +1,197 @@ +/* + * Copyright 2020-2024 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 + */ + +#include <string.h> +#include <openssl/crypto.h> +#include <openssl/core_dispatch.h> +#include <openssl/proverr.h> +#include "prov/implementations.h" +#include "prov/ciphercommon.h" +#include "prov/providercommon.h" + +typedef struct prov_cipher_null_ctx_st { + int enc; + size_t tlsmacsize; + const unsigned char *tlsmac; +} PROV_CIPHER_NULL_CTX; + +static OSSL_FUNC_cipher_newctx_fn null_newctx; +static void *null_newctx(void *provctx) +{ + if (!ossl_prov_is_running()) + return NULL; + + return OPENSSL_zalloc(sizeof(PROV_CIPHER_NULL_CTX)); +} + +static OSSL_FUNC_cipher_freectx_fn null_freectx; +static void null_freectx(void *vctx) +{ + OPENSSL_free(vctx); +} + +static OSSL_FUNC_cipher_encrypt_init_fn null_einit; +static int null_einit(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + PROV_CIPHER_NULL_CTX *ctx = (PROV_CIPHER_NULL_CTX *)vctx; + + if (!ossl_prov_is_running()) + return 0; + + ctx->enc = 1; + return 1; +} + +static OSSL_FUNC_cipher_decrypt_init_fn null_dinit; +static int null_dinit(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + if (!ossl_prov_is_running()) + return 0; + + return 1; +} + +static OSSL_FUNC_cipher_cipher_fn null_cipher; +static int null_cipher(void *vctx, unsigned char *out, size_t *outl, + size_t outsize, const unsigned char *in, size_t inl) +{ + PROV_CIPHER_NULL_CTX *ctx = (PROV_CIPHER_NULL_CTX *)vctx; + + if (!ossl_prov_is_running()) + return 0; + + if (!ctx->enc && ctx->tlsmacsize > 0) { + /* + * TLS NULL cipher as per: + * https://tools.ietf.org/html/rfc5246#section-6.2.3.1 + */ + if (inl < ctx->tlsmacsize) + return 0; + ctx->tlsmac = in + inl - ctx->tlsmacsize; + inl -= ctx->tlsmacsize; + } + if (outsize < inl) + return 0; + if (out != NULL && in != out) + memcpy(out, in, inl); + *outl = inl; + return 1; +} + +static OSSL_FUNC_cipher_final_fn null_final; +static int null_final(void *vctx, unsigned char *out, size_t *outl, + size_t outsize) +{ + if (!ossl_prov_is_running()) + return 0; + + *outl = 0; + return 1; +} + +static OSSL_FUNC_cipher_get_params_fn null_get_params; +static int null_get_params(OSSL_PARAM params[]) +{ + return ossl_cipher_generic_get_params(params, 0, 0, 0, 8, 0); +} + +static const OSSL_PARAM null_known_gettable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL), + { OSSL_CIPHER_PARAM_TLS_MAC, OSSL_PARAM_OCTET_PTR, NULL, 0, OSSL_PARAM_UNMODIFIED }, + OSSL_PARAM_END +}; + +static OSSL_FUNC_cipher_gettable_ctx_params_fn null_gettable_ctx_params; +static const OSSL_PARAM *null_gettable_ctx_params(ossl_unused void *cctx, + ossl_unused void *provctx) +{ + return null_known_gettable_ctx_params; +} + +static OSSL_FUNC_cipher_get_ctx_params_fn null_get_ctx_params; +static int null_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + PROV_CIPHER_NULL_CTX *ctx = (PROV_CIPHER_NULL_CTX *)vctx; + OSSL_PARAM *p; + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, 0)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, 0)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_TLS_MAC); + if (p != NULL + && !OSSL_PARAM_set_octet_ptr(p, ctx->tlsmac, ctx->tlsmacsize)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + return 1; +} + +static const OSSL_PARAM null_known_settable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_TLS_MAC_SIZE, NULL), + OSSL_PARAM_END +}; + +static OSSL_FUNC_cipher_settable_ctx_params_fn null_settable_ctx_params; +static const OSSL_PARAM *null_settable_ctx_params(ossl_unused void *cctx, + ossl_unused void *provctx) +{ + return null_known_settable_ctx_params; +} + + +static OSSL_FUNC_cipher_set_ctx_params_fn null_set_ctx_params; +static int null_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_CIPHER_NULL_CTX *ctx = (PROV_CIPHER_NULL_CTX *)vctx; + const OSSL_PARAM *p; + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_TLS_MAC_SIZE); + if (p != NULL) { + if (!OSSL_PARAM_get_size_t(p, &ctx->tlsmacsize)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + } + + return 1; +} + +const OSSL_DISPATCH ossl_null_functions[] = { + { OSSL_FUNC_CIPHER_NEWCTX, + (void (*)(void)) null_newctx }, + { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void)) null_freectx }, + { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void)) null_newctx }, + { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))null_einit }, + { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))null_dinit }, + { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))null_cipher }, + { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))null_final }, + { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))null_cipher }, + { OSSL_FUNC_CIPHER_GET_PARAMS, (void (*)(void)) null_get_params }, + { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, + (void (*)(void))ossl_cipher_generic_gettable_params }, + { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, (void (*)(void))null_get_ctx_params }, + { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, + (void (*)(void))null_gettable_ctx_params }, + { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, (void (*)(void))null_set_ctx_params }, + { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, + (void (*)(void))null_settable_ctx_params }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_rc2.c b/crypto/openssl/providers/implementations/ciphers/cipher_rc2.c new file mode 100644 index 000000000000..c535bd7e9724 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_rc2.c @@ -0,0 +1,295 @@ +/* + * Copyright 2019-2024 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 + */ + +/* Dispatch functions for RC2 cipher modes ecb, cbc, ofb, cfb */ + +/* + * RC2 low level APIs are deprecated for public use, but still ok for internal + * use. + */ +#include "internal/deprecated.h" + +#include <openssl/proverr.h> +#include "cipher_rc2.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" + +#define RC2_40_MAGIC 0xa0 +#define RC2_64_MAGIC 0x78 +#define RC2_128_MAGIC 0x3a +#define RC2_FLAGS PROV_CIPHER_FLAG_VARIABLE_LENGTH + +static OSSL_FUNC_cipher_encrypt_init_fn rc2_einit; +static OSSL_FUNC_cipher_decrypt_init_fn rc2_dinit; +static OSSL_FUNC_cipher_freectx_fn rc2_freectx; +static OSSL_FUNC_cipher_dupctx_fn rc2_dupctx; +static OSSL_FUNC_cipher_gettable_ctx_params_fn rc2_gettable_ctx_params; +static OSSL_FUNC_cipher_settable_ctx_params_fn rc2_settable_ctx_params; +static OSSL_FUNC_cipher_set_ctx_params_fn rc2_set_ctx_params; + +static void rc2_freectx(void *vctx) +{ + PROV_RC2_CTX *ctx = (PROV_RC2_CTX *)vctx; + + ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx); + OPENSSL_clear_free(ctx, sizeof(*ctx)); +} + +static void *rc2_dupctx(void *ctx) +{ + PROV_RC2_CTX *in = (PROV_RC2_CTX *)ctx; + PROV_RC2_CTX *ret; + + if (!ossl_prov_is_running()) + return NULL; + + ret = OPENSSL_malloc(sizeof(*ret)); + if (ret == NULL) + return NULL; + *ret = *in; + + return ret; +} + +static int rc2_keybits_to_magic(int keybits) +{ + switch (keybits) { + case 128: + return RC2_128_MAGIC; + case 64: + return RC2_64_MAGIC; + case 40: + return RC2_40_MAGIC; + } + ERR_raise(ERR_LIB_PROV, PROV_R_UNSUPPORTED_KEY_SIZE); + return 0; +} + +static int rc2_magic_to_keybits(int magic) +{ + switch (magic) { + case RC2_128_MAGIC: + return 128; + case RC2_64_MAGIC: + return 64; + case RC2_40_MAGIC: + return 40; + } + ERR_raise(ERR_LIB_PROV, PROV_R_UNSUPPORTED_KEY_SIZE); + return 0; +} + +static int rc2_einit(void *ctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + if (!ossl_cipher_generic_einit(ctx, key, keylen, iv, ivlen, NULL)) + return 0; + return rc2_set_ctx_params(ctx, params); +} + +static int rc2_dinit(void *ctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + if (!ossl_cipher_generic_dinit(ctx, key, keylen, iv, ivlen, NULL)) + return 0; + return rc2_set_ctx_params(ctx, params); +} + +static int rc2_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + PROV_RC2_CTX *ctx = (PROV_RC2_CTX *)vctx; + OSSL_PARAM *p, *p1, *p2; + + if (!ossl_cipher_generic_get_ctx_params(vctx, params)) + return 0; + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_RC2_KEYBITS); + if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->key_bits)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p1 = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_ALGORITHM_ID_PARAMS); + p2 = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_ALGORITHM_ID_PARAMS_OLD); + if (p1 != NULL || p2 != NULL) { + long num; + int i; + ASN1_TYPE *type; + unsigned char *d1 = (p1 == NULL) ? NULL : p1->data; + unsigned char *d2 = (p2 == NULL) ? NULL : p2->data; + unsigned char **dd1 = d1 == NULL ? NULL : &d1; + unsigned char **dd2 = d2 == NULL ? NULL : &d2; + + if ((p1 != NULL && p1->data_type != OSSL_PARAM_OCTET_STRING) + || (p2 != NULL && p2->data_type != OSSL_PARAM_OCTET_STRING)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + if ((type = ASN1_TYPE_new()) == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_ASN1_LIB); + return 0; + } + + /* Is this the original IV or the running IV? */ + num = rc2_keybits_to_magic(ctx->key_bits); + if (!ASN1_TYPE_set_int_octetstring(type, num, + ctx->base.iv, ctx->base.ivlen)) { + ASN1_TYPE_free(type); + ERR_raise(ERR_LIB_PROV, ERR_R_ASN1_LIB); + return 0; + } + + /* + * IF the caller has a buffer, we pray to the gods they got the + * size right. There's no way to tell the i2d functions... + */ + i = i2d_ASN1_TYPE(type, dd1); + if (p1 != NULL && i >= 0) + p1->return_size = (size_t)i; + + /* + * If the buffers differ, redo the i2d on the second buffer. + * Otherwise, just use |i| as computed above + */ + if (d1 != d2) + i = i2d_ASN1_TYPE(type, dd2); + if (p2 != NULL && i >= 0) + p2->return_size = (size_t)i; + + ASN1_TYPE_free(type); + if (i < 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + } + return 1; +} + +static int rc2_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_RC2_CTX *ctx = (PROV_RC2_CTX *)vctx; + const OSSL_PARAM *p; + + if (ossl_param_is_empty(params)) + return 1; + + if (!ossl_cipher_var_keylen_set_ctx_params(vctx, params)) + return 0; + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_RC2_KEYBITS); + if (p != NULL) { + if (!OSSL_PARAM_get_size_t(p, &ctx->key_bits)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + } + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_ALGORITHM_ID_PARAMS); + if (p != NULL) { + ASN1_TYPE *type = NULL; + long num = 0; + const unsigned char *d = p->data; + int ret = 1; + unsigned char iv[16]; + + if (p->data_type != OSSL_PARAM_OCTET_STRING + || ctx->base.ivlen > sizeof(iv) + || (type = d2i_ASN1_TYPE(NULL, &d, p->data_size)) == NULL + || ((size_t)ASN1_TYPE_get_int_octetstring(type, &num, iv, + ctx->base.ivlen) + != ctx->base.ivlen) + || !ossl_cipher_generic_initiv(&ctx->base, iv, ctx->base.ivlen) + || (ctx->key_bits = rc2_magic_to_keybits(num)) == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + ret = 0; + } + ASN1_TYPE_free(type); + if (ret == 0) + return 0; + /* + * This code assumes that the caller will call + * EVP_CipherInit_ex() with a non NULL key in order to setup a key that + * uses the keylen and keybits that were set here. + */ + ctx->base.keylen = ctx->key_bits / 8; + } + return 1; +} + +CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_START(rc2) +OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_RC2_KEYBITS, NULL), +OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_ALGORITHM_ID_PARAMS, NULL, 0), +CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_END(rc2) + +CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_START(rc2) +OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), +OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_RC2_KEYBITS, NULL), +OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_ALGORITHM_ID_PARAMS, NULL, 0), +CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_END(rc2) + +#define IMPLEMENT_cipher(alg, UCALG, lcmode, UCMODE, flags, kbits, blkbits, \ + ivbits, typ) \ +static OSSL_FUNC_cipher_get_params_fn alg##_##kbits##_##lcmode##_get_params; \ +static int alg##_##kbits##_##lcmode##_get_params(OSSL_PARAM params[]) \ +{ \ + return ossl_cipher_generic_get_params(params, EVP_CIPH_##UCMODE##_MODE, \ + flags, kbits, blkbits, ivbits); \ +} \ +static OSSL_FUNC_cipher_newctx_fn alg##_##kbits##_##lcmode##_newctx; \ +static void *alg##_##kbits##_##lcmode##_newctx(void *provctx) \ +{ \ + PROV_##UCALG##_CTX *ctx; \ + if (!ossl_prov_is_running()) \ + return NULL; \ + ctx = OPENSSL_zalloc(sizeof(*ctx)); \ + if (ctx != NULL) { \ + ossl_cipher_generic_initkey(ctx, kbits, blkbits, ivbits, \ + EVP_CIPH_##UCMODE##_MODE, flags, \ + ossl_prov_cipher_hw_##alg##_##lcmode(kbits), \ + NULL); \ + ctx->key_bits = kbits; \ + } \ + return ctx; \ +} \ +const OSSL_DISPATCH ossl_##alg##kbits##lcmode##_functions[] = { \ + { OSSL_FUNC_CIPHER_NEWCTX, \ + (void (*)(void)) alg##_##kbits##_##lcmode##_newctx }, \ + { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void)) alg##_freectx }, \ + { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void)) alg##_dupctx }, \ + { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))rc2_einit }, \ + { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))rc2_dinit }, \ + { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))ossl_cipher_generic_##typ##_update },\ + { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))ossl_cipher_generic_##typ##_final }, \ + { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))ossl_cipher_generic_cipher }, \ + { OSSL_FUNC_CIPHER_GET_PARAMS, \ + (void (*)(void)) alg##_##kbits##_##lcmode##_get_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \ + (void (*)(void))ossl_cipher_generic_gettable_params }, \ + { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, \ + (void (*)(void))rc2_get_ctx_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \ + (void (*)(void))rc2_gettable_ctx_params }, \ + { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \ + (void (*)(void))rc2_set_ctx_params }, \ + { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \ + (void (*)(void))rc2_settable_ctx_params }, \ + OSSL_DISPATCH_END \ +}; + +/* ossl_rc2128ecb_functions */ +IMPLEMENT_cipher(rc2, RC2, ecb, ECB, RC2_FLAGS, 128, 64, 0, block) +/* ossl_rc2128cbc_functions */ +IMPLEMENT_cipher(rc2, RC2, cbc, CBC, RC2_FLAGS, 128, 64, 64, block) +/* ossl_rc240cbc_functions */ +IMPLEMENT_cipher(rc2, RC2, cbc, CBC, RC2_FLAGS, 40, 64, 64, block) +/* ossl_rc264cbc_functions */ +IMPLEMENT_cipher(rc2, RC2, cbc, CBC, RC2_FLAGS, 64, 64, 64, block) + +/* ossl_rc2128ofb128_functions */ +IMPLEMENT_cipher(rc2, RC2, ofb128, OFB, RC2_FLAGS, 128, 8, 64, stream) +/* ossl_rc2128cfb128_functions */ +IMPLEMENT_cipher(rc2, RC2, cfb128, CFB, RC2_FLAGS, 128, 8, 64, stream) diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_rc2.h b/crypto/openssl/providers/implementations/ciphers/cipher_rc2.h new file mode 100644 index 000000000000..7a4bea5ac404 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_rc2.h @@ -0,0 +1,28 @@ +/* + * Copyright 2019-2020 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 + */ + +#include <openssl/rc2.h> +#include "prov/ciphercommon.h" + +typedef struct prov_rc2_ctx_st { + PROV_CIPHER_CTX base; /* Must be first */ + union { + OSSL_UNION_ALIGN; + RC2_KEY ks; + } ks; + size_t key_bits; +} PROV_RC2_CTX; + +#define ossl_prov_cipher_hw_rc2_ofb128 ossl_prov_cipher_hw_rc2_ofb64 +#define ossl_prov_cipher_hw_rc2_cfb128 ossl_prov_cipher_hw_rc2_cfb64 + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_rc2_cbc(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_rc2_ecb(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_rc2_ofb64(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_rc2_cfb64(size_t keybits); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_rc2_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_rc2_hw.c new file mode 100644 index 000000000000..da9ff729cda0 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_rc2_hw.c @@ -0,0 +1,43 @@ +/* + * Copyright 2019-2020 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 + */ + +/* + * RC2 low level APIs are deprecated for public use, but still ok for internal + * use. + */ +#include "internal/deprecated.h" + +#include "cipher_rc2.h" + +static int cipher_hw_rc2_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + PROV_RC2_CTX *rctx = (PROV_RC2_CTX *)ctx; + RC2_KEY *ks = &(rctx->ks.ks); + + RC2_set_key(ks, (int)ctx->keylen, key, (int)rctx->key_bits); + return 1; +} + +# define PROV_CIPHER_HW_rc2_mode(mode, UCMODE) \ +IMPLEMENT_CIPHER_HW_##UCMODE(mode, rc2, PROV_RC2_CTX, RC2_KEY, \ + RC2_##mode) \ +static const PROV_CIPHER_HW rc2_##mode = { \ + cipher_hw_rc2_initkey, \ + cipher_hw_rc2_##mode##_cipher \ +}; \ +const PROV_CIPHER_HW *ossl_prov_cipher_hw_rc2_##mode(size_t keybits) \ +{ \ + return &rc2_##mode; \ +} + +PROV_CIPHER_HW_rc2_mode(cbc, CBC) +PROV_CIPHER_HW_rc2_mode(ecb, ECB) +PROV_CIPHER_HW_rc2_mode(ofb64, OFB) +PROV_CIPHER_HW_rc2_mode(cfb64, CFB) diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_rc4.c b/crypto/openssl/providers/implementations/ciphers/cipher_rc4.c new file mode 100644 index 000000000000..733524d36f5a --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_rc4.c @@ -0,0 +1,119 @@ +/* + * Copyright 2019-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 + */ + +/* Dispatch functions for RC4 ciphers */ + +/* + * RC4 low level APIs are deprecated for public use, but still ok for internal + * use. + */ +#include "internal/deprecated.h" + +#include "cipher_rc4.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" + +#define RC4_FLAGS PROV_CIPHER_FLAG_VARIABLE_LENGTH + +static OSSL_FUNC_cipher_encrypt_init_fn rc4_einit; +static OSSL_FUNC_cipher_decrypt_init_fn rc4_dinit; +static OSSL_FUNC_cipher_freectx_fn rc4_freectx; +static OSSL_FUNC_cipher_dupctx_fn rc4_dupctx; + +static void rc4_freectx(void *vctx) +{ + PROV_RC4_CTX *ctx = (PROV_RC4_CTX *)vctx; + + ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx); + OPENSSL_clear_free(ctx, sizeof(*ctx)); +} + +static void *rc4_dupctx(void *ctx) +{ + PROV_RC4_CTX *in = (PROV_RC4_CTX *)ctx; + PROV_RC4_CTX *ret; + + if (!ossl_prov_is_running()) + return NULL; + + ret = OPENSSL_malloc(sizeof(*ret)); + if (ret == NULL) + return NULL; + *ret = *in; + + return ret; +} + +static int rc4_einit(void *ctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + if (!ossl_cipher_generic_einit(ctx, key, keylen, iv, ivlen, NULL)) + return 0; + return ossl_cipher_var_keylen_set_ctx_params(ctx, params); +} + +static int rc4_dinit(void *ctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + if (!ossl_cipher_generic_dinit(ctx, key, keylen, iv, ivlen, NULL)) + return 0; + return ossl_cipher_var_keylen_set_ctx_params(ctx, params); +} + +#define IMPLEMENT_cipher(alg, UCALG, flags, kbits, blkbits, ivbits, typ) \ +static OSSL_FUNC_cipher_get_params_fn alg##_##kbits##_get_params; \ +static int alg##_##kbits##_get_params(OSSL_PARAM params[]) \ +{ \ + return ossl_cipher_generic_get_params(params, 0, flags, \ + kbits, blkbits, ivbits); \ +} \ +static OSSL_FUNC_cipher_newctx_fn alg##_##kbits##_newctx; \ +static void *alg##_##kbits##_newctx(void *provctx) \ +{ \ + PROV_##UCALG##_CTX *ctx; \ + if (!ossl_prov_is_running()) \ + return NULL; \ + ctx = OPENSSL_zalloc(sizeof(*ctx)); \ + if (ctx != NULL) { \ + ossl_cipher_generic_initkey(ctx, kbits, blkbits, ivbits, 0, flags, \ + ossl_prov_cipher_hw_##alg(kbits), NULL); \ + } \ + return ctx; \ +} \ +const OSSL_DISPATCH ossl_##alg##kbits##_functions[] = { \ + { OSSL_FUNC_CIPHER_NEWCTX, \ + (void (*)(void)) alg##_##kbits##_newctx }, \ + { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void)) alg##_freectx }, \ + { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void)) alg##_dupctx }, \ + { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))rc4_einit }, \ + { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))rc4_dinit }, \ + { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))ossl_cipher_generic_##typ##_update },\ + { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))ossl_cipher_generic_##typ##_final }, \ + { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))ossl_cipher_generic_cipher }, \ + { OSSL_FUNC_CIPHER_GET_PARAMS, \ + (void (*)(void)) alg##_##kbits##_get_params }, \ + { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, \ + (void (*)(void))ossl_cipher_generic_get_ctx_params }, \ + { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \ + (void (*)(void))ossl_cipher_var_keylen_set_ctx_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \ + (void (*)(void))ossl_cipher_generic_gettable_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \ + (void (*)(void))ossl_cipher_generic_gettable_ctx_params }, \ + { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \ + (void (*)(void))ossl_cipher_var_keylen_settable_ctx_params }, \ + OSSL_DISPATCH_END \ +}; + +/* ossl_rc440_functions */ +IMPLEMENT_cipher(rc4, RC4, RC4_FLAGS, 40, 8, 0, stream) +/* ossl_rc4128_functions */ +IMPLEMENT_cipher(rc4, RC4, RC4_FLAGS, 128, 8, 0, stream) diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_rc4.h b/crypto/openssl/providers/implementations/ciphers/cipher_rc4.h new file mode 100644 index 000000000000..40d822ceb2ef --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_rc4.h @@ -0,0 +1,21 @@ +/* + * Copyright 2019-2020 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 + */ + +#include <openssl/rc4.h> +#include "prov/ciphercommon.h" + +typedef struct prov_rc4_ctx_st { + PROV_CIPHER_CTX base; /* Must be first */ + union { + OSSL_UNION_ALIGN; + RC4_KEY ks; + } ks; +} PROV_RC4_CTX; + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_rc4(size_t keybits); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_rc4_hmac_md5.c b/crypto/openssl/providers/implementations/ciphers/cipher_rc4_hmac_md5.c new file mode 100644 index 000000000000..ec18777143bb --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_rc4_hmac_md5.c @@ -0,0 +1,245 @@ +/* + * Copyright 2019-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 + */ + +/* Dispatch functions for RC4_HMAC_MD5 cipher */ + +/* + * MD5 and RC4 low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include <openssl/proverr.h> +#include "cipher_rc4_hmac_md5.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" + +#define RC4_HMAC_MD5_FLAGS (PROV_CIPHER_FLAG_VARIABLE_LENGTH \ + | PROV_CIPHER_FLAG_AEAD) + +#define RC4_HMAC_MD5_KEY_BITS (16 * 8) +#define RC4_HMAC_MD5_BLOCK_BITS (1 * 8) +#define RC4_HMAC_MD5_IV_BITS 0 +#define RC4_HMAC_MD5_MODE 0 + +#define GET_HW(ctx) ((PROV_CIPHER_HW_RC4_HMAC_MD5 *)ctx->base.hw) + +static OSSL_FUNC_cipher_encrypt_init_fn rc4_hmac_md5_einit; +static OSSL_FUNC_cipher_decrypt_init_fn rc4_hmac_md5_dinit; +static OSSL_FUNC_cipher_newctx_fn rc4_hmac_md5_newctx; +static OSSL_FUNC_cipher_freectx_fn rc4_hmac_md5_freectx; +static OSSL_FUNC_cipher_dupctx_fn rc4_hmac_md5_dupctx; +static OSSL_FUNC_cipher_get_ctx_params_fn rc4_hmac_md5_get_ctx_params; +static OSSL_FUNC_cipher_gettable_ctx_params_fn rc4_hmac_md5_gettable_ctx_params; +static OSSL_FUNC_cipher_set_ctx_params_fn rc4_hmac_md5_set_ctx_params; +static OSSL_FUNC_cipher_settable_ctx_params_fn rc4_hmac_md5_settable_ctx_params; +static OSSL_FUNC_cipher_get_params_fn rc4_hmac_md5_get_params; +#define rc4_hmac_md5_gettable_params ossl_cipher_generic_gettable_params +#define rc4_hmac_md5_update ossl_cipher_generic_stream_update +#define rc4_hmac_md5_final ossl_cipher_generic_stream_final +#define rc4_hmac_md5_cipher ossl_cipher_generic_cipher + +static void *rc4_hmac_md5_newctx(void *provctx) +{ + PROV_RC4_HMAC_MD5_CTX *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + ctx = OPENSSL_zalloc(sizeof(*ctx)); + if (ctx != NULL) + ossl_cipher_generic_initkey(ctx, RC4_HMAC_MD5_KEY_BITS, + RC4_HMAC_MD5_BLOCK_BITS, + RC4_HMAC_MD5_IV_BITS, + RC4_HMAC_MD5_MODE, RC4_HMAC_MD5_FLAGS, + ossl_prov_cipher_hw_rc4_hmac_md5( + RC4_HMAC_MD5_KEY_BITS + ), NULL); + return ctx; +} + +static void rc4_hmac_md5_freectx(void *vctx) +{ + PROV_RC4_HMAC_MD5_CTX *ctx = (PROV_RC4_HMAC_MD5_CTX *)vctx; + + ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx); + OPENSSL_clear_free(ctx, sizeof(*ctx)); +} + +static void *rc4_hmac_md5_dupctx(void *vctx) +{ + PROV_RC4_HMAC_MD5_CTX *ctx = vctx; + + if (ctx == NULL) + return NULL; + return OPENSSL_memdup(ctx, sizeof(*ctx)); +} + +static int rc4_hmac_md5_einit(void *ctx, const unsigned char *key, + size_t keylen, const unsigned char *iv, + size_t ivlen, const OSSL_PARAM params[]) +{ + if (!ossl_cipher_generic_einit(ctx, key, keylen, iv, ivlen, NULL)) + return 0; + return rc4_hmac_md5_set_ctx_params(ctx, params); +} + +static int rc4_hmac_md5_dinit(void *ctx, const unsigned char *key, + size_t keylen, const unsigned char *iv, + size_t ivlen, const OSSL_PARAM params[]) +{ + if (!ossl_cipher_generic_dinit(ctx, key, keylen, iv, ivlen, NULL)) + return 0; + return rc4_hmac_md5_set_ctx_params(ctx, params); +} + +static const OSSL_PARAM rc4_hmac_md5_known_gettable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_TLS1_AAD_PAD, NULL), + OSSL_PARAM_END +}; +const OSSL_PARAM *rc4_hmac_md5_gettable_ctx_params(ossl_unused void *cctx, + ossl_unused void *provctx) +{ + return rc4_hmac_md5_known_gettable_ctx_params; +} + +static int rc4_hmac_md5_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + PROV_RC4_HMAC_MD5_CTX *ctx = (PROV_RC4_HMAC_MD5_CTX *)vctx; + OSSL_PARAM *p; + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->base.keylen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->base.ivlen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TLS1_AAD_PAD); + if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->tls_aad_pad_sz)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + return 1; +} + +static const OSSL_PARAM rc4_hmac_md5_known_settable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL), + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TLS1_AAD, NULL, 0), + OSSL_PARAM_END +}; +const OSSL_PARAM *rc4_hmac_md5_settable_ctx_params(ossl_unused void *cctx, + ossl_unused void *provctx) +{ + return rc4_hmac_md5_known_settable_ctx_params; +} + +static int rc4_hmac_md5_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_RC4_HMAC_MD5_CTX *ctx = (PROV_RC4_HMAC_MD5_CTX *)vctx; + const OSSL_PARAM *p; + size_t sz; + + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_KEYLEN); + if (p != NULL) { + if (!OSSL_PARAM_get_size_t(p, &sz)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + if (ctx->base.keylen != sz) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + } + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_IVLEN); + if (p != NULL) { + if (!OSSL_PARAM_get_size_t(p, &sz)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + if (ctx->base.ivlen != sz) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); + return 0; + } + } + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TLS1_AAD); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_OCTET_STRING) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + sz = GET_HW(ctx)->tls_init(&ctx->base, p->data, p->data_size); + if (sz == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DATA); + return 0; + } + ctx->tls_aad_pad_sz = sz; + } + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_MAC_KEY); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_OCTET_STRING) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + GET_HW(ctx)->init_mackey(&ctx->base, p->data, p->data_size); + } + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_TLS_VERSION); + if (p != NULL) { + if (!OSSL_PARAM_get_uint(p, &ctx->base.tlsversion)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + } + + return 1; +} + +static int rc4_hmac_md5_get_params(OSSL_PARAM params[]) +{ + return ossl_cipher_generic_get_params(params, RC4_HMAC_MD5_MODE, + RC4_HMAC_MD5_FLAGS, + RC4_HMAC_MD5_KEY_BITS, + RC4_HMAC_MD5_BLOCK_BITS, + RC4_HMAC_MD5_IV_BITS); +} + +const OSSL_DISPATCH ossl_rc4_hmac_ossl_md5_functions[] = { + { OSSL_FUNC_CIPHER_NEWCTX, (void (*)(void))rc4_hmac_md5_newctx }, + { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))rc4_hmac_md5_freectx }, + { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void))rc4_hmac_md5_dupctx }, + { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))rc4_hmac_md5_einit }, + { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))rc4_hmac_md5_dinit }, + { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))rc4_hmac_md5_update }, + { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))rc4_hmac_md5_final }, + { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))rc4_hmac_md5_cipher }, + { OSSL_FUNC_CIPHER_GET_PARAMS, (void (*)(void))rc4_hmac_md5_get_params }, + { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, + (void (*)(void))rc4_hmac_md5_gettable_params }, + { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, + (void (*)(void))rc4_hmac_md5_get_ctx_params }, + { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, + (void (*)(void))rc4_hmac_md5_gettable_ctx_params }, + { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, + (void (*)(void))rc4_hmac_md5_set_ctx_params }, + { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, + (void (*)(void))rc4_hmac_md5_settable_ctx_params }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_rc4_hmac_md5.h b/crypto/openssl/providers/implementations/ciphers/cipher_rc4_hmac_md5.h new file mode 100644 index 000000000000..4a1d154a7ceb --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_rc4_hmac_md5.h @@ -0,0 +1,36 @@ +/* + * Copyright 2019-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 + */ + +#include <openssl/rc4.h> +#include <openssl/md5.h> +#include "prov/ciphercommon.h" + +typedef struct prov_rc4_hmac_md5_ctx_st { + PROV_CIPHER_CTX base; /* Must be first */ + union { + OSSL_UNION_ALIGN; + RC4_KEY ks; + } ks; + MD5_CTX head, tail, md; + size_t payload_length; + size_t tls_aad_pad_sz; +} PROV_RC4_HMAC_MD5_CTX; + +typedef struct prov_cipher_hw_rc4_hmac_md5_st { + PROV_CIPHER_HW base; /* Must be first */ + int (*tls_init)(PROV_CIPHER_CTX *ctx, unsigned char *aad, size_t aad_len); + void (*init_mackey)(PROV_CIPHER_CTX *ctx, const unsigned char *key, + size_t len); + +} PROV_CIPHER_HW_RC4_HMAC_MD5; + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_rc4_hmac_md5(size_t keybits); + +void rc4_md5_enc(RC4_KEY *key, const void *in0, void *out, + MD5_CTX *ctx, const void *inp, size_t blocks); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_rc4_hmac_md5_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_rc4_hmac_md5_hw.c new file mode 100644 index 000000000000..8cce02b1c5af --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_rc4_hmac_md5_hw.c @@ -0,0 +1,233 @@ +/* + * Copyright 2019-2020 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 + */ + +/* RC4_HMAC_MD5 cipher implementation */ + +/* + * MD5 and RC4 low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include "cipher_rc4_hmac_md5.h" + +#define NO_PAYLOAD_LENGTH ((size_t)-1) + +#if defined(RC4_ASM) \ + && defined(MD5_ASM) \ + && (defined(__x86_64) \ + || defined(__x86_64__) \ + || defined(_M_AMD64) \ + || defined(_M_X64)) +# define STITCHED_CALL +# define MOD 32 /* 32 is $MOD from rc4_md5-x86_64.pl */ +#else +# define rc4_off 0 +# define md5_off 0 +#endif + +static int cipher_hw_rc4_hmac_md5_initkey(PROV_CIPHER_CTX *bctx, + const uint8_t *key, size_t keylen) +{ + PROV_RC4_HMAC_MD5_CTX *ctx = (PROV_RC4_HMAC_MD5_CTX *)bctx; + + RC4_set_key(&ctx->ks.ks, keylen, key); + MD5_Init(&ctx->head); /* handy when benchmarking */ + ctx->tail = ctx->head; + ctx->md = ctx->head; + ctx->payload_length = NO_PAYLOAD_LENGTH; + bctx->removetlsfixed = MD5_DIGEST_LENGTH; + return 1; +} + +static int cipher_hw_rc4_hmac_md5_cipher(PROV_CIPHER_CTX *bctx, + unsigned char *out, + const unsigned char *in, size_t len) +{ + PROV_RC4_HMAC_MD5_CTX *ctx = (PROV_RC4_HMAC_MD5_CTX *)bctx; + RC4_KEY *ks = &ctx->ks.ks; + +#if defined(STITCHED_CALL) + size_t rc4_off = MOD - 1 - (ks->x & (MOD - 1)); + size_t md5_off = MD5_CBLOCK - ctx->md.num, blocks; + unsigned int l; +#endif + size_t plen = ctx->payload_length; + + if (plen != NO_PAYLOAD_LENGTH && len != (plen + MD5_DIGEST_LENGTH)) + return 0; + + if (ctx->base.enc) { + if (plen == NO_PAYLOAD_LENGTH) + plen = len; +#if defined(STITCHED_CALL) + /* cipher has to "fall behind" */ + if (rc4_off > md5_off) + md5_off += MD5_CBLOCK; + + if (plen > md5_off + && (blocks = (plen - md5_off) / MD5_CBLOCK) + && (OPENSSL_ia32cap_P[0] & (1 << 20)) == 0) { + MD5_Update(&ctx->md, in, md5_off); + RC4(ks, rc4_off, in, out); + + rc4_md5_enc(ks, in + rc4_off, out + rc4_off, + &ctx->md, in + md5_off, blocks); + blocks *= MD5_CBLOCK; + rc4_off += blocks; + md5_off += blocks; + ctx->md.Nh += blocks >> 29; + ctx->md.Nl += blocks <<= 3; + if (ctx->md.Nl < (unsigned int)blocks) + ctx->md.Nh++; + } else { + rc4_off = 0; + md5_off = 0; + } +#endif + MD5_Update(&ctx->md, in + md5_off, plen - md5_off); + + if (plen != len) { /* "TLS" mode of operation */ + if (in != out) + memcpy(out + rc4_off, in + rc4_off, plen - rc4_off); + + /* calculate HMAC and append it to payload */ + MD5_Final(out + plen, &ctx->md); + ctx->md = ctx->tail; + MD5_Update(&ctx->md, out + plen, MD5_DIGEST_LENGTH); + MD5_Final(out + plen, &ctx->md); + /* encrypt HMAC at once */ + RC4(ks, len - rc4_off, out + rc4_off, out + rc4_off); + } else { + RC4(ks, len - rc4_off, in + rc4_off, out + rc4_off); + } + } else { + unsigned char mac[MD5_DIGEST_LENGTH]; + +#if defined(STITCHED_CALL) + /* digest has to "fall behind" */ + if (md5_off > rc4_off) + rc4_off += 2 * MD5_CBLOCK; + else + rc4_off += MD5_CBLOCK; + + if (len > rc4_off + && (blocks = (len - rc4_off) / MD5_CBLOCK) + && (OPENSSL_ia32cap_P[0] & (1 << 20)) == 0) { + RC4(ks, rc4_off, in, out); + MD5_Update(&ctx->md, out, md5_off); + + rc4_md5_enc(ks, in + rc4_off, out + rc4_off, + &ctx->md, out + md5_off, blocks); + blocks *= MD5_CBLOCK; + rc4_off += blocks; + md5_off += blocks; + l = (ctx->md.Nl + (blocks << 3)) & 0xffffffffU; + if (l < ctx->md.Nl) + ctx->md.Nh++; + ctx->md.Nl = l; + ctx->md.Nh += blocks >> 29; + } else { + md5_off = 0; + rc4_off = 0; + } +#endif + /* decrypt HMAC at once */ + RC4(ks, len - rc4_off, in + rc4_off, out + rc4_off); + if (plen != NO_PAYLOAD_LENGTH) { + /* "TLS" mode of operation */ + MD5_Update(&ctx->md, out + md5_off, plen - md5_off); + + /* calculate HMAC and verify it */ + MD5_Final(mac, &ctx->md); + ctx->md = ctx->tail; + MD5_Update(&ctx->md, mac, MD5_DIGEST_LENGTH); + MD5_Final(mac, &ctx->md); + + if (CRYPTO_memcmp(out + plen, mac, MD5_DIGEST_LENGTH)) + return 0; + } else { + MD5_Update(&ctx->md, out + md5_off, len - md5_off); + } + } + + ctx->payload_length = NO_PAYLOAD_LENGTH; + + return 1; +} + +static int cipher_hw_rc4_hmac_md5_tls_init(PROV_CIPHER_CTX *bctx, + unsigned char *aad, size_t aad_len) +{ + PROV_RC4_HMAC_MD5_CTX *ctx = (PROV_RC4_HMAC_MD5_CTX *)bctx; + unsigned int len; + + if (aad_len != EVP_AEAD_TLS1_AAD_LEN) + return 0; + + len = aad[aad_len - 2] << 8 | aad[aad_len - 1]; + + if (!bctx->enc) { + if (len < MD5_DIGEST_LENGTH) + return 0; + len -= MD5_DIGEST_LENGTH; + aad[aad_len - 2] = len >> 8; + aad[aad_len - 1] = len; + } + ctx->payload_length = len; + ctx->md = ctx->head; + MD5_Update(&ctx->md, aad, aad_len); + + return MD5_DIGEST_LENGTH; +} + +static void cipher_hw_rc4_hmac_md5_init_mackey(PROV_CIPHER_CTX *bctx, + const unsigned char *key, + size_t len) +{ + PROV_RC4_HMAC_MD5_CTX *ctx = (PROV_RC4_HMAC_MD5_CTX *)bctx; + unsigned int i; + unsigned char hmac_key[64]; + + memset(hmac_key, 0, sizeof(hmac_key)); + + if (len > (int)sizeof(hmac_key)) { + MD5_Init(&ctx->head); + MD5_Update(&ctx->head, key, len); + MD5_Final(hmac_key, &ctx->head); + } else { + memcpy(hmac_key, key, len); + } + + for (i = 0; i < sizeof(hmac_key); i++) + hmac_key[i] ^= 0x36; /* ipad */ + MD5_Init(&ctx->head); + MD5_Update(&ctx->head, hmac_key, sizeof(hmac_key)); + + for (i = 0; i < sizeof(hmac_key); i++) + hmac_key[i] ^= 0x36 ^ 0x5c; /* opad */ + MD5_Init(&ctx->tail); + MD5_Update(&ctx->tail, hmac_key, sizeof(hmac_key)); + + OPENSSL_cleanse(hmac_key, sizeof(hmac_key)); +} + +static const PROV_CIPHER_HW_RC4_HMAC_MD5 rc4_hmac_md5_hw = { + { + cipher_hw_rc4_hmac_md5_initkey, + cipher_hw_rc4_hmac_md5_cipher + }, + cipher_hw_rc4_hmac_md5_tls_init, + cipher_hw_rc4_hmac_md5_init_mackey +}; + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_rc4_hmac_md5(size_t keybits) +{ + return (PROV_CIPHER_HW *)&rc4_hmac_md5_hw; +} diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_rc4_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_rc4_hw.c new file mode 100644 index 000000000000..09192b5d5e14 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_rc4_hw.c @@ -0,0 +1,44 @@ +/* + * Copyright 2019-2020 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 + */ + +/* + * RC4 low level APIs are deprecated for public use, but still ok for internal + * use. + */ +#include "internal/deprecated.h" + +#include "cipher_rc4.h" + +static int cipher_hw_rc4_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + PROV_RC4_CTX *rctx = (PROV_RC4_CTX *)ctx; + + RC4_set_key(&rctx->ks.ks, keylen, key); + return 1; +} + +static int cipher_hw_rc4_cipher(PROV_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t len) +{ + PROV_RC4_CTX *rctx = (PROV_RC4_CTX *)ctx; + + RC4(&rctx->ks.ks, len, in, out); + return 1; +} + +static const PROV_CIPHER_HW rc4_hw = { + cipher_hw_rc4_initkey, + cipher_hw_rc4_cipher +}; +const PROV_CIPHER_HW *ossl_prov_cipher_hw_rc4(size_t keybits) +{ + return &rc4_hw; +} + diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_rc5.c b/crypto/openssl/providers/implementations/ciphers/cipher_rc5.c new file mode 100644 index 000000000000..6fa491d83ef3 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_rc5.c @@ -0,0 +1,186 @@ +/* + * Copyright 2019-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 + */ + +/* Dispatch functions for RC5 cipher modes ecb, cbc, ofb, cfb */ + +/* + * RC5 low level APIs are deprecated for public use, but still ok for internal + * use. + */ +#include "internal/deprecated.h" + +#include <openssl/proverr.h> +#include "cipher_rc5.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" + +#define RC5_FLAGS PROV_CIPHER_FLAG_VARIABLE_LENGTH + +static OSSL_FUNC_cipher_encrypt_init_fn rc5_einit; +static OSSL_FUNC_cipher_decrypt_init_fn rc5_dinit; +static OSSL_FUNC_cipher_freectx_fn rc5_freectx; +static OSSL_FUNC_cipher_dupctx_fn rc5_dupctx; +OSSL_FUNC_cipher_gettable_ctx_params_fn rc5_gettable_ctx_params; +OSSL_FUNC_cipher_settable_ctx_params_fn rc5_settable_ctx_params; +static OSSL_FUNC_cipher_set_ctx_params_fn rc5_set_ctx_params; + +static void rc5_freectx(void *vctx) +{ + PROV_RC5_CTX *ctx = (PROV_RC5_CTX *)vctx; + + ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx); + OPENSSL_clear_free(ctx, sizeof(*ctx)); +} + +static void *rc5_dupctx(void *ctx) +{ + PROV_RC5_CTX *in = (PROV_RC5_CTX *)ctx; + PROV_RC5_CTX *ret; + + if (!ossl_prov_is_running()) + return NULL; + + ret = OPENSSL_malloc(sizeof(*ret)); + if (ret == NULL) + return NULL; + *ret = *in; + + return ret; +} + +static int rc5_einit(void *ctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + if (!ossl_cipher_generic_einit(ctx, key, keylen, iv, ivlen, NULL)) + return 0; + return rc5_set_ctx_params(ctx, params); +} + +static int rc5_dinit(void *ctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + if (!ossl_cipher_generic_dinit(ctx, key, keylen, iv, ivlen, NULL)) + return 0; + return rc5_set_ctx_params(ctx, params); +} + +static int rc5_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_RC5_CTX *ctx = (PROV_RC5_CTX *)vctx; + const OSSL_PARAM *p; + + if (ossl_param_is_empty(params)) + return 1; + + if (!ossl_cipher_var_keylen_set_ctx_params(vctx, params)) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_ROUNDS); + if (p != NULL) { + unsigned int rounds; + + if (!OSSL_PARAM_get_uint(p, &rounds)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + if (rounds != RC5_8_ROUNDS + && rounds != RC5_12_ROUNDS + && rounds != RC5_16_ROUNDS) { + ERR_raise(ERR_LIB_PROV, PROV_R_UNSUPPORTED_NUMBER_OF_ROUNDS); + return 0; + } + ctx->rounds = rounds; + } + return 1; +} + +CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_START(rc5) + OSSL_PARAM_uint(OSSL_CIPHER_PARAM_ROUNDS, NULL), +CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_END(rc5) + +CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_START(rc5) + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), + OSSL_PARAM_uint(OSSL_CIPHER_PARAM_ROUNDS, NULL), +CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_END(rc5) + + +static int rc5_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + PROV_RC5_CTX *ctx = (PROV_RC5_CTX *)vctx; + OSSL_PARAM *p; + + if (!ossl_cipher_generic_get_ctx_params(vctx, params)) + return 0; + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_ROUNDS); + if (p != NULL && !OSSL_PARAM_set_uint(p, ctx->rounds)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + return 1; +} + +#define IMPLEMENT_cipher(alg, UCALG, lcmode, UCMODE, flags, kbits, \ + blkbits, ivbits, typ) \ +static OSSL_FUNC_cipher_get_params_fn alg##_##kbits##_##lcmode##_get_params; \ +static int alg##_##kbits##_##lcmode##_get_params(OSSL_PARAM params[]) \ +{ \ + return ossl_cipher_generic_get_params(params, EVP_CIPH_##UCMODE##_MODE, \ + flags, kbits, blkbits, ivbits); \ +} \ +static OSSL_FUNC_cipher_newctx_fn alg##_##kbits##_##lcmode##_newctx; \ +static void *alg##_##kbits##_##lcmode##_newctx(void *provctx) \ +{ \ + PROV_##UCALG##_CTX *ctx; \ + if (!ossl_prov_is_running()) \ + return NULL; \ + ctx = OPENSSL_zalloc(sizeof(*ctx)); \ + if (ctx != NULL) { \ + ossl_cipher_generic_initkey(ctx, kbits, blkbits, ivbits, \ + EVP_CIPH_##UCMODE##_MODE, flags, \ + ossl_prov_cipher_hw_##alg##_##lcmode(kbits),\ + NULL); \ + ctx->rounds = RC5_12_ROUNDS; \ + } \ + return ctx; \ +} \ +const OSSL_DISPATCH ossl_##alg##kbits##lcmode##_functions[] = { \ + { OSSL_FUNC_CIPHER_NEWCTX, \ + (void (*)(void)) alg##_##kbits##_##lcmode##_newctx }, \ + { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void)) alg##_freectx }, \ + { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void)) alg##_dupctx }, \ + { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))rc5_einit }, \ + { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))rc5_dinit }, \ + { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))ossl_cipher_generic_##typ##_update },\ + { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))ossl_cipher_generic_##typ##_final }, \ + { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))ossl_cipher_generic_cipher }, \ + { OSSL_FUNC_CIPHER_GET_PARAMS, \ + (void (*)(void)) alg##_##kbits##_##lcmode##_get_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \ + (void (*)(void))ossl_cipher_generic_gettable_params }, \ + { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, \ + (void (*)(void))rc5_get_ctx_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \ + (void (*)(void))rc5_gettable_ctx_params }, \ + { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \ + (void (*)(void))rc5_set_ctx_params }, \ + { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \ + (void (*)(void))rc5_settable_ctx_params }, \ + OSSL_DISPATCH_END \ +}; + +/* ossl_rc5128ecb_functions */ +IMPLEMENT_cipher(rc5, RC5, ecb, ECB, RC5_FLAGS, 128, 64, 0, block) +/* ossl_rc5128cbc_functions */ +IMPLEMENT_cipher(rc5, RC5, cbc, CBC, RC5_FLAGS, 128, 64, 64, block) +/* ossl_rc5128ofb64_functions */ +IMPLEMENT_cipher(rc5, RC5, ofb64, OFB, RC5_FLAGS, 128, 8, 64, stream) +/* ossl_rc5128cfb64_functions */ +IMPLEMENT_cipher(rc5, RC5, cfb64, CFB, RC5_FLAGS, 128, 8, 64, stream) diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_rc5.h b/crypto/openssl/providers/implementations/ciphers/cipher_rc5.h new file mode 100644 index 000000000000..421632f2ea1e --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_rc5.h @@ -0,0 +1,25 @@ +/* + * Copyright 2019-2020 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 + */ + +#include <openssl/rc5.h> +#include "prov/ciphercommon.h" + +typedef struct prov_rc5_ctx_st { + PROV_CIPHER_CTX base; /* Must be first */ + union { + OSSL_UNION_ALIGN; + RC5_32_KEY ks; /* key schedule */ + } ks; + unsigned int rounds; /* number of rounds */ +} PROV_RC5_CTX; + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_rc5_cbc(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_rc5_ecb(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_rc5_ofb64(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_rc5_cfb64(size_t keybits); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_rc5_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_rc5_hw.c new file mode 100644 index 000000000000..898bd383f95a --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_rc5_hw.c @@ -0,0 +1,41 @@ +/* + * Copyright 2019-2020 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 + */ + +/* + * RC5 low level APIs are deprecated for public use, but still ok for internal + * use. + */ +#include "internal/deprecated.h" + +#include "cipher_rc5.h" + +static int cipher_hw_rc5_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + PROV_RC5_CTX *rctx = (PROV_RC5_CTX *)ctx; + + return RC5_32_set_key(&rctx->ks.ks, keylen, key, rctx->rounds); +} + +# define PROV_CIPHER_HW_rc5_mode(mode, UCMODE) \ +IMPLEMENT_CIPHER_HW_##UCMODE(mode, rc5, PROV_RC5_CTX, RC5_32_KEY, \ + RC5_32_##mode) \ +static const PROV_CIPHER_HW rc5_##mode = { \ + cipher_hw_rc5_initkey, \ + cipher_hw_rc5_##mode##_cipher \ +}; \ +const PROV_CIPHER_HW *ossl_prov_cipher_hw_rc5_##mode(size_t keybits) \ +{ \ + return &rc5_##mode; \ +} + +PROV_CIPHER_HW_rc5_mode(cbc, CBC) +PROV_CIPHER_HW_rc5_mode(ecb, ECB) +PROV_CIPHER_HW_rc5_mode(ofb64, OFB) +PROV_CIPHER_HW_rc5_mode(cfb64, CFB) diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_seed.c b/crypto/openssl/providers/implementations/ciphers/cipher_seed.c new file mode 100644 index 000000000000..3644cb5e2252 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_seed.c @@ -0,0 +1,56 @@ +/* + * Copyright 2019-2020 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 + */ + +/* Dispatch functions for Seed cipher modes ecb, cbc, ofb, cfb */ + +/* + * SEED low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include "cipher_seed.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" + +static OSSL_FUNC_cipher_freectx_fn seed_freectx; +static OSSL_FUNC_cipher_dupctx_fn seed_dupctx; + +static void seed_freectx(void *vctx) +{ + PROV_SEED_CTX *ctx = (PROV_SEED_CTX *)vctx; + + ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx); + OPENSSL_clear_free(ctx, sizeof(*ctx)); +} + +static void *seed_dupctx(void *ctx) +{ + PROV_SEED_CTX *in = (PROV_SEED_CTX *)ctx; + PROV_SEED_CTX *ret; + + if (!ossl_prov_is_running()) + return NULL; + + ret = OPENSSL_malloc(sizeof(*ret)); + if (ret == NULL) + return NULL; + *ret = *in; + + return ret; +} + +/* ossl_seed128ecb_functions */ +IMPLEMENT_generic_cipher(seed, SEED, ecb, ECB, 0, 128, 128, 0, block) +/* ossl_seed128cbc_functions */ +IMPLEMENT_generic_cipher(seed, SEED, cbc, CBC, 0, 128, 128, 128, block) +/* ossl_seed128ofb128_functions */ +IMPLEMENT_generic_cipher(seed, SEED, ofb128, OFB, 0, 128, 8, 128, stream) +/* ossl_seed128cfb128_functions */ +IMPLEMENT_generic_cipher(seed, SEED, cfb128, CFB, 0, 128, 8, 128, stream) diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_seed.h b/crypto/openssl/providers/implementations/ciphers/cipher_seed.h new file mode 100644 index 000000000000..9006a9183b55 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_seed.h @@ -0,0 +1,24 @@ +/* + * Copyright 2019-2020 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 + */ + +#include <openssl/seed.h> +#include "prov/ciphercommon.h" + +typedef struct prov_seed_ctx_st { + PROV_CIPHER_CTX base; /* Must be first */ + union { + OSSL_UNION_ALIGN; + SEED_KEY_SCHEDULE ks; + } ks; +} PROV_SEED_CTX; + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_seed_cbc(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_seed_ecb(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_seed_ofb128(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_seed_cfb128(size_t keybits); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_seed_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_seed_hw.c new file mode 100644 index 000000000000..2d1dba92bc73 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_seed_hw.c @@ -0,0 +1,42 @@ +/* + * Copyright 2019-2020 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 + */ + +/* + * SEED low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include "cipher_seed.h" + +static int cipher_hw_seed_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + PROV_SEED_CTX *sctx = (PROV_SEED_CTX *)ctx; + + SEED_set_key(key, &(sctx->ks.ks)); + return 1; +} + +# define PROV_CIPHER_HW_seed_mode(mode, UCMODE) \ +IMPLEMENT_CIPHER_HW_##UCMODE(mode, seed, PROV_SEED_CTX, SEED_KEY_SCHEDULE, \ + SEED_##mode) \ +static const PROV_CIPHER_HW seed_##mode = { \ + cipher_hw_seed_initkey, \ + cipher_hw_seed_##mode##_cipher \ +}; \ +const PROV_CIPHER_HW *ossl_prov_cipher_hw_seed_##mode(size_t keybits) \ +{ \ + return &seed_##mode; \ +} + +PROV_CIPHER_HW_seed_mode(cbc, CBC) +PROV_CIPHER_HW_seed_mode(ecb, ECB) +PROV_CIPHER_HW_seed_mode(ofb128, OFB) +PROV_CIPHER_HW_seed_mode(cfb128, CFB) diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4.c b/crypto/openssl/providers/implementations/ciphers/cipher_sm4.c new file mode 100644 index 000000000000..863c9997f5aa --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4.c @@ -0,0 +1,52 @@ +/* + * Copyright 2019-2020 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 + */ + +/* Dispatch functions for cast cipher modes ecb, cbc, ofb, cfb */ + +#include "cipher_sm4.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" + +static OSSL_FUNC_cipher_freectx_fn sm4_freectx; +static OSSL_FUNC_cipher_dupctx_fn sm4_dupctx; + +static void sm4_freectx(void *vctx) +{ + PROV_SM4_CTX *ctx = (PROV_SM4_CTX *)vctx; + + ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx); + OPENSSL_clear_free(ctx, sizeof(*ctx)); +} + +static void *sm4_dupctx(void *ctx) +{ + PROV_SM4_CTX *in = (PROV_SM4_CTX *)ctx; + PROV_SM4_CTX *ret; + + if (!ossl_prov_is_running()) + return NULL; + + ret = OPENSSL_malloc(sizeof(*ret)); + if (ret == NULL) + return NULL; + in->base.hw->copyctx(&ret->base, &in->base); + + return ret; +} + +/* ossl_sm4128ecb_functions */ +IMPLEMENT_generic_cipher(sm4, SM4, ecb, ECB, 0, 128, 128, 0, block) +/* ossl_sm4128cbc_functions */ +IMPLEMENT_generic_cipher(sm4, SM4, cbc, CBC, 0, 128, 128, 128, block) +/* ossl_sm4128ctr_functions */ +IMPLEMENT_generic_cipher(sm4, SM4, ctr, CTR, 0, 128, 8, 128, stream) +/* ossl_sm4128ofb128_functions */ +IMPLEMENT_generic_cipher(sm4, SM4, ofb128, OFB, 0, 128, 8, 128, stream) +/* ossl_sm4128cfb128_functions */ +IMPLEMENT_generic_cipher(sm4, SM4, cfb128, CFB, 0, 128, 8, 128, stream) diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4.h b/crypto/openssl/providers/implementations/ciphers/cipher_sm4.h new file mode 100644 index 000000000000..9ab49e3279f1 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4.h @@ -0,0 +1,26 @@ +/* + * Copyright 2019-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 + */ + +#include "prov/ciphercommon.h" +#include "crypto/sm4.h" +#include "crypto/sm4_platform.h" + +typedef struct prov_cast_ctx_st { + PROV_CIPHER_CTX base; /* Must be first */ + union { + OSSL_UNION_ALIGN; + SM4_KEY ks; + } ks; +} PROV_SM4_CTX; + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_sm4_cbc(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_sm4_ecb(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_sm4_ctr(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_sm4_ofb128(size_t keybits); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_sm4_cfb128(size_t keybits); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4_ccm.c b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_ccm.c new file mode 100644 index 000000000000..3af84d85b7dc --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_ccm.c @@ -0,0 +1,54 @@ +/* + * Copyright 2021-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 + */ + +/* Dispatch functions for SM4 CCM mode */ + +#include "cipher_sm4_ccm.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" + +static OSSL_FUNC_cipher_freectx_fn sm4_ccm_freectx; + +static void *sm4_ccm_newctx(void *provctx, size_t keybits) +{ + PROV_SM4_CCM_CTX *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + ctx = OPENSSL_zalloc(sizeof(*ctx)); + if (ctx != NULL) + ossl_ccm_initctx(&ctx->base, keybits, ossl_prov_sm4_hw_ccm(keybits)); + return ctx; +} + +static void *sm4_ccm_dupctx(void *provctx) +{ + PROV_SM4_CCM_CTX *ctx = provctx; + PROV_SM4_CCM_CTX *dctx = NULL; + + if (ctx == NULL) + return NULL; + + dctx = OPENSSL_memdup(ctx, sizeof(*ctx)); + if (dctx != NULL && dctx->base.ccm_ctx.key != NULL) + dctx->base.ccm_ctx.key = &dctx->ks.ks; + + return dctx; +} + +static void sm4_ccm_freectx(void *vctx) +{ + PROV_SM4_CCM_CTX *ctx = (PROV_SM4_CCM_CTX *)vctx; + + OPENSSL_clear_free(ctx, sizeof(*ctx)); +} + +/* sm4128ccm functions */ +IMPLEMENT_aead_cipher(sm4, ccm, CCM, AEAD_FLAGS, 128, 8, 96); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4_ccm.h b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_ccm.h new file mode 100644 index 000000000000..561437993a4a --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_ccm.h @@ -0,0 +1,23 @@ +/* + * Copyright 2021 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 + */ + +#include "crypto/sm4.h" +#include "prov/ciphercommon.h" +#include "prov/ciphercommon_ccm.h" +#include "crypto/sm4_platform.h" + +typedef struct prov_sm4_ccm_ctx_st { + PROV_CCM_CTX base; /* Must be first */ + union { + OSSL_UNION_ALIGN; + SM4_KEY ks; + } ks; /* SM4 key schedule to use */ +} PROV_SM4_CCM_CTX; + +const PROV_CCM_HW *ossl_prov_sm4_hw_ccm(size_t keylen); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4_ccm_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_ccm_hw.c new file mode 100644 index 000000000000..1c1d60494ab6 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_ccm_hw.c @@ -0,0 +1,69 @@ +/* + * Copyright 2021-2024 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 + */ + +/*- + * Generic support for SM4 CCM. + */ + +#include "cipher_sm4_ccm.h" +#include "crypto/sm4_platform.h" + +#define SM4_HW_CCM_SET_KEY_FN(fn_set_enc_key, fn_blk, fn_ccm_enc, fn_ccm_dec) \ + fn_set_enc_key(key, &actx->ks.ks); \ + CRYPTO_ccm128_init(&ctx->ccm_ctx, ctx->m, ctx->l, &actx->ks.ks, \ + (block128_f)fn_blk); \ + ctx->str = ctx->enc ? (ccm128_f)fn_ccm_enc : (ccm128_f)fn_ccm_dec; \ + ctx->key_set = 1; + +static int ccm_sm4_initkey(PROV_CCM_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + PROV_SM4_CCM_CTX *actx = (PROV_SM4_CCM_CTX *)ctx; + +#ifdef HWSM4_CAPABLE + if (HWSM4_CAPABLE) { + SM4_HW_CCM_SET_KEY_FN(HWSM4_set_encrypt_key, HWSM4_encrypt, NULL, NULL); + } else +#endif /* HWSM4_CAPABLE */ + +#ifdef VPSM4_EX_CAPABLE + if (VPSM4_EX_CAPABLE) { + SM4_HW_CCM_SET_KEY_FN(vpsm4_ex_set_encrypt_key, vpsm4_ex_encrypt, NULL, + NULL); + } else +#endif /* VPSM4_EX_CAPABLE */ + +#ifdef VPSM4_CAPABLE + if (VPSM4_CAPABLE) { + SM4_HW_CCM_SET_KEY_FN(vpsm4_set_encrypt_key, vpsm4_encrypt, NULL, NULL); + } else +#endif /* VPSM4_CAPABLE */ + { + SM4_HW_CCM_SET_KEY_FN(ossl_sm4_set_key, ossl_sm4_encrypt, NULL, NULL); + } + return 1; +} + +static const PROV_CCM_HW ccm_sm4 = { + ccm_sm4_initkey, + ossl_ccm_generic_setiv, + ossl_ccm_generic_setaad, + ossl_ccm_generic_auth_encrypt, + ossl_ccm_generic_auth_decrypt, + ossl_ccm_generic_gettag +}; + +#if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 +# include "cipher_sm4_ccm_hw_rv64i.inc" +#else +const PROV_CCM_HW *ossl_prov_sm4_hw_ccm(size_t keybits) +{ + return &ccm_sm4; +} +#endif diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4_ccm_hw_rv64i.inc b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_ccm_hw_rv64i.inc new file mode 100644 index 000000000000..c20c9f688ae8 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_ccm_hw_rv64i.inc @@ -0,0 +1,41 @@ +/* + * Copyright 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 + */ + +/*- + * RV64I ZVKSED support for SM4 CCM. + * This file is included by cipher_sm4_ccm_hw.c + */ + +static int rv64i_zvksed_sm4_ccm_initkey(PROV_CCM_CTX *ctx, + const unsigned char *key, + size_t keylen) +{ + PROV_SM4_CCM_CTX *actx = (PROV_SM4_CCM_CTX *)ctx; + + SM4_HW_CCM_SET_KEY_FN(rv64i_zvksed_sm4_set_encrypt_key, + rv64i_zvksed_sm4_encrypt, NULL, NULL); + return 1; +} + +static const PROV_CCM_HW rv64i_zvksed_sm4_ccm = { + rv64i_zvksed_sm4_ccm_initkey, + ossl_ccm_generic_setiv, + ossl_ccm_generic_setaad, + ossl_ccm_generic_auth_encrypt, + ossl_ccm_generic_auth_decrypt, + ossl_ccm_generic_gettag +}; + +const PROV_CCM_HW *ossl_prov_sm4_hw_ccm(size_t keybits) +{ + if (RISCV_HAS_ZVKB_AND_ZVKSED() && riscv_vlen() >= 128) + return &rv64i_zvksed_sm4_ccm; + else + return &ccm_sm4; +} diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4_gcm.c b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_gcm.c new file mode 100644 index 000000000000..1128f659393e --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_gcm.c @@ -0,0 +1,55 @@ +/* + * Copyright 2021-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 + */ + +/* Dispatch functions for SM4 GCM mode */ + +#include "cipher_sm4_gcm.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" + +static OSSL_FUNC_cipher_freectx_fn sm4_gcm_freectx; + +static void *sm4_gcm_newctx(void *provctx, size_t keybits) +{ + PROV_SM4_GCM_CTX *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + ctx = OPENSSL_zalloc(sizeof(*ctx)); + if (ctx != NULL) + ossl_gcm_initctx(provctx, &ctx->base, keybits, + ossl_prov_sm4_hw_gcm(keybits)); + return ctx; +} + +static void *sm4_gcm_dupctx(void *provctx) +{ + PROV_SM4_GCM_CTX *ctx = provctx; + PROV_SM4_GCM_CTX *dctx = NULL; + + if (ctx == NULL) + return NULL; + + dctx = OPENSSL_memdup(ctx, sizeof(*ctx)); + if (dctx != NULL && dctx->base.gcm.key != NULL) + dctx->base.gcm.key = &dctx->ks.ks; + + return dctx; +} + +static void sm4_gcm_freectx(void *vctx) +{ + PROV_SM4_GCM_CTX *ctx = (PROV_SM4_GCM_CTX *)vctx; + + OPENSSL_clear_free(ctx, sizeof(*ctx)); +} + +/* ossl_sm4128gcm_functions */ +IMPLEMENT_aead_cipher(sm4, gcm, GCM, AEAD_FLAGS, 128, 8, 96); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4_gcm.h b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_gcm.h new file mode 100644 index 000000000000..2b6b5f3ece74 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_gcm.h @@ -0,0 +1,22 @@ +/* + * Copyright 2021 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 + */ + +#include "crypto/sm4.h" +#include "prov/ciphercommon.h" +#include "prov/ciphercommon_gcm.h" + +typedef struct prov_sm4_gcm_ctx_st { + PROV_GCM_CTX base; /* must be first entry in struct */ + union { + OSSL_UNION_ALIGN; + SM4_KEY ks; + } ks; +} PROV_SM4_GCM_CTX; + +const PROV_GCM_HW *ossl_prov_sm4_hw_gcm(size_t keybits); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4_gcm_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_gcm_hw.c new file mode 100644 index 000000000000..c1e354be45ba --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_gcm_hw.c @@ -0,0 +1,99 @@ +/* + * Copyright 2021-2024 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 + */ + +/*- + * Generic support for SM4 GCM. + */ + +#include "cipher_sm4_gcm.h" +#include "crypto/sm4_platform.h" + +# define SM4_GCM_HW_SET_KEY_CTR_FN(ks, fn_set_enc_key, fn_block, fn_ctr) \ + fn_set_enc_key(key, ks); \ + CRYPTO_gcm128_init(&ctx->gcm, ks, (block128_f)fn_block); \ + ctx->ctr = (ctr128_f)fn_ctr; \ + ctx->key_set = 1; + +static int sm4_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key, + size_t keylen) +{ + PROV_SM4_GCM_CTX *actx = (PROV_SM4_GCM_CTX *)ctx; + SM4_KEY *ks = &actx->ks.ks; + +# ifdef HWSM4_CAPABLE + if (HWSM4_CAPABLE) { +# ifdef HWSM4_ctr32_encrypt_blocks + SM4_GCM_HW_SET_KEY_CTR_FN(ks, HWSM4_set_encrypt_key, HWSM4_encrypt, + HWSM4_ctr32_encrypt_blocks); +# else /* HWSM4_ctr32_encrypt_blocks */ + SM4_GCM_HW_SET_KEY_CTR_FN(ks, HWSM4_set_encrypt_key, HWSM4_encrypt, NULL); +# endif + } else +# endif /* HWSM4_CAPABLE */ + +#ifdef VPSM4_EX_CAPABLE + if (VPSM4_EX_CAPABLE) { + SM4_GCM_HW_SET_KEY_CTR_FN(ks, vpsm4_ex_set_encrypt_key, vpsm4_ex_encrypt, + vpsm4_ex_ctr32_encrypt_blocks); + } else +#endif /* VPSM4_EX_CAPABLE */ + +# ifdef VPSM4_CAPABLE + if (VPSM4_CAPABLE) { + SM4_GCM_HW_SET_KEY_CTR_FN(ks, vpsm4_set_encrypt_key, vpsm4_encrypt, + vpsm4_ctr32_encrypt_blocks); + } else +# endif /* VPSM4_CAPABLE */ + { + SM4_GCM_HW_SET_KEY_CTR_FN(ks, ossl_sm4_set_key, ossl_sm4_encrypt, NULL); + } + + return 1; +} + +static int hw_gcm_cipher_update(PROV_GCM_CTX *ctx, const unsigned char *in, + size_t len, unsigned char *out) +{ + if (ctx->enc) { + if (ctx->ctr != NULL) { + if (CRYPTO_gcm128_encrypt_ctr32(&ctx->gcm, in, out, len, ctx->ctr)) + return 0; + } else { + if (CRYPTO_gcm128_encrypt(&ctx->gcm, in, out, len)) + return 0; + } + } else { + if (ctx->ctr != NULL) { + if (CRYPTO_gcm128_decrypt_ctr32(&ctx->gcm, in, out, len, ctx->ctr)) + return 0; + } else { + if (CRYPTO_gcm128_decrypt(&ctx->gcm, in, out, len)) + return 0; + } + } + return 1; +} + +static const PROV_GCM_HW sm4_gcm = { + sm4_gcm_initkey, + ossl_gcm_setiv, + ossl_gcm_aad_update, + hw_gcm_cipher_update, + ossl_gcm_cipher_final, + ossl_gcm_one_shot +}; + +#if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 +# include "cipher_sm4_gcm_hw_rv64i.inc" +#else +const PROV_GCM_HW *ossl_prov_sm4_hw_gcm(size_t keybits) +{ + return &sm4_gcm; +} +#endif diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4_gcm_hw_rv64i.inc b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_gcm_hw_rv64i.inc new file mode 100644 index 000000000000..109d13b438e5 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_gcm_hw_rv64i.inc @@ -0,0 +1,42 @@ +/* + * Copyright 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 + */ + +/*- + * RISC-V 64 ZVKSED support for SM4 GCM. + * This file is included by cipher_sm4_gcm_hw.c + */ + +static int rv64i_zvksed_sm4_gcm_initkey(PROV_GCM_CTX *ctx, + const unsigned char *key, + size_t keylen) +{ + PROV_SM4_GCM_CTX *actx = (PROV_SM4_GCM_CTX *)ctx; + SM4_KEY *ks = &actx->ks.ks; + + SM4_GCM_HW_SET_KEY_CTR_FN(ks, rv64i_zvksed_sm4_set_encrypt_key, + rv64i_zvksed_sm4_encrypt, NULL); + return 1; +} + +static const PROV_GCM_HW rv64i_zvksed_sm4_gcm = { + rv64i_zvksed_sm4_gcm_initkey, + ossl_gcm_setiv, + ossl_gcm_aad_update, + hw_gcm_cipher_update, + ossl_gcm_cipher_final, + ossl_gcm_one_shot +}; + +const PROV_GCM_HW *ossl_prov_sm4_hw_gcm(size_t keybits) +{ + if (RISCV_HAS_ZVKB_AND_ZVKSED() && riscv_vlen() >= 128) + return &rv64i_zvksed_sm4_gcm; + else + return &sm4_gcm; +} diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_hw.c new file mode 100644 index 000000000000..05a83843eb49 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_hw.c @@ -0,0 +1,149 @@ +/* + * Copyright 2019-2024 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 + */ + +#include "cipher_sm4.h" + +static int cipher_hw_sm4_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + PROV_SM4_CTX *sctx = (PROV_SM4_CTX *)ctx; + SM4_KEY *ks = &sctx->ks.ks; + + ctx->ks = ks; + if (ctx->enc + || (ctx->mode != EVP_CIPH_ECB_MODE + && ctx->mode != EVP_CIPH_CBC_MODE)) { +#ifdef HWSM4_CAPABLE + if (HWSM4_CAPABLE) { + HWSM4_set_encrypt_key(key, ks); + ctx->block = (block128_f)HWSM4_encrypt; + ctx->stream.cbc = NULL; +#ifdef HWSM4_cbc_encrypt + if (ctx->mode == EVP_CIPH_CBC_MODE) + ctx->stream.cbc = (cbc128_f)HWSM4_cbc_encrypt; + else +#endif +#ifdef HWSM4_ecb_encrypt + if (ctx->mode == EVP_CIPH_ECB_MODE) + ctx->stream.ecb = (ecb128_f)HWSM4_ecb_encrypt; + else +#endif +#ifdef HWSM4_ctr32_encrypt_blocks + if (ctx->mode == EVP_CIPH_CTR_MODE) + ctx->stream.ctr = (ctr128_f)HWSM4_ctr32_encrypt_blocks; + else +#endif + (void)0; /* terminate potentially open 'else' */ + } else +#endif +#ifdef VPSM4_EX_CAPABLE + if (VPSM4_EX_CAPABLE) { + vpsm4_ex_set_encrypt_key(key, ks); + ctx->block = (block128_f)vpsm4_ex_encrypt; + ctx->stream.cbc = NULL; + if (ctx->mode == EVP_CIPH_CBC_MODE) + ctx->stream.cbc = (cbc128_f)vpsm4_ex_cbc_encrypt; + else if (ctx->mode == EVP_CIPH_ECB_MODE) + ctx->stream.ecb = (ecb128_f)vpsm4_ex_ecb_encrypt; + else if (ctx->mode == EVP_CIPH_CTR_MODE) + ctx->stream.ctr = (ctr128_f)vpsm4_ex_ctr32_encrypt_blocks; + } else +#endif +#ifdef VPSM4_CAPABLE + if (VPSM4_CAPABLE) { + vpsm4_set_encrypt_key(key, ks); + ctx->block = (block128_f)vpsm4_encrypt; + ctx->stream.cbc = NULL; + if (ctx->mode == EVP_CIPH_CBC_MODE) + ctx->stream.cbc = (cbc128_f)vpsm4_cbc_encrypt; + else if (ctx->mode == EVP_CIPH_ECB_MODE) + ctx->stream.ecb = (ecb128_f)vpsm4_ecb_encrypt; + else if (ctx->mode == EVP_CIPH_CTR_MODE) + ctx->stream.ctr = (ctr128_f)vpsm4_ctr32_encrypt_blocks; + } else +#endif + { + ossl_sm4_set_key(key, ks); + ctx->block = (block128_f)ossl_sm4_encrypt; + } + } else { +#ifdef HWSM4_CAPABLE + if (HWSM4_CAPABLE) { + HWSM4_set_decrypt_key(key, ks); + ctx->block = (block128_f)HWSM4_decrypt; + ctx->stream.cbc = NULL; +#ifdef HWSM4_cbc_encrypt + if (ctx->mode == EVP_CIPH_CBC_MODE) + ctx->stream.cbc = (cbc128_f)HWSM4_cbc_encrypt; +#endif +#ifdef HWSM4_ecb_encrypt + if (ctx->mode == EVP_CIPH_ECB_MODE) + ctx->stream.ecb = (ecb128_f)HWSM4_ecb_encrypt; +#endif + } else +#endif +#ifdef VPSM4_EX_CAPABLE + if (VPSM4_EX_CAPABLE) { + vpsm4_ex_set_decrypt_key(key, ks); + ctx->block = (block128_f)vpsm4_ex_decrypt; + ctx->stream.cbc = NULL; + if (ctx->mode == EVP_CIPH_CBC_MODE) + ctx->stream.cbc = (cbc128_f)vpsm4_ex_cbc_encrypt; + else if (ctx->mode == EVP_CIPH_ECB_MODE) + ctx->stream.ecb = (ecb128_f)vpsm4_ex_ecb_encrypt; + } else +#endif +#ifdef VPSM4_CAPABLE + if (VPSM4_CAPABLE) { + vpsm4_set_decrypt_key(key, ks); + ctx->block = (block128_f)vpsm4_decrypt; + ctx->stream.cbc = NULL; + if (ctx->mode == EVP_CIPH_CBC_MODE) + ctx->stream.cbc = (cbc128_f)vpsm4_cbc_encrypt; + else if (ctx->mode == EVP_CIPH_ECB_MODE) + ctx->stream.ecb = (ecb128_f)vpsm4_ecb_encrypt; + } else +#endif + { + ossl_sm4_set_key(key, ks); + ctx->block = (block128_f)ossl_sm4_decrypt; + } + } + + return 1; +} + +IMPLEMENT_CIPHER_HW_COPYCTX(cipher_hw_sm4_copyctx, PROV_SM4_CTX) + +# define PROV_CIPHER_HW_sm4_mode(mode) \ +static const PROV_CIPHER_HW sm4_##mode = { \ + cipher_hw_sm4_initkey, \ + ossl_cipher_hw_generic_##mode, \ + cipher_hw_sm4_copyctx \ +}; \ +PROV_CIPHER_HW_declare(mode) \ +const PROV_CIPHER_HW *ossl_prov_cipher_hw_sm4_##mode(size_t keybits) \ +{ \ + PROV_CIPHER_HW_select(mode) \ + return &sm4_##mode; \ +} + +#if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 +# include "cipher_sm4_hw_rv64i.inc" +#else +/* The generic case */ +# define PROV_CIPHER_HW_declare(mode) +# define PROV_CIPHER_HW_select(mode) +#endif + +PROV_CIPHER_HW_sm4_mode(cbc) +PROV_CIPHER_HW_sm4_mode(ecb) +PROV_CIPHER_HW_sm4_mode(ofb128) +PROV_CIPHER_HW_sm4_mode(cfb128) +PROV_CIPHER_HW_sm4_mode(ctr) diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4_hw_rv64i.inc b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_hw_rv64i.inc new file mode 100644 index 000000000000..763d9d09dd9e --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_hw_rv64i.inc @@ -0,0 +1,52 @@ +/* + * Copyright 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 + */ + +/*- + * RV64 ZVKSED support for AES modes ecb, cbc, ofb, cfb, ctr. + * This file is included by cipher_sm4_hw.c + */ + +#define cipher_hw_rv64i_zvksed_sm4_cbc ossl_cipher_hw_generic_cbc +#define cipher_hw_rv64i_zvksed_sm4_ecb ossl_cipher_hw_generic_ecb +#define cipher_hw_rv64i_zvksed_sm4_ofb128 ossl_cipher_hw_generic_ofb128 +#define cipher_hw_rv64i_zvksed_sm4_cfb128 ossl_cipher_hw_generic_cfb128 +#define cipher_hw_rv64i_zvksed_sm4_ctr ossl_cipher_hw_generic_ctr + +static int cipher_hw_rv64i_zvksed_sm4_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, + size_t keylen) +{ + PROV_SM4_CTX *sctx = (PROV_SM4_CTX *)ctx; + SM4_KEY *ks = &sctx->ks.ks; + + ctx->ks = ks; + if (ctx->enc + || (ctx->mode != EVP_CIPH_ECB_MODE + && ctx->mode != EVP_CIPH_CBC_MODE)) { + rv64i_zvksed_sm4_set_encrypt_key(key, ks); + ctx->block = (block128_f) rv64i_zvksed_sm4_encrypt; + ctx->stream.cbc = NULL; + } else { + rv64i_zvksed_sm4_set_decrypt_key(key, ks); + ctx->block = (block128_f) rv64i_zvksed_sm4_decrypt; + ctx->stream.cbc = NULL; + } + + return 1; +} + +#define PROV_CIPHER_HW_declare(mode) \ +static const PROV_CIPHER_HW rv64i_zvksed_sm4_##mode = { \ + cipher_hw_rv64i_zvksed_sm4_initkey, \ + cipher_hw_rv64i_zvksed_sm4_##mode, \ + cipher_hw_sm4_copyctx \ +}; +#define PROV_CIPHER_HW_select(mode) \ +if (RISCV_HAS_ZVKB_AND_ZVKSED() && riscv_vlen() >= 128) \ + return &rv64i_zvksed_sm4_##mode; diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4_xts.c b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_xts.c new file mode 100644 index 000000000000..31e86d6a8a0a --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_xts.c @@ -0,0 +1,281 @@ + +/* + * 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 + */ + +/* Dispatch functions for SM4 XTS mode */ + +#include <openssl/proverr.h> +#include "cipher_sm4_xts.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" + +#define SM4_XTS_FLAGS PROV_CIPHER_FLAG_CUSTOM_IV +#define SM4_XTS_IV_BITS 128 +#define SM4_XTS_BLOCK_BITS 8 + +/* forward declarations */ +static OSSL_FUNC_cipher_encrypt_init_fn sm4_xts_einit; +static OSSL_FUNC_cipher_decrypt_init_fn sm4_xts_dinit; +static OSSL_FUNC_cipher_update_fn sm4_xts_stream_update; +static OSSL_FUNC_cipher_final_fn sm4_xts_stream_final; +static OSSL_FUNC_cipher_cipher_fn sm4_xts_cipher; +static OSSL_FUNC_cipher_freectx_fn sm4_xts_freectx; +static OSSL_FUNC_cipher_dupctx_fn sm4_xts_dupctx; +static OSSL_FUNC_cipher_set_ctx_params_fn sm4_xts_set_ctx_params; +static OSSL_FUNC_cipher_settable_ctx_params_fn sm4_xts_settable_ctx_params; + +/*- + * Provider dispatch functions + */ +static int sm4_xts_init(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[], int enc) +{ + PROV_SM4_XTS_CTX *xctx = (PROV_SM4_XTS_CTX *)vctx; + PROV_CIPHER_CTX *ctx = &xctx->base; + + if (!ossl_prov_is_running()) + return 0; + + ctx->enc = enc; + + if (iv != NULL) { + if (!ossl_cipher_generic_initiv(vctx, iv, ivlen)) + return 0; + } + if (key != NULL) { + if (keylen != ctx->keylen) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + if (!ctx->hw->init(ctx, key, keylen)) + return 0; + } + return sm4_xts_set_ctx_params(xctx, params); +} + +static int sm4_xts_einit(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + return sm4_xts_init(vctx, key, keylen, iv, ivlen, params, 1); +} + +static int sm4_xts_dinit(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + return sm4_xts_init(vctx, key, keylen, iv, ivlen, params, 0); +} + +static void *sm4_xts_newctx(void *provctx, unsigned int mode, uint64_t flags, + size_t kbits, size_t blkbits, size_t ivbits) +{ + PROV_SM4_XTS_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx)); + + if (ctx != NULL) { + ossl_cipher_generic_initkey(&ctx->base, kbits, blkbits, ivbits, mode, + flags, ossl_prov_cipher_hw_sm4_xts(kbits), + NULL); + } + return ctx; +} + +static void sm4_xts_freectx(void *vctx) +{ + PROV_SM4_XTS_CTX *ctx = (PROV_SM4_XTS_CTX *)vctx; + + ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx); + OPENSSL_clear_free(ctx, sizeof(*ctx)); +} + +static void *sm4_xts_dupctx(void *vctx) +{ + PROV_SM4_XTS_CTX *in = (PROV_SM4_XTS_CTX *)vctx; + PROV_SM4_XTS_CTX *ret = NULL; + + if (!ossl_prov_is_running()) + return NULL; + + if (in->xts.key1 != NULL) { + if (in->xts.key1 != &in->ks1) + return NULL; + } + if (in->xts.key2 != NULL) { + if (in->xts.key2 != &in->ks2) + return NULL; + } + ret = OPENSSL_malloc(sizeof(*ret)); + if (ret == NULL) + return NULL; + in->base.hw->copyctx(&ret->base, &in->base); + return ret; +} + +static int sm4_xts_cipher(void *vctx, unsigned char *out, size_t *outl, + size_t outsize, const unsigned char *in, size_t inl) +{ + PROV_SM4_XTS_CTX *ctx = (PROV_SM4_XTS_CTX *)vctx; + + if (!ossl_prov_is_running() + || ctx->xts.key1 == NULL + || ctx->xts.key2 == NULL + || !ctx->base.iv_set + || out == NULL + || in == NULL + || inl < SM4_BLOCK_SIZE) + return 0; + + /* + * Impose a limit of 2^20 blocks per data unit as specified by + * IEEE Std 1619-2018. The earlier and obsolete IEEE Std 1619-2007 + * indicated that this was a SHOULD NOT rather than a MUST NOT. + * NIST SP 800-38E mandates the same limit. + */ + if (inl > XTS_MAX_BLOCKS_PER_DATA_UNIT * SM4_BLOCK_SIZE) { + ERR_raise(ERR_LIB_PROV, PROV_R_XTS_DATA_UNIT_IS_TOO_LARGE); + return 0; + } + if (ctx->xts_standard) { + if (ctx->stream != NULL) + (*ctx->stream)(in, out, inl, ctx->xts.key1, ctx->xts.key2, + ctx->base.iv, ctx->base.enc); + else if (CRYPTO_xts128_encrypt(&ctx->xts, ctx->base.iv, in, out, inl, + ctx->base.enc)) + return 0; + } else { + if (ctx->stream_gb != NULL) + (*ctx->stream_gb)(in, out, inl, ctx->xts.key1, ctx->xts.key2, + ctx->base.iv, ctx->base.enc); + else if (ossl_crypto_xts128gb_encrypt(&ctx->xts, ctx->base.iv, in, out, + inl, ctx->base.enc)) + return 0; + } + *outl = inl; + return 1; +} + +static int sm4_xts_stream_update(void *vctx, unsigned char *out, size_t *outl, + size_t outsize, const unsigned char *in, + size_t inl) +{ + PROV_SM4_XTS_CTX *ctx = (PROV_SM4_XTS_CTX *)vctx; + + if (outsize < inl) { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + return 0; + } + + if (!sm4_xts_cipher(ctx, out, outl, outsize, in, inl)) { + ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); + return 0; + } + + return 1; +} + +static int sm4_xts_stream_final(void *vctx, unsigned char *out, size_t *outl, + size_t outsize) +{ + if (!ossl_prov_is_running()) + return 0; + *outl = 0; + return 1; +} + +static const OSSL_PARAM sm4_xts_known_settable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_CIPHER_PARAM_XTS_STANDARD, NULL, 0), + OSSL_PARAM_END +}; + +static const OSSL_PARAM *sm4_xts_settable_ctx_params(ossl_unused void *cctx, + ossl_unused void *provctx) +{ + return sm4_xts_known_settable_ctx_params; +} + +static int sm4_xts_set_ctx_params(void *vxctx, const OSSL_PARAM params[]) +{ + PROV_SM4_XTS_CTX *xctx = (PROV_SM4_XTS_CTX *)vxctx; + const OSSL_PARAM *p; + + if (ossl_param_is_empty(params)) + return 1; + + /*- + * Sets the XTS standard to use with SM4-XTS algorithm. + * + * Must be utf8 string "GB" or "IEEE", + * "GB" means the GB/T 17964-2021 standard + * "IEEE" means the IEEE Std 1619-2007 standard + */ + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_XTS_STANDARD); + + if (p != NULL) { + const char *xts_standard = NULL; + + if (p->data_type != OSSL_PARAM_UTF8_STRING) + return 0; + + if (!OSSL_PARAM_get_utf8_string_ptr(p, &xts_standard)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + if (OPENSSL_strcasecmp(xts_standard, "GB") == 0) { + xctx->xts_standard = 0; + } else if (OPENSSL_strcasecmp(xts_standard, "IEEE") == 0) { + xctx->xts_standard = 1; + } else { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + } + + return 1; +} + +#define IMPLEMENT_cipher(lcmode, UCMODE, kbits, flags) \ +static OSSL_FUNC_cipher_get_params_fn sm4_##kbits##_##lcmode##_get_params; \ +static int sm4_##kbits##_##lcmode##_get_params(OSSL_PARAM params[]) \ +{ \ + return ossl_cipher_generic_get_params(params, EVP_CIPH_##UCMODE##_MODE, \ + flags, 2 * kbits, SM4_XTS_BLOCK_BITS,\ + SM4_XTS_IV_BITS); \ +} \ +static OSSL_FUNC_cipher_newctx_fn sm4_##kbits##_xts_newctx; \ +static void *sm4_##kbits##_xts_newctx(void *provctx) \ +{ \ + return sm4_xts_newctx(provctx, EVP_CIPH_##UCMODE##_MODE, flags, 2 * kbits, \ + SM4_XTS_BLOCK_BITS, SM4_XTS_IV_BITS); \ +} \ +const OSSL_DISPATCH ossl_sm4##kbits##xts_functions[] = { \ + { OSSL_FUNC_CIPHER_NEWCTX, (void (*)(void))sm4_##kbits##_xts_newctx }, \ + { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))sm4_xts_einit }, \ + { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))sm4_xts_dinit }, \ + { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))sm4_xts_stream_update }, \ + { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))sm4_xts_stream_final }, \ + { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))sm4_xts_cipher }, \ + { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))sm4_xts_freectx }, \ + { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void))sm4_xts_dupctx }, \ + { OSSL_FUNC_CIPHER_GET_PARAMS, \ + (void (*)(void))sm4_##kbits##_##lcmode##_get_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \ + (void (*)(void))ossl_cipher_generic_gettable_params }, \ + { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, \ + (void (*)(void))ossl_cipher_generic_get_ctx_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \ + (void (*)(void))ossl_cipher_generic_gettable_ctx_params }, \ + { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \ + (void (*)(void))sm4_xts_set_ctx_params }, \ + { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \ + (void (*)(void))sm4_xts_settable_ctx_params }, \ + OSSL_DISPATCH_END \ +} +/* ossl_sm4128xts_functions */ +IMPLEMENT_cipher(xts, XTS, 128, SM4_XTS_FLAGS); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4_xts.h b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_xts.h new file mode 100644 index 000000000000..43d9a212e55f --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_xts.h @@ -0,0 +1,46 @@ +/* + * 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 + */ + +#include <crypto/sm4.h> +#include "prov/ciphercommon.h" +#include "crypto/sm4_platform.h" + +PROV_CIPHER_FUNC(void, xts_stream, + (const unsigned char *in, unsigned char *out, size_t len, + const SM4_KEY *key1, const SM4_KEY *key2, + const unsigned char iv[16], const int enc)); + +typedef struct prov_sm4_xts_ctx_st { + /* Must be first */ + PROV_CIPHER_CTX base; + + /* SM4 key schedules to use */ + union { + OSSL_UNION_ALIGN; + SM4_KEY ks; + } ks1, ks2; + + /*- + * XTS standard to use with SM4-XTS algorithm + * + * Must be 0 or 1, + * 0 for XTS mode specified by GB/T 17964-2021 + * 1 for XTS mode specified by IEEE Std 1619-2007 + */ + int xts_standard; + + XTS128_CONTEXT xts; + + /* Stream function for XTS mode specified by GB/T 17964-2021 */ + OSSL_xts_stream_fn stream_gb; + /* Stream function for XTS mode specified by IEEE Std 1619-2007 */ + OSSL_xts_stream_fn stream; +} PROV_SM4_XTS_CTX; + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_sm4_xts(size_t keybits); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4_xts_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_xts_hw.c new file mode 100644 index 000000000000..d147cf1a6114 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_xts_hw.c @@ -0,0 +1,99 @@ +/* + * Copyright 2022-2024 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 + */ + +#include "cipher_sm4_xts.h" + +#define XTS_SET_KEY_FN(fn_set_enc_key, fn_set_dec_key, \ + fn_block_enc, fn_block_dec, \ + fn_stream, fn_stream_gb) { \ + size_t bytes = keylen / 2; \ + \ + if (ctx->enc) { \ + fn_set_enc_key(key, &xctx->ks1.ks); \ + xctx->xts.block1 = (block128_f)fn_block_enc; \ + } else { \ + fn_set_dec_key(key, &xctx->ks1.ks); \ + xctx->xts.block1 = (block128_f)fn_block_dec; \ + } \ + fn_set_enc_key(key + bytes, &xctx->ks2.ks); \ + xctx->xts.block2 = (block128_f)fn_block_enc; \ + xctx->xts.key1 = &xctx->ks1; \ + xctx->xts.key2 = &xctx->ks2; \ + xctx->stream = fn_stream; \ + xctx->stream_gb = fn_stream_gb; \ +} + +static int cipher_hw_sm4_xts_generic_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, + size_t keylen) +{ + PROV_SM4_XTS_CTX *xctx = (PROV_SM4_XTS_CTX *)ctx; + OSSL_xts_stream_fn stream = NULL; + OSSL_xts_stream_fn stream_gb = NULL; +#ifdef HWSM4_CAPABLE + if (HWSM4_CAPABLE) { + XTS_SET_KEY_FN(HWSM4_set_encrypt_key, HWSM4_set_decrypt_key, + HWSM4_encrypt, HWSM4_decrypt, stream, stream_gb); + return 1; + } else +#endif /* HWSM4_CAPABLE */ +#ifdef VPSM4_EX_CAPABLE + if (VPSM4_EX_CAPABLE) { + stream = vpsm4_ex_xts_encrypt; + stream_gb = vpsm4_ex_xts_encrypt_gb; + XTS_SET_KEY_FN(vpsm4_ex_set_encrypt_key, vpsm4_ex_set_decrypt_key, + vpsm4_ex_encrypt, vpsm4_ex_decrypt, stream, stream_gb); + return 1; + } else +#endif /* VPSM4_EX_CAPABLE */ +#ifdef VPSM4_CAPABLE + if (VPSM4_CAPABLE) { + stream = vpsm4_xts_encrypt; + stream_gb = vpsm4_xts_encrypt_gb; + XTS_SET_KEY_FN(vpsm4_set_encrypt_key, vpsm4_set_decrypt_key, + vpsm4_encrypt, vpsm4_decrypt, stream, stream_gb); + return 1; + } else +#endif /* VPSM4_CAPABLE */ + { + (void)0; + } + { + XTS_SET_KEY_FN(ossl_sm4_set_key, ossl_sm4_set_key, ossl_sm4_encrypt, + ossl_sm4_decrypt, stream, stream_gb); + } + return 1; +} + +static void cipher_hw_sm4_xts_copyctx(PROV_CIPHER_CTX *dst, + const PROV_CIPHER_CTX *src) +{ + PROV_SM4_XTS_CTX *sctx = (PROV_SM4_XTS_CTX *)src; + PROV_SM4_XTS_CTX *dctx = (PROV_SM4_XTS_CTX *)dst; + + *dctx = *sctx; + dctx->xts.key1 = &dctx->ks1.ks; + dctx->xts.key2 = &dctx->ks2.ks; +} + + +static const PROV_CIPHER_HW sm4_generic_xts = { + cipher_hw_sm4_xts_generic_initkey, + NULL, + cipher_hw_sm4_xts_copyctx +}; + +#if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64 +# include "cipher_sm4_xts_hw_rv64i.inc" +#else +const PROV_CIPHER_HW *ossl_prov_cipher_hw_sm4_xts(size_t keybits) +{ + return &sm4_generic_xts; +} +#endif diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4_xts_hw_rv64i.inc b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_xts_hw_rv64i.inc new file mode 100644 index 000000000000..2ab15269cc75 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_xts_hw_rv64i.inc @@ -0,0 +1,43 @@ +/* + * Copyright 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 + */ + +/*- + * RISC-V 64 ZVKSED support for SM4 GCM. + * This file is included by cipher_sm4_gcm_hw.c + */ + +static int rv64i_zvksed_sm4_xts_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, + size_t keylen) +{ + PROV_SM4_XTS_CTX *xctx = (PROV_SM4_XTS_CTX *)ctx; + OSSL_xts_stream_fn stream_fn = NULL; + OSSL_xts_stream_fn stream_gb_fn = NULL; + + XTS_SET_KEY_FN(rv64i_zvksed_sm4_set_encrypt_key, + rv64i_zvksed_sm4_set_decrypt_key, + rv64i_zvksed_sm4_encrypt, + rv64i_zvksed_sm4_decrypt, + stream_fn, stream_gb_fn); + return 1; +} + +static const PROV_CIPHER_HW rv64i_zvksed_sm4_xts = { + rv64i_zvksed_sm4_xts_initkey, + NULL, + cipher_hw_sm4_xts_copyctx +}; + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_sm4_xts(size_t keybits) +{ + if (RISCV_HAS_ZVKB_AND_ZVKSED() && riscv_vlen() >= 128) + return &rv64i_zvksed_sm4_xts; + else + return &sm4_generic_xts; +} diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_tdes.c b/crypto/openssl/providers/implementations/ciphers/cipher_tdes.c new file mode 100644 index 000000000000..2e5f8c3f05bb --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_tdes.c @@ -0,0 +1,25 @@ +/* + * Copyright 2019-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 + */ + +/* + * DES low level APIs are deprecated for public use, but still ok for internal + * use. + */ +#include "internal/deprecated.h" + +#include <openssl/rand.h> +#include <openssl/proverr.h> +#include "prov/ciphercommon.h" +#include "cipher_tdes.h" +#include "prov/implementations.h" + +/* ossl_tdes_ede3_ecb_functions */ +IMPLEMENT_tdes_cipher(ede3, EDE3, ecb, ECB, TDES_FLAGS, 64*3, 64, 0, block); +/* ossl_tdes_ede3_cbc_functions */ +IMPLEMENT_tdes_cipher(ede3, EDE3, cbc, CBC, TDES_FLAGS, 64*3, 64, 64, block); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_tdes.h b/crypto/openssl/providers/implementations/ciphers/cipher_tdes.h new file mode 100644 index 000000000000..55bd76045176 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_tdes.h @@ -0,0 +1,110 @@ +/* + * Copyright 2019-2024 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 + */ + +#include <openssl/des.h> +#include <openssl/core_dispatch.h> +#include "prov/securitycheck.h" +#include "crypto/des_platform.h" + +#define DES_BLOCK_SIZE 8 +#define TDES_IVLEN 8 +#define TDES_FLAGS PROV_CIPHER_FLAG_RAND_KEY + +typedef struct prov_tdes_ctx_st { + PROV_CIPHER_CTX base; /* Must be first */ + union { + OSSL_UNION_ALIGN; + DES_key_schedule ks[3]; + } tks; + union { + void (*cbc) (const void *, void *, size_t, + const DES_key_schedule *, unsigned char *); + } tstream; + OSSL_FIPS_IND_DECLARE + +} PROV_TDES_CTX; + +#define IMPLEMENT_tdes_cipher(type, UCTYPE, lcmode, UCMODE, flags, \ + kbits, blkbits, ivbits, block) \ +static OSSL_FUNC_cipher_newctx_fn tdes_##type##_##lcmode##_newctx; \ +static void *tdes_##type##_##lcmode##_newctx(void *provctx) \ +{ \ + return ossl_tdes_newctx(provctx, EVP_CIPH_##UCMODE##_MODE, kbits, blkbits, \ + ivbits, flags, \ + ossl_prov_cipher_hw_tdes_##type##_##lcmode()); \ +} \ +static OSSL_FUNC_cipher_get_params_fn tdes_##type##_##lcmode##_get_params; \ +static int tdes_##type##_##lcmode##_get_params(OSSL_PARAM params[]) \ +{ \ + return ossl_tdes_get_params(params, EVP_CIPH_##UCMODE##_MODE, \ + flags, kbits, blkbits, ivbits); \ +} \ +const OSSL_DISPATCH ossl_tdes_##type##_##lcmode##_functions[] = { \ + { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))ossl_tdes_einit }, \ + { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))ossl_tdes_dinit }, \ + { OSSL_FUNC_CIPHER_UPDATE, \ + (void (*)(void))ossl_cipher_generic_##block##_update }, \ + { OSSL_FUNC_CIPHER_FINAL, \ + (void (*)(void))ossl_cipher_generic_##block##_final }, \ + { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))ossl_cipher_generic_cipher }, \ + { OSSL_FUNC_CIPHER_NEWCTX, \ + (void (*)(void))tdes_##type##_##lcmode##_newctx }, \ + { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void))ossl_tdes_dupctx }, \ + { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))ossl_tdes_freectx }, \ + { OSSL_FUNC_CIPHER_GET_PARAMS, \ + (void (*)(void))tdes_##type##_##lcmode##_get_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \ + (void (*)(void))ossl_cipher_generic_gettable_params }, \ + { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, \ + (void (*)(void))ossl_tdes_get_ctx_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \ + (void (*)(void))ossl_tdes_gettable_ctx_params }, \ + { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \ + (void (*)(void))ossl_tdes_set_ctx_params }, \ + { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \ + (void (*)(void))ossl_tdes_settable_ctx_params }, \ + OSSL_DISPATCH_END \ +} + +void *ossl_tdes_newctx(void *provctx, int mode, size_t kbits, size_t blkbits, + size_t ivbits, uint64_t flags, const PROV_CIPHER_HW *hw); +int ossl_tdes_get_params(OSSL_PARAM params[], unsigned int md, uint64_t flags, + size_t kbits, size_t blkbits, size_t ivbits); + +OSSL_FUNC_cipher_dupctx_fn ossl_tdes_dupctx; +OSSL_FUNC_cipher_freectx_fn ossl_tdes_freectx; +OSSL_FUNC_cipher_encrypt_init_fn ossl_tdes_einit; +OSSL_FUNC_cipher_decrypt_init_fn ossl_tdes_dinit; +OSSL_FUNC_cipher_get_ctx_params_fn ossl_tdes_get_ctx_params; +OSSL_FUNC_cipher_gettable_ctx_params_fn ossl_tdes_gettable_ctx_params; +OSSL_FUNC_cipher_set_ctx_params_fn ossl_tdes_set_ctx_params; +OSSL_FUNC_cipher_settable_ctx_params_fn ossl_tdes_settable_ctx_params; + +#define PROV_CIPHER_HW_tdes_mode(type, mode) \ +static const PROV_CIPHER_HW type##_##mode = { \ + ossl_cipher_hw_tdes_##type##_initkey, \ + ossl_cipher_hw_tdes_##mode, \ + ossl_cipher_hw_tdes_copyctx \ +}; \ +const PROV_CIPHER_HW *ossl_prov_cipher_hw_tdes_##type##_##mode(void) \ +{ \ + return &type##_##mode; \ +} + +int ossl_cipher_hw_tdes_ede3_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen); +void ossl_cipher_hw_tdes_copyctx(PROV_CIPHER_CTX *dst, + const PROV_CIPHER_CTX *src); +int ossl_cipher_hw_tdes_cbc(PROV_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t inl); +int ossl_cipher_hw_tdes_ecb(PROV_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t len); + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_tdes_ede3_cbc(void); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_tdes_ede3_ecb(void); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_tdes_common.c b/crypto/openssl/providers/implementations/ciphers/cipher_tdes_common.c new file mode 100644 index 000000000000..e8564c3d0f83 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_tdes_common.c @@ -0,0 +1,206 @@ +/* + * Copyright 2019-2024 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 + */ + +/* + * DES low level APIs are deprecated for public use, but still ok for internal + * use. + */ +#include "internal/deprecated.h" + +#include <openssl/rand.h> +#include <openssl/proverr.h> +#include "prov/ciphercommon.h" +#include "cipher_tdes.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" + +void *ossl_tdes_newctx(void *provctx, int mode, size_t kbits, size_t blkbits, + size_t ivbits, uint64_t flags, const PROV_CIPHER_HW *hw) +{ + PROV_TDES_CTX *tctx; + + if (!ossl_prov_is_running()) + return NULL; + + tctx = OPENSSL_zalloc(sizeof(*tctx)); + if (tctx != NULL) { + OSSL_FIPS_IND_INIT(tctx) + ossl_cipher_generic_initkey(tctx, kbits, blkbits, ivbits, mode, flags, + hw, provctx); + } + return tctx; +} + +void *ossl_tdes_dupctx(void *ctx) +{ + PROV_TDES_CTX *in = (PROV_TDES_CTX *)ctx; + PROV_TDES_CTX *ret; + + if (!ossl_prov_is_running()) + return NULL; + + ret = OPENSSL_malloc(sizeof(*ret)); + if (ret == NULL) + return NULL; + OSSL_FIPS_IND_COPY(ret, in) + in->base.hw->copyctx(&ret->base, &in->base); + + return ret; +} + +void ossl_tdes_freectx(void *vctx) +{ + PROV_TDES_CTX *ctx = (PROV_TDES_CTX *)vctx; + + ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx); + OPENSSL_clear_free(ctx, sizeof(*ctx)); +} + +#ifdef FIPS_MODULE +static int tdes_encrypt_check_approved(PROV_TDES_CTX *ctx, int enc) +{ + /* Triple-DES encryption is not approved in FIPS 140-3 */ + if (enc && !OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE0, + ctx->base.libctx, + "Triple-DES", "Encryption", + ossl_fips_config_tdes_encrypt_disallowed)) + return 0; + return 1; +} +#endif + +static int tdes_init(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[], int enc) +{ + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + + if (!ossl_prov_is_running()) + return 0; + + ctx->num = 0; + ctx->bufsz = 0; + ctx->enc = enc; + + if (iv != NULL) { + if (!ossl_cipher_generic_initiv(ctx, iv, ivlen)) + return 0; + } else if (ctx->iv_set + && (ctx->mode == EVP_CIPH_CBC_MODE + || ctx->mode == EVP_CIPH_CFB_MODE + || ctx->mode == EVP_CIPH_OFB_MODE)) { + /* reset IV to keep compatibility with 1.1.1 */ + memcpy(ctx->iv, ctx->oiv, ctx->ivlen); + } + + if (key != NULL) { + if (keylen != ctx->keylen) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + if (!ctx->hw->init(ctx, key, ctx->keylen)) + return 0; + ctx->key_set = 1; + } + if (!ossl_tdes_set_ctx_params(ctx, params)) + return 0; +#ifdef FIPS_MODULE + if (!tdes_encrypt_check_approved((PROV_TDES_CTX *)ctx, enc)) + return 0; +#endif + return 1; +} + +int ossl_tdes_einit(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + return tdes_init(vctx, key, keylen, iv, ivlen, params, 1); +} + +int ossl_tdes_dinit(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + return tdes_init(vctx, key, keylen, iv, ivlen, params, 0); +} + +CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_START(ossl_tdes) + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_RANDOM_KEY, NULL, 0), + OSSL_FIPS_IND_GETTABLE_CTX_PARAM() +CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_END(ossl_tdes) + +static int tdes_generatekey(PROV_CIPHER_CTX *ctx, void *ptr) +{ + DES_cblock *deskey = ptr; + size_t kl = ctx->keylen; + + if (kl == 0 || RAND_priv_bytes_ex(ctx->libctx, ptr, kl, 0) <= 0) + return 0; + DES_set_odd_parity(deskey); + if (kl >= 16) { + DES_set_odd_parity(deskey + 1); + if (kl >= 24) + DES_set_odd_parity(deskey + 2); + } + return 1; +} + +int ossl_tdes_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + OSSL_PARAM *p; + + if (!ossl_cipher_generic_get_ctx_params(vctx, params)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_RANDOM_KEY); + if (p != NULL && !tdes_generatekey(ctx, p->data)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GENERATE_KEY); + return 0; + } + if (!OSSL_FIPS_IND_GET_CTX_PARAM((PROV_TDES_CTX *)vctx, params)) + return 0; + return 1; +} + +CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_START(ossl_tdes) + OSSL_PARAM_uint(OSSL_CIPHER_PARAM_PADDING, NULL), + OSSL_PARAM_uint(OSSL_CIPHER_PARAM_NUM, NULL), + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_CIPHER_PARAM_FIPS_ENCRYPT_CHECK) +CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_END(ossl_tdes) + +int ossl_tdes_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + if (!OSSL_FIPS_IND_SET_CTX_PARAM((PROV_TDES_CTX *)vctx, + OSSL_FIPS_IND_SETTABLE0, params, + OSSL_CIPHER_PARAM_FIPS_ENCRYPT_CHECK)) + return 0; + return ossl_cipher_generic_set_ctx_params(vctx, params); +} + +int ossl_tdes_get_params(OSSL_PARAM params[], unsigned int md, uint64_t flags, + size_t kbits, size_t blkbits, size_t ivbits) +{ +#ifdef FIPS_MODULE + const int decrypt_only = 1; +#else + const int decrypt_only = 0; +#endif + OSSL_PARAM *p; + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_DECRYPT_ONLY); + if (p != NULL && !OSSL_PARAM_set_int(p, decrypt_only)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + + return ossl_cipher_generic_get_params(params, md, flags, + kbits, blkbits, ivbits); +} diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_tdes_default.c b/crypto/openssl/providers/implementations/ciphers/cipher_tdes_default.c new file mode 100644 index 000000000000..3b8908ff3988 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_tdes_default.c @@ -0,0 +1,35 @@ +/* + * Copyright 2019-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 + */ + +/* + * DES low level APIs are deprecated for public use, but still ok for internal + * use. + */ +#include "internal/deprecated.h" + +#include "cipher_tdes_default.h" +#include "prov/implementations.h" + +/* ossl_tdes_ede3_ofb_functions */ +IMPLEMENT_tdes_cipher(ede3, EDE3, ofb, OFB, TDES_FLAGS, 64*3, 8, 64, stream); +/* ossl_tdes_ede3_cfb_functions */ +IMPLEMENT_tdes_cipher(ede3, EDE3, cfb, CFB, TDES_FLAGS, 64*3, 8, 64, stream); +/* ossl_tdes_ede3_cfb1_functions */ +IMPLEMENT_tdes_cipher(ede3, EDE3, cfb1, CFB, TDES_FLAGS, 64*3, 8, 64, stream); +/* ossl_tdes_ede3_cfb8_functions */ +IMPLEMENT_tdes_cipher(ede3, EDE3, cfb8, CFB, TDES_FLAGS, 64*3, 8, 64, stream); + +/* ossl_tdes_ede2_ecb_functions */ +IMPLEMENT_tdes_cipher(ede2, EDE2, ecb, ECB, TDES_FLAGS, 64*2, 64, 0, block); +/* ossl_tdes_ede2_cbc_functions */ +IMPLEMENT_tdes_cipher(ede2, EDE2, cbc, CBC, TDES_FLAGS, 64*2, 64, 64, block); +/* ossl_tdes_ede2_ofb_functions */ +IMPLEMENT_tdes_cipher(ede2, EDE2, ofb, OFB, TDES_FLAGS, 64*2, 8, 64, stream); +/* ossl_tdes_ede2_cfb_functions */ +IMPLEMENT_tdes_cipher(ede2, EDE2, cfb, CFB, TDES_FLAGS, 64*2, 8, 64, stream); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_tdes_default.h b/crypto/openssl/providers/implementations/ciphers/cipher_tdes_default.h new file mode 100644 index 000000000000..dc8458b5dafa --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_tdes_default.h @@ -0,0 +1,25 @@ +/* + * Copyright 1995-2020 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 + */ + +#include "prov/ciphercommon.h" +#include "cipher_tdes.h" + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_tdes_ede3_ofb(void); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_tdes_ede3_cfb(void); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_tdes_ede3_cfb1(void); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_tdes_ede3_cfb8(void); + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_tdes_ede2_cbc(void); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_tdes_ede2_ecb(void); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_tdes_ede2_ofb(void); +const PROV_CIPHER_HW *ossl_prov_cipher_hw_tdes_ede2_cfb(void); + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_tdes_desx_cbc(void); + +const PROV_CIPHER_HW *ossl_prov_cipher_hw_tdes_wrap_cbc(void); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_tdes_default_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_tdes_default_hw.c new file mode 100644 index 000000000000..ccdf3941c86b --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_tdes_default_hw.c @@ -0,0 +1,148 @@ +/* + * Copyright 1995-2021 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 + */ + +/* + * DES low level APIs are deprecated for public use, but still ok for internal + * use. + */ +#include "internal/deprecated.h" + +#include "cipher_tdes_default.h" + +#define ks1 tks.ks[0] +#define ks2 tks.ks[1] +#define ks3 tks.ks[2] + +static int ossl_cipher_hw_tdes_ede2_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, + size_t keylen) +{ + PROV_TDES_CTX *tctx = (PROV_TDES_CTX *)ctx; + DES_cblock *deskey = (DES_cblock *)key; + + tctx->tstream.cbc = NULL; +# if defined(SPARC_DES_CAPABLE) + if (SPARC_DES_CAPABLE) { + if (ctx->mode == EVP_CIPH_CBC_MODE) { + des_t4_key_expand(&deskey[0], &tctx->ks1); + des_t4_key_expand(&deskey[1], &tctx->ks2); + memcpy(&tctx->ks3, &tctx->ks1, sizeof(tctx->ks1)); + tctx->tstream.cbc = ctx->enc ? des_t4_ede3_cbc_encrypt : + des_t4_ede3_cbc_decrypt; + return 1; + } + } +# endif + DES_set_key_unchecked(&deskey[0], &tctx->ks1); + DES_set_key_unchecked(&deskey[1], &tctx->ks2); + memcpy(&tctx->ks3, &tctx->ks1, sizeof(tctx->ks1)); + return 1; +} + +static int ossl_cipher_hw_tdes_ofb(PROV_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t inl) +{ + PROV_TDES_CTX *tctx = (PROV_TDES_CTX *)ctx; + int num = ctx->num; + + while (inl >= MAXCHUNK) { + DES_ede3_ofb64_encrypt(in, out, (long)MAXCHUNK, &tctx->ks1, &tctx->ks2, + &tctx->ks3, (DES_cblock *)ctx->iv, &num); + inl -= MAXCHUNK; + in += MAXCHUNK; + out += MAXCHUNK; + } + if (inl > 0) { + DES_ede3_ofb64_encrypt(in, out, (long)inl, &tctx->ks1, &tctx->ks2, + &tctx->ks3, (DES_cblock *)ctx->iv, &num); + } + ctx->num = num; + return 1; +} + +static int ossl_cipher_hw_tdes_cfb(PROV_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t inl) +{ + PROV_TDES_CTX *tctx = (PROV_TDES_CTX *)ctx; + int num = ctx->num; + + while (inl >= MAXCHUNK) { + + DES_ede3_cfb64_encrypt(in, out, (long)MAXCHUNK, + &tctx->ks1, &tctx->ks2, &tctx->ks3, + (DES_cblock *)ctx->iv, &num, ctx->enc); + inl -= MAXCHUNK; + in += MAXCHUNK; + out += MAXCHUNK; + } + if (inl > 0) { + DES_ede3_cfb64_encrypt(in, out, (long)inl, + &tctx->ks1, &tctx->ks2, &tctx->ks3, + (DES_cblock *)ctx->iv, &num, ctx->enc); + } + ctx->num = num; + return 1; +} + +/* + * Although we have a CFB-r implementation for 3-DES, it doesn't pack the + * right way, so wrap it here + */ +static int ossl_cipher_hw_tdes_cfb1(PROV_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t inl) +{ + PROV_TDES_CTX *tctx = (PROV_TDES_CTX *)ctx; + size_t n; + unsigned char c[1]; + unsigned char d[1] = { 0 }; + + if (ctx->use_bits == 0) + inl *= 8; + for (n = 0; n < inl; ++n) { + c[0] = (in[n / 8] & (1 << (7 - n % 8))) ? 0x80 : 0; + DES_ede3_cfb_encrypt(c, d, 1, 1, + &tctx->ks1, &tctx->ks2, &tctx->ks3, + (DES_cblock *)ctx->iv, ctx->enc); + out[n / 8] = (out[n / 8] & ~(0x80 >> (unsigned int)(n % 8))) + | ((d[0] & 0x80) >> (unsigned int)(n % 8)); + } + + return 1; +} + +static int ossl_cipher_hw_tdes_cfb8(PROV_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t inl) +{ + PROV_TDES_CTX *tctx = (PROV_TDES_CTX *)ctx; + + while (inl >= MAXCHUNK) { + DES_ede3_cfb_encrypt(in, out, 8, (long)MAXCHUNK, + &tctx->ks1, &tctx->ks2, &tctx->ks3, + (DES_cblock *)ctx->iv, ctx->enc); + inl -= MAXCHUNK; + in += MAXCHUNK; + out += MAXCHUNK; + } + if (inl > 0) + DES_ede3_cfb_encrypt(in, out, 8, (long)inl, + &tctx->ks1, &tctx->ks2, &tctx->ks3, + (DES_cblock *)ctx->iv, ctx->enc); + return 1; +} + +PROV_CIPHER_HW_tdes_mode(ede3, ofb) +PROV_CIPHER_HW_tdes_mode(ede3, cfb) +PROV_CIPHER_HW_tdes_mode(ede3, cfb1) +PROV_CIPHER_HW_tdes_mode(ede3, cfb8) + +PROV_CIPHER_HW_tdes_mode(ede2, ecb) +PROV_CIPHER_HW_tdes_mode(ede2, cbc) +PROV_CIPHER_HW_tdes_mode(ede2, ofb) +PROV_CIPHER_HW_tdes_mode(ede2, cfb) + diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_tdes_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_tdes_hw.c new file mode 100644 index 000000000000..4382969f44bb --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_tdes_hw.c @@ -0,0 +1,98 @@ +/* + * Copyright 1995-2020 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 + */ + +/* + * DES low level APIs are deprecated for public use, but still ok for internal + * use. + */ +#include "internal/deprecated.h" + +#include "prov/ciphercommon.h" +#include "cipher_tdes.h" + +#define ks1 tks.ks[0] +#define ks2 tks.ks[1] +#define ks3 tks.ks[2] + +int ossl_cipher_hw_tdes_ede3_initkey(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen) +{ + PROV_TDES_CTX *tctx = (PROV_TDES_CTX *)ctx; + DES_cblock *deskey = (DES_cblock *)key; + + tctx->tstream.cbc = NULL; +# if defined(SPARC_DES_CAPABLE) + if (SPARC_DES_CAPABLE) { + if (ctx->mode == EVP_CIPH_CBC_MODE) { + des_t4_key_expand(&deskey[0], &tctx->ks1); + des_t4_key_expand(&deskey[1], &tctx->ks2); + des_t4_key_expand(&deskey[2], &tctx->ks3); + tctx->tstream.cbc = ctx->enc ? des_t4_ede3_cbc_encrypt : + des_t4_ede3_cbc_decrypt; + return 1; + } + } +# endif + DES_set_key_unchecked(&deskey[0], &tctx->ks1); + DES_set_key_unchecked(&deskey[1], &tctx->ks2); + DES_set_key_unchecked(&deskey[2], &tctx->ks3); + return 1; +} + +void ossl_cipher_hw_tdes_copyctx(PROV_CIPHER_CTX *dst, + const PROV_CIPHER_CTX *src) +{ + PROV_TDES_CTX *sctx = (PROV_TDES_CTX *)src; + PROV_TDES_CTX *dctx = (PROV_TDES_CTX *)dst; + + *dctx = *sctx; + dst->ks = &dctx->tks.ks; +} + +int ossl_cipher_hw_tdes_cbc(PROV_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t inl) +{ + PROV_TDES_CTX *tctx = (PROV_TDES_CTX *)ctx; + + if (tctx->tstream.cbc != NULL) { + (*tctx->tstream.cbc) (in, out, inl, tctx->tks.ks, ctx->iv); + return 1; + } + + while (inl >= MAXCHUNK) { + DES_ede3_cbc_encrypt(in, out, (long)MAXCHUNK, &tctx->ks1, &tctx->ks2, + &tctx->ks3, (DES_cblock *)ctx->iv, ctx->enc); + inl -= MAXCHUNK; + in += MAXCHUNK; + out += MAXCHUNK; + } + if (inl > 0) + DES_ede3_cbc_encrypt(in, out, (long)inl, &tctx->ks1, &tctx->ks2, + &tctx->ks3, (DES_cblock *)ctx->iv, ctx->enc); + return 1; +} + +int ossl_cipher_hw_tdes_ecb(PROV_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t len) +{ + size_t i; + PROV_TDES_CTX *tctx = (PROV_TDES_CTX *)ctx; + + if (len < DES_BLOCK_SIZE) + return 1; + + for (i = 0, len -= DES_BLOCK_SIZE; i <= len; i += DES_BLOCK_SIZE) { + DES_ecb3_encrypt((const_DES_cblock *)(in + i), (DES_cblock *)(out + i), + &tctx->ks1, &tctx->ks2, &tctx->ks3, ctx->enc); + } + return 1; +} + +PROV_CIPHER_HW_tdes_mode(ede3, ecb) +PROV_CIPHER_HW_tdes_mode(ede3, cbc) diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_tdes_wrap.c b/crypto/openssl/providers/implementations/ciphers/cipher_tdes_wrap.c new file mode 100644 index 000000000000..b48c02ae2581 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_tdes_wrap.c @@ -0,0 +1,209 @@ +/* + * Copyright 1995-2024 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 + */ + +/* + * DES and SHA-1 low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include <openssl/sha.h> +#include <openssl/rand.h> +#include <openssl/proverr.h> +#include "cipher_tdes_default.h" +#include "crypto/evp.h" +#include "crypto/sha.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" + +#define TDES_WRAP_FLAGS PROV_CIPHER_FLAG_CUSTOM_IV | PROV_CIPHER_FLAG_RAND_KEY + +static OSSL_FUNC_cipher_update_fn tdes_wrap_update; +static OSSL_FUNC_cipher_cipher_fn tdes_wrap_cipher; + +static const unsigned char wrap_iv[8] = { + 0x4a, 0xdd, 0xa2, 0x2c, 0x79, 0xe8, 0x21, 0x05 +}; + +static int des_ede3_unwrap(PROV_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t inl) +{ + unsigned char icv[8], iv[TDES_IVLEN], sha1tmp[SHA_DIGEST_LENGTH]; + int rv = -1; + + if (inl < 24) + return -1; + if (out == NULL) + return inl - 16; + + memcpy(ctx->iv, wrap_iv, 8); + /* Decrypt first block which will end up as icv */ + ctx->hw->cipher(ctx, icv, in, 8); + /* Decrypt central blocks */ + /* + * If decrypting in place move whole output along a block so the next + * des_ede_cbc_cipher is in place. + */ + if (out == in) { + memmove(out, out + 8, inl - 8); + in -= 8; + } + ctx->hw->cipher(ctx, out, in + 8, inl - 16); + /* Decrypt final block which will be IV */ + ctx->hw->cipher(ctx, iv, in + inl - 8, 8); + /* Reverse order of everything */ + BUF_reverse(icv, NULL, 8); + BUF_reverse(out, NULL, inl - 16); + BUF_reverse(ctx->iv, iv, 8); + /* Decrypt again using new IV */ + ctx->hw->cipher(ctx, out, out, inl - 16); + ctx->hw->cipher(ctx, icv, icv, 8); + if (ossl_sha1(out, inl - 16, sha1tmp) /* Work out hash of first portion */ + && CRYPTO_memcmp(sha1tmp, icv, 8) == 0) + rv = inl - 16; + OPENSSL_cleanse(icv, 8); + OPENSSL_cleanse(sha1tmp, SHA_DIGEST_LENGTH); + OPENSSL_cleanse(iv, 8); + OPENSSL_cleanse(ctx->iv, sizeof(ctx->iv)); + if (rv == -1) + OPENSSL_cleanse(out, inl - 16); + + return rv; +} + +static int des_ede3_wrap(PROV_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t inl) +{ + unsigned char sha1tmp[SHA_DIGEST_LENGTH]; + size_t ivlen = TDES_IVLEN; + size_t icvlen = TDES_IVLEN; + size_t len = inl + ivlen + icvlen; + + if (out == NULL) + return len; + + /* Copy input to output buffer + 8 so we have space for IV */ + memmove(out + ivlen, in, inl); + /* Work out ICV */ + if (!ossl_sha1(in, inl, sha1tmp)) + return 0; + memcpy(out + inl + ivlen, sha1tmp, icvlen); + OPENSSL_cleanse(sha1tmp, SHA_DIGEST_LENGTH); + /* Generate random IV */ + if (RAND_bytes_ex(ctx->libctx, ctx->iv, ivlen, 0) <= 0) + return 0; + memcpy(out, ctx->iv, ivlen); + /* Encrypt everything after IV in place */ + ctx->hw->cipher(ctx, out + ivlen, out + ivlen, inl + ivlen); + BUF_reverse(out, NULL, len); + memcpy(ctx->iv, wrap_iv, ivlen); + ctx->hw->cipher(ctx, out, out, len); + return len; +} + +static int tdes_wrap_cipher_internal(PROV_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t inl) +{ + /* + * Sanity check input length: we typically only wrap keys so EVP_MAXCHUNK + * is more than will ever be needed. Also input length must be a multiple + * of 8 bits. + */ + if (inl >= EVP_MAXCHUNK || inl % 8) + return -1; + if (ctx->enc) + return des_ede3_wrap(ctx, out, in, inl); + else + return des_ede3_unwrap(ctx, out, in, inl); +} + +static int tdes_wrap_cipher(void *vctx, + unsigned char *out, size_t *outl, size_t outsize, + const unsigned char *in, size_t inl) +{ + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + int ret; + + *outl = 0; + if (!ossl_prov_is_running()) + return 0; + + if (outsize < inl) { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + return 0; + } + + ret = tdes_wrap_cipher_internal(ctx, out, in, inl); + if (ret <= 0) + return 0; + + *outl = ret; + return 1; +} + +static int tdes_wrap_update(void *vctx, unsigned char *out, size_t *outl, + size_t outsize, const unsigned char *in, + size_t inl) +{ + *outl = 0; + if (inl == 0) + return 1; + if (outsize < inl) { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + return 0; + } + + if (!tdes_wrap_cipher(vctx, out, outl, outsize, in, inl)) { + ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); + return 0; + } + return 1; +} + + +# define IMPLEMENT_WRAP_CIPHER(flags, kbits, blkbits, ivbits) \ +static OSSL_FUNC_cipher_newctx_fn tdes_wrap_newctx; \ +static void *tdes_wrap_newctx(void *provctx) \ +{ \ + return ossl_tdes_newctx(provctx, EVP_CIPH_WRAP_MODE, kbits, blkbits, \ + ivbits, flags, \ + ossl_prov_cipher_hw_tdes_wrap_cbc()); \ +} \ +static OSSL_FUNC_cipher_get_params_fn tdes_wrap_get_params; \ +static int tdes_wrap_get_params(OSSL_PARAM params[]) \ +{ \ + return ossl_cipher_generic_get_params(params, EVP_CIPH_WRAP_MODE, flags, \ + kbits, blkbits, ivbits); \ +} \ +const OSSL_DISPATCH ossl_tdes_wrap_cbc_functions[] = \ +{ \ + { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void)) ossl_tdes_einit }, \ + { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void)) ossl_tdes_dinit }, \ + { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))tdes_wrap_cipher }, \ + { OSSL_FUNC_CIPHER_NEWCTX, (void (*)(void))tdes_wrap_newctx }, \ + { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))ossl_tdes_freectx }, \ + { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))tdes_wrap_update }, \ + { OSSL_FUNC_CIPHER_FINAL, \ + (void (*)(void))ossl_cipher_generic_stream_final }, \ + { OSSL_FUNC_CIPHER_GET_PARAMS, (void (*)(void))tdes_wrap_get_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \ + (void (*)(void))ossl_cipher_generic_gettable_params }, \ + { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, \ + (void (*)(void))ossl_tdes_get_ctx_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \ + (void (*)(void))ossl_tdes_gettable_ctx_params }, \ + { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \ + (void (*)(void))ossl_cipher_generic_set_ctx_params }, \ + { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \ + (void (*)(void))ossl_cipher_generic_settable_ctx_params }, \ + OSSL_DISPATCH_END \ +} + +/* ossl_tdes_wrap_cbc_functions */ +IMPLEMENT_WRAP_CIPHER(TDES_WRAP_FLAGS, 64*3, 64, 0); diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_tdes_wrap_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_tdes_wrap_hw.c new file mode 100644 index 000000000000..3c8c83332070 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/cipher_tdes_wrap_hw.c @@ -0,0 +1,20 @@ +/* + * Copyright 1995-2020 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 + */ + +/* + * DES low level APIs are deprecated for public use, but still ok for internal + * use. + */ +#include "internal/deprecated.h" + +#include "cipher_tdes_default.h" + +#define ossl_cipher_hw_tdes_wrap_initkey ossl_cipher_hw_tdes_ede3_initkey + +PROV_CIPHER_HW_tdes_mode(wrap, cbc) diff --git a/crypto/openssl/providers/implementations/ciphers/ciphercommon.c b/crypto/openssl/providers/implementations/ciphers/ciphercommon.c new file mode 100644 index 000000000000..b1331b5b64f1 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/ciphercommon.c @@ -0,0 +1,736 @@ +/* + * Copyright 2019-2025 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 + */ + +/* + * Generic dispatch table functions for ciphers. + */ + +/* For SSL3_VERSION */ +#include <openssl/prov_ssl.h> +#include <openssl/proverr.h> +#include "ciphercommon_local.h" +#include "prov/provider_ctx.h" +#include "prov/providercommon.h" +#include "internal/skey.h" +#include "crypto/types.h" + +/*- + * Generic cipher functions for OSSL_PARAM gettables and settables + */ +static const OSSL_PARAM cipher_known_gettable_params[] = { + OSSL_PARAM_uint(OSSL_CIPHER_PARAM_MODE, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_BLOCK_SIZE, NULL), + OSSL_PARAM_int(OSSL_CIPHER_PARAM_AEAD, NULL), + OSSL_PARAM_int(OSSL_CIPHER_PARAM_CUSTOM_IV, NULL), + OSSL_PARAM_int(OSSL_CIPHER_PARAM_CTS, NULL), + OSSL_PARAM_int(OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK, NULL), + OSSL_PARAM_int(OSSL_CIPHER_PARAM_HAS_RAND_KEY, NULL), + OSSL_PARAM_END +}; +const OSSL_PARAM *ossl_cipher_generic_gettable_params(ossl_unused void *provctx) +{ + return cipher_known_gettable_params; +} + +int ossl_cipher_generic_get_params(OSSL_PARAM params[], unsigned int md, + uint64_t flags, + size_t kbits, size_t blkbits, size_t ivbits) +{ + OSSL_PARAM *p; + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_MODE); + if (p != NULL && !OSSL_PARAM_set_uint(p, md)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD); + if (p != NULL + && !OSSL_PARAM_set_int(p, (flags & PROV_CIPHER_FLAG_AEAD) != 0)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_CUSTOM_IV); + if (p != NULL + && !OSSL_PARAM_set_int(p, (flags & PROV_CIPHER_FLAG_CUSTOM_IV) != 0)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_CTS); + if (p != NULL + && !OSSL_PARAM_set_int(p, (flags & PROV_CIPHER_FLAG_CTS) != 0)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK); + if (p != NULL + && !OSSL_PARAM_set_int(p, (flags & PROV_CIPHER_FLAG_TLS1_MULTIBLOCK) != 0)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_HAS_RAND_KEY); + if (p != NULL + && !OSSL_PARAM_set_int(p, (flags & PROV_CIPHER_FLAG_RAND_KEY) != 0)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, kbits / 8)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_BLOCK_SIZE); + if (p != NULL && !OSSL_PARAM_set_size_t(p, blkbits / 8)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, ivbits / 8)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + return 1; +} + +CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_START(ossl_cipher_generic) +{ OSSL_CIPHER_PARAM_TLS_MAC, OSSL_PARAM_OCTET_PTR, NULL, 0, OSSL_PARAM_UNMODIFIED }, +CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_END(ossl_cipher_generic) + +CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_START(ossl_cipher_generic) +OSSL_PARAM_uint(OSSL_CIPHER_PARAM_USE_BITS, NULL), +OSSL_PARAM_uint(OSSL_CIPHER_PARAM_TLS_VERSION, NULL), +OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_TLS_MAC_SIZE, NULL), +CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_END(ossl_cipher_generic) + +/* + * Variable key length cipher functions for OSSL_PARAM settables + */ +int ossl_cipher_var_keylen_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + const OSSL_PARAM *p; + + if (ossl_param_is_empty(params)) + return 1; + + if (!ossl_cipher_generic_set_ctx_params(vctx, params)) + return 0; + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_KEYLEN); + if (p != NULL) { + size_t keylen; + + if (!OSSL_PARAM_get_size_t(p, &keylen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + if (ctx->keylen != keylen) { + ctx->keylen = keylen; + ctx->key_set = 0; + } + } + return 1; +} + +CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_START(ossl_cipher_var_keylen) +OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), +CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_END(ossl_cipher_var_keylen) + +/*- + * AEAD cipher functions for OSSL_PARAM gettables and settables + */ +static const OSSL_PARAM cipher_aead_known_gettable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_TAGLEN, NULL), + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_IV, NULL, 0), + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_UPDATED_IV, NULL, 0), + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, NULL, 0), + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_TLS1_AAD_PAD, NULL), + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TLS1_GET_IV_GEN, NULL, 0), + OSSL_PARAM_uint(OSSL_CIPHER_PARAM_AEAD_IV_GENERATED, NULL), + OSSL_PARAM_END +}; +const OSSL_PARAM *ossl_cipher_aead_gettable_ctx_params( + ossl_unused void *cctx, ossl_unused void *provctx + ) +{ + return cipher_aead_known_gettable_ctx_params; +} + +static const OSSL_PARAM cipher_aead_known_settable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_IVLEN, NULL), + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, NULL, 0), + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TLS1_AAD, NULL, 0), + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TLS1_IV_FIXED, NULL, 0), + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TLS1_SET_IV_INV, NULL, 0), + OSSL_PARAM_END +}; +const OSSL_PARAM *ossl_cipher_aead_settable_ctx_params( + ossl_unused void *cctx, ossl_unused void *provctx + ) +{ + return cipher_aead_known_settable_ctx_params; +} + +void ossl_cipher_generic_reset_ctx(PROV_CIPHER_CTX *ctx) +{ + if (ctx != NULL && ctx->alloced) { + OPENSSL_free(ctx->tlsmac); + ctx->alloced = 0; + ctx->tlsmac = NULL; + } +} + +static int cipher_generic_init_internal(PROV_CIPHER_CTX *ctx, + const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[], int enc) +{ + ctx->num = 0; + ctx->bufsz = 0; + ctx->updated = 0; + ctx->enc = enc ? 1 : 0; + + if (!ossl_prov_is_running()) + return 0; + + if (iv != NULL && ctx->mode != EVP_CIPH_ECB_MODE) { + if (!ossl_cipher_generic_initiv(ctx, iv, ivlen)) + return 0; + } + if (iv == NULL && ctx->iv_set + && (ctx->mode == EVP_CIPH_CBC_MODE + || ctx->mode == EVP_CIPH_CFB_MODE + || ctx->mode == EVP_CIPH_OFB_MODE)) + /* reset IV for these modes to keep compatibility with 1.1.1 */ + memcpy(ctx->iv, ctx->oiv, ctx->ivlen); + + if (key != NULL) { + if (ctx->variable_keylength == 0) { + if (keylen != ctx->keylen) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + } else { + ctx->keylen = keylen; + } + if (!ctx->hw->init(ctx, key, ctx->keylen)) + return 0; + ctx->key_set = 1; + } + return ossl_cipher_generic_set_ctx_params(ctx, params); +} + +int ossl_cipher_generic_einit(void *vctx, const unsigned char *key, + size_t keylen, const unsigned char *iv, + size_t ivlen, const OSSL_PARAM params[]) +{ + return cipher_generic_init_internal((PROV_CIPHER_CTX *)vctx, key, keylen, + iv, ivlen, params, 1); +} + +int ossl_cipher_generic_dinit(void *vctx, const unsigned char *key, + size_t keylen, const unsigned char *iv, + size_t ivlen, const OSSL_PARAM params[]) +{ + return cipher_generic_init_internal((PROV_CIPHER_CTX *)vctx, key, keylen, + iv, ivlen, params, 0); +} + +int ossl_cipher_generic_skey_einit(void *vctx, void *skeydata, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + PROV_SKEY *key = skeydata; + + return cipher_generic_init_internal((PROV_CIPHER_CTX *)vctx, + key->data, key->length, + iv, ivlen, params, 1); +} + +int ossl_cipher_generic_skey_dinit(void *vctx, void *skeydata, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + PROV_SKEY *key = skeydata; + + return cipher_generic_init_internal((PROV_CIPHER_CTX *)vctx, + key->data, key->length, + iv, ivlen, params, 0); +} + +/* Max padding including padding length byte */ +#define MAX_PADDING 256 + +int ossl_cipher_generic_block_update(void *vctx, unsigned char *out, + size_t *outl, size_t outsize, + const unsigned char *in, size_t inl) +{ + size_t outlint = 0; + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + size_t blksz = ctx->blocksize; + size_t nextblocks; + + if (!ctx->key_set) { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET); + return 0; + } + + if (ctx->tlsversion > 0) { + /* + * Each update call corresponds to a TLS record and is individually + * padded + */ + + /* Sanity check inputs */ + if (in == NULL + || in != out + || outsize < inl + || !ctx->pad) { + ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); + return 0; + } + + if (ctx->enc) { + unsigned char padval; + size_t padnum, loop; + + /* Add padding */ + + padnum = blksz - (inl % blksz); + + if (outsize < inl + padnum) { + ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); + return 0; + } + + if (padnum > MAX_PADDING) { + ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); + return 0; + } + padval = (unsigned char)(padnum - 1); + if (ctx->tlsversion == SSL3_VERSION) { + if (padnum > 1) + memset(out + inl, 0, padnum - 1); + *(out + inl + padnum - 1) = padval; + } else { + /* we need to add 'padnum' padding bytes of value padval */ + for (loop = inl; loop < inl + padnum; loop++) + out[loop] = padval; + } + inl += padnum; + } + + if ((inl % blksz) != 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); + return 0; + } + + + /* Shouldn't normally fail */ + if (!ctx->hw->cipher(ctx, out, in, inl)) { + ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); + return 0; + } + + if (ctx->alloced) { + OPENSSL_free(ctx->tlsmac); + ctx->alloced = 0; + ctx->tlsmac = NULL; + } + + /* This only fails if padding is publicly invalid */ + *outl = inl; + if (!ctx->enc + && !ossl_cipher_tlsunpadblock(ctx->libctx, ctx->tlsversion, + out, outl, + blksz, &ctx->tlsmac, &ctx->alloced, + ctx->tlsmacsize, 0)) { + ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); + return 0; + } + return 1; + } + + if (ctx->bufsz != 0) + nextblocks = ossl_cipher_fillblock(ctx->buf, &ctx->bufsz, blksz, + &in, &inl); + else + nextblocks = inl & ~(blksz-1); + + /* + * If we're decrypting and we end an update on a block boundary we hold + * the last block back in case this is the last update call and the last + * block is padded. + */ + if (ctx->bufsz == blksz && (ctx->enc || inl > 0 || !ctx->pad)) { + if (outsize < blksz) { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + return 0; + } + if (!ctx->hw->cipher(ctx, out, ctx->buf, blksz)) { + ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); + return 0; + } + ctx->bufsz = 0; + outlint = blksz; + out += blksz; + } + if (nextblocks > 0) { + if (!ctx->enc && ctx->pad && nextblocks == inl) { + if (!ossl_assert(inl >= blksz)) { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + return 0; + } + nextblocks -= blksz; + } + outlint += nextblocks; + if (outsize < outlint) { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + return 0; + } + } + if (nextblocks > 0) { + if (!ctx->hw->cipher(ctx, out, in, nextblocks)) { + ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); + return 0; + } + in += nextblocks; + inl -= nextblocks; + } + if (inl != 0 + && !ossl_cipher_trailingdata(ctx->buf, &ctx->bufsz, blksz, &in, &inl)) { + /* ERR_raise already called */ + return 0; + } + + *outl = outlint; + return inl == 0; +} + +int ossl_cipher_generic_block_final(void *vctx, unsigned char *out, + size_t *outl, size_t outsize) +{ + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + size_t blksz = ctx->blocksize; + + if (!ossl_prov_is_running()) + return 0; + + if (!ctx->key_set) { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET); + return 0; + } + + if (ctx->tlsversion > 0) { + /* We never finalize TLS, so this is an error */ + ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); + return 0; + } + + if (ctx->enc) { + if (ctx->pad) { + ossl_cipher_padblock(ctx->buf, &ctx->bufsz, blksz); + } else if (ctx->bufsz == 0) { + *outl = 0; + return 1; + } else if (ctx->bufsz != blksz) { + ERR_raise(ERR_LIB_PROV, PROV_R_WRONG_FINAL_BLOCK_LENGTH); + return 0; + } + + if (outsize < blksz) { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + return 0; + } + if (!ctx->hw->cipher(ctx, out, ctx->buf, blksz)) { + ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); + return 0; + } + ctx->bufsz = 0; + *outl = blksz; + return 1; + } + + /* Decrypting */ + if (ctx->bufsz != blksz) { + if (ctx->bufsz == 0 && !ctx->pad) { + *outl = 0; + return 1; + } + ERR_raise(ERR_LIB_PROV, PROV_R_WRONG_FINAL_BLOCK_LENGTH); + return 0; + } + + if (!ctx->hw->cipher(ctx, ctx->buf, ctx->buf, blksz)) { + ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); + return 0; + } + + if (ctx->pad && !ossl_cipher_unpadblock(ctx->buf, &ctx->bufsz, blksz)) { + /* ERR_raise already called */ + return 0; + } + + if (outsize < ctx->bufsz) { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + return 0; + } + memcpy(out, ctx->buf, ctx->bufsz); + *outl = ctx->bufsz; + ctx->bufsz = 0; + return 1; +} + +int ossl_cipher_generic_stream_update(void *vctx, unsigned char *out, + size_t *outl, size_t outsize, + const unsigned char *in, size_t inl) +{ + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + + if (!ctx->key_set) { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET); + return 0; + } + + if (inl == 0) { + *outl = 0; + return 1; + } + + if (outsize < inl) { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + return 0; + } + + if (!ctx->hw->cipher(ctx, out, in, inl)) { + ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); + return 0; + } + + *outl = inl; + if (!ctx->enc && ctx->tlsversion > 0) { + /* + * Remove any TLS padding. Only used by cipher_aes_cbc_hmac_sha1_hw.c and + * cipher_aes_cbc_hmac_sha256_hw.c + */ + if (ctx->removetlspad) { + /* + * We should have already failed in the cipher() call above if this + * isn't true. + */ + if (!ossl_assert(*outl >= (size_t)(out[inl - 1] + 1))) + return 0; + /* The actual padding length */ + *outl -= out[inl - 1] + 1; + } + + /* TLS MAC and explicit IV if relevant. We should have already failed + * in the cipher() call above if *outl is too short. + */ + if (!ossl_assert(*outl >= ctx->removetlsfixed)) + return 0; + *outl -= ctx->removetlsfixed; + + /* Extract the MAC if there is one */ + if (ctx->tlsmacsize > 0) { + if (*outl < ctx->tlsmacsize) + return 0; + + ctx->tlsmac = out + *outl - ctx->tlsmacsize; + *outl -= ctx->tlsmacsize; + } + } + + return 1; +} +int ossl_cipher_generic_stream_final(void *vctx, unsigned char *out, + size_t *outl, size_t outsize) +{ + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + + if (!ossl_prov_is_running()) + return 0; + + if (!ctx->key_set) { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET); + return 0; + } + + *outl = 0; + return 1; +} + +int ossl_cipher_generic_cipher(void *vctx, unsigned char *out, size_t *outl, + size_t outsize, const unsigned char *in, + size_t inl) +{ + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + + if (!ossl_prov_is_running()) + return 0; + + if (!ctx->key_set) { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET); + return 0; + } + + if (outsize < inl) { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + return 0; + } + + if (!ctx->hw->cipher(ctx, out, in, inl)) { + ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); + return 0; + } + + *outl = inl; + return 1; +} + +int ossl_cipher_generic_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + OSSL_PARAM *p; + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->ivlen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_PADDING); + if (p != NULL && !OSSL_PARAM_set_uint(p, ctx->pad)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IV); + if (p != NULL + && !OSSL_PARAM_set_octet_ptr(p, &ctx->oiv, ctx->ivlen) + && !OSSL_PARAM_set_octet_string(p, &ctx->oiv, ctx->ivlen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_UPDATED_IV); + if (p != NULL + && !OSSL_PARAM_set_octet_ptr(p, &ctx->iv, ctx->ivlen) + && !OSSL_PARAM_set_octet_string(p, &ctx->iv, ctx->ivlen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_NUM); + if (p != NULL && !OSSL_PARAM_set_uint(p, ctx->num)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->keylen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_TLS_MAC); + if (p != NULL + && !OSSL_PARAM_set_octet_ptr(p, ctx->tlsmac, ctx->tlsmacsize)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + return 1; +} + +int ossl_cipher_generic_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + const OSSL_PARAM *p; + + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_PADDING); + if (p != NULL) { + unsigned int pad; + + if (!OSSL_PARAM_get_uint(p, &pad)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + ctx->pad = pad ? 1 : 0; + } + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_USE_BITS); + if (p != NULL) { + unsigned int bits; + + if (!OSSL_PARAM_get_uint(p, &bits)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + ctx->use_bits = bits ? 1 : 0; + } + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_TLS_VERSION); + if (p != NULL) { + if (!OSSL_PARAM_get_uint(p, &ctx->tlsversion)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + } + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_TLS_MAC_SIZE); + if (p != NULL) { + if (!OSSL_PARAM_get_size_t(p, &ctx->tlsmacsize)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + } + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_NUM); + if (p != NULL) { + unsigned int num; + + if (!OSSL_PARAM_get_uint(p, &num)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + ctx->num = num; + } + return 1; +} + +int ossl_cipher_generic_initiv(PROV_CIPHER_CTX *ctx, const unsigned char *iv, + size_t ivlen) +{ + if (ivlen != ctx->ivlen + || ivlen > sizeof(ctx->iv)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); + return 0; + } + ctx->iv_set = 1; + memcpy(ctx->iv, iv, ivlen); + memcpy(ctx->oiv, iv, ivlen); + return 1; +} + +void ossl_cipher_generic_initkey(void *vctx, size_t kbits, size_t blkbits, + size_t ivbits, unsigned int mode, + uint64_t flags, const PROV_CIPHER_HW *hw, + void *provctx) +{ + PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; + + if ((flags & PROV_CIPHER_FLAG_INVERSE_CIPHER) != 0) + ctx->inverse_cipher = 1; + if ((flags & PROV_CIPHER_FLAG_VARIABLE_LENGTH) != 0) + ctx->variable_keylength = 1; + + ctx->pad = 1; + ctx->keylen = ((kbits) / 8); + ctx->ivlen = ((ivbits) / 8); + ctx->hw = hw; + ctx->mode = mode; + ctx->blocksize = blkbits / 8; + if (provctx != NULL) + ctx->libctx = PROV_LIBCTX_OF(provctx); /* used for rand */ +} diff --git a/crypto/openssl/providers/implementations/ciphers/ciphercommon_block.c b/crypto/openssl/providers/implementations/ciphers/ciphercommon_block.c new file mode 100644 index 000000000000..cfc78e07709f --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/ciphercommon_block.c @@ -0,0 +1,173 @@ +/* + * Copyright 2019-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 + */ + +#include <assert.h> +/* For SSL3_VERSION, TLS1_VERSION etc */ +#include <openssl/prov_ssl.h> +#include <openssl/rand.h> +#include <openssl/proverr.h> +#include "internal/constant_time.h" +#include "internal/ssl3_cbc.h" +#include "ciphercommon_local.h" + +/* + * Fills a single block of buffered data from the input, and returns the amount + * of data remaining in the input that is a multiple of the blocksize. The buffer + * is only filled if it already has some data in it, isn't full already or we + * don't have at least one block in the input. + * + * buf: a buffer of blocksize bytes + * buflen: contains the amount of data already in buf on entry. Updated with the + * amount of data in buf at the end. On entry *buflen must always be + * less than the blocksize + * blocksize: size of a block. Must be greater than 0 and a power of 2 + * in: pointer to a pointer containing the input data + * inlen: amount of input data available + * + * On return buf is filled with as much data as possible up to a full block, + * *buflen is updated containing the amount of data in buf. *in is updated to + * the new location where input data should be read from, *inlen is updated with + * the remaining amount of data in *in. Returns the largest value <= *inlen + * which is a multiple of the blocksize. + */ +size_t ossl_cipher_fillblock(unsigned char *buf, size_t *buflen, + size_t blocksize, + const unsigned char **in, size_t *inlen) +{ + size_t blockmask = ~(blocksize - 1); + size_t bufremain = blocksize - *buflen; + + assert(*buflen <= blocksize); + assert(blocksize > 0 && (blocksize & (blocksize - 1)) == 0); + + if (*inlen < bufremain) + bufremain = *inlen; + memcpy(buf + *buflen, *in, bufremain); + *in += bufremain; + *inlen -= bufremain; + *buflen += bufremain; + + return *inlen & blockmask; +} + +/* + * Fills the buffer with trailing data from an encryption/decryption that didn't + * fit into a full block. + */ +int ossl_cipher_trailingdata(unsigned char *buf, size_t *buflen, size_t blocksize, + const unsigned char **in, size_t *inlen) +{ + if (*inlen == 0) + return 1; + + if (*buflen + *inlen > blocksize) { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + return 0; + } + + memcpy(buf + *buflen, *in, *inlen); + *buflen += *inlen; + *inlen = 0; + + return 1; +} + +/* Pad the final block for encryption */ +void ossl_cipher_padblock(unsigned char *buf, size_t *buflen, size_t blocksize) +{ + size_t i; + unsigned char pad = (unsigned char)(blocksize - *buflen); + + for (i = *buflen; i < blocksize; i++) + buf[i] = pad; +} + +int ossl_cipher_unpadblock(unsigned char *buf, size_t *buflen, size_t blocksize) +{ + size_t pad, i; + size_t len = *buflen; + + if (len != blocksize) { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + return 0; + } + + /* + * The following assumes that the ciphertext has been authenticated. + * Otherwise it provides a padding oracle. + */ + pad = buf[blocksize - 1]; + if (pad == 0 || pad > blocksize) { + ERR_raise(ERR_LIB_PROV, PROV_R_BAD_DECRYPT); + return 0; + } + for (i = 0; i < pad; i++) { + if (buf[--len] != pad) { + ERR_raise(ERR_LIB_PROV, PROV_R_BAD_DECRYPT); + return 0; + } + } + *buflen = len; + return 1; +} + +/*- + * ossl_cipher_tlsunpadblock removes the CBC padding from the decrypted, TLS, CBC + * record in constant time. Also removes the MAC from the record in constant + * time. + * + * libctx: Our library context + * tlsversion: The TLS version in use, e.g. SSL3_VERSION, TLS1_VERSION, etc + * buf: The decrypted TLS record data + * buflen: The length of the decrypted TLS record data. Updated with the new + * length after the padding is removed + * block_size: the block size of the cipher used to encrypt the record. + * mac: Location to store the pointer to the MAC + * alloced: Whether the MAC is stored in a newly allocated buffer, or whether + * *mac points into *buf + * macsize: the size of the MAC inside the record (or 0 if there isn't one) + * aead: whether this is an aead cipher + * returns: + * 0: (in non-constant time) if the record is publicly invalid. + * 1: (in constant time) Record is publicly valid. If padding is invalid then + * the mac is random + */ +int ossl_cipher_tlsunpadblock(OSSL_LIB_CTX *libctx, unsigned int tlsversion, + unsigned char *buf, size_t *buflen, + size_t blocksize, + unsigned char **mac, int *alloced, size_t macsize, + int aead) +{ + int ret; + + switch (tlsversion) { + case SSL3_VERSION: + return ssl3_cbc_remove_padding_and_mac(buflen, *buflen, buf, mac, + alloced, blocksize, macsize, + libctx); + + case TLS1_2_VERSION: + case DTLS1_2_VERSION: + case TLS1_1_VERSION: + case DTLS1_VERSION: + case DTLS1_BAD_VER: + /* Remove the explicit IV */ + buf += blocksize; + *buflen -= blocksize; + /* Fall through */ + case TLS1_VERSION: + ret = tls1_cbc_remove_padding_and_mac(buflen, *buflen, buf, mac, + alloced, blocksize, macsize, + aead, libctx); + return ret; + + default: + return 0; + } +} diff --git a/crypto/openssl/providers/implementations/ciphers/ciphercommon_ccm.c b/crypto/openssl/providers/implementations/ciphers/ciphercommon_ccm.c new file mode 100644 index 000000000000..80008a815182 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/ciphercommon_ccm.c @@ -0,0 +1,456 @@ +/* + * Copyright 2019-2021 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 + */ + +/* Dispatch functions for ccm mode */ + +#include <openssl/proverr.h> +#include "prov/ciphercommon.h" +#include "prov/ciphercommon_ccm.h" +#include "prov/providercommon.h" + +static int ccm_cipher_internal(PROV_CCM_CTX *ctx, unsigned char *out, + size_t *padlen, const unsigned char *in, + size_t len); + +static int ccm_tls_init(PROV_CCM_CTX *ctx, unsigned char *aad, size_t alen) +{ + size_t len; + + if (!ossl_prov_is_running() || alen != EVP_AEAD_TLS1_AAD_LEN) + return 0; + + /* Save the aad for later use. */ + memcpy(ctx->buf, aad, alen); + ctx->tls_aad_len = alen; + + len = ctx->buf[alen - 2] << 8 | ctx->buf[alen - 1]; + if (len < EVP_CCM_TLS_EXPLICIT_IV_LEN) + return 0; + + /* Correct length for explicit iv. */ + len -= EVP_CCM_TLS_EXPLICIT_IV_LEN; + + if (!ctx->enc) { + if (len < ctx->m) + return 0; + /* Correct length for tag. */ + len -= ctx->m; + } + ctx->buf[alen - 2] = (unsigned char)(len >> 8); + ctx->buf[alen - 1] = (unsigned char)(len & 0xff); + + /* Extra padding: tag appended to record. */ + return ctx->m; +} + +static int ccm_tls_iv_set_fixed(PROV_CCM_CTX *ctx, unsigned char *fixed, + size_t flen) +{ + if (flen != EVP_CCM_TLS_FIXED_IV_LEN) + return 0; + + /* Copy to first part of the iv. */ + memcpy(ctx->iv, fixed, flen); + return 1; +} + +static size_t ccm_get_ivlen(PROV_CCM_CTX *ctx) +{ + return 15 - ctx->l; +} + +int ossl_ccm_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_CCM_CTX *ctx = (PROV_CCM_CTX *)vctx; + const OSSL_PARAM *p; + size_t sz; + + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TAG); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_OCTET_STRING) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + if ((p->data_size & 1) || (p->data_size < 4) || p->data_size > 16) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_TAG_LENGTH); + return 0; + } + + if (p->data != NULL) { + if (ctx->enc) { + ERR_raise(ERR_LIB_PROV, PROV_R_TAG_NOT_NEEDED); + return 0; + } + memcpy(ctx->buf, p->data, p->data_size); + ctx->tag_set = 1; + } + ctx->m = p->data_size; + } + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_IVLEN); + if (p != NULL) { + size_t ivlen; + + if (!OSSL_PARAM_get_size_t(p, &sz)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + ivlen = 15 - sz; + if (ivlen < 2 || ivlen > 8) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); + return 0; + } + if (ctx->l != ivlen) { + ctx->l = ivlen; + ctx->iv_set = 0; + } + } + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TLS1_AAD); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_OCTET_STRING) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + sz = ccm_tls_init(ctx, p->data, p->data_size); + if (sz == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DATA); + return 0; + } + ctx->tls_aad_pad_sz = sz; + } + + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TLS1_IV_FIXED); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_OCTET_STRING) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + if (ccm_tls_iv_set_fixed(ctx, p->data, p->data_size) == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); + return 0; + } + } + + return 1; +} + +int ossl_ccm_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + PROV_CCM_CTX *ctx = (PROV_CCM_CTX *)vctx; + OSSL_PARAM *p; + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, ccm_get_ivlen(ctx))) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TAGLEN); + if (p != NULL) { + size_t m = ctx->m; + + if (!OSSL_PARAM_set_size_t(p, m)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + } + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IV); + if (p != NULL) { + if (ccm_get_ivlen(ctx) > p->data_size) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); + return 0; + } + if (!OSSL_PARAM_set_octet_string(p, ctx->iv, p->data_size) + && !OSSL_PARAM_set_octet_ptr(p, &ctx->iv, p->data_size)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + } + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_UPDATED_IV); + if (p != NULL) { + if (ccm_get_ivlen(ctx) > p->data_size) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); + return 0; + } + if (!OSSL_PARAM_set_octet_string(p, ctx->iv, p->data_size) + && !OSSL_PARAM_set_octet_ptr(p, &ctx->iv, p->data_size)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + } + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->keylen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TLS1_AAD_PAD); + if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->tls_aad_pad_sz)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TAG); + if (p != NULL) { + if (!ctx->enc || !ctx->tag_set) { + ERR_raise(ERR_LIB_PROV, PROV_R_TAG_NOT_SET); + return 0; + } + if (p->data_type != OSSL_PARAM_OCTET_STRING) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + if (!ctx->hw->gettag(ctx, p->data, p->data_size)) + return 0; + ctx->tag_set = 0; + ctx->iv_set = 0; + ctx->len_set = 0; + } + return 1; +} + +static int ccm_init(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[], int enc) +{ + PROV_CCM_CTX *ctx = (PROV_CCM_CTX *)vctx; + + if (!ossl_prov_is_running()) + return 0; + + ctx->enc = enc; + + if (iv != NULL) { + if (ivlen != ccm_get_ivlen(ctx)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); + return 0; + } + memcpy(ctx->iv, iv, ivlen); + ctx->iv_set = 1; + } + if (key != NULL) { + if (keylen != ctx->keylen) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + if (!ctx->hw->setkey(ctx, key, keylen)) + return 0; + } + return ossl_ccm_set_ctx_params(ctx, params); +} + +int ossl_ccm_einit(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + return ccm_init(vctx, key, keylen, iv, ivlen, params, 1); +} + +int ossl_ccm_dinit(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + return ccm_init(vctx, key, keylen, iv, ivlen, params, 0); +} + +int ossl_ccm_stream_update(void *vctx, unsigned char *out, size_t *outl, + size_t outsize, const unsigned char *in, + size_t inl) +{ + PROV_CCM_CTX *ctx = (PROV_CCM_CTX *)vctx; + + if (outsize < inl) { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + return 0; + } + + if (!ccm_cipher_internal(ctx, out, outl, in, inl)) { + ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); + return 0; + } + return 1; +} + +int ossl_ccm_stream_final(void *vctx, unsigned char *out, size_t *outl, + size_t outsize) +{ + PROV_CCM_CTX *ctx = (PROV_CCM_CTX *)vctx; + int i; + + if (!ossl_prov_is_running()) + return 0; + + i = ccm_cipher_internal(ctx, out, outl, NULL, 0); + if (i <= 0) + return 0; + + *outl = 0; + return 1; +} + +int ossl_ccm_cipher(void *vctx, unsigned char *out, size_t *outl, size_t outsize, + const unsigned char *in, size_t inl) +{ + PROV_CCM_CTX *ctx = (PROV_CCM_CTX *)vctx; + + if (!ossl_prov_is_running()) + return 0; + + if (outsize < inl) { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + return 0; + } + + if (ccm_cipher_internal(ctx, out, outl, in, inl) <= 0) + return 0; + + *outl = inl; + return 1; +} + +/* Copy the buffered iv */ +static int ccm_set_iv(PROV_CCM_CTX *ctx, size_t mlen) +{ + const PROV_CCM_HW *hw = ctx->hw; + + if (!hw->setiv(ctx, ctx->iv, ccm_get_ivlen(ctx), mlen)) + return 0; + ctx->len_set = 1; + return 1; +} + +static int ccm_tls_cipher(PROV_CCM_CTX *ctx, + unsigned char *out, size_t *padlen, + const unsigned char *in, size_t len) +{ + int rv = 0; + size_t olen = 0; + + if (!ossl_prov_is_running()) + goto err; + + /* Encrypt/decrypt must be performed in place */ + if (in == NULL || out != in || len < EVP_CCM_TLS_EXPLICIT_IV_LEN + ctx->m) + goto err; + + /* If encrypting set explicit IV from sequence number (start of AAD) */ + if (ctx->enc) + memcpy(out, ctx->buf, EVP_CCM_TLS_EXPLICIT_IV_LEN); + /* Get rest of IV from explicit IV */ + memcpy(ctx->iv + EVP_CCM_TLS_FIXED_IV_LEN, in, EVP_CCM_TLS_EXPLICIT_IV_LEN); + /* Correct length value */ + len -= EVP_CCM_TLS_EXPLICIT_IV_LEN + ctx->m; + if (!ccm_set_iv(ctx, len)) + goto err; + + /* Use saved AAD */ + if (!ctx->hw->setaad(ctx, ctx->buf, ctx->tls_aad_len)) + goto err; + + /* Fix buffer to point to payload */ + in += EVP_CCM_TLS_EXPLICIT_IV_LEN; + out += EVP_CCM_TLS_EXPLICIT_IV_LEN; + if (ctx->enc) { + if (!ctx->hw->auth_encrypt(ctx, in, out, len, out + len, ctx->m)) + goto err; + olen = len + EVP_CCM_TLS_EXPLICIT_IV_LEN + ctx->m; + } else { + if (!ctx->hw->auth_decrypt(ctx, in, out, len, + (unsigned char *)in + len, ctx->m)) + goto err; + olen = len; + } + rv = 1; +err: + *padlen = olen; + return rv; +} + +static int ccm_cipher_internal(PROV_CCM_CTX *ctx, unsigned char *out, + size_t *padlen, const unsigned char *in, + size_t len) +{ + int rv = 0; + size_t olen = 0; + const PROV_CCM_HW *hw = ctx->hw; + + /* If no key set, return error */ + if (!ctx->key_set) + return 0; + + if (ctx->tls_aad_len != UNINITIALISED_SIZET) + return ccm_tls_cipher(ctx, out, padlen, in, len); + + /* EVP_*Final() doesn't return any data */ + if (in == NULL && out != NULL) + goto finish; + + if (!ctx->iv_set) + goto err; + + if (out == NULL) { + if (in == NULL) { + if (!ccm_set_iv(ctx, len)) + goto err; + } else { + /* If we have AAD, we need a message length */ + if (!ctx->len_set && len) + goto err; + if (!hw->setaad(ctx, in, len)) + goto err; + } + } else { + /* If not set length yet do it */ + if (!ctx->len_set && !ccm_set_iv(ctx, len)) + goto err; + + if (ctx->enc) { + if (!hw->auth_encrypt(ctx, in, out, len, NULL, 0)) + goto err; + ctx->tag_set = 1; + } else { + /* The tag must be set before actually decrypting data */ + if (!ctx->tag_set) + goto err; + + if (!hw->auth_decrypt(ctx, in, out, len, ctx->buf, ctx->m)) + goto err; + /* Finished - reset flags so calling this method again will fail */ + ctx->iv_set = 0; + ctx->tag_set = 0; + ctx->len_set = 0; + } + } + olen = len; +finish: + rv = 1; +err: + *padlen = olen; + return rv; +} + +void ossl_ccm_initctx(PROV_CCM_CTX *ctx, size_t keybits, const PROV_CCM_HW *hw) +{ + ctx->keylen = keybits / 8; + ctx->key_set = 0; + ctx->iv_set = 0; + ctx->tag_set = 0; + ctx->len_set = 0; + ctx->l = 8; + ctx->m = 12; + ctx->tls_aad_len = UNINITIALISED_SIZET; + ctx->hw = hw; +} diff --git a/crypto/openssl/providers/implementations/ciphers/ciphercommon_ccm_hw.c b/crypto/openssl/providers/implementations/ciphers/ciphercommon_ccm_hw.c new file mode 100644 index 000000000000..ad3fbc59e4fc --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/ciphercommon_ccm_hw.c @@ -0,0 +1,69 @@ +/* + * Copyright 2019-2021 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 + */ + +#include "prov/ciphercommon.h" +#include "prov/ciphercommon_ccm.h" + +int ossl_ccm_generic_setiv(PROV_CCM_CTX *ctx, const unsigned char *nonce, + size_t nlen, size_t mlen) +{ + return CRYPTO_ccm128_setiv(&ctx->ccm_ctx, nonce, nlen, mlen) == 0; +} + +int ossl_ccm_generic_setaad(PROV_CCM_CTX *ctx, const unsigned char *aad, + size_t alen) +{ + CRYPTO_ccm128_aad(&ctx->ccm_ctx, aad, alen); + return 1; +} + +int ossl_ccm_generic_gettag(PROV_CCM_CTX *ctx, unsigned char *tag, size_t tlen) +{ + return CRYPTO_ccm128_tag(&ctx->ccm_ctx, tag, tlen) > 0; +} + +int ossl_ccm_generic_auth_encrypt(PROV_CCM_CTX *ctx, const unsigned char *in, + unsigned char *out, size_t len, + unsigned char *tag, size_t taglen) +{ + int rv; + + if (ctx->str != NULL) + rv = CRYPTO_ccm128_encrypt_ccm64(&ctx->ccm_ctx, in, + out, len, ctx->str) == 0; + else + rv = CRYPTO_ccm128_encrypt(&ctx->ccm_ctx, in, out, len) == 0; + + if (rv == 1 && tag != NULL) + rv = (CRYPTO_ccm128_tag(&ctx->ccm_ctx, tag, taglen) > 0); + return rv; +} + +int ossl_ccm_generic_auth_decrypt(PROV_CCM_CTX *ctx, const unsigned char *in, + unsigned char *out, size_t len, + unsigned char *expected_tag, size_t taglen) +{ + int rv = 0; + + if (ctx->str != NULL) + rv = CRYPTO_ccm128_decrypt_ccm64(&ctx->ccm_ctx, in, out, len, + ctx->str) == 0; + else + rv = CRYPTO_ccm128_decrypt(&ctx->ccm_ctx, in, out, len) == 0; + if (rv) { + unsigned char tag[16]; + + if (!CRYPTO_ccm128_tag(&ctx->ccm_ctx, tag, taglen) + || CRYPTO_memcmp(tag, expected_tag, taglen) != 0) + rv = 0; + } + if (rv == 0) + OPENSSL_cleanse(out, len); + return rv; +} diff --git a/crypto/openssl/providers/implementations/ciphers/ciphercommon_gcm.c b/crypto/openssl/providers/implementations/ciphers/ciphercommon_gcm.c new file mode 100644 index 000000000000..475693f336a4 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/ciphercommon_gcm.c @@ -0,0 +1,596 @@ +/* + * Copyright 2019-2024 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 + */ + +/* Dispatch functions for gcm mode */ + +#include <openssl/rand.h> +#include <openssl/proverr.h> +#include "prov/ciphercommon.h" +#include "prov/ciphercommon_gcm.h" +#include "prov/providercommon.h" +#include "prov/provider_ctx.h" +#include "internal/param_names.h" + +static int gcm_tls_init(PROV_GCM_CTX *dat, unsigned char *aad, size_t aad_len); +static int gcm_tls_iv_set_fixed(PROV_GCM_CTX *ctx, unsigned char *iv, + size_t len); +static int gcm_tls_cipher(PROV_GCM_CTX *ctx, unsigned char *out, size_t *padlen, + const unsigned char *in, size_t len); +static int gcm_cipher_internal(PROV_GCM_CTX *ctx, unsigned char *out, + size_t *padlen, const unsigned char *in, + size_t len); + +/* + * Called from EVP_CipherInit when there is currently no context via + * the new_ctx() function + */ +void ossl_gcm_initctx(void *provctx, PROV_GCM_CTX *ctx, size_t keybits, + const PROV_GCM_HW *hw) +{ + ctx->pad = 1; + ctx->mode = EVP_CIPH_GCM_MODE; + ctx->taglen = UNINITIALISED_SIZET; + ctx->tls_aad_len = UNINITIALISED_SIZET; + ctx->ivlen = (EVP_GCM_TLS_FIXED_IV_LEN + EVP_GCM_TLS_EXPLICIT_IV_LEN); + ctx->keylen = keybits / 8; + ctx->hw = hw; + ctx->libctx = PROV_LIBCTX_OF(provctx); +} + +/* + * Called by EVP_CipherInit via the _einit and _dinit functions + */ +static int gcm_init(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[], int enc) +{ + PROV_GCM_CTX *ctx = (PROV_GCM_CTX *)vctx; + + if (!ossl_prov_is_running()) + return 0; + + ctx->enc = enc; + + if (iv != NULL) { + if (ivlen == 0 || ivlen > sizeof(ctx->iv)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); + return 0; + } + ctx->ivlen = ivlen; + memcpy(ctx->iv, iv, ivlen); + ctx->iv_state = IV_STATE_BUFFERED; + } + + if (key != NULL) { + if (keylen != ctx->keylen) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + if (!ctx->hw->setkey(ctx, key, ctx->keylen)) + return 0; + ctx->tls_enc_records = 0; + } + return ossl_gcm_set_ctx_params(ctx, params); +} + +int ossl_gcm_einit(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + return gcm_init(vctx, key, keylen, iv, ivlen, params, 1); +} + +int ossl_gcm_dinit(void *vctx, const unsigned char *key, size_t keylen, + const unsigned char *iv, size_t ivlen, + const OSSL_PARAM params[]) +{ + return gcm_init(vctx, key, keylen, iv, ivlen, params, 0); +} + +/* increment counter (64-bit int) by 1 */ +static void ctr64_inc(unsigned char *counter) +{ + int n = 8; + unsigned char c; + + do { + --n; + c = counter[n]; + ++c; + counter[n] = c; + if (c > 0) + return; + } while (n > 0); +} + +static int getivgen(PROV_GCM_CTX *ctx, unsigned char *out, size_t olen) +{ + if (!ctx->iv_gen + || !ctx->key_set + || !ctx->hw->setiv(ctx, ctx->iv, ctx->ivlen)) + return 0; + if (olen == 0 || olen > ctx->ivlen) + olen = ctx->ivlen; + memcpy(out, ctx->iv + ctx->ivlen - olen, olen); + /* + * Invocation field will be at least 8 bytes in size and so no need + * to check wrap around or increment more than last 8 bytes. + */ + ctr64_inc(ctx->iv + ctx->ivlen - 8); + ctx->iv_state = IV_STATE_COPIED; + return 1; +} + +static int setivinv(PROV_GCM_CTX *ctx, unsigned char *in, size_t inl) +{ + if (!ctx->iv_gen + || !ctx->key_set + || ctx->enc) + return 0; + + memcpy(ctx->iv + ctx->ivlen - inl, in, inl); + if (!ctx->hw->setiv(ctx, ctx->iv, ctx->ivlen)) + return 0; + ctx->iv_state = IV_STATE_COPIED; + return 1; +} + +int ossl_gcm_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + PROV_GCM_CTX *ctx = (PROV_GCM_CTX *)vctx; + OSSL_PARAM *p; + size_t sz; + int type; + + for (p = params; p->key != NULL; p++) { + type = ossl_param_find_pidx(p->key); + switch (type) { + default: + break; + + case PIDX_CIPHER_PARAM_IVLEN: + if (!OSSL_PARAM_set_size_t(p, ctx->ivlen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + break; + + case PIDX_CIPHER_PARAM_KEYLEN: + if (!OSSL_PARAM_set_size_t(p, ctx->keylen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + break; + + case PIDX_CIPHER_PARAM_AEAD_TAGLEN: + { + size_t taglen = (ctx->taglen != UNINITIALISED_SIZET) ? ctx->taglen : + GCM_TAG_MAX_SIZE; + + if (!OSSL_PARAM_set_size_t(p, taglen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + } + break; + + case PIDX_CIPHER_PARAM_IV: + if (ctx->iv_state == IV_STATE_UNINITIALISED) + return 0; + if (ctx->ivlen > p->data_size) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); + return 0; + } + if (!OSSL_PARAM_set_octet_string(p, ctx->iv, ctx->ivlen) + && !OSSL_PARAM_set_octet_ptr(p, &ctx->iv, ctx->ivlen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + break; + + case PIDX_CIPHER_PARAM_UPDATED_IV: + if (ctx->iv_state == IV_STATE_UNINITIALISED) + return 0; + if (ctx->ivlen > p->data_size) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); + return 0; + } + if (!OSSL_PARAM_set_octet_string(p, ctx->iv, ctx->ivlen) + && !OSSL_PARAM_set_octet_ptr(p, &ctx->iv, ctx->ivlen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + break; + + case PIDX_CIPHER_PARAM_AEAD_TLS1_AAD_PAD: + if (!OSSL_PARAM_set_size_t(p, ctx->tls_aad_pad_sz)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + break; + + case PIDX_CIPHER_PARAM_AEAD_TAG: + sz = p->data_size; + if (sz == 0 + || sz > EVP_GCM_TLS_TAG_LEN + || !ctx->enc + || ctx->taglen == UNINITIALISED_SIZET) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_TAG); + return 0; + } + if (!OSSL_PARAM_set_octet_string(p, ctx->buf, sz)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + break; + + case PIDX_CIPHER_PARAM_AEAD_TLS1_GET_IV_GEN: + if (p->data == NULL + || p->data_type != OSSL_PARAM_OCTET_STRING + || !getivgen(ctx, p->data, p->data_size)) + return 0; + break; + case PIDX_CIPHER_PARAM_AEAD_IV_GENERATED: + if (!OSSL_PARAM_set_uint(p, ctx->iv_gen_rand)) + return 0; + } + } + return 1; +} + +int ossl_gcm_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_GCM_CTX *ctx = (PROV_GCM_CTX *)vctx; + const OSSL_PARAM *p; + size_t sz; + void *vp; + int type; + + if (ossl_param_is_empty(params)) + return 1; + + for (p = params; p->key != NULL; p++) { + type = ossl_param_find_pidx(p->key); + switch (type) { + default: + break; + + case PIDX_CIPHER_PARAM_AEAD_TAG: + vp = ctx->buf; + if (!OSSL_PARAM_get_octet_string(p, &vp, EVP_GCM_TLS_TAG_LEN, &sz)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + if (sz == 0 || ctx->enc) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_TAG); + return 0; + } + ctx->taglen = sz; + break; + + case PIDX_CIPHER_PARAM_AEAD_IVLEN: + if (!OSSL_PARAM_get_size_t(p, &sz)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + if (sz == 0 || sz > sizeof(ctx->iv)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH); + return 0; + } + if (ctx->ivlen != sz) { + /* If the iv was already set or autogenerated, it is invalid. */ + if (ctx->iv_state != IV_STATE_UNINITIALISED) + ctx->iv_state = IV_STATE_FINISHED; + ctx->ivlen = sz; + } + break; + + case PIDX_CIPHER_PARAM_AEAD_TLS1_AAD: + if (p->data_type != OSSL_PARAM_OCTET_STRING) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + sz = gcm_tls_init(ctx, p->data, p->data_size); + if (sz == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_AAD); + return 0; + } + ctx->tls_aad_pad_sz = sz; + break; + + case PIDX_CIPHER_PARAM_AEAD_TLS1_IV_FIXED: + if (p->data_type != OSSL_PARAM_OCTET_STRING) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + if (gcm_tls_iv_set_fixed(ctx, p->data, p->data_size) == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + break; + + case PIDX_CIPHER_PARAM_AEAD_TLS1_SET_IV_INV: + if (p->data == NULL + || p->data_type != OSSL_PARAM_OCTET_STRING + || !setivinv(ctx, p->data, p->data_size)) + return 0; + break; + } + } + + return 1; +} + +int ossl_gcm_stream_update(void *vctx, unsigned char *out, size_t *outl, + size_t outsize, const unsigned char *in, size_t inl) +{ + PROV_GCM_CTX *ctx = (PROV_GCM_CTX *)vctx; + + if (inl == 0) { + *outl = 0; + return 1; + } + + if (outsize < inl) { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + return 0; + } + + if (gcm_cipher_internal(ctx, out, outl, in, inl) <= 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); + return 0; + } + return 1; +} + +int ossl_gcm_stream_final(void *vctx, unsigned char *out, size_t *outl, + size_t outsize) +{ + PROV_GCM_CTX *ctx = (PROV_GCM_CTX *)vctx; + int i; + + if (!ossl_prov_is_running()) + return 0; + + i = gcm_cipher_internal(ctx, out, outl, NULL, 0); + if (i <= 0) + return 0; + + *outl = 0; + return 1; +} + +int ossl_gcm_cipher(void *vctx, + unsigned char *out, size_t *outl, size_t outsize, + const unsigned char *in, size_t inl) +{ + PROV_GCM_CTX *ctx = (PROV_GCM_CTX *)vctx; + + if (!ossl_prov_is_running()) + return 0; + + if (outsize < inl) { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + return 0; + } + + if (gcm_cipher_internal(ctx, out, outl, in, inl) <= 0) + return 0; + + *outl = inl; + return 1; +} + +/* + * See SP800-38D (GCM) Section 8 "Uniqueness requirement on IVS and keys" + * + * See also 8.2.2 RBG-based construction. + * Random construction consists of a free field (which can be NULL) and a + * random field which will use a DRBG that can return at least 96 bits of + * entropy strength. (The DRBG must be seeded by the FIPS module). + */ +static int gcm_iv_generate(PROV_GCM_CTX *ctx, int offset) +{ + int sz = ctx->ivlen - offset; + + /* Must be at least 96 bits */ + if (sz <= 0 || ctx->ivlen < GCM_IV_DEFAULT_SIZE) + return 0; + + /* Use DRBG to generate random iv */ + if (RAND_bytes_ex(ctx->libctx, ctx->iv + offset, sz, 0) <= 0) + return 0; + ctx->iv_state = IV_STATE_BUFFERED; + ctx->iv_gen_rand = 1; + return 1; +} + +static int gcm_cipher_internal(PROV_GCM_CTX *ctx, unsigned char *out, + size_t *padlen, const unsigned char *in, + size_t len) +{ + size_t olen = 0; + int rv = 0; + const PROV_GCM_HW *hw = ctx->hw; + + if (ctx->tls_aad_len != UNINITIALISED_SIZET) + return gcm_tls_cipher(ctx, out, padlen, in, len); + + if (!ctx->key_set || ctx->iv_state == IV_STATE_FINISHED) + goto err; + + /* + * FIPS requires generation of AES-GCM IV's inside the FIPS module. + * The IV can still be set externally (the security policy will state that + * this is not FIPS compliant). There are some applications + * where setting the IV externally is the only option available. + */ + if (ctx->iv_state == IV_STATE_UNINITIALISED) { + if (!ctx->enc || !gcm_iv_generate(ctx, 0)) + goto err; + } + + if (ctx->iv_state == IV_STATE_BUFFERED) { + if (!hw->setiv(ctx, ctx->iv, ctx->ivlen)) + goto err; + ctx->iv_state = IV_STATE_COPIED; + } + + if (in != NULL) { + /* The input is AAD if out is NULL */ + if (out == NULL) { + if (!hw->aadupdate(ctx, in, len)) + goto err; + } else { + /* The input is ciphertext OR plaintext */ + if (!hw->cipherupdate(ctx, in, len, out)) + goto err; + } + } else { + /* The tag must be set before actually decrypting data */ + if (!ctx->enc && ctx->taglen == UNINITIALISED_SIZET) + goto err; + if (!hw->cipherfinal(ctx, ctx->buf)) + goto err; + ctx->iv_state = IV_STATE_FINISHED; /* Don't reuse the IV */ + goto finish; + } + olen = len; +finish: + rv = 1; +err: + *padlen = olen; + return rv; +} + +static int gcm_tls_init(PROV_GCM_CTX *dat, unsigned char *aad, size_t aad_len) +{ + unsigned char *buf; + size_t len; + + if (!ossl_prov_is_running() || aad_len != EVP_AEAD_TLS1_AAD_LEN) + return 0; + + /* Save the aad for later use. */ + buf = dat->buf; + memcpy(buf, aad, aad_len); + dat->tls_aad_len = aad_len; + + len = buf[aad_len - 2] << 8 | buf[aad_len - 1]; + /* Correct length for explicit iv. */ + if (len < EVP_GCM_TLS_EXPLICIT_IV_LEN) + return 0; + len -= EVP_GCM_TLS_EXPLICIT_IV_LEN; + + /* If decrypting correct for tag too. */ + if (!dat->enc) { + if (len < EVP_GCM_TLS_TAG_LEN) + return 0; + len -= EVP_GCM_TLS_TAG_LEN; + } + buf[aad_len - 2] = (unsigned char)(len >> 8); + buf[aad_len - 1] = (unsigned char)(len & 0xff); + /* Extra padding: tag appended to record. */ + return EVP_GCM_TLS_TAG_LEN; +} + +static int gcm_tls_iv_set_fixed(PROV_GCM_CTX *ctx, unsigned char *iv, + size_t len) +{ + /* Special case: -1 length restores whole IV */ + if (len == (size_t)-1) { + memcpy(ctx->iv, iv, ctx->ivlen); + ctx->iv_gen = 1; + ctx->iv_state = IV_STATE_BUFFERED; + return 1; + } + /* Fixed field must be at least 4 bytes and invocation field at least 8 */ + if ((len < EVP_GCM_TLS_FIXED_IV_LEN) + || (ctx->ivlen - (int)len) < EVP_GCM_TLS_EXPLICIT_IV_LEN) + return 0; + if (len > 0) + memcpy(ctx->iv, iv, len); + if (ctx->enc) { + if (RAND_bytes_ex(ctx->libctx, ctx->iv + len, ctx->ivlen - len, 0) <= 0) + return 0; + ctx->iv_gen_rand = 1; + } + ctx->iv_gen = 1; + ctx->iv_state = IV_STATE_BUFFERED; + return 1; +} + +/* + * Handle TLS GCM packet format. This consists of the last portion of the IV + * followed by the payload and finally the tag. On encrypt generate IV, + * encrypt payload and write the tag. On verify retrieve IV, decrypt payload + * and verify tag. + */ +static int gcm_tls_cipher(PROV_GCM_CTX *ctx, unsigned char *out, size_t *padlen, + const unsigned char *in, size_t len) +{ + int rv = 0; + size_t arg = EVP_GCM_TLS_EXPLICIT_IV_LEN; + size_t plen = 0; + unsigned char *tag = NULL; + + if (!ossl_prov_is_running() || !ctx->key_set) + goto err; + + /* Encrypt/decrypt must be performed in place */ + if (out != in || len < (EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN)) + goto err; + + /* + * Check for too many keys as per FIPS 140-2 IG A.5 "Key/IV Pair Uniqueness + * Requirements from SP 800-38D". The requirements is for one party to the + * communication to fail after 2^64 - 1 keys. We do this on the encrypting + * side only. + */ + if (ctx->enc && ++ctx->tls_enc_records == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_TOO_MANY_RECORDS); + goto err; + } + + /* + * Set IV from start of buffer or generate IV and write to start of + * buffer. + */ + if (ctx->enc) { + if (!getivgen(ctx, out, arg)) + goto err; + } else { + if (!setivinv(ctx, out, arg)) + goto err; + } + + /* Fix buffer and length to point to payload */ + in += EVP_GCM_TLS_EXPLICIT_IV_LEN; + out += EVP_GCM_TLS_EXPLICIT_IV_LEN; + len -= EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN; + + tag = ctx->enc ? out + len : (unsigned char *)in + len; + if (!ctx->hw->oneshot(ctx, ctx->buf, ctx->tls_aad_len, in, len, out, tag, + EVP_GCM_TLS_TAG_LEN)) { + if (!ctx->enc) + OPENSSL_cleanse(out, len); + goto err; + } + if (ctx->enc) + plen = len + EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN; + else + plen = len; + + rv = 1; +err: + ctx->iv_state = IV_STATE_FINISHED; + ctx->tls_aad_len = UNINITIALISED_SIZET; + *padlen = plen; + return rv; +} diff --git a/crypto/openssl/providers/implementations/ciphers/ciphercommon_gcm_hw.c b/crypto/openssl/providers/implementations/ciphers/ciphercommon_gcm_hw.c new file mode 100644 index 000000000000..c0a7399640fd --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/ciphercommon_gcm_hw.c @@ -0,0 +1,69 @@ +/* + * Copyright 2001-2021 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 + */ + +#include "prov/ciphercommon.h" +#include "prov/ciphercommon_gcm.h" + + +int ossl_gcm_setiv(PROV_GCM_CTX *ctx, const unsigned char *iv, size_t ivlen) +{ + CRYPTO_gcm128_setiv(&ctx->gcm, iv, ivlen); + return 1; +} + +int ossl_gcm_aad_update(PROV_GCM_CTX *ctx, const unsigned char *aad, + size_t aad_len) +{ + return CRYPTO_gcm128_aad(&ctx->gcm, aad, aad_len) == 0; +} + +int ossl_gcm_cipher_update(PROV_GCM_CTX *ctx, const unsigned char *in, + size_t len, unsigned char *out) +{ + if (ctx->enc) { + if (CRYPTO_gcm128_encrypt(&ctx->gcm, in, out, len)) + return 0; + } else { + if (CRYPTO_gcm128_decrypt(&ctx->gcm, in, out, len)) + return 0; + } + return 1; +} + +int ossl_gcm_cipher_final(PROV_GCM_CTX *ctx, unsigned char *tag) +{ + if (ctx->enc) { + CRYPTO_gcm128_tag(&ctx->gcm, tag, GCM_TAG_MAX_SIZE); + ctx->taglen = GCM_TAG_MAX_SIZE; + } else { + if (CRYPTO_gcm128_finish(&ctx->gcm, tag, ctx->taglen) != 0) + return 0; + } + return 1; +} + +int ossl_gcm_one_shot(PROV_GCM_CTX *ctx, unsigned char *aad, size_t aad_len, + const unsigned char *in, size_t in_len, + unsigned char *out, unsigned char *tag, size_t tag_len) +{ + int ret = 0; + + /* Use saved AAD */ + if (!ctx->hw->aadupdate(ctx, aad, aad_len)) + goto err; + if (!ctx->hw->cipherupdate(ctx, in, in_len, out)) + goto err; + ctx->taglen = GCM_TAG_MAX_SIZE; + if (!ctx->hw->cipherfinal(ctx, tag)) + goto err; + ret = 1; + +err: + return ret; +} diff --git a/crypto/openssl/providers/implementations/ciphers/ciphercommon_hw.c b/crypto/openssl/providers/implementations/ciphers/ciphercommon_hw.c new file mode 100644 index 000000000000..e73416a1c5c8 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/ciphercommon_hw.c @@ -0,0 +1,194 @@ +/* + * Copyright 2019-2021 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 + */ + +#include "prov/ciphercommon.h" + +/*- + * The generic cipher functions for cipher modes cbc, ecb, ofb, cfb and ctr. + * Used if there is no special hardware implementations. + */ +int ossl_cipher_hw_generic_cbc(PROV_CIPHER_CTX *dat, unsigned char *out, + const unsigned char *in, size_t len) +{ + if (dat->stream.cbc) + (*dat->stream.cbc) (in, out, len, dat->ks, dat->iv, dat->enc); + else if (dat->enc) + CRYPTO_cbc128_encrypt(in, out, len, dat->ks, dat->iv, dat->block); + else + CRYPTO_cbc128_decrypt(in, out, len, dat->ks, dat->iv, dat->block); + + return 1; +} + +int ossl_cipher_hw_generic_ecb(PROV_CIPHER_CTX *dat, unsigned char *out, + const unsigned char *in, size_t len) +{ + size_t i, bl = dat->blocksize; + + if (len < bl) + return 1; + + if (dat->stream.ecb) { + (*dat->stream.ecb) (in, out, len, dat->ks, dat->enc); + } + else { + for (i = 0, len -= bl; i <= len; i += bl) + (*dat->block) (in + i, out + i, dat->ks); + } + + return 1; +} + +int ossl_cipher_hw_generic_ofb128(PROV_CIPHER_CTX *dat, unsigned char *out, + const unsigned char *in, size_t len) +{ + int num = dat->num; + + CRYPTO_ofb128_encrypt(in, out, len, dat->ks, dat->iv, &num, dat->block); + dat->num = num; + + return 1; +} + +int ossl_cipher_hw_generic_cfb128(PROV_CIPHER_CTX *dat, unsigned char *out, + const unsigned char *in, size_t len) +{ + int num = dat->num; + + CRYPTO_cfb128_encrypt(in, out, len, dat->ks, dat->iv, &num, dat->enc, + dat->block); + dat->num = num; + + return 1; +} + +int ossl_cipher_hw_generic_cfb8(PROV_CIPHER_CTX *dat, unsigned char *out, + const unsigned char *in, size_t len) +{ + int num = dat->num; + + CRYPTO_cfb128_8_encrypt(in, out, len, dat->ks, dat->iv, &num, dat->enc, + dat->block); + dat->num = num; + + return 1; +} + +int ossl_cipher_hw_generic_cfb1(PROV_CIPHER_CTX *dat, unsigned char *out, + const unsigned char *in, size_t len) +{ + int num = dat->num; + + if (dat->use_bits) { + CRYPTO_cfb128_1_encrypt(in, out, len, dat->ks, dat->iv, &num, + dat->enc, dat->block); + dat->num = num; + return 1; + } + + while (len >= MAXBITCHUNK) { + CRYPTO_cfb128_1_encrypt(in, out, MAXBITCHUNK * 8, dat->ks, + dat->iv, &num, dat->enc, dat->block); + len -= MAXBITCHUNK; + out += MAXBITCHUNK; + in += MAXBITCHUNK; + } + if (len) + CRYPTO_cfb128_1_encrypt(in, out, len * 8, dat->ks, dat->iv, &num, + dat->enc, dat->block); + + dat->num = num; + + return 1; +} + +int ossl_cipher_hw_generic_ctr(PROV_CIPHER_CTX *dat, unsigned char *out, + const unsigned char *in, size_t len) +{ + unsigned int num = dat->num; + + if (dat->stream.ctr) + CRYPTO_ctr128_encrypt_ctr32(in, out, len, dat->ks, dat->iv, dat->buf, + &num, dat->stream.ctr); + else + CRYPTO_ctr128_encrypt(in, out, len, dat->ks, dat->iv, dat->buf, + &num, dat->block); + dat->num = num; + + return 1; +} + +/*- + * The chunked cipher functions for cipher modes cbc, ecb, ofb, cfb and ctr. + * Used if there is no special hardware implementations. + */ + +int ossl_cipher_hw_chunked_cbc(PROV_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t inl) +{ + while (inl >= MAXCHUNK) { + ossl_cipher_hw_generic_cbc(ctx, out, in, MAXCHUNK); + inl -= MAXCHUNK; + in += MAXCHUNK; + out += MAXCHUNK; + } + if (inl > 0) + ossl_cipher_hw_generic_cbc(ctx, out, in, inl); + return 1; +} + +int ossl_cipher_hw_chunked_cfb8(PROV_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t inl) +{ + size_t chunk = MAXCHUNK; + + if (inl < chunk) + chunk = inl; + while (inl > 0 && inl >= chunk) { + ossl_cipher_hw_generic_cfb8(ctx, out, in, inl); + inl -= chunk; + in += chunk; + out += chunk; + if (inl < chunk) + chunk = inl; + } + return 1; +} + +int ossl_cipher_hw_chunked_cfb128(PROV_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t inl) +{ + size_t chunk = MAXCHUNK; + + if (inl < chunk) + chunk = inl; + while (inl > 0 && inl >= chunk) { + ossl_cipher_hw_generic_cfb128(ctx, out, in, inl); + inl -= chunk; + in += chunk; + out += chunk; + if (inl < chunk) + chunk = inl; + } + return 1; +} + +int ossl_cipher_hw_chunked_ofb128(PROV_CIPHER_CTX *ctx, unsigned char *out, + const unsigned char *in, size_t inl) +{ + while (inl >= MAXCHUNK) { + ossl_cipher_hw_generic_ofb128(ctx, out, in, MAXCHUNK); + inl -= MAXCHUNK; + in += MAXCHUNK; + out += MAXCHUNK; + } + if (inl > 0) + ossl_cipher_hw_generic_ofb128(ctx, out, in, inl); + return 1; +} diff --git a/crypto/openssl/providers/implementations/ciphers/ciphercommon_local.h b/crypto/openssl/providers/implementations/ciphers/ciphercommon_local.h new file mode 100644 index 000000000000..11cb6116a815 --- /dev/null +++ b/crypto/openssl/providers/implementations/ciphers/ciphercommon_local.h @@ -0,0 +1,16 @@ +/* + * Copyright 2019-2021 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 + */ + +#include "prov/ciphercommon.h" + +void ossl_cipher_padblock(unsigned char *buf, size_t *buflen, size_t blocksize); +int ossl_cipher_unpadblock(unsigned char *buf, size_t *buflen, size_t blocksize); +int ossl_cipher_tlsunpadblock(OSSL_LIB_CTX *libctx, unsigned int tlsversion, + unsigned char *buf, size_t *buflen, size_t blocksize, + unsigned char **mac, int *alloced, size_t macsize, int aead); diff --git a/crypto/openssl/providers/implementations/digests/blake2_impl.h b/crypto/openssl/providers/implementations/digests/blake2_impl.h new file mode 100644 index 000000000000..e7c31474a364 --- /dev/null +++ b/crypto/openssl/providers/implementations/digests/blake2_impl.h @@ -0,0 +1,118 @@ +/* + * Copyright 2016-2020 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 + */ + +/* + * Derived from the BLAKE2 reference implementation written by Samuel Neves. + * Copyright 2012, Samuel Neves <sneves@dei.uc.pt> + * More information about the BLAKE2 hash function and its implementations + * can be found at https://blake2.net. + */ + +#include <string.h> +#include "internal/endian.h" + +static ossl_inline uint32_t load32(const uint8_t *src) +{ + DECLARE_IS_ENDIAN; + + if (IS_LITTLE_ENDIAN) { + uint32_t w; + memcpy(&w, src, sizeof(w)); + return w; + } else { + uint32_t w = ((uint32_t)src[0]) + | ((uint32_t)src[1] << 8) + | ((uint32_t)src[2] << 16) + | ((uint32_t)src[3] << 24); + return w; + } +} + +static ossl_inline uint64_t load64(const uint8_t *src) +{ + DECLARE_IS_ENDIAN; + + if (IS_LITTLE_ENDIAN) { + uint64_t w; + memcpy(&w, src, sizeof(w)); + return w; + } else { + uint64_t w = ((uint64_t)src[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); + return w; + } +} + +static ossl_inline void store32(uint8_t *dst, uint32_t w) +{ + DECLARE_IS_ENDIAN; + + if (IS_LITTLE_ENDIAN) { + memcpy(dst, &w, sizeof(w)); + } else { + uint8_t *p = (uint8_t *)dst; + int i; + + for (i = 0; i < 4; i++) + p[i] = (uint8_t)(w >> (8 * i)); + } +} + +static ossl_inline void store64(uint8_t *dst, uint64_t w) +{ + DECLARE_IS_ENDIAN; + + if (IS_LITTLE_ENDIAN) { + memcpy(dst, &w, sizeof(w)); + } else { + uint8_t *p = (uint8_t *)dst; + int i; + + for (i = 0; i < 8; i++) + p[i] = (uint8_t)(w >> (8 * i)); + } +} + +static ossl_inline uint64_t load48(const uint8_t *src) +{ + uint64_t w = ((uint64_t)src[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); + return w; +} + +static ossl_inline void store48(uint8_t *dst, uint64_t w) +{ + uint8_t *p = (uint8_t *)dst; + p[0] = (uint8_t)w; + p[1] = (uint8_t)(w>>8); + p[2] = (uint8_t)(w>>16); + p[3] = (uint8_t)(w>>24); + p[4] = (uint8_t)(w>>32); + p[5] = (uint8_t)(w>>40); +} + +static ossl_inline uint32_t rotr32(const uint32_t w, const unsigned int c) +{ + return (w >> c) | (w << (32 - c)); +} + +static ossl_inline uint64_t rotr64(const uint64_t w, const unsigned int c) +{ + return (w >> c) | (w << (64 - c)); +} diff --git a/crypto/openssl/providers/implementations/digests/blake2_prov.c b/crypto/openssl/providers/implementations/digests/blake2_prov.c new file mode 100644 index 000000000000..5495aab61a87 --- /dev/null +++ b/crypto/openssl/providers/implementations/digests/blake2_prov.c @@ -0,0 +1,198 @@ +/* + * Copyright 2019-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 + */ + +#include <openssl/crypto.h> +#include <openssl/core_names.h> +#include <openssl/proverr.h> +#include <openssl/err.h> +#include "prov/blake2.h" +#include "prov/digestcommon.h" +#include "prov/implementations.h" + +#define IMPLEMENT_BLAKE_functions(variant, VARIANT, variantsize) \ +static const OSSL_PARAM known_blake##variant##_ctx_params[] = { \ + {OSSL_DIGEST_PARAM_SIZE, OSSL_PARAM_UNSIGNED_INTEGER, NULL, 0, 0}, \ + OSSL_PARAM_END \ +}; \ + \ +const OSSL_PARAM *ossl_blake##variant##_gettable_ctx_params(ossl_unused void *ctx, \ + ossl_unused void *pctx) \ +{ \ + return known_blake##variant##_ctx_params; \ +} \ + \ +const OSSL_PARAM *ossl_blake##variant##_settable_ctx_params(ossl_unused void *ctx, \ + ossl_unused void *pctx) \ +{ \ + return known_blake##variant##_ctx_params; \ +} \ + \ +int ossl_blake##variant##_get_ctx_params(void *vctx, OSSL_PARAM params[]) \ +{ \ + struct blake##variant##_md_data_st *mdctx = vctx; \ + OSSL_PARAM *p; \ + \ + BLAKE##VARIANT##_CTX *ctx = &mdctx->ctx; \ + \ + if (ctx == NULL) \ + return 0; \ + if (ossl_param_is_empty(params)) \ + return 1; \ + \ + p = OSSL_PARAM_locate(params, OSSL_DIGEST_PARAM_SIZE); \ + if (p != NULL \ + && !OSSL_PARAM_set_uint(p, (unsigned int)mdctx->params.digest_length)) { \ + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); \ + return 0; \ + } \ + \ + return 1; \ +} \ + \ +int ossl_blake##variant##_set_ctx_params(void *vctx, const OSSL_PARAM params[]) \ +{ \ + size_t size; \ + struct blake##variant##_md_data_st *mdctx = vctx; \ + const OSSL_PARAM *p; \ + \ + BLAKE##VARIANT##_CTX *ctx = &mdctx->ctx; \ + \ + if (ctx == NULL) \ + return 0; \ + if (ossl_param_is_empty(params)) \ + return 1; \ + \ + p = OSSL_PARAM_locate_const(params, OSSL_DIGEST_PARAM_SIZE); \ + if (p != NULL) { \ + if (!OSSL_PARAM_get_size_t(p, &size)) { \ + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); \ + return 0; \ + } \ + if (size < 1 || size > BLAKE##VARIANT##_OUTBYTES) { \ + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_SIZE); \ + return 0; \ + } \ + ossl_blake##variant##_param_set_digest_length(&mdctx->params, (uint8_t)size); \ + } \ + \ + return 1; \ +} \ + \ +static int ossl_blake##variantsize##_init(void *ctx) \ +{ \ + struct blake##variant##_md_data_st *mdctx = ctx; \ + uint8_t digest_length = mdctx->params.digest_length; \ + \ + ossl_blake##variant##_param_init(&mdctx->params); \ + if (digest_length != 0) \ + mdctx->params.digest_length = digest_length; \ + return ossl_blake##variant##_init(&mdctx->ctx, &mdctx->params); \ +} \ + \ +static OSSL_FUNC_digest_init_fn blake##variantsize##_internal_init; \ +static OSSL_FUNC_digest_newctx_fn blake##variantsize##_newctx; \ +static OSSL_FUNC_digest_freectx_fn blake##variantsize##_freectx; \ +static OSSL_FUNC_digest_dupctx_fn blake##variantsize##_dupctx; \ +static OSSL_FUNC_digest_final_fn blake##variantsize##_internal_final; \ +static OSSL_FUNC_digest_get_params_fn blake##variantsize##_get_params; \ + \ +static int blake##variantsize##_internal_init(void *ctx, const OSSL_PARAM params[]) \ +{ \ + return ossl_prov_is_running() && ossl_blake##variant##_set_ctx_params(ctx, params) \ + && ossl_blake##variantsize##_init(ctx); \ +} \ + \ +static void *blake##variantsize##_newctx(void *prov_ctx) \ +{ \ + struct blake##variant##_md_data_st *ctx; \ + \ + ctx = ossl_prov_is_running() ? OPENSSL_zalloc(sizeof(*ctx)) : NULL; \ + return ctx; \ +} \ + \ +static void blake##variantsize##_freectx(void *vctx) \ +{ \ + struct blake##variant##_md_data_st *ctx; \ + \ + ctx = (struct blake##variant##_md_data_st *)vctx; \ + OPENSSL_clear_free(ctx, sizeof(*ctx)); \ +} \ + \ +static void *blake##variantsize##_dupctx(void *ctx) \ +{ \ + struct blake##variant##_md_data_st *in, *ret; \ + \ + in = (struct blake##variant##_md_data_st *)ctx; \ + ret = ossl_prov_is_running()? OPENSSL_malloc(sizeof(*ret)) : NULL; \ + if (ret != NULL) \ + *ret = *in; \ + return ret; \ +} \ +\ +static void blake##variantsize##_copyctx(void *voutctx, void *vinctx) \ +{ \ + struct blake##variant##_md_data_st *inctx, *outctx; \ + \ + outctx = (struct blake##variant##_md_data_st *)voutctx; \ + inctx = (struct blake##variant##_md_data_st *)vinctx; \ + *outctx = *inctx; \ +} \ + \ +static int blake##variantsize##_internal_final(void *ctx, unsigned char *out, \ + size_t *outl, size_t outsz) \ +{ \ + struct blake##variant##_md_data_st *b_ctx; \ + \ + b_ctx = (struct blake##variant##_md_data_st *)ctx; \ + \ + if (!ossl_prov_is_running()) \ + return 0; \ + \ + *outl = b_ctx->ctx.outlen; \ + \ + if (outsz == 0) \ + return 1; \ + \ + if (outsz < *outl) { \ + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_SIZE); \ + return 0; \ + } \ + \ + return ossl_blake##variant##_final(out, ctx); \ +} \ + \ +static int blake##variantsize##_get_params(OSSL_PARAM params[]) \ +{ \ + return ossl_digest_default_get_params(params, BLAKE##VARIANT##_BLOCKBYTES, BLAKE##VARIANT##_OUTBYTES, 0); \ +} \ + \ +const OSSL_DISPATCH ossl_blake##variantsize##_functions[] = { \ + {OSSL_FUNC_DIGEST_NEWCTX, (void (*)(void))blake##variantsize##_newctx}, \ + {OSSL_FUNC_DIGEST_UPDATE, (void (*)(void))ossl_blake##variant##_update}, \ + {OSSL_FUNC_DIGEST_FINAL, (void (*)(void))blake##variantsize##_internal_final}, \ + {OSSL_FUNC_DIGEST_FREECTX, (void (*)(void))blake##variantsize##_freectx}, \ + {OSSL_FUNC_DIGEST_DUPCTX, (void (*)(void))blake##variantsize##_dupctx}, \ + {OSSL_FUNC_DIGEST_COPYCTX, (void (*)(void))blake##variantsize##_copyctx}, \ + {OSSL_FUNC_DIGEST_GET_PARAMS, (void (*)(void))blake##variantsize##_get_params}, \ + {OSSL_FUNC_DIGEST_GETTABLE_PARAMS, \ + (void (*)(void))ossl_digest_default_gettable_params}, \ + {OSSL_FUNC_DIGEST_INIT, (void (*)(void))blake##variantsize##_internal_init}, \ + {OSSL_FUNC_DIGEST_GETTABLE_CTX_PARAMS, \ + (void (*)(void))ossl_blake##variant##_gettable_ctx_params}, \ + {OSSL_FUNC_DIGEST_SETTABLE_CTX_PARAMS, \ + (void (*)(void))ossl_blake##variant##_settable_ctx_params}, \ + {OSSL_FUNC_DIGEST_GET_CTX_PARAMS, \ + (void (*)(void))ossl_blake##variant##_get_ctx_params}, \ + {OSSL_FUNC_DIGEST_SET_CTX_PARAMS, \ + (void (*)(void))ossl_blake##variant##_set_ctx_params}, \ + {0, NULL} \ +}; + +IMPLEMENT_BLAKE_functions(2s, 2S, 2s256) +IMPLEMENT_BLAKE_functions(2b, 2B, 2b512) diff --git a/crypto/openssl/providers/implementations/digests/blake2b_prov.c b/crypto/openssl/providers/implementations/digests/blake2b_prov.c new file mode 100644 index 000000000000..6ef7fac008cc --- /dev/null +++ b/crypto/openssl/providers/implementations/digests/blake2b_prov.c @@ -0,0 +1,334 @@ +/* + * Copyright 2016-2024 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 + */ + +/* + * Derived from the BLAKE2 reference implementation written by Samuel Neves. + * Copyright 2012, Samuel Neves <sneves@dei.uc.pt> + * More information about the BLAKE2 hash function and its implementations + * can be found at https://blake2.net. + */ + +#include <assert.h> +#include <string.h> +#include <openssl/crypto.h> +#include "internal/numbers.h" +#include "blake2_impl.h" +#include "prov/blake2.h" + +static const uint64_t blake2b_IV[8] = +{ + 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, + 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, + 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, + 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL +}; + +static const uint8_t blake2b_sigma[12][16] = +{ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , + { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , + { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , + { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , + { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , + { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , + { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , + { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , + { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } +}; + +/* Set that it's the last block we'll compress */ +static ossl_inline void blake2b_set_lastblock(BLAKE2B_CTX *S) +{ + S->f[0] = -1; +} + +/* Initialize the hashing state. */ +static ossl_inline void blake2b_init0(BLAKE2B_CTX *S) +{ + int i; + + memset(S, 0, sizeof(BLAKE2B_CTX)); + for (i = 0; i < 8; ++i) { + S->h[i] = blake2b_IV[i]; + } +} + +/* init xors IV with input parameter block and sets the output length */ +static void blake2b_init_param(BLAKE2B_CTX *S, const BLAKE2B_PARAM *P) +{ + size_t i; + const uint8_t *p = (const uint8_t *)(P); + + blake2b_init0(S); + S->outlen = P->digest_length; + + /* The param struct is carefully hand packed, and should be 64 bytes on + * every platform. */ + assert(sizeof(BLAKE2B_PARAM) == 64); + /* IV XOR ParamBlock */ + for (i = 0; i < 8; ++i) { + S->h[i] ^= load64(p + sizeof(S->h[i]) * i); + } +} + +/* Initialize the parameter block with default values */ +void ossl_blake2b_param_init(BLAKE2B_PARAM *P) +{ + P->digest_length = BLAKE2B_DIGEST_LENGTH; + P->key_length = 0; + P->fanout = 1; + P->depth = 1; + store32(P->leaf_length, 0); + store64(P->node_offset, 0); + P->node_depth = 0; + P->inner_length = 0; + memset(P->reserved, 0, sizeof(P->reserved)); + memset(P->salt, 0, sizeof(P->salt)); + memset(P->personal, 0, sizeof(P->personal)); +} + +void ossl_blake2b_param_set_digest_length(BLAKE2B_PARAM *P, uint8_t outlen) +{ + P->digest_length = outlen; +} + +void ossl_blake2b_param_set_key_length(BLAKE2B_PARAM *P, uint8_t keylen) +{ + P->key_length = keylen; +} + +void ossl_blake2b_param_set_personal(BLAKE2B_PARAM *P, const uint8_t *personal, + size_t len) +{ + memcpy(P->personal, personal, len); + memset(P->personal + len, 0, BLAKE2B_PERSONALBYTES - len); +} + +void ossl_blake2b_param_set_salt(BLAKE2B_PARAM *P, const uint8_t *salt, + size_t len) +{ + memcpy(P->salt, salt, len); + memset(P->salt + len, 0, BLAKE2B_SALTBYTES - len); +} + +/* + * Initialize the hashing context with the given parameter block. + * Always returns 1. + */ +int ossl_blake2b_init(BLAKE2B_CTX *c, const BLAKE2B_PARAM *P) +{ + blake2b_init_param(c, P); + return 1; +} + +/* + * Initialize the hashing context with the given parameter block and key. + * Always returns 1. + */ +int ossl_blake2b_init_key(BLAKE2B_CTX *c, const BLAKE2B_PARAM *P, + const void *key) +{ + blake2b_init_param(c, P); + + /* Pad the key to form first data block */ + { + uint8_t block[BLAKE2B_BLOCKBYTES] = {0}; + + memcpy(block, key, P->key_length); + ossl_blake2b_update(c, block, BLAKE2B_BLOCKBYTES); + OPENSSL_cleanse(block, BLAKE2B_BLOCKBYTES); + } + + return 1; +} + +/* Permute the state while xoring in the block of data. */ +static void blake2b_compress(BLAKE2B_CTX *S, + const uint8_t *blocks, + size_t len) +{ + uint64_t m[16]; + uint64_t v[16]; + int i; + size_t increment; + + /* + * There are two distinct usage vectors for this function: + * + * a) BLAKE2b_Update uses it to process complete blocks, + * possibly more than one at a time; + * + * b) BLAK2b_Final uses it to process last block, always + * single but possibly incomplete, in which case caller + * pads input with zeros. + */ + assert(len < BLAKE2B_BLOCKBYTES || len % BLAKE2B_BLOCKBYTES == 0); + + /* + * Since last block is always processed with separate call, + * |len| not being multiple of complete blocks can be observed + * only with |len| being less than BLAKE2B_BLOCKBYTES ("less" + * including even zero), which is why following assignment doesn't + * have to reside inside the main loop below. + */ + increment = len < BLAKE2B_BLOCKBYTES ? len : BLAKE2B_BLOCKBYTES; + + for (i = 0; i < 8; ++i) { + v[i] = S->h[i]; + } + + do { + for (i = 0; i < 16; ++i) { + m[i] = load64(blocks + i * sizeof(m[i])); + } + + /* blake2b_increment_counter */ + S->t[0] += increment; + S->t[1] += (S->t[0] < increment); + + v[8] = blake2b_IV[0]; + v[9] = blake2b_IV[1]; + v[10] = blake2b_IV[2]; + v[11] = blake2b_IV[3]; + v[12] = S->t[0] ^ blake2b_IV[4]; + v[13] = S->t[1] ^ blake2b_IV[5]; + v[14] = S->f[0] ^ blake2b_IV[6]; + v[15] = S->f[1] ^ blake2b_IV[7]; +#define G(r,i,a,b,c,d) \ + do { \ + a = a + b + m[blake2b_sigma[r][2*i+0]]; \ + d = rotr64(d ^ a, 32); \ + c = c + d; \ + b = rotr64(b ^ c, 24); \ + a = a + b + m[blake2b_sigma[r][2*i+1]]; \ + d = rotr64(d ^ a, 16); \ + c = c + d; \ + b = rotr64(b ^ c, 63); \ + } while (0) +#define ROUND(r) \ + do { \ + G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \ + G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \ + G(r,2,v[ 2],v[ 6],v[10],v[14]); \ + G(r,3,v[ 3],v[ 7],v[11],v[15]); \ + G(r,4,v[ 0],v[ 5],v[10],v[15]); \ + G(r,5,v[ 1],v[ 6],v[11],v[12]); \ + G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \ + G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \ + } while (0) +#if defined(OPENSSL_SMALL_FOOTPRINT) + /* 3x size reduction on x86_64, almost 7x on ARMv8, 9x on ARMv4 */ + for (i = 0; i < 12; i++) { + ROUND(i); + } +#else + ROUND(0); + ROUND(1); + ROUND(2); + ROUND(3); + ROUND(4); + ROUND(5); + ROUND(6); + ROUND(7); + ROUND(8); + ROUND(9); + ROUND(10); + ROUND(11); +#endif + + for (i = 0; i < 8; ++i) { + S->h[i] = v[i] ^= v[i + 8] ^ S->h[i]; + } +#undef G +#undef ROUND + blocks += increment; + len -= increment; + } while (len); +} + +/* Absorb the input data into the hash state. Always returns 1. */ +int ossl_blake2b_update(BLAKE2B_CTX *c, const void *data, size_t datalen) +{ + const uint8_t *in = data; + size_t fill; + + /* + * Intuitively one would expect intermediate buffer, c->buf, to + * store incomplete blocks. But in this case we are interested to + * temporarily stash even complete blocks, because last one in the + * stream has to be treated in special way, and at this point we + * don't know if last block in *this* call is last one "ever". This + * is the reason for why |datalen| is compared as >, and not >=. + */ + fill = sizeof(c->buf) - c->buflen; + if (datalen > fill) { + if (c->buflen) { + memcpy(c->buf + c->buflen, in, fill); /* Fill buffer */ + blake2b_compress(c, c->buf, BLAKE2B_BLOCKBYTES); + c->buflen = 0; + in += fill; + datalen -= fill; + } + if (datalen > BLAKE2B_BLOCKBYTES) { + size_t stashlen = datalen % BLAKE2B_BLOCKBYTES; + /* + * If |datalen| is a multiple of the blocksize, stash + * last complete block, it can be final one... + */ + stashlen = stashlen ? stashlen : BLAKE2B_BLOCKBYTES; + datalen -= stashlen; + blake2b_compress(c, in, datalen); + in += datalen; + datalen = stashlen; + } + } + + assert(datalen <= BLAKE2B_BLOCKBYTES); + + memcpy(c->buf + c->buflen, in, datalen); + c->buflen += datalen; /* Be lazy, do not compress */ + + return 1; +} + +/* + * Calculate the final hash and save it in md. + * Always returns 1. + */ +int ossl_blake2b_final(unsigned char *md, BLAKE2B_CTX *c) +{ + uint8_t outbuffer[BLAKE2B_OUTBYTES] = {0}; + uint8_t *target = outbuffer; + int iter = (c->outlen + 7) / 8; + int i; + + /* Avoid writing to the temporary buffer if possible */ + if ((c->outlen % sizeof(c->h[0])) == 0) + target = md; + + blake2b_set_lastblock(c); + /* Padding */ + memset(c->buf + c->buflen, 0, sizeof(c->buf) - c->buflen); + blake2b_compress(c, c->buf, c->buflen); + + /* Output full hash to buffer */ + for (i = 0; i < iter; ++i) + store64(target + sizeof(c->h[i]) * i, c->h[i]); + + if (target != md) { + memcpy(md, target, c->outlen); + OPENSSL_cleanse(target, sizeof(outbuffer)); + } + + OPENSSL_cleanse(c, sizeof(BLAKE2B_CTX)); + return 1; +} diff --git a/crypto/openssl/providers/implementations/digests/blake2s_prov.c b/crypto/openssl/providers/implementations/digests/blake2s_prov.c new file mode 100644 index 000000000000..a9251d8996d9 --- /dev/null +++ b/crypto/openssl/providers/implementations/digests/blake2s_prov.c @@ -0,0 +1,322 @@ +/* + * Copyright 2016-2024 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 + */ + +/* + * Derived from the BLAKE2 reference implementation written by Samuel Neves. + * Copyright 2012, Samuel Neves <sneves@dei.uc.pt> + * More information about the BLAKE2 hash function and its implementations + * can be found at https://blake2.net. + */ + +#include <assert.h> +#include <string.h> +#include <openssl/crypto.h> +#include "blake2_impl.h" +#include "prov/blake2.h" + +static const uint32_t blake2s_IV[8] = { + 0x6A09E667U, 0xBB67AE85U, 0x3C6EF372U, 0xA54FF53AU, + 0x510E527FU, 0x9B05688CU, 0x1F83D9ABU, 0x5BE0CD19U +}; + +static const uint8_t blake2s_sigma[10][16] = { + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , + { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , + { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , + { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , + { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , + { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , + { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , + { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , + { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , +}; + +/* Set that it's the last block we'll compress */ +static ossl_inline void blake2s_set_lastblock(BLAKE2S_CTX *S) +{ + S->f[0] = -1; +} + +/* Initialize the hashing state. */ +static ossl_inline void blake2s_init0(BLAKE2S_CTX *S) +{ + int i; + + memset(S, 0, sizeof(BLAKE2S_CTX)); + for (i = 0; i < 8; ++i) { + S->h[i] = blake2s_IV[i]; + } +} + +/* init xors IV with input parameter block and sets the output length */ +static void blake2s_init_param(BLAKE2S_CTX *S, const BLAKE2S_PARAM *P) +{ + size_t i; + const uint8_t *p = (const uint8_t *)(P); + + blake2s_init0(S); + S->outlen = P->digest_length; + + /* The param struct is carefully hand packed, and should be 32 bytes on + * every platform. */ + assert(sizeof(BLAKE2S_PARAM) == 32); + /* IV XOR ParamBlock */ + for (i = 0; i < 8; ++i) { + S->h[i] ^= load32(&p[i*4]); + } +} + +void ossl_blake2s_param_init(BLAKE2S_PARAM *P) +{ + P->digest_length = BLAKE2S_DIGEST_LENGTH; + P->key_length = 0; + P->fanout = 1; + P->depth = 1; + store32(P->leaf_length, 0); + store48(P->node_offset, 0); + P->node_depth = 0; + P->inner_length = 0; + memset(P->salt, 0, sizeof(P->salt)); + memset(P->personal, 0, sizeof(P->personal)); +} + +void ossl_blake2s_param_set_digest_length(BLAKE2S_PARAM *P, uint8_t outlen) +{ + P->digest_length = outlen; +} + +void ossl_blake2s_param_set_key_length(BLAKE2S_PARAM *P, uint8_t keylen) +{ + P->key_length = keylen; +} + +void ossl_blake2s_param_set_personal(BLAKE2S_PARAM *P, const uint8_t *personal, + size_t len) +{ + memcpy(P->personal, personal, len); + memset(P->personal + len, 0, BLAKE2S_PERSONALBYTES - len); +} + +void ossl_blake2s_param_set_salt(BLAKE2S_PARAM *P, const uint8_t *salt, + size_t len) +{ + memcpy(P->salt, salt, len); + memset(P->salt + len, 0, BLAKE2S_SALTBYTES - len);} + +/* + * Initialize the hashing context with the given parameter block. + * Always returns 1. + */ +int ossl_blake2s_init(BLAKE2S_CTX *c, const BLAKE2S_PARAM *P) +{ + blake2s_init_param(c, P); + return 1; +} + +/* + * Initialize the hashing context with the given parameter block and key. + * Always returns 1. + */ +int ossl_blake2s_init_key(BLAKE2S_CTX *c, const BLAKE2S_PARAM *P, + const void *key) +{ + blake2s_init_param(c, P); + + /* Pad the key to form first data block */ + { + uint8_t block[BLAKE2S_BLOCKBYTES] = {0}; + + memcpy(block, key, P->key_length); + ossl_blake2s_update(c, block, BLAKE2S_BLOCKBYTES); + OPENSSL_cleanse(block, BLAKE2S_BLOCKBYTES); + } + + return 1; +} + +/* Permute the state while xoring in the block of data. */ +static void blake2s_compress(BLAKE2S_CTX *S, + const uint8_t *blocks, + size_t len) +{ + uint32_t m[16]; + uint32_t v[16]; + size_t i; + size_t increment; + + /* + * There are two distinct usage vectors for this function: + * + * a) BLAKE2s_Update uses it to process complete blocks, + * possibly more than one at a time; + * + * b) BLAK2s_Final uses it to process last block, always + * single but possibly incomplete, in which case caller + * pads input with zeros. + */ + assert(len < BLAKE2S_BLOCKBYTES || len % BLAKE2S_BLOCKBYTES == 0); + + /* + * Since last block is always processed with separate call, + * |len| not being multiple of complete blocks can be observed + * only with |len| being less than BLAKE2S_BLOCKBYTES ("less" + * including even zero), which is why following assignment doesn't + * have to reside inside the main loop below. + */ + increment = len < BLAKE2S_BLOCKBYTES ? len : BLAKE2S_BLOCKBYTES; + + for (i = 0; i < 8; ++i) { + v[i] = S->h[i]; + } + + do { + for (i = 0; i < 16; ++i) { + m[i] = load32(blocks + i * sizeof(m[i])); + } + + /* blake2s_increment_counter */ + S->t[0] += increment; + S->t[1] += (S->t[0] < increment); + + v[ 8] = blake2s_IV[0]; + v[ 9] = blake2s_IV[1]; + v[10] = blake2s_IV[2]; + v[11] = blake2s_IV[3]; + v[12] = S->t[0] ^ blake2s_IV[4]; + v[13] = S->t[1] ^ blake2s_IV[5]; + v[14] = S->f[0] ^ blake2s_IV[6]; + v[15] = S->f[1] ^ blake2s_IV[7]; +#define G(r,i,a,b,c,d) \ + do { \ + a = a + b + m[blake2s_sigma[r][2*i+0]]; \ + d = rotr32(d ^ a, 16); \ + c = c + d; \ + b = rotr32(b ^ c, 12); \ + a = a + b + m[blake2s_sigma[r][2*i+1]]; \ + d = rotr32(d ^ a, 8); \ + c = c + d; \ + b = rotr32(b ^ c, 7); \ + } while (0) +#define ROUND(r) \ + do { \ + G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \ + G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \ + G(r,2,v[ 2],v[ 6],v[10],v[14]); \ + G(r,3,v[ 3],v[ 7],v[11],v[15]); \ + G(r,4,v[ 0],v[ 5],v[10],v[15]); \ + G(r,5,v[ 1],v[ 6],v[11],v[12]); \ + G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \ + G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \ + } while (0) +#if defined(OPENSSL_SMALL_FOOTPRINT) + /* almost 3x reduction on x86_64, 4.5x on ARMv8, 4x on ARMv4 */ + for (i = 0; i < 10; i++) { + ROUND(i); + } +#else + ROUND(0); + ROUND(1); + ROUND(2); + ROUND(3); + ROUND(4); + ROUND(5); + ROUND(6); + ROUND(7); + ROUND(8); + ROUND(9); +#endif + + for (i = 0; i < 8; ++i) { + S->h[i] = v[i] ^= v[i + 8] ^ S->h[i]; + } +#undef G +#undef ROUND + blocks += increment; + len -= increment; + } while (len); +} + +/* Absorb the input data into the hash state. Always returns 1. */ +int ossl_blake2s_update(BLAKE2S_CTX *c, const void *data, size_t datalen) +{ + const uint8_t *in = data; + size_t fill; + + /* + * Intuitively one would expect intermediate buffer, c->buf, to + * store incomplete blocks. But in this case we are interested to + * temporarily stash even complete blocks, because last one in the + * stream has to be treated in special way, and at this point we + * don't know if last block in *this* call is last one "ever". This + * is the reason for why |datalen| is compared as >, and not >=. + */ + fill = sizeof(c->buf) - c->buflen; + if (datalen > fill) { + if (c->buflen) { + memcpy(c->buf + c->buflen, in, fill); /* Fill buffer */ + blake2s_compress(c, c->buf, BLAKE2S_BLOCKBYTES); + c->buflen = 0; + in += fill; + datalen -= fill; + } + if (datalen > BLAKE2S_BLOCKBYTES) { + size_t stashlen = datalen % BLAKE2S_BLOCKBYTES; + /* + * If |datalen| is a multiple of the blocksize, stash + * last complete block, it can be final one... + */ + stashlen = stashlen ? stashlen : BLAKE2S_BLOCKBYTES; + datalen -= stashlen; + blake2s_compress(c, in, datalen); + in += datalen; + datalen = stashlen; + } + } + + assert(datalen <= BLAKE2S_BLOCKBYTES); + + memcpy(c->buf + c->buflen, in, datalen); + c->buflen += datalen; /* Be lazy, do not compress */ + + return 1; +} + +/* + * Calculate the final hash and save it in md. + * Always returns 1. + */ +int ossl_blake2s_final(unsigned char *md, BLAKE2S_CTX *c) +{ + uint8_t outbuffer[BLAKE2S_OUTBYTES] = {0}; + uint8_t *target = outbuffer; + int iter = (c->outlen + 3) / 4; + int i; + + /* Avoid writing to the temporary buffer if possible */ + if ((c->outlen % sizeof(c->h[0])) == 0) + target = md; + + blake2s_set_lastblock(c); + /* Padding */ + memset(c->buf + c->buflen, 0, sizeof(c->buf) - c->buflen); + blake2s_compress(c, c->buf, c->buflen); + + /* Output full hash to buffer */ + for (i = 0; i < iter; ++i) + store32(target + sizeof(c->h[i]) * i, c->h[i]); + + if (target != md) { + memcpy(md, target, c->outlen); + OPENSSL_cleanse(target, sizeof(outbuffer)); + } + + OPENSSL_cleanse(c, sizeof(BLAKE2S_CTX)); + return 1; +} diff --git a/crypto/openssl/providers/implementations/digests/build.info b/crypto/openssl/providers/implementations/digests/build.info new file mode 100644 index 000000000000..d30975028e9f --- /dev/null +++ b/crypto/openssl/providers/implementations/digests/build.info @@ -0,0 +1,62 @@ +# We make separate GOAL variables for each algorithm, to make it easy to +# switch each to the Legacy provider when needed. + +$COMMON_GOAL=../../libcommon.a + +$SHA1_GOAL=../../libdefault.a ../../libfips.a +$SHA2_GOAL=../../libdefault.a ../../libfips.a +$SHA3_GOAL=../../libdefault.a ../../libfips.a +$BLAKE2_GOAL=../../libdefault.a +$SM3_GOAL=../../libdefault.a +$MD5_GOAL=../../libdefault.a +$NULL_GOAL=../../libdefault.a + +$MD2_GOAL=../../liblegacy.a +$MD4_GOAL=../../liblegacy.a +$MDC2_GOAL=../../liblegacy.a +$WHIRLPOOL_GOAL=../../liblegacy.a +IF[{- !$disabled{module} -}] + $RIPEMD_GOAL=../../libdefault.a ../../liblegacy.a +ELSE + $RIPEMD_GOAL=../../libdefault.a +ENDIF + +# This source is common for all digests in all our providers. +SOURCE[$COMMON_GOAL]=digestcommon.c + +SOURCE[$SHA2_GOAL]=sha2_prov.c +SOURCE[$SHA3_GOAL]=sha3_prov.c + +SOURCE[$NULL_GOAL]=null_prov.c + +IF[{- !$disabled{blake2} -}] + SOURCE[$BLAKE2_GOAL]=blake2_prov.c blake2b_prov.c blake2s_prov.c +ENDIF + +IF[{- !$disabled{sm3} -}] + SOURCE[$SM3_GOAL]=sm3_prov.c +ENDIF + +IF[{- !$disabled{md5} -}] + SOURCE[$MD5_GOAL]=md5_prov.c md5_sha1_prov.c +ENDIF + +IF[{- !$disabled{md2} -}] + SOURCE[$MD2_GOAL]=md2_prov.c +ENDIF + +IF[{- !$disabled{md4} -}] + SOURCE[$MD4_GOAL]=md4_prov.c +ENDIF + +IF[{- !$disabled{mdc2} -}] + SOURCE[$MDC2_GOAL]=mdc2_prov.c +ENDIF + +IF[{- !$disabled{whirlpool} -}] + SOURCE[$WHIRLPOOL_GOAL]=wp_prov.c +ENDIF + +IF[{- !$disabled{rmd160} -}] + SOURCE[$RIPEMD_GOAL]=ripemd_prov.c +ENDIF diff --git a/crypto/openssl/providers/implementations/digests/digestcommon.c b/crypto/openssl/providers/implementations/digests/digestcommon.c new file mode 100644 index 000000000000..5cd1d1620062 --- /dev/null +++ b/crypto/openssl/providers/implementations/digests/digestcommon.c @@ -0,0 +1,54 @@ +/* + * Copyright 2019-2021 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 + */ + +#include <openssl/err.h> +#include <openssl/proverr.h> +#include "prov/digestcommon.h" + +int ossl_digest_default_get_params(OSSL_PARAM params[], size_t blksz, + size_t paramsz, unsigned long flags) +{ + OSSL_PARAM *p = NULL; + + p = OSSL_PARAM_locate(params, OSSL_DIGEST_PARAM_BLOCK_SIZE); + if (p != NULL && !OSSL_PARAM_set_size_t(p, blksz)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_DIGEST_PARAM_SIZE); + if (p != NULL && !OSSL_PARAM_set_size_t(p, paramsz)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_DIGEST_PARAM_XOF); + if (p != NULL + && !OSSL_PARAM_set_int(p, (flags & PROV_DIGEST_FLAG_XOF) != 0)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + p = OSSL_PARAM_locate(params, OSSL_DIGEST_PARAM_ALGID_ABSENT); + if (p != NULL + && !OSSL_PARAM_set_int(p, (flags & PROV_DIGEST_FLAG_ALGID_ABSENT) != 0)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } + return 1; +} + +static const OSSL_PARAM digest_default_known_gettable_params[] = { + OSSL_PARAM_size_t(OSSL_DIGEST_PARAM_BLOCK_SIZE, NULL), + OSSL_PARAM_size_t(OSSL_DIGEST_PARAM_SIZE, NULL), + OSSL_PARAM_int(OSSL_DIGEST_PARAM_XOF, NULL), + OSSL_PARAM_int(OSSL_DIGEST_PARAM_ALGID_ABSENT, NULL), + OSSL_PARAM_END +}; +const OSSL_PARAM *ossl_digest_default_gettable_params(void *provctx) +{ + return digest_default_known_gettable_params; +} diff --git a/crypto/openssl/providers/implementations/digests/md2_prov.c b/crypto/openssl/providers/implementations/digests/md2_prov.c new file mode 100644 index 000000000000..a41a02c19890 --- /dev/null +++ b/crypto/openssl/providers/implementations/digests/md2_prov.c @@ -0,0 +1,24 @@ +/* + * Copyright 2019-2020 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 + */ + +/* + * MD2 low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include <openssl/crypto.h> +#include <openssl/md2.h> +#include "prov/digestcommon.h" +#include "prov/implementations.h" + +/* ossl_md2_functions */ +IMPLEMENT_digest_functions(md2, MD2_CTX, + MD2_BLOCK, MD2_DIGEST_LENGTH, 0, + MD2_Init, MD2_Update, MD2_Final) diff --git a/crypto/openssl/providers/implementations/digests/md4_prov.c b/crypto/openssl/providers/implementations/digests/md4_prov.c new file mode 100644 index 000000000000..97f73018c275 --- /dev/null +++ b/crypto/openssl/providers/implementations/digests/md4_prov.c @@ -0,0 +1,24 @@ +/* + * Copyright 2019-2020 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 + */ + +/* + * MD4 low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include <openssl/crypto.h> +#include <openssl/md4.h> +#include "prov/digestcommon.h" +#include "prov/implementations.h" + +/* ossl_md4_functions */ +IMPLEMENT_digest_functions(md4, MD4_CTX, + MD4_CBLOCK, MD4_DIGEST_LENGTH, 0, + MD4_Init, MD4_Update, MD4_Final) diff --git a/crypto/openssl/providers/implementations/digests/md5_prov.c b/crypto/openssl/providers/implementations/digests/md5_prov.c new file mode 100644 index 000000000000..a330e057f547 --- /dev/null +++ b/crypto/openssl/providers/implementations/digests/md5_prov.c @@ -0,0 +1,24 @@ +/* + * Copyright 2019-2020 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 + */ + +/* + * MD5 low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include <openssl/crypto.h> +#include <openssl/md5.h> +#include "prov/digestcommon.h" +#include "prov/implementations.h" + +/* ossl_md5_functions */ +IMPLEMENT_digest_functions(md5, MD5_CTX, + MD5_CBLOCK, MD5_DIGEST_LENGTH, 0, + MD5_Init, MD5_Update, MD5_Final) diff --git a/crypto/openssl/providers/implementations/digests/md5_sha1_prov.c b/crypto/openssl/providers/implementations/digests/md5_sha1_prov.c new file mode 100644 index 000000000000..9735c3f7e40f --- /dev/null +++ b/crypto/openssl/providers/implementations/digests/md5_sha1_prov.c @@ -0,0 +1,61 @@ +/* + * Copyright 2019-2021 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 + */ + +/* + * MD5 and SHA-1 low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include <string.h> +#include <openssl/crypto.h> +#include <openssl/evp.h> +#include <openssl/params.h> +#include <openssl/core_names.h> +#include "prov/md5_sha1.h" +#include "prov/digestcommon.h" +#include "prov/implementations.h" + +static OSSL_FUNC_digest_set_ctx_params_fn md5_sha1_set_ctx_params; +static OSSL_FUNC_digest_settable_ctx_params_fn md5_sha1_settable_ctx_params; + +static const OSSL_PARAM known_md5_sha1_settable_ctx_params[] = { + {OSSL_DIGEST_PARAM_SSL3_MS, OSSL_PARAM_OCTET_STRING, NULL, 0, 0}, + OSSL_PARAM_END +}; + +static const OSSL_PARAM *md5_sha1_settable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + return known_md5_sha1_settable_ctx_params; +} + +/* Special set_params method for SSL3 */ +static int md5_sha1_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p; + MD5_SHA1_CTX *ctx = (MD5_SHA1_CTX *)vctx; + + if (ctx == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_DIGEST_PARAM_SSL3_MS); + if (p != NULL && p->data_type == OSSL_PARAM_OCTET_STRING) + return ossl_md5_sha1_ctrl(ctx, EVP_CTRL_SSL3_MASTER_SECRET, + p->data_size, p->data); + return 1; +} + +/* ossl_md5_sha1_functions */ +IMPLEMENT_digest_functions_with_settable_ctx( + md5_sha1, MD5_SHA1_CTX, MD5_SHA1_CBLOCK, MD5_SHA1_DIGEST_LENGTH, 0, + ossl_md5_sha1_init, ossl_md5_sha1_update, ossl_md5_sha1_final, + md5_sha1_settable_ctx_params, md5_sha1_set_ctx_params) diff --git a/crypto/openssl/providers/implementations/digests/mdc2_prov.c b/crypto/openssl/providers/implementations/digests/mdc2_prov.c new file mode 100644 index 000000000000..e1fc477d21e5 --- /dev/null +++ b/crypto/openssl/providers/implementations/digests/mdc2_prov.c @@ -0,0 +1,61 @@ +/* + * Copyright 2019-2021 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 + */ + +/* + * MDC2 low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include <openssl/crypto.h> +#include <openssl/params.h> +#include <openssl/mdc2.h> +#include <openssl/core_names.h> +#include <openssl/err.h> +#include <openssl/proverr.h> +#include "prov/digestcommon.h" +#include "prov/implementations.h" + +static OSSL_FUNC_digest_set_ctx_params_fn mdc2_set_ctx_params; +static OSSL_FUNC_digest_settable_ctx_params_fn mdc2_settable_ctx_params; + +static const OSSL_PARAM known_mdc2_settable_ctx_params[] = { + OSSL_PARAM_uint(OSSL_DIGEST_PARAM_PAD_TYPE, NULL), + OSSL_PARAM_END +}; + +static const OSSL_PARAM *mdc2_settable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + return known_mdc2_settable_ctx_params; +} + +static int mdc2_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p; + MDC2_CTX *ctx = (MDC2_CTX *)vctx; + + if (ctx == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_DIGEST_PARAM_PAD_TYPE); + if (p != NULL && !OSSL_PARAM_get_uint(p, &ctx->pad_type)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + return 1; +} + +/* ossl_mdc2_functions */ +IMPLEMENT_digest_functions_with_settable_ctx( + mdc2, MDC2_CTX, MDC2_BLOCK, MDC2_DIGEST_LENGTH, 0, + MDC2_Init, MDC2_Update, MDC2_Final, + mdc2_settable_ctx_params, mdc2_set_ctx_params) diff --git a/crypto/openssl/providers/implementations/digests/null_prov.c b/crypto/openssl/providers/implementations/digests/null_prov.c new file mode 100644 index 000000000000..b220a1966ff7 --- /dev/null +++ b/crypto/openssl/providers/implementations/digests/null_prov.c @@ -0,0 +1,52 @@ +/* + * Copyright 2021 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 + */ + +#include <openssl/crypto.h> +#include "prov/digestcommon.h" +#include "prov/implementations.h" + +typedef struct { + unsigned char nothing; +} NULLMD_CTX; + +static int null_init(NULLMD_CTX *ctx) +{ + return 1; +} + +static int null_update(NULLMD_CTX *ctx, const void *data, size_t datalen) +{ + return 1; +} + +static int null_final(unsigned char *md, NULLMD_CTX *ctx) +{ + return 1; +} + +/* + * We must override the PROV_FUNC_DIGEST_FINAL as dgstsize == 0 + * and that would cause compilation warnings with the default implementation. + */ +#undef PROV_FUNC_DIGEST_FINAL +#define PROV_FUNC_DIGEST_FINAL(name, dgstsize, fin) \ +static OSSL_FUNC_digest_final_fn name##_internal_final; \ +static int name##_internal_final(void *ctx, unsigned char *out, size_t *outl, \ + size_t outsz) \ +{ \ + if (ossl_prov_is_running() && fin(out, ctx)) { \ + *outl = dgstsize; \ + return 1; \ + } \ + return 0; \ +} + +IMPLEMENT_digest_functions(nullmd, NULLMD_CTX, + 0, 0, 0, + null_init, null_update, null_final) diff --git a/crypto/openssl/providers/implementations/digests/ripemd_prov.c b/crypto/openssl/providers/implementations/digests/ripemd_prov.c new file mode 100644 index 000000000000..526706c06dcc --- /dev/null +++ b/crypto/openssl/providers/implementations/digests/ripemd_prov.c @@ -0,0 +1,24 @@ +/* + * Copyright 2019-2020 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 + */ + +/* + * RIPEMD160 low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include <openssl/crypto.h> +#include <openssl/ripemd.h> +#include "prov/digestcommon.h" +#include "prov/implementations.h" + +/* ossl_ripemd160_functions */ +IMPLEMENT_digest_functions(ripemd160, RIPEMD160_CTX, + RIPEMD160_CBLOCK, RIPEMD160_DIGEST_LENGTH, 0, + RIPEMD160_Init, RIPEMD160_Update, RIPEMD160_Final) diff --git a/crypto/openssl/providers/implementations/digests/sha2_prov.c b/crypto/openssl/providers/implementations/digests/sha2_prov.c new file mode 100644 index 000000000000..4c35586b00ed --- /dev/null +++ b/crypto/openssl/providers/implementations/digests/sha2_prov.c @@ -0,0 +1,98 @@ +/* + * Copyright 2019-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 + */ + +/* + * SHA low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include <openssl/crypto.h> +#include <openssl/core_dispatch.h> +#include <openssl/evp.h> +#include <openssl/sha.h> +#include <openssl/params.h> +#include <openssl/core_names.h> +#include "prov/digestcommon.h" +#include "prov/implementations.h" +#include "crypto/sha.h" + +#define SHA2_FLAGS PROV_DIGEST_FLAG_ALGID_ABSENT + +static OSSL_FUNC_digest_set_ctx_params_fn sha1_set_ctx_params; +static OSSL_FUNC_digest_settable_ctx_params_fn sha1_settable_ctx_params; + +static const OSSL_PARAM known_sha1_settable_ctx_params[] = { + {OSSL_DIGEST_PARAM_SSL3_MS, OSSL_PARAM_OCTET_STRING, NULL, 0, 0}, + OSSL_PARAM_END +}; +static const OSSL_PARAM *sha1_settable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + return known_sha1_settable_ctx_params; +} + +/* Special set_params method for SSL3 */ +static int sha1_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p; + SHA_CTX *ctx = (SHA_CTX *)vctx; + + if (ctx == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_DIGEST_PARAM_SSL3_MS); + if (p != NULL && p->data_type == OSSL_PARAM_OCTET_STRING) + return ossl_sha1_ctrl(ctx, EVP_CTRL_SSL3_MASTER_SECRET, + p->data_size, p->data); + return 1; +} + +/* ossl_sha1_functions */ +IMPLEMENT_digest_functions_with_settable_ctx( + sha1, SHA_CTX, SHA_CBLOCK, SHA_DIGEST_LENGTH, SHA2_FLAGS, + SHA1_Init, SHA1_Update, SHA1_Final, + sha1_settable_ctx_params, sha1_set_ctx_params) + +/* ossl_sha224_functions */ +IMPLEMENT_digest_functions(sha224, SHA256_CTX, + SHA256_CBLOCK, SHA224_DIGEST_LENGTH, SHA2_FLAGS, + SHA224_Init, SHA224_Update, SHA224_Final) + +/* ossl_sha256_functions */ +IMPLEMENT_digest_functions(sha256, SHA256_CTX, + SHA256_CBLOCK, SHA256_DIGEST_LENGTH, SHA2_FLAGS, + SHA256_Init, SHA256_Update, SHA256_Final) +#ifndef FIPS_MODULE +/* ossl_sha256_192_functions */ +IMPLEMENT_digest_functions(sha256_192, SHA256_CTX, + SHA256_CBLOCK, SHA256_192_DIGEST_LENGTH, SHA2_FLAGS, + ossl_sha256_192_init, SHA256_Update, SHA256_Final) +#endif +/* ossl_sha384_functions */ +IMPLEMENT_digest_functions(sha384, SHA512_CTX, + SHA512_CBLOCK, SHA384_DIGEST_LENGTH, SHA2_FLAGS, + SHA384_Init, SHA384_Update, SHA384_Final) + +/* ossl_sha512_functions */ +IMPLEMENT_digest_functions(sha512, SHA512_CTX, + SHA512_CBLOCK, SHA512_DIGEST_LENGTH, SHA2_FLAGS, + SHA512_Init, SHA512_Update, SHA512_Final) + +/* ossl_sha512_224_functions */ +IMPLEMENT_digest_functions(sha512_224, SHA512_CTX, + SHA512_CBLOCK, SHA224_DIGEST_LENGTH, SHA2_FLAGS, + sha512_224_init, SHA512_Update, SHA512_Final) + +/* ossl_sha512_256_functions */ +IMPLEMENT_digest_functions(sha512_256, SHA512_CTX, + SHA512_CBLOCK, SHA256_DIGEST_LENGTH, SHA2_FLAGS, + sha512_256_init, SHA512_Update, SHA512_Final) diff --git a/crypto/openssl/providers/implementations/digests/sha3_prov.c b/crypto/openssl/providers/implementations/digests/sha3_prov.c new file mode 100644 index 000000000000..a3d209e0eac3 --- /dev/null +++ b/crypto/openssl/providers/implementations/digests/sha3_prov.c @@ -0,0 +1,700 @@ +/* + * Copyright 2019-2025 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 + */ + +#include <string.h> +#include <openssl/core_names.h> +#include <openssl/crypto.h> +#include <openssl/evp.h> +#include <openssl/params.h> +#include <openssl/err.h> +#include <openssl/proverr.h> +#include "internal/numbers.h" +#include "internal/sha3.h" +#include "prov/digestcommon.h" +#include "prov/implementations.h" + +#define SHA3_FLAGS PROV_DIGEST_FLAG_ALGID_ABSENT +#define SHAKE_FLAGS (PROV_DIGEST_FLAG_XOF | PROV_DIGEST_FLAG_ALGID_ABSENT) +#define KMAC_FLAGS PROV_DIGEST_FLAG_XOF + +/* + * Forward declaration of any unique methods implemented here. This is not strictly + * necessary for the compiler, but provides an assurance that the signatures + * of the functions in the dispatch table are correct. + */ +static OSSL_FUNC_digest_init_fn keccak_init; +static OSSL_FUNC_digest_init_fn keccak_init_params; +static OSSL_FUNC_digest_update_fn keccak_update; +static OSSL_FUNC_digest_final_fn keccak_final; +static OSSL_FUNC_digest_freectx_fn keccak_freectx; +static OSSL_FUNC_digest_copyctx_fn keccak_copyctx; +static OSSL_FUNC_digest_dupctx_fn keccak_dupctx; +static OSSL_FUNC_digest_squeeze_fn shake_squeeze; +static OSSL_FUNC_digest_get_ctx_params_fn shake_get_ctx_params; +static OSSL_FUNC_digest_gettable_ctx_params_fn shake_gettable_ctx_params; +static OSSL_FUNC_digest_set_ctx_params_fn shake_set_ctx_params; +static OSSL_FUNC_digest_settable_ctx_params_fn shake_settable_ctx_params; +static sha3_absorb_fn generic_sha3_absorb; +static sha3_final_fn generic_sha3_final; +static sha3_squeeze_fn generic_sha3_squeeze; + +#if defined(OPENSSL_CPUID_OBJ) && defined(__s390__) && defined(KECCAK1600_ASM) +/* + * IBM S390X support + */ +# include "s390x_arch.h" +# define S390_SHA3 1 +# define S390_SHA3_CAPABLE(name) \ + ((OPENSSL_s390xcap_P.kimd[0] & S390X_CAPBIT(S390X_##name)) && \ + (OPENSSL_s390xcap_P.klmd[0] & S390X_CAPBIT(S390X_##name))) + +#endif + +static int keccak_init(void *vctx, ossl_unused const OSSL_PARAM params[]) +{ + if (!ossl_prov_is_running()) + return 0; + /* The newctx() handles most of the ctx fixed setup. */ + ossl_sha3_reset((KECCAK1600_CTX *)vctx); + return 1; +} + +static int keccak_init_params(void *vctx, const OSSL_PARAM params[]) +{ + return keccak_init(vctx, NULL) + && shake_set_ctx_params(vctx, params); +} + +static int keccak_update(void *vctx, const unsigned char *inp, size_t len) +{ + KECCAK1600_CTX *ctx = vctx; + const size_t bsz = ctx->block_size; + size_t num, rem; + + if (len == 0) + return 1; + + /* Is there anything in the buffer already ? */ + if ((num = ctx->bufsz) != 0) { + /* Calculate how much space is left in the buffer */ + rem = bsz - num; + /* If the new input does not fill the buffer then just add it */ + if (len < rem) { + memcpy(ctx->buf + num, inp, len); + ctx->bufsz += len; + return 1; + } + /* otherwise fill up the buffer and absorb the buffer */ + memcpy(ctx->buf + num, inp, rem); + /* Update the input pointer */ + inp += rem; + len -= rem; + ctx->meth.absorb(ctx, ctx->buf, bsz); + ctx->bufsz = 0; + } + /* Absorb the input - rem = leftover part of the input < blocksize) */ + rem = ctx->meth.absorb(ctx, inp, len); + /* Copy the leftover bit of the input into the buffer */ + if (rem) { + memcpy(ctx->buf, inp + len - rem, rem); + ctx->bufsz = rem; + } + return 1; +} + +static int keccak_final(void *vctx, unsigned char *out, size_t *outl, + size_t outlen) +{ + int ret = 1; + KECCAK1600_CTX *ctx = vctx; + + if (!ossl_prov_is_running()) + return 0; + if (ctx->md_size == SIZE_MAX) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_LENGTH); + return 0; + } + if (outlen > 0) + ret = ctx->meth.final(ctx, out, ctx->md_size); + + *outl = ctx->md_size; + return ret; +} + +static int shake_squeeze(void *vctx, unsigned char *out, size_t *outl, + size_t outlen) +{ + int ret = 1; + KECCAK1600_CTX *ctx = vctx; + + if (!ossl_prov_is_running()) + return 0; + if (ctx->meth.squeeze == NULL) + return 0; + if (outlen > 0) + ret = ctx->meth.squeeze(ctx, out, outlen); + + *outl = outlen; + return ret; +} + +/*- + * Generic software version of the absorb() and final(). + */ +static size_t generic_sha3_absorb(void *vctx, const void *inp, size_t len) +{ + KECCAK1600_CTX *ctx = vctx; + + if (!(ctx->xof_state == XOF_STATE_INIT || + ctx->xof_state == XOF_STATE_ABSORB)) + return 0; + ctx->xof_state = XOF_STATE_ABSORB; + return SHA3_absorb(ctx->A, inp, len, ctx->block_size); +} + +static int generic_sha3_final(void *vctx, unsigned char *out, size_t outlen) +{ + return ossl_sha3_final((KECCAK1600_CTX *)vctx, out, outlen); +} + +static int generic_sha3_squeeze(void *vctx, unsigned char *out, size_t outlen) +{ + return ossl_sha3_squeeze((KECCAK1600_CTX *)vctx, out, outlen); +} + +static PROV_SHA3_METHOD sha3_generic_md = { + generic_sha3_absorb, + generic_sha3_final, + NULL +}; + +static PROV_SHA3_METHOD shake_generic_md = +{ + generic_sha3_absorb, + generic_sha3_final, + generic_sha3_squeeze +}; + +#if defined(S390_SHA3) + +static sha3_absorb_fn s390x_sha3_absorb; +static sha3_final_fn s390x_sha3_final; +static sha3_final_fn s390x_shake_final; + +/*- + * The platform specific parts of the absorb() and final() for S390X. + */ +static size_t s390x_sha3_absorb(void *vctx, const void *inp, size_t len) +{ + KECCAK1600_CTX *ctx = vctx; + size_t rem = len % ctx->block_size; + unsigned int fc; + + if (!(ctx->xof_state == XOF_STATE_INIT || + ctx->xof_state == XOF_STATE_ABSORB)) + return 0; + if (len - rem > 0) { + fc = ctx->pad; + fc |= ctx->xof_state == XOF_STATE_INIT ? S390X_KIMD_NIP : 0; + ctx->xof_state = XOF_STATE_ABSORB; + s390x_kimd(inp, len - rem, fc, ctx->A); + } + return rem; +} + +static int s390x_sha3_final(void *vctx, unsigned char *out, size_t outlen) +{ + KECCAK1600_CTX *ctx = vctx; + unsigned int fc; + + if (!ossl_prov_is_running()) + return 0; + if (!(ctx->xof_state == XOF_STATE_INIT || + ctx->xof_state == XOF_STATE_ABSORB)) + return 0; + fc = ctx->pad | S390X_KLMD_DUFOP; + fc |= ctx->xof_state == XOF_STATE_INIT ? S390X_KLMD_NIP : 0; + ctx->xof_state = XOF_STATE_FINAL; + s390x_klmd(ctx->buf, ctx->bufsz, NULL, 0, fc, ctx->A); + memcpy(out, ctx->A, outlen); + return 1; +} + +static int s390x_shake_final(void *vctx, unsigned char *out, size_t outlen) +{ + KECCAK1600_CTX *ctx = vctx; + unsigned int fc; + + if (!ossl_prov_is_running()) + return 0; + if (!(ctx->xof_state == XOF_STATE_INIT || + ctx->xof_state == XOF_STATE_ABSORB)) + return 0; + fc = ctx->pad | S390X_KLMD_DUFOP; + fc |= ctx->xof_state == XOF_STATE_INIT ? S390X_KLMD_NIP : 0; + ctx->xof_state = XOF_STATE_FINAL; + s390x_klmd(ctx->buf, ctx->bufsz, out, outlen, fc, ctx->A); + return 1; +} + +static int s390x_shake_squeeze(void *vctx, unsigned char *out, size_t outlen) +{ + KECCAK1600_CTX *ctx = vctx; + unsigned int fc; + size_t len; + + if (!ossl_prov_is_running()) + return 0; + if (ctx->xof_state == XOF_STATE_FINAL) + return 0; + /* + * On the first squeeze call, finish the absorb process (incl. padding). + */ + if (ctx->xof_state != XOF_STATE_SQUEEZE) { + fc = ctx->pad; + fc |= ctx->xof_state == XOF_STATE_INIT ? S390X_KLMD_NIP : 0; + ctx->xof_state = XOF_STATE_SQUEEZE; + s390x_klmd(ctx->buf, ctx->bufsz, out, outlen, fc, ctx->A); + ctx->bufsz = outlen % ctx->block_size; + /* reuse ctx->bufsz to count bytes squeezed from current sponge */ + return 1; + } + ctx->xof_state = XOF_STATE_SQUEEZE; + if (ctx->bufsz != 0) { + len = ctx->block_size - ctx->bufsz; + if (outlen < len) + len = outlen; + memcpy(out, (char *)ctx->A + ctx->bufsz, len); + out += len; + outlen -= len; + ctx->bufsz += len; + if (ctx->bufsz == ctx->block_size) + ctx->bufsz = 0; + } + if (outlen == 0) + return 1; + s390x_klmd(NULL, 0, out, outlen, ctx->pad | S390X_KLMD_PS, ctx->A); + ctx->bufsz = outlen % ctx->block_size; + + return 1; +} + +static int s390x_keccakc_final(void *vctx, unsigned char *out, size_t outlen, + int padding) +{ + KECCAK1600_CTX *ctx = vctx; + size_t bsz = ctx->block_size; + size_t num = ctx->bufsz; + size_t needed = outlen; + unsigned int fc; + + if (!ossl_prov_is_running()) + return 0; + if (!(ctx->xof_state == XOF_STATE_INIT || + ctx->xof_state == XOF_STATE_ABSORB)) + return 0; + fc = ctx->pad; + fc |= ctx->xof_state == XOF_STATE_INIT ? S390X_KIMD_NIP : 0; + ctx->xof_state = XOF_STATE_FINAL; + if (outlen == 0) + return 1; + memset(ctx->buf + num, 0, bsz - num); + ctx->buf[num] = padding; + ctx->buf[bsz - 1] |= 0x80; + s390x_kimd(ctx->buf, bsz, fc, ctx->A); + num = needed > bsz ? bsz : needed; + memcpy(out, ctx->A, num); + needed -= num; + if (needed > 0) + s390x_klmd(NULL, 0, out + bsz, needed, + ctx->pad | S390X_KLMD_PS | S390X_KLMD_DUFOP, ctx->A); + + return 1; +} + +static int s390x_keccak_final(void *vctx, unsigned char *out, size_t outlen) +{ + return s390x_keccakc_final(vctx, out, outlen, 0x01); +} + +static int s390x_kmac_final(void *vctx, unsigned char *out, size_t outlen) +{ + return s390x_keccakc_final(vctx, out, outlen, 0x04); +} + +static int s390x_keccakc_squeeze(void *vctx, unsigned char *out, size_t outlen, + int padding) +{ + KECCAK1600_CTX *ctx = vctx; + size_t len; + unsigned int fc; + + if (!ossl_prov_is_running()) + return 0; + if (ctx->xof_state == XOF_STATE_FINAL) + return 0; + /* + * On the first squeeze call, finish the absorb process + * by adding the trailing padding and then doing + * a final absorb. + */ + if (ctx->xof_state != XOF_STATE_SQUEEZE) { + len = ctx->block_size - ctx->bufsz; + memset(ctx->buf + ctx->bufsz, 0, len); + ctx->buf[ctx->bufsz] = padding; + ctx->buf[ctx->block_size - 1] |= 0x80; + fc = ctx->pad; + fc |= ctx->xof_state == XOF_STATE_INIT ? S390X_KIMD_NIP : 0; + s390x_kimd(ctx->buf, ctx->block_size, fc, ctx->A); + ctx->bufsz = 0; + /* reuse ctx->bufsz to count bytes squeezed from current sponge */ + } + if (ctx->bufsz != 0 || ctx->xof_state != XOF_STATE_SQUEEZE) { + len = ctx->block_size - ctx->bufsz; + if (outlen < len) + len = outlen; + memcpy(out, (char *)ctx->A + ctx->bufsz, len); + out += len; + outlen -= len; + ctx->bufsz += len; + if (ctx->bufsz == ctx->block_size) + ctx->bufsz = 0; + } + ctx->xof_state = XOF_STATE_SQUEEZE; + if (outlen == 0) + return 1; + s390x_klmd(NULL, 0, out, outlen, ctx->pad | S390X_KLMD_PS, ctx->A); + ctx->bufsz = outlen % ctx->block_size; + + return 1; +} + +static int s390x_keccak_squeeze(void *vctx, unsigned char *out, size_t outlen) +{ + return s390x_keccakc_squeeze(vctx, out, outlen, 0x01); +} + +static int s390x_kmac_squeeze(void *vctx, unsigned char *out, size_t outlen) +{ + return s390x_keccakc_squeeze(vctx, out, outlen, 0x04); +} + +static PROV_SHA3_METHOD sha3_s390x_md = { + s390x_sha3_absorb, + s390x_sha3_final, + NULL, +}; + +static PROV_SHA3_METHOD keccak_s390x_md = { + s390x_sha3_absorb, + s390x_keccak_final, + s390x_keccak_squeeze, +}; + +static PROV_SHA3_METHOD shake_s390x_md = { + s390x_sha3_absorb, + s390x_shake_final, + s390x_shake_squeeze, +}; + +static PROV_SHA3_METHOD kmac_s390x_md = { + s390x_sha3_absorb, + s390x_kmac_final, + s390x_kmac_squeeze, +}; + +# define SHAKE_SET_MD(uname, typ) \ + if (S390_SHA3_CAPABLE(uname)) { \ + ctx->pad = S390X_##uname; \ + ctx->meth = typ##_s390x_md; \ + } else { \ + ctx->meth = shake_generic_md; \ + } + +# define SHA3_SET_MD(uname, typ) \ + if (S390_SHA3_CAPABLE(uname)) { \ + ctx->pad = S390X_##uname; \ + ctx->meth = typ##_s390x_md; \ + } else { \ + ctx->meth = sha3_generic_md; \ + } +# define KMAC_SET_MD(bitlen) \ + if (S390_SHA3_CAPABLE(SHAKE_##bitlen)) { \ + ctx->pad = S390X_SHAKE_##bitlen; \ + ctx->meth = kmac_s390x_md; \ + } else { \ + ctx->meth = sha3_generic_md; \ + } +#elif defined(__aarch64__) && defined(KECCAK1600_ASM) +# include "arm_arch.h" + +static sha3_absorb_fn armsha3_sha3_absorb; + +size_t SHA3_absorb_cext(uint64_t A[5][5], const unsigned char *inp, size_t len, + size_t r); +/*- + * Hardware-assisted ARMv8.2 SHA3 extension version of the absorb() + */ +static size_t armsha3_sha3_absorb(void *vctx, const void *inp, size_t len) +{ + KECCAK1600_CTX *ctx = vctx; + + return SHA3_absorb_cext(ctx->A, inp, len, ctx->block_size); +} + +static PROV_SHA3_METHOD sha3_ARMSHA3_md = { + armsha3_sha3_absorb, + generic_sha3_final +}; +static PROV_SHA3_METHOD shake_ARMSHA3_md = +{ + armsha3_sha3_absorb, + generic_sha3_final, + generic_sha3_squeeze +}; +# define SHAKE_SET_MD(uname, typ) \ + if (OPENSSL_armcap_P & ARMV8_HAVE_SHA3_AND_WORTH_USING) { \ + ctx->meth = shake_ARMSHA3_md; \ + } else { \ + ctx->meth = shake_generic_md; \ + } + +# define SHA3_SET_MD(uname, typ) \ + if (OPENSSL_armcap_P & ARMV8_HAVE_SHA3_AND_WORTH_USING) { \ + ctx->meth = sha3_ARMSHA3_md; \ + } else { \ + ctx->meth = sha3_generic_md; \ + } +# define KMAC_SET_MD(bitlen) \ + if (OPENSSL_armcap_P & ARMV8_HAVE_SHA3_AND_WORTH_USING) { \ + ctx->meth = sha3_ARMSHA3_md; \ + } else { \ + ctx->meth = sha3_generic_md; \ + } +#else +# define SHA3_SET_MD(uname, typ) ctx->meth = sha3_generic_md; +# define KMAC_SET_MD(bitlen) ctx->meth = sha3_generic_md; +# define SHAKE_SET_MD(uname, typ) ctx->meth = shake_generic_md; +#endif /* S390_SHA3 */ + +#define SHA3_newctx(typ, uname, name, bitlen, pad) \ +static OSSL_FUNC_digest_newctx_fn name##_newctx; \ +static void *name##_newctx(void *provctx) \ +{ \ + KECCAK1600_CTX *ctx = ossl_prov_is_running() ? OPENSSL_zalloc(sizeof(*ctx)) \ + : NULL; \ + \ + if (ctx == NULL) \ + return NULL; \ + ossl_sha3_init(ctx, pad, bitlen); \ + SHA3_SET_MD(uname, typ) \ + return ctx; \ +} + +#define SHAKE_newctx(typ, uname, name, bitlen, mdlen, pad) \ +static OSSL_FUNC_digest_newctx_fn name##_newctx; \ +static void *name##_newctx(void *provctx) \ +{ \ + KECCAK1600_CTX *ctx = ossl_prov_is_running() ? OPENSSL_zalloc(sizeof(*ctx))\ + : NULL; \ + \ + if (ctx == NULL) \ + return NULL; \ + ossl_keccak_init(ctx, pad, bitlen, mdlen); \ + if (mdlen == 0) \ + ctx->md_size = SIZE_MAX; \ + SHAKE_SET_MD(uname, typ) \ + return ctx; \ +} + +#define KMAC_newctx(uname, bitlen, pad) \ +static OSSL_FUNC_digest_newctx_fn uname##_newctx; \ +static void *uname##_newctx(void *provctx) \ +{ \ + KECCAK1600_CTX *ctx = ossl_prov_is_running() ? OPENSSL_zalloc(sizeof(*ctx)) \ + : NULL; \ + \ + if (ctx == NULL) \ + return NULL; \ + ossl_keccak_init(ctx, pad, bitlen, 2 * bitlen); \ + KMAC_SET_MD(bitlen) \ + return ctx; \ +} + +#define PROV_FUNC_SHA3_DIGEST_COMMON(name, bitlen, blksize, dgstsize, flags) \ +PROV_FUNC_DIGEST_GET_PARAM(name, blksize, dgstsize, flags) \ +const OSSL_DISPATCH ossl_##name##_functions[] = { \ + { OSSL_FUNC_DIGEST_NEWCTX, (void (*)(void))name##_newctx }, \ + { OSSL_FUNC_DIGEST_UPDATE, (void (*)(void))keccak_update }, \ + { OSSL_FUNC_DIGEST_FINAL, (void (*)(void))keccak_final }, \ + { OSSL_FUNC_DIGEST_FREECTX, (void (*)(void))keccak_freectx }, \ + { OSSL_FUNC_DIGEST_DUPCTX, (void (*)(void))keccak_dupctx }, \ + { OSSL_FUNC_DIGEST_COPYCTX, (void (*)(void))keccak_copyctx }, \ + PROV_DISPATCH_FUNC_DIGEST_GET_PARAMS(name) + +#define PROV_FUNC_SHA3_DIGEST(name, bitlen, blksize, dgstsize, flags) \ + PROV_FUNC_SHA3_DIGEST_COMMON(name, bitlen, blksize, dgstsize, flags), \ + { OSSL_FUNC_DIGEST_INIT, (void (*)(void))keccak_init }, \ + PROV_DISPATCH_FUNC_DIGEST_CONSTRUCT_END + +#define PROV_FUNC_SHAKE_DIGEST(name, bitlen, blksize, dgstsize, flags) \ + PROV_FUNC_SHA3_DIGEST_COMMON(name, bitlen, blksize, dgstsize, flags), \ + { OSSL_FUNC_DIGEST_SQUEEZE, (void (*)(void))shake_squeeze }, \ + { OSSL_FUNC_DIGEST_INIT, (void (*)(void))keccak_init_params }, \ + { OSSL_FUNC_DIGEST_SET_CTX_PARAMS, (void (*)(void))shake_set_ctx_params }, \ + { OSSL_FUNC_DIGEST_SETTABLE_CTX_PARAMS, \ + (void (*)(void))shake_settable_ctx_params }, \ + { OSSL_FUNC_DIGEST_GET_CTX_PARAMS, (void (*)(void))shake_get_ctx_params }, \ + { OSSL_FUNC_DIGEST_GETTABLE_CTX_PARAMS, \ + (void (*)(void))shake_gettable_ctx_params }, \ + PROV_DISPATCH_FUNC_DIGEST_CONSTRUCT_END + +static void keccak_freectx(void *vctx) +{ + KECCAK1600_CTX *ctx = (KECCAK1600_CTX *)vctx; + + OPENSSL_clear_free(ctx, sizeof(*ctx)); +} + +static void keccak_copyctx(void *voutctx, void *vinctx) +{ + KECCAK1600_CTX *outctx = (KECCAK1600_CTX *)voutctx; + KECCAK1600_CTX *inctx = (KECCAK1600_CTX *)vinctx; + + *outctx = *inctx; +} + +static void *keccak_dupctx(void *ctx) +{ + KECCAK1600_CTX *in = (KECCAK1600_CTX *)ctx; + KECCAK1600_CTX *ret = ossl_prov_is_running() ? OPENSSL_malloc(sizeof(*ret)) + : NULL; + + if (ret != NULL) + *ret = *in; + return ret; +} + +static const OSSL_PARAM *shake_gettable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + static const OSSL_PARAM known_shake_gettable_ctx_params[] = { + {OSSL_DIGEST_PARAM_XOFLEN, OSSL_PARAM_UNSIGNED_INTEGER, NULL, 0, 0}, + {OSSL_DIGEST_PARAM_SIZE, OSSL_PARAM_UNSIGNED_INTEGER, NULL, 0, 0}, + OSSL_PARAM_END + }; + return known_shake_gettable_ctx_params; +} + +static int shake_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + OSSL_PARAM *p; + KECCAK1600_CTX *ctx = (KECCAK1600_CTX *)vctx; + + if (ctx == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate(params, OSSL_DIGEST_PARAM_XOFLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->md_size)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + /* Size is an alias of xoflen */ + p = OSSL_PARAM_locate(params, OSSL_DIGEST_PARAM_SIZE); + if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->md_size)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + return 1; +} + +static const OSSL_PARAM *shake_settable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + static const OSSL_PARAM known_shake_settable_ctx_params[] = { + {OSSL_DIGEST_PARAM_XOFLEN, OSSL_PARAM_UNSIGNED_INTEGER, NULL, 0, 0}, + {OSSL_DIGEST_PARAM_SIZE, OSSL_PARAM_UNSIGNED_INTEGER, NULL, 0, 0}, + OSSL_PARAM_END + }; + + return known_shake_settable_ctx_params; +} + +static int shake_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p; + KECCAK1600_CTX *ctx = (KECCAK1600_CTX *)vctx; + + if (ctx == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_DIGEST_PARAM_XOFLEN); + if (p == NULL) + p = OSSL_PARAM_locate_const(params, OSSL_DIGEST_PARAM_SIZE); + + if (p != NULL && !OSSL_PARAM_get_size_t(p, &ctx->md_size)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + return 1; +} + +#define IMPLEMENT_SHA3_functions(bitlen) \ + SHA3_newctx(sha3, SHA3_##bitlen, sha3_##bitlen, bitlen, '\x06') \ + PROV_FUNC_SHA3_DIGEST(sha3_##bitlen, bitlen, \ + SHA3_BLOCKSIZE(bitlen), SHA3_MDSIZE(bitlen), \ + SHA3_FLAGS) + +#define IMPLEMENT_KECCAK_functions(bitlen) \ + SHA3_newctx(keccak, KECCAK_##bitlen, keccak_##bitlen, bitlen, '\x01') \ + PROV_FUNC_SHA3_DIGEST(keccak_##bitlen, bitlen, \ + SHA3_BLOCKSIZE(bitlen), SHA3_MDSIZE(bitlen), \ + SHA3_FLAGS) + +#define IMPLEMENT_SHAKE_functions(bitlen) \ + SHAKE_newctx(shake, SHAKE_##bitlen, shake_##bitlen, bitlen, \ + 0 /* no default md length */, '\x1f') \ + PROV_FUNC_SHAKE_DIGEST(shake_##bitlen, bitlen, \ + SHA3_BLOCKSIZE(bitlen), 0, \ + SHAKE_FLAGS) + +#define IMPLEMENT_KMAC_functions(bitlen) \ + KMAC_newctx(keccak_kmac_##bitlen, bitlen, '\x04') \ + PROV_FUNC_SHAKE_DIGEST(keccak_kmac_##bitlen, bitlen, \ + SHA3_BLOCKSIZE(bitlen), KMAC_MDSIZE(bitlen), \ + KMAC_FLAGS) + +/* ossl_sha3_224_functions */ +IMPLEMENT_SHA3_functions(224) +/* ossl_sha3_256_functions */ +IMPLEMENT_SHA3_functions(256) +/* ossl_sha3_384_functions */ +IMPLEMENT_SHA3_functions(384) +/* ossl_sha3_512_functions */ +IMPLEMENT_SHA3_functions(512) +/* ossl_keccak_224_functions */ +IMPLEMENT_KECCAK_functions(224) +/* ossl_keccak_256_functions */ +IMPLEMENT_KECCAK_functions(256) +/* ossl_keccak_384_functions */ +IMPLEMENT_KECCAK_functions(384) +/* ossl_keccak_512_functions */ +IMPLEMENT_KECCAK_functions(512) +/* ossl_shake_128_functions */ +IMPLEMENT_SHAKE_functions(128) +/* ossl_shake_256_functions */ +IMPLEMENT_SHAKE_functions(256) +/* ossl_keccak_kmac_128_functions */ +IMPLEMENT_KMAC_functions(128) +/* ossl_keccak_kmac_256_functions */ +IMPLEMENT_KMAC_functions(256) diff --git a/crypto/openssl/providers/implementations/digests/sm3_prov.c b/crypto/openssl/providers/implementations/digests/sm3_prov.c new file mode 100644 index 000000000000..9d6de5b6ac19 --- /dev/null +++ b/crypto/openssl/providers/implementations/digests/sm3_prov.c @@ -0,0 +1,18 @@ +/* + * Copyright 2019-2021 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 + */ + +#include <openssl/crypto.h> +#include "internal/sm3.h" +#include "prov/digestcommon.h" +#include "prov/implementations.h" + +/* ossl_sm3_functions */ +IMPLEMENT_digest_functions(sm3, SM3_CTX, + SM3_CBLOCK, SM3_DIGEST_LENGTH, 0, + ossl_sm3_init, ossl_sm3_update, ossl_sm3_final) diff --git a/crypto/openssl/providers/implementations/digests/wp_prov.c b/crypto/openssl/providers/implementations/digests/wp_prov.c new file mode 100644 index 000000000000..2af70b337281 --- /dev/null +++ b/crypto/openssl/providers/implementations/digests/wp_prov.c @@ -0,0 +1,24 @@ +/* + * Copyright 2019-2020 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 + */ + +/* + * Whirlpool low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include <openssl/crypto.h> +#include <openssl/whrlpool.h> +#include "prov/digestcommon.h" +#include "prov/implementations.h" + +/* ossl_wp_functions */ +IMPLEMENT_digest_functions(wp, WHIRLPOOL_CTX, + WHIRLPOOL_BBLOCK / 8, WHIRLPOOL_DIGEST_LENGTH, 0, + WHIRLPOOL_Init, WHIRLPOOL_Update, WHIRLPOOL_Final) diff --git a/crypto/openssl/providers/implementations/encode_decode/build.info b/crypto/openssl/providers/implementations/encode_decode/build.info new file mode 100644 index 000000000000..a1529fdf1258 --- /dev/null +++ b/crypto/openssl/providers/implementations/encode_decode/build.info @@ -0,0 +1,32 @@ +# We make separate GOAL variables for each algorithm, to make it easy to +# switch each to the Legacy provider when needed. + +$ENCODER_GOAL=../../libdefault.a +$DECODER_GOAL=../../libdefault.a + +SOURCE[$ENCODER_GOAL]=endecoder_common.c + +SOURCE[$DECODER_GOAL]=decode_der2key.c decode_epki2pki.c decode_pem2der.c \ + decode_msblob2key.c decode_pvk2key.c \ + decode_spki2typespki.c + +SOURCE[$ENCODER_GOAL]=encode_key2any.c encode_key2text.c encode_key2ms.c +# encode_key2blob.c is only being included when EC is enabled, because we +# currently only define a "blob" output type for EC public keys. This may +# change in the future. +IF[{- !$disabled{ec} -}] + SOURCE[$ENCODER_GOAL]=encode_key2blob.c +ENDIF +DEPEND[encode_key2any.o]=../../common/include/prov/der_rsa.h + +IF[{- !$disabled{'ml-dsa'} -}] + SOURCE[$DECODER_GOAL]=ml_dsa_codecs.c +ENDIF + +IF[{- !$disabled{'ml-kem'} -}] + SOURCE[$DECODER_GOAL]=ml_kem_codecs.c +ENDIF + +IF[{- !$disabled{'ml-dsa'} || !$disabled{'ml-kem'} -}] + SOURCE[$DECODER_GOAL]=ml_common_codecs.c +ENDIF diff --git a/crypto/openssl/providers/implementations/encode_decode/decode_der2key.c b/crypto/openssl/providers/implementations/encode_decode/decode_der2key.c new file mode 100644 index 000000000000..a3f0d0897dae --- /dev/null +++ b/crypto/openssl/providers/implementations/encode_decode/decode_der2key.c @@ -0,0 +1,1307 @@ +/* + * Copyright 2020-2025 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 + */ + +/* + * low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include <openssl/byteorder.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/core_object.h> +#include <openssl/crypto.h> +#include <openssl/err.h> +#include <openssl/params.h> +#include <openssl/pem.h> /* PEM_BUFSIZE and public PEM functions */ +#include <openssl/pkcs12.h> +#include <openssl/provider.h> +#include <openssl/x509.h> +#include <openssl/proverr.h> +#include <openssl/asn1t.h> +#include "internal/cryptlib.h" /* ossl_assert() */ +#include "crypto/dh.h" +#include "crypto/dsa.h" +#include "crypto/ec.h" +#include "crypto/evp.h" +#include "crypto/ecx.h" +#include "crypto/rsa.h" +#include "crypto/ml_dsa.h" +#include "crypto/slh_dsa.h" +#include "crypto/x509.h" +#include "crypto/ml_kem.h" +#include "openssl/obj_mac.h" +#include "prov/bio.h" +#include "prov/implementations.h" +#include "endecoder_local.h" +#include "internal/nelem.h" +#include "ml_dsa_codecs.h" +#include "ml_kem_codecs.h" + +#ifndef OPENSSL_NO_SLH_DSA +typedef struct { + ASN1_OBJECT *oid; +} BARE_ALGOR; + +typedef struct { + BARE_ALGOR algor; + ASN1_BIT_STRING *pubkey; +} BARE_PUBKEY; + +ASN1_SEQUENCE(BARE_ALGOR) = { + ASN1_SIMPLE(BARE_ALGOR, oid, ASN1_OBJECT), +} static_ASN1_SEQUENCE_END(BARE_ALGOR) + +ASN1_SEQUENCE(BARE_PUBKEY) = { + ASN1_EMBED(BARE_PUBKEY, algor, BARE_ALGOR), + ASN1_SIMPLE(BARE_PUBKEY, pubkey, ASN1_BIT_STRING) +} static_ASN1_SEQUENCE_END(BARE_PUBKEY) +#endif /* OPENSSL_NO_SLH_DSA */ + +struct der2key_ctx_st; /* Forward declaration */ +typedef int check_key_fn(void *, struct der2key_ctx_st *ctx); +typedef void adjust_key_fn(void *, struct der2key_ctx_st *ctx); +typedef void free_key_fn(void *); +typedef void *d2i_PKCS8_fn(const unsigned char **, long, + struct der2key_ctx_st *); +typedef void *d2i_PUBKEY_fn(const unsigned char **, long, + struct der2key_ctx_st *); +struct keytype_desc_st { + const char *keytype_name; + const OSSL_DISPATCH *fns; /* Keymgmt (to pilfer functions from) */ + + /* The input structure name */ + const char *structure_name; + + /* + * The EVP_PKEY_xxx type macro. Should be zero for type specific + * structures, non-zero when the outermost structure is PKCS#8 or + * SubjectPublicKeyInfo. This determines which of the function + * pointers below will be used. + */ + int evp_type; + + /* The selection mask for OSSL_FUNC_decoder_does_selection() */ + int selection_mask; + + /* For type specific decoders, we use the corresponding d2i */ + d2i_of_void *d2i_private_key; /* From type-specific DER */ + d2i_of_void *d2i_public_key; /* From type-specific DER */ + d2i_of_void *d2i_key_params; /* From type-specific DER */ + d2i_PKCS8_fn *d2i_PKCS8; /* Wrapped in a PrivateKeyInfo */ + d2i_PUBKEY_fn *d2i_PUBKEY; /* Wrapped in a SubjectPublicKeyInfo */ + + /* + * For any key, we may need to check that the key meets expectations. + * This is useful when the same functions can decode several variants + * of a key. + */ + check_key_fn *check_key; + + /* + * For any key, we may need to make provider specific adjustments, such + * as ensure the key carries the correct library context. + */ + adjust_key_fn *adjust_key; + /* {type}_free() */ + free_key_fn *free_key; +}; + +/* + * Context used for DER to key decoding. + */ +struct der2key_ctx_st { + PROV_CTX *provctx; + char propq[OSSL_MAX_PROPQUERY_SIZE]; + const struct keytype_desc_st *desc; + /* The selection that is passed to der2key_decode() */ + int selection; + /* Flag used to signal that a failure is fatal */ + unsigned int flag_fatal : 1; +}; + +typedef void *key_from_pkcs8_t(const PKCS8_PRIV_KEY_INFO *p8inf, + OSSL_LIB_CTX *libctx, const char *propq); +static void *der2key_decode_p8(const unsigned char **input_der, + long input_der_len, struct der2key_ctx_st *ctx, + key_from_pkcs8_t *key_from_pkcs8) +{ + PKCS8_PRIV_KEY_INFO *p8inf = NULL; + const X509_ALGOR *alg = NULL; + void *key = NULL; + + if ((p8inf = d2i_PKCS8_PRIV_KEY_INFO(NULL, input_der, input_der_len)) != NULL + && PKCS8_pkey_get0(NULL, NULL, NULL, &alg, p8inf) + && (OBJ_obj2nid(alg->algorithm) == ctx->desc->evp_type + /* Allow decoding sm2 private key with id_ecPublicKey */ + || (OBJ_obj2nid(alg->algorithm) == NID_X9_62_id_ecPublicKey + && ctx->desc->evp_type == NID_sm2))) + key = key_from_pkcs8(p8inf, PROV_LIBCTX_OF(ctx->provctx), ctx->propq); + PKCS8_PRIV_KEY_INFO_free(p8inf); + + return key; +} + +/* ---------------------------------------------------------------------- */ + +static OSSL_FUNC_decoder_freectx_fn der2key_freectx; +static OSSL_FUNC_decoder_decode_fn der2key_decode; +static OSSL_FUNC_decoder_export_object_fn der2key_export_object; +static OSSL_FUNC_decoder_settable_ctx_params_fn der2key_settable_ctx_params; +static OSSL_FUNC_decoder_set_ctx_params_fn der2key_set_ctx_params; + +static struct der2key_ctx_st * +der2key_newctx(void *provctx, const struct keytype_desc_st *desc) +{ + struct der2key_ctx_st *ctx = OPENSSL_zalloc(sizeof(*ctx)); + + if (ctx != NULL) { + ctx->provctx = provctx; + ctx->desc = desc; + } + return ctx; +} + +static const OSSL_PARAM *der2key_settable_ctx_params(ossl_unused void *provctx) +{ + static const OSSL_PARAM settables[] = { + OSSL_PARAM_utf8_string(OSSL_DECODER_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_END + }; + return settables; +} + +static int der2key_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + struct der2key_ctx_st *ctx = vctx; + const OSSL_PARAM *p; + char *str = ctx->propq; + + p = OSSL_PARAM_locate_const(params, OSSL_DECODER_PARAM_PROPERTIES); + if (p != NULL && !OSSL_PARAM_get_utf8_string(p, &str, sizeof(ctx->propq))) + return 0; + + return 1; +} + +static void der2key_freectx(void *vctx) +{ + struct der2key_ctx_st *ctx = vctx; + + OPENSSL_free(ctx); +} + +static int der2key_check_selection(int selection, + const struct keytype_desc_st *desc) +{ + /* + * The selections are kinda sorta "levels", i.e. each selection given + * here is assumed to include those following. + */ + int checks[] = { + OSSL_KEYMGMT_SELECT_PRIVATE_KEY, + OSSL_KEYMGMT_SELECT_PUBLIC_KEY, + OSSL_KEYMGMT_SELECT_ALL_PARAMETERS + }; + size_t i; + + /* The decoder implementations made here support guessing */ + if (selection == 0) + return 1; + + for (i = 0; i < OSSL_NELEM(checks); i++) { + int check1 = (selection & checks[i]) != 0; + int check2 = (desc->selection_mask & checks[i]) != 0; + + /* + * If the caller asked for the currently checked bit(s), return + * whether the decoder description says it's supported. + */ + if (check1) + return check2; + } + + /* This should be dead code, but just to be safe... */ + return 0; +} + +static int der2key_decode(void *vctx, OSSL_CORE_BIO *cin, int selection, + OSSL_CALLBACK *data_cb, void *data_cbarg, + OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg) +{ + struct der2key_ctx_st *ctx = vctx; + unsigned char *der = NULL; + const unsigned char *derp; + long der_len = 0; + void *key = NULL; + int ok = 0; + + ctx->selection = selection; + /* + * The caller is allowed to specify 0 as a selection mask, to have the + * structure and key type guessed. For type-specific structures, this + * is not recommended, as some structures are very similar. + * Note that 0 isn't the same as OSSL_KEYMGMT_SELECT_ALL, as the latter + * signifies a private key structure, where everything else is assumed + * to be present as well. + */ + if (selection == 0) + selection = ctx->desc->selection_mask; + if ((selection & ctx->desc->selection_mask) == 0) { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } + + ok = ossl_read_der(ctx->provctx, cin, &der, &der_len); + if (!ok) + goto next; + + ok = 0; /* Assume that we fail */ + + ERR_set_mark(); + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) { + derp = der; + if (ctx->desc->d2i_PKCS8 != NULL) { + key = ctx->desc->d2i_PKCS8(&derp, der_len, ctx); + if (ctx->flag_fatal) { + ERR_clear_last_mark(); + goto end; + } + } else if (ctx->desc->d2i_private_key != NULL) { + key = ctx->desc->d2i_private_key(NULL, &derp, der_len); + } + if (key == NULL && ctx->selection != 0) { + ERR_clear_last_mark(); + goto next; + } + } + if (key == NULL && (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) { + derp = der; + if (ctx->desc->d2i_PUBKEY != NULL) + key = ctx->desc->d2i_PUBKEY(&derp, der_len, ctx); + else if (ctx->desc->d2i_public_key != NULL) + key = ctx->desc->d2i_public_key(NULL, &derp, der_len); + if (key == NULL && ctx->selection != 0) { + ERR_clear_last_mark(); + goto next; + } + } + if (key == NULL && (selection & OSSL_KEYMGMT_SELECT_ALL_PARAMETERS) != 0) { + derp = der; + if (ctx->desc->d2i_key_params != NULL) + key = ctx->desc->d2i_key_params(NULL, &derp, der_len); + if (key == NULL && ctx->selection != 0) { + ERR_clear_last_mark(); + goto next; + } + } + if (key == NULL) + ERR_clear_last_mark(); + else + ERR_pop_to_mark(); + + /* + * Last minute check to see if this was the correct type of key. This + * should never lead to a fatal error, i.e. the decoding itself was + * correct, it was just an unexpected key type. This is generally for + * classes of key types that have subtle variants, like RSA-PSS keys as + * opposed to plain RSA keys. + */ + if (key != NULL + && ctx->desc->check_key != NULL + && !ctx->desc->check_key(key, ctx)) { + ctx->desc->free_key(key); + key = NULL; + } + + if (key != NULL && ctx->desc->adjust_key != NULL) + ctx->desc->adjust_key(key, ctx); + + next: + /* + * Indicated that we successfully decoded something, or not at all. + * Ending up "empty handed" is not an error. + */ + ok = 1; + + /* + * We free memory here so it's not held up during the callback, because + * we know the process is recursive and the allocated chunks of memory + * add up. + */ + OPENSSL_free(der); + der = NULL; + + if (key != NULL) { + OSSL_PARAM params[4]; + int object_type = OSSL_OBJECT_PKEY; + + params[0] = + OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, &object_type); + +#ifndef OPENSSL_NO_SM2 + if (strcmp(ctx->desc->keytype_name, "EC") == 0 + && (EC_KEY_get_flags(key) & EC_FLAG_SM2_RANGE) != 0) + params[1] = + OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE, + "SM2", 0); + else +#endif + params[1] = + OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE, + (char *)ctx->desc->keytype_name, + 0); + /* The address of the key becomes the octet string */ + params[2] = + OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_REFERENCE, + &key, sizeof(key)); + params[3] = OSSL_PARAM_construct_end(); + + ok = data_cb(params, data_cbarg); + } + + end: + ctx->desc->free_key(key); + OPENSSL_free(der); + + return ok; +} + +static int der2key_export_object(void *vctx, + const void *reference, size_t reference_sz, + OSSL_CALLBACK *export_cb, void *export_cbarg) +{ + struct der2key_ctx_st *ctx = vctx; + OSSL_FUNC_keymgmt_export_fn *export = + ossl_prov_get_keymgmt_export(ctx->desc->fns); + void *keydata; + + if (reference_sz == sizeof(keydata) && export != NULL) { + int selection = ctx->selection; + + if (selection == 0) + selection = OSSL_KEYMGMT_SELECT_ALL; + /* The contents of the reference is the address to our object */ + keydata = *(void **)reference; + + return export(keydata, selection, export_cb, export_cbarg); + } + return 0; +} + +#define D2I_PUBKEY_NOCTX(n, f) \ + static void * \ + n##_d2i_PUBKEY(const unsigned char **der, long der_len, \ + ossl_unused struct der2key_ctx_st *ctx) \ + { \ + return f(NULL, der, der_len); \ + } + +/* ---------------------------------------------------------------------- */ + +#ifndef OPENSSL_NO_DH +# define dh_evp_type EVP_PKEY_DH +# define dh_d2i_private_key NULL +# define dh_d2i_public_key NULL +# define dh_d2i_key_params (d2i_of_void *)d2i_DHparams +# define dh_free (free_key_fn *)DH_free +# define dh_check NULL + +static void *dh_d2i_PKCS8(const unsigned char **der, long der_len, + struct der2key_ctx_st *ctx) +{ + return der2key_decode_p8(der, der_len, ctx, + (key_from_pkcs8_t *)ossl_dh_key_from_pkcs8); +} + +D2I_PUBKEY_NOCTX(dh, ossl_d2i_DH_PUBKEY) +D2I_PUBKEY_NOCTX(dhx, ossl_d2i_DHx_PUBKEY) + +static void dh_adjust(void *key, struct der2key_ctx_st *ctx) +{ + ossl_dh_set0_libctx(key, PROV_LIBCTX_OF(ctx->provctx)); +} + +# define dhx_evp_type EVP_PKEY_DHX +# define dhx_d2i_private_key NULL +# define dhx_d2i_public_key NULL +# define dhx_d2i_key_params (d2i_of_void *)d2i_DHxparams +# define dhx_d2i_PKCS8 dh_d2i_PKCS8 +# define dhx_free (free_key_fn *)DH_free +# define dhx_check NULL +# define dhx_adjust dh_adjust +#endif + +/* ---------------------------------------------------------------------- */ + +#ifndef OPENSSL_NO_DSA +# define dsa_evp_type EVP_PKEY_DSA +# define dsa_d2i_private_key (d2i_of_void *)d2i_DSAPrivateKey +# define dsa_d2i_public_key (d2i_of_void *)d2i_DSAPublicKey +# define dsa_d2i_key_params (d2i_of_void *)d2i_DSAparams +# define dsa_free (free_key_fn *)DSA_free +# define dsa_check NULL + +static void *dsa_d2i_PKCS8(const unsigned char **der, long der_len, + struct der2key_ctx_st *ctx) +{ + return der2key_decode_p8(der, der_len, ctx, + (key_from_pkcs8_t *)ossl_dsa_key_from_pkcs8); +} + +D2I_PUBKEY_NOCTX(dsa, ossl_d2i_DSA_PUBKEY) + +static void dsa_adjust(void *key, struct der2key_ctx_st *ctx) +{ + ossl_dsa_set0_libctx(key, PROV_LIBCTX_OF(ctx->provctx)); +} +#endif + +/* ---------------------------------------------------------------------- */ + +#ifndef OPENSSL_NO_EC +# define ec_evp_type EVP_PKEY_EC +# define ec_d2i_private_key (d2i_of_void *)d2i_ECPrivateKey +# define ec_d2i_public_key NULL +# define ec_d2i_key_params (d2i_of_void *)d2i_ECParameters +# define ec_free (free_key_fn *)EC_KEY_free + +static void *ec_d2i_PKCS8(const unsigned char **der, long der_len, + struct der2key_ctx_st *ctx) +{ + return der2key_decode_p8(der, der_len, ctx, + (key_from_pkcs8_t *)ossl_ec_key_from_pkcs8); +} + +D2I_PUBKEY_NOCTX(ec, d2i_EC_PUBKEY) + +static int ec_check(void *key, struct der2key_ctx_st *ctx) +{ + /* We're trying to be clever by comparing two truths */ + int ret = 0; + int sm2 = (EC_KEY_get_flags(key) & EC_FLAG_SM2_RANGE) != 0; + + if (sm2) + ret = ctx->desc->evp_type == EVP_PKEY_SM2 + || ctx->desc->evp_type == NID_X9_62_id_ecPublicKey; + else + ret = ctx->desc->evp_type != EVP_PKEY_SM2; + + return ret; +} + +static void ec_adjust(void *key, struct der2key_ctx_st *ctx) +{ + ossl_ec_key_set0_libctx(key, PROV_LIBCTX_OF(ctx->provctx)); +} + +# ifndef OPENSSL_NO_ECX +/* + * ED25519, ED448, X25519, X448 only implement PKCS#8 and SubjectPublicKeyInfo, + * so no d2i functions to be had. + */ + +static void *ecx_d2i_PKCS8(const unsigned char **der, long der_len, + struct der2key_ctx_st *ctx) +{ + return der2key_decode_p8(der, der_len, ctx, + (key_from_pkcs8_t *)ossl_ecx_key_from_pkcs8); +} + +D2I_PUBKEY_NOCTX(ed25519, ossl_d2i_ED25519_PUBKEY) +D2I_PUBKEY_NOCTX(ed448, ossl_d2i_ED448_PUBKEY) +D2I_PUBKEY_NOCTX(x25519, ossl_d2i_X25519_PUBKEY) +D2I_PUBKEY_NOCTX(x448, ossl_d2i_X448_PUBKEY) + +static void ecx_key_adjust(void *key, struct der2key_ctx_st *ctx) +{ + ossl_ecx_key_set0_libctx(key, PROV_LIBCTX_OF(ctx->provctx)); +} + +# define ed25519_evp_type EVP_PKEY_ED25519 +# define ed25519_d2i_private_key NULL +# define ed25519_d2i_public_key NULL +# define ed25519_d2i_key_params NULL +# define ed25519_d2i_PKCS8 ecx_d2i_PKCS8 +# define ed25519_free (free_key_fn *)ossl_ecx_key_free +# define ed25519_check NULL +# define ed25519_adjust ecx_key_adjust + +# define ed448_evp_type EVP_PKEY_ED448 +# define ed448_d2i_private_key NULL +# define ed448_d2i_public_key NULL +# define ed448_d2i_key_params NULL +# define ed448_d2i_PKCS8 ecx_d2i_PKCS8 +# define ed448_free (free_key_fn *)ossl_ecx_key_free +# define ed448_check NULL +# define ed448_adjust ecx_key_adjust + +# define x25519_evp_type EVP_PKEY_X25519 +# define x25519_d2i_private_key NULL +# define x25519_d2i_public_key NULL +# define x25519_d2i_key_params NULL +# define x25519_d2i_PKCS8 ecx_d2i_PKCS8 +# define x25519_free (free_key_fn *)ossl_ecx_key_free +# define x25519_check NULL +# define x25519_adjust ecx_key_adjust + +# define x448_evp_type EVP_PKEY_X448 +# define x448_d2i_private_key NULL +# define x448_d2i_public_key NULL +# define x448_d2i_key_params NULL +# define x448_d2i_PKCS8 ecx_d2i_PKCS8 +# define x448_free (free_key_fn *)ossl_ecx_key_free +# define x448_check NULL +# define x448_adjust ecx_key_adjust +# endif /* OPENSSL_NO_ECX */ + +# ifndef OPENSSL_NO_SM2 +# define sm2_evp_type EVP_PKEY_SM2 +# define sm2_d2i_private_key (d2i_of_void *)d2i_ECPrivateKey +# define sm2_d2i_public_key NULL +# define sm2_d2i_key_params (d2i_of_void *)d2i_ECParameters +# define sm2_d2i_PUBKEY ec_d2i_PUBKEY +# define sm2_free (free_key_fn *)EC_KEY_free +# define sm2_check ec_check +# define sm2_adjust ec_adjust + +static void *sm2_d2i_PKCS8(const unsigned char **der, long der_len, + struct der2key_ctx_st *ctx) +{ + return der2key_decode_p8(der, der_len, ctx, + (key_from_pkcs8_t *)ossl_ec_key_from_pkcs8); +} +# endif + +#endif + +/* ---------------------------------------------------------------------- */ + +#ifndef OPENSSL_NO_ML_KEM +static void * +ml_kem_d2i_PKCS8(const uint8_t **der, long der_len, struct der2key_ctx_st *ctx) +{ + ML_KEM_KEY *key; + + key = ossl_ml_kem_d2i_PKCS8(*der, der_len, ctx->desc->evp_type, + ctx->provctx, ctx->propq); + if (key != NULL) + *der += der_len; + return key; +} + +static ossl_inline void * +ml_kem_d2i_PUBKEY(const uint8_t **der, long der_len, + struct der2key_ctx_st *ctx) +{ + ML_KEM_KEY *key; + + key = ossl_ml_kem_d2i_PUBKEY(*der, der_len, ctx->desc->evp_type, + ctx->provctx, ctx->propq); + if (key != NULL) + *der += der_len; + return key; +} + +# define ml_kem_512_evp_type EVP_PKEY_ML_KEM_512 +# define ml_kem_512_d2i_private_key NULL +# define ml_kem_512_d2i_public_key NULL +# define ml_kem_512_d2i_key_params NULL +# define ml_kem_512_d2i_PUBKEY ml_kem_d2i_PUBKEY +# define ml_kem_512_d2i_PKCS8 ml_kem_d2i_PKCS8 +# define ml_kem_512_free (free_key_fn *)ossl_ml_kem_key_free +# define ml_kem_512_check NULL +# define ml_kem_512_adjust NULL + +# define ml_kem_768_evp_type EVP_PKEY_ML_KEM_768 +# define ml_kem_768_d2i_private_key NULL +# define ml_kem_768_d2i_public_key NULL +# define ml_kem_768_d2i_key_params NULL +# define ml_kem_768_d2i_PUBKEY ml_kem_d2i_PUBKEY +# define ml_kem_768_d2i_PKCS8 ml_kem_d2i_PKCS8 +# define ml_kem_768_free (free_key_fn *)ossl_ml_kem_key_free +# define ml_kem_768_check NULL +# define ml_kem_768_adjust NULL + +# define ml_kem_1024_evp_type EVP_PKEY_ML_KEM_1024 +# define ml_kem_1024_d2i_private_key NULL +# define ml_kem_1024_d2i_public_key NULL +# define ml_kem_1024_d2i_PUBKEY ml_kem_d2i_PUBKEY +# define ml_kem_1024_d2i_PKCS8 ml_kem_d2i_PKCS8 +# define ml_kem_1024_d2i_key_params NULL +# define ml_kem_1024_free (free_key_fn *)ossl_ml_kem_key_free +# define ml_kem_1024_check NULL +# define ml_kem_1024_adjust NULL + +#endif + +#ifndef OPENSSL_NO_SLH_DSA +static void * +slh_dsa_d2i_PKCS8(const uint8_t **der, long der_len, struct der2key_ctx_st *ctx) +{ + SLH_DSA_KEY *key = NULL, *ret = NULL; + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + PKCS8_PRIV_KEY_INFO *p8inf = NULL; + const unsigned char *p; + const X509_ALGOR *alg = NULL; + int plen, ptype; + + if ((p8inf = d2i_PKCS8_PRIV_KEY_INFO(NULL, der, der_len)) == NULL + || !PKCS8_pkey_get0(NULL, &p, &plen, &alg, p8inf)) + goto end; + + /* Algorithm parameters must be absent. */ + if ((X509_ALGOR_get0(NULL, &ptype, NULL, alg), ptype != V_ASN1_UNDEF)) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_UNEXPECTED_KEY_PARAMETERS, + "unexpected parameters with a PKCS#8 %s private key", + ctx->desc->keytype_name); + goto end; + } + if (OBJ_obj2nid(alg->algorithm) != ctx->desc->evp_type) + goto end; + if ((key = ossl_slh_dsa_key_new(libctx, ctx->propq, + ctx->desc->keytype_name)) == NULL) + goto end; + + if (!ossl_slh_dsa_set_priv(key, p, plen)) + goto end; + ret = key; + end: + PKCS8_PRIV_KEY_INFO_free(p8inf); + if (ret == NULL) + ossl_slh_dsa_key_free(key); + return ret; +} + +static ossl_inline void *slh_dsa_d2i_PUBKEY(const uint8_t **der, long der_len, + struct der2key_ctx_st *ctx) +{ + int ok = 0; + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + SLH_DSA_KEY *ret = NULL; + BARE_PUBKEY *spki = NULL; + const uint8_t *end = *der; + size_t len; + + ret = ossl_slh_dsa_key_new(libctx, ctx->propq, ctx->desc->keytype_name); + if (ret == NULL) + return NULL; + len = ossl_slh_dsa_key_get_pub_len(ret); + + /*- + * The DER ASN.1 encoding of SLH-DSA public keys prepends 18 bytes to the + * encoded public key (since the largest public key size is 64 bytes): + * + * - 2 byte outer sequence tag and length + * - 2 byte algorithm sequence tag and length + * - 2 byte algorithm OID tag and length + * - 9 byte algorithm OID + * - 2 byte bit string tag and length + * - 1 bitstring lead byte + * + * Check that we have the right OID, the bit string has no "bits left" and + * that we consume all the input exactly. + */ + if (der_len != 18 + (long)len) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_ENCODING, + "unexpected %s public key length: %ld != %ld", + ctx->desc->keytype_name, der_len, + 18 + (long)len); + goto err; + } + + if ((spki = OPENSSL_zalloc(sizeof(*spki))) == NULL) + goto err; + + /* The spki storage is freed on error */ + if (ASN1_item_d2i_ex((ASN1_VALUE **)&spki, &end, der_len, + ASN1_ITEM_rptr(BARE_PUBKEY), NULL, NULL) == NULL) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_ENCODING, + "malformed %s public key ASN.1 encoding", + ossl_slh_dsa_key_get_name(ret)); + goto err; + } + + /* The spki structure now owns some memory */ + if ((spki->pubkey->flags & 0x7) != 0 || end != *der + der_len) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_ENCODING, + "malformed %s public key ASN.1 encoding", + ossl_slh_dsa_key_get_name(ret)); + goto err; + } + if (OBJ_cmp(OBJ_nid2obj(ctx->desc->evp_type), spki->algor.oid) != 0) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_ENCODING, + "unexpected algorithm OID for an %s public key", + ossl_slh_dsa_key_get_name(ret)); + goto err; + } + + if (!ossl_slh_dsa_set_pub(ret, spki->pubkey->data, spki->pubkey->length)) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_ENCODING, + "failed to parse %s public key from the input data", + ossl_slh_dsa_key_get_name(ret)); + goto err; + } + ok = 1; + err: + if (spki != NULL) { + ASN1_OBJECT_free(spki->algor.oid); + ASN1_BIT_STRING_free(spki->pubkey); + OPENSSL_free(spki); + } + if (!ok) { + ossl_slh_dsa_key_free(ret); + ret = NULL; + } + return ret; +} + +# define slh_dsa_sha2_128s_evp_type EVP_PKEY_SLH_DSA_SHA2_128S +# define slh_dsa_sha2_128s_d2i_private_key NULL +# define slh_dsa_sha2_128s_d2i_public_key NULL +# define slh_dsa_sha2_128s_d2i_key_params NULL +# define slh_dsa_sha2_128s_d2i_PKCS8 slh_dsa_d2i_PKCS8 +# define slh_dsa_sha2_128s_d2i_PUBKEY slh_dsa_d2i_PUBKEY +# define slh_dsa_sha2_128s_free (free_key_fn *)ossl_slh_dsa_key_free +# define slh_dsa_sha2_128s_check NULL +# define slh_dsa_sha2_128s_adjust NULL + +# define slh_dsa_sha2_128f_evp_type EVP_PKEY_SLH_DSA_SHA2_128F +# define slh_dsa_sha2_128f_d2i_private_key NULL +# define slh_dsa_sha2_128f_d2i_public_key NULL +# define slh_dsa_sha2_128f_d2i_key_params NULL +# define slh_dsa_sha2_128f_d2i_PKCS8 slh_dsa_d2i_PKCS8 +# define slh_dsa_sha2_128f_d2i_PUBKEY slh_dsa_d2i_PUBKEY +# define slh_dsa_sha2_128f_free (free_key_fn *)ossl_slh_dsa_key_free +# define slh_dsa_sha2_128f_check NULL +# define slh_dsa_sha2_128f_adjust NULL + +# define slh_dsa_sha2_192s_evp_type EVP_PKEY_SLH_DSA_SHA2_192S +# define slh_dsa_sha2_192s_d2i_private_key NULL +# define slh_dsa_sha2_192s_d2i_public_key NULL +# define slh_dsa_sha2_192s_d2i_key_params NULL +# define slh_dsa_sha2_192s_d2i_PKCS8 slh_dsa_d2i_PKCS8 +# define slh_dsa_sha2_192s_d2i_PUBKEY slh_dsa_d2i_PUBKEY +# define slh_dsa_sha2_192s_free (free_key_fn *)ossl_slh_dsa_key_free +# define slh_dsa_sha2_192s_check NULL +# define slh_dsa_sha2_192s_adjust NULL + +# define slh_dsa_sha2_192f_evp_type EVP_PKEY_SLH_DSA_SHA2_192F +# define slh_dsa_sha2_192f_d2i_private_key NULL +# define slh_dsa_sha2_192f_d2i_public_key NULL +# define slh_dsa_sha2_192f_d2i_key_params NULL +# define slh_dsa_sha2_192f_d2i_PKCS8 slh_dsa_d2i_PKCS8 +# define slh_dsa_sha2_192f_d2i_PUBKEY slh_dsa_d2i_PUBKEY +# define slh_dsa_sha2_192f_free (free_key_fn *)ossl_slh_dsa_key_free +# define slh_dsa_sha2_192f_check NULL +# define slh_dsa_sha2_192f_adjust NULL + +# define slh_dsa_sha2_256s_evp_type EVP_PKEY_SLH_DSA_SHA2_256S +# define slh_dsa_sha2_256s_d2i_private_key NULL +# define slh_dsa_sha2_256s_d2i_public_key NULL +# define slh_dsa_sha2_256s_d2i_key_params NULL +# define slh_dsa_sha2_256s_d2i_PKCS8 slh_dsa_d2i_PKCS8 +# define slh_dsa_sha2_256s_d2i_PUBKEY slh_dsa_d2i_PUBKEY +# define slh_dsa_sha2_256s_free (free_key_fn *)ossl_slh_dsa_key_free +# define slh_dsa_sha2_256s_check NULL +# define slh_dsa_sha2_256s_adjust NULL + +# define slh_dsa_sha2_256f_evp_type EVP_PKEY_SLH_DSA_SHA2_256F +# define slh_dsa_sha2_256f_d2i_private_key NULL +# define slh_dsa_sha2_256f_d2i_public_key NULL +# define slh_dsa_sha2_256f_d2i_key_params NULL +# define slh_dsa_sha2_256f_d2i_PKCS8 slh_dsa_d2i_PKCS8 +# define slh_dsa_sha2_256f_d2i_PUBKEY slh_dsa_d2i_PUBKEY +# define slh_dsa_sha2_256f_free (free_key_fn *)ossl_slh_dsa_key_free +# define slh_dsa_sha2_256f_check NULL +# define slh_dsa_sha2_256f_adjust NULL + +# define slh_dsa_shake_128s_evp_type EVP_PKEY_SLH_DSA_SHAKE_128S +# define slh_dsa_shake_128s_d2i_private_key NULL +# define slh_dsa_shake_128s_d2i_public_key NULL +# define slh_dsa_shake_128s_d2i_key_params NULL +# define slh_dsa_shake_128s_d2i_PKCS8 slh_dsa_d2i_PKCS8 +# define slh_dsa_shake_128s_d2i_PUBKEY slh_dsa_d2i_PUBKEY +# define slh_dsa_shake_128s_free (free_key_fn *)ossl_slh_dsa_key_free +# define slh_dsa_shake_128s_check NULL +# define slh_dsa_shake_128s_adjust NULL + +# define slh_dsa_shake_128f_evp_type EVP_PKEY_SLH_DSA_SHAKE_128F +# define slh_dsa_shake_128f_d2i_private_key NULL +# define slh_dsa_shake_128f_d2i_public_key NULL +# define slh_dsa_shake_128f_d2i_key_params NULL +# define slh_dsa_shake_128f_d2i_PKCS8 slh_dsa_d2i_PKCS8 +# define slh_dsa_shake_128f_d2i_PUBKEY slh_dsa_d2i_PUBKEY +# define slh_dsa_shake_128f_free (free_key_fn *)ossl_slh_dsa_key_free +# define slh_dsa_shake_128f_check NULL +# define slh_dsa_shake_128f_adjust NULL + +# define slh_dsa_shake_192s_evp_type EVP_PKEY_SLH_DSA_SHAKE_192S +# define slh_dsa_shake_192s_d2i_private_key NULL +# define slh_dsa_shake_192s_d2i_public_key NULL +# define slh_dsa_shake_192s_d2i_key_params NULL +# define slh_dsa_shake_192s_d2i_PKCS8 slh_dsa_d2i_PKCS8 +# define slh_dsa_shake_192s_d2i_PUBKEY slh_dsa_d2i_PUBKEY +# define slh_dsa_shake_192s_free (free_key_fn *)ossl_slh_dsa_key_free +# define slh_dsa_shake_192s_check NULL +# define slh_dsa_shake_192s_adjust NULL + +# define slh_dsa_shake_192f_evp_type EVP_PKEY_SLH_DSA_SHAKE_192F +# define slh_dsa_shake_192f_d2i_private_key NULL +# define slh_dsa_shake_192f_d2i_public_key NULL +# define slh_dsa_shake_192f_d2i_key_params NULL +# define slh_dsa_shake_192f_d2i_PKCS8 slh_dsa_d2i_PKCS8 +# define slh_dsa_shake_192f_d2i_PUBKEY slh_dsa_d2i_PUBKEY +# define slh_dsa_shake_192f_free (free_key_fn *)ossl_slh_dsa_key_free +# define slh_dsa_shake_192f_check NULL +# define slh_dsa_shake_192f_adjust NULL + +# define slh_dsa_shake_256s_evp_type EVP_PKEY_SLH_DSA_SHAKE_256S +# define slh_dsa_shake_256s_d2i_private_key NULL +# define slh_dsa_shake_256s_d2i_public_key NULL +# define slh_dsa_shake_256s_d2i_key_params NULL +# define slh_dsa_shake_256s_d2i_PKCS8 slh_dsa_d2i_PKCS8 +# define slh_dsa_shake_256s_d2i_PUBKEY slh_dsa_d2i_PUBKEY +# define slh_dsa_shake_256s_free (free_key_fn *)ossl_slh_dsa_key_free +# define slh_dsa_shake_256s_check NULL +# define slh_dsa_shake_256s_adjust NULL + +# define slh_dsa_shake_256f_evp_type EVP_PKEY_SLH_DSA_SHAKE_256F +# define slh_dsa_shake_256f_d2i_private_key NULL +# define slh_dsa_shake_256f_d2i_public_key NULL +# define slh_dsa_shake_256f_d2i_key_params NULL +# define slh_dsa_shake_256f_d2i_PKCS8 slh_dsa_d2i_PKCS8 +# define slh_dsa_shake_256f_d2i_PUBKEY slh_dsa_d2i_PUBKEY +# define slh_dsa_shake_256f_free (free_key_fn *)ossl_slh_dsa_key_free +# define slh_dsa_shake_256f_check NULL +# define slh_dsa_shake_256f_adjust NULL +#endif /* OPENSSL_NO_SLH_DSA */ + +/* ---------------------------------------------------------------------- */ + +#define rsa_evp_type EVP_PKEY_RSA +#define rsa_d2i_private_key (d2i_of_void *)d2i_RSAPrivateKey +#define rsa_d2i_public_key (d2i_of_void *)d2i_RSAPublicKey +#define rsa_d2i_key_params NULL +#define rsa_free (free_key_fn *)RSA_free + +static void *rsa_d2i_PKCS8(const unsigned char **der, long der_len, + struct der2key_ctx_st *ctx) +{ + return der2key_decode_p8(der, der_len, ctx, + (key_from_pkcs8_t *)ossl_rsa_key_from_pkcs8); +} + +static void * +rsa_d2i_PUBKEY(const unsigned char **der, long der_len, + ossl_unused struct der2key_ctx_st *ctx) +{ + return d2i_RSA_PUBKEY(NULL, der, der_len); +} + +static int rsa_check(void *key, struct der2key_ctx_st *ctx) +{ + int valid; + + switch (RSA_test_flags(key, RSA_FLAG_TYPE_MASK)) { + case RSA_FLAG_TYPE_RSA: + valid = (ctx->desc->evp_type == EVP_PKEY_RSA); + break; + case RSA_FLAG_TYPE_RSASSAPSS: + valid = (ctx->desc->evp_type == EVP_PKEY_RSA_PSS); + break; + default: + /* Currently unsupported RSA key type */ + valid = 0; + } + + valid = (valid && ossl_rsa_check_factors(key)); + + return valid; +} + +static void rsa_adjust(void *key, struct der2key_ctx_st *ctx) +{ + ossl_rsa_set0_libctx(key, PROV_LIBCTX_OF(ctx->provctx)); +} + +#define rsapss_evp_type EVP_PKEY_RSA_PSS +#define rsapss_d2i_private_key (d2i_of_void *)d2i_RSAPrivateKey +#define rsapss_d2i_public_key (d2i_of_void *)d2i_RSAPublicKey +#define rsapss_d2i_key_params NULL +#define rsapss_d2i_PKCS8 rsa_d2i_PKCS8 +#define rsapss_d2i_PUBKEY rsa_d2i_PUBKEY +#define rsapss_free (free_key_fn *)RSA_free +#define rsapss_check rsa_check +#define rsapss_adjust rsa_adjust + +/* ---------------------------------------------------------------------- */ + +#ifndef OPENSSL_NO_ML_DSA +static void * +ml_dsa_d2i_PKCS8(const uint8_t **der, long der_len, struct der2key_ctx_st *ctx) +{ + ML_DSA_KEY *key; + + key = ossl_ml_dsa_d2i_PKCS8(*der, der_len, ctx->desc->evp_type, + ctx->provctx, ctx->propq); + if (key != NULL) + *der += der_len; + return key; +} + +static ossl_inline void * ml_dsa_d2i_PUBKEY(const uint8_t **der, long der_len, + struct der2key_ctx_st *ctx) +{ + ML_DSA_KEY *key; + + key = ossl_ml_dsa_d2i_PUBKEY(*der, der_len, ctx->desc->evp_type, + ctx->provctx, ctx->propq); + if (key != NULL) + *der += der_len; + return key; +} + +# define ml_dsa_44_evp_type EVP_PKEY_ML_DSA_44 +# define ml_dsa_44_d2i_private_key NULL +# define ml_dsa_44_d2i_public_key NULL +# define ml_dsa_44_d2i_key_params NULL +# define ml_dsa_44_d2i_PUBKEY ml_dsa_d2i_PUBKEY +# define ml_dsa_44_d2i_PKCS8 ml_dsa_d2i_PKCS8 +# define ml_dsa_44_free (free_key_fn *)ossl_ml_dsa_key_free +# define ml_dsa_44_check NULL +# define ml_dsa_44_adjust NULL + +# define ml_dsa_65_evp_type EVP_PKEY_ML_DSA_65 +# define ml_dsa_65_d2i_private_key NULL +# define ml_dsa_65_d2i_public_key NULL +# define ml_dsa_65_d2i_key_params NULL +# define ml_dsa_65_d2i_PUBKEY ml_dsa_d2i_PUBKEY +# define ml_dsa_65_d2i_PKCS8 ml_dsa_d2i_PKCS8 +# define ml_dsa_65_free (free_key_fn *)ossl_ml_dsa_key_free +# define ml_dsa_65_check NULL +# define ml_dsa_65_adjust NULL + +# define ml_dsa_87_evp_type EVP_PKEY_ML_DSA_87 +# define ml_dsa_87_d2i_private_key NULL +# define ml_dsa_87_d2i_public_key NULL +# define ml_dsa_87_d2i_PUBKEY ml_dsa_d2i_PUBKEY +# define ml_dsa_87_d2i_PKCS8 ml_dsa_d2i_PKCS8 +# define ml_dsa_87_d2i_key_params NULL +# define ml_dsa_87_free (free_key_fn *)ossl_ml_dsa_key_free +# define ml_dsa_87_check NULL +# define ml_dsa_87_adjust NULL + +#endif + +/* ---------------------------------------------------------------------- */ + +/* + * The DO_ macros help define the selection mask and the method functions + * for each kind of object we want to decode. + */ +#define DO_type_specific_keypair(keytype) \ + "type-specific", keytype##_evp_type, \ + ( OSSL_KEYMGMT_SELECT_KEYPAIR ), \ + keytype##_d2i_private_key, \ + keytype##_d2i_public_key, \ + NULL, \ + NULL, \ + NULL, \ + keytype##_check, \ + keytype##_adjust, \ + keytype##_free + +#define DO_type_specific_pub(keytype) \ + "type-specific", keytype##_evp_type, \ + ( OSSL_KEYMGMT_SELECT_PUBLIC_KEY ), \ + NULL, \ + keytype##_d2i_public_key, \ + NULL, \ + NULL, \ + NULL, \ + keytype##_check, \ + keytype##_adjust, \ + keytype##_free + +#define DO_type_specific_priv(keytype) \ + "type-specific", keytype##_evp_type, \ + ( OSSL_KEYMGMT_SELECT_PRIVATE_KEY ), \ + keytype##_d2i_private_key, \ + NULL, \ + NULL, \ + NULL, \ + NULL, \ + keytype##_check, \ + keytype##_adjust, \ + keytype##_free + +#define DO_type_specific_params(keytype) \ + "type-specific", keytype##_evp_type, \ + ( OSSL_KEYMGMT_SELECT_ALL_PARAMETERS ), \ + NULL, \ + NULL, \ + keytype##_d2i_key_params, \ + NULL, \ + NULL, \ + keytype##_check, \ + keytype##_adjust, \ + keytype##_free + +#define DO_type_specific(keytype) \ + "type-specific", keytype##_evp_type, \ + ( OSSL_KEYMGMT_SELECT_ALL ), \ + keytype##_d2i_private_key, \ + keytype##_d2i_public_key, \ + keytype##_d2i_key_params, \ + NULL, \ + NULL, \ + keytype##_check, \ + keytype##_adjust, \ + keytype##_free + +#define DO_type_specific_no_pub(keytype) \ + "type-specific", keytype##_evp_type, \ + ( OSSL_KEYMGMT_SELECT_PRIVATE_KEY \ + | OSSL_KEYMGMT_SELECT_ALL_PARAMETERS ), \ + keytype##_d2i_private_key, \ + NULL, \ + keytype##_d2i_key_params, \ + NULL, \ + NULL, \ + keytype##_check, \ + keytype##_adjust, \ + keytype##_free + +#define DO_PrivateKeyInfo(keytype) \ + "PrivateKeyInfo", keytype##_evp_type, \ + ( OSSL_KEYMGMT_SELECT_PRIVATE_KEY ), \ + NULL, \ + NULL, \ + NULL, \ + keytype##_d2i_PKCS8, \ + NULL, \ + keytype##_check, \ + keytype##_adjust, \ + keytype##_free + +#define DO_SubjectPublicKeyInfo(keytype) \ + "SubjectPublicKeyInfo", keytype##_evp_type, \ + ( OSSL_KEYMGMT_SELECT_PUBLIC_KEY ), \ + NULL, \ + NULL, \ + NULL, \ + NULL, \ + keytype##_d2i_PUBKEY, \ + keytype##_check, \ + keytype##_adjust, \ + keytype##_free + +#define DO_DH(keytype) \ + "DH", keytype##_evp_type, \ + ( OSSL_KEYMGMT_SELECT_ALL_PARAMETERS ), \ + NULL, \ + NULL, \ + keytype##_d2i_key_params, \ + NULL, \ + NULL, \ + keytype##_check, \ + keytype##_adjust, \ + keytype##_free + +#define DO_DHX(keytype) \ + "DHX", keytype##_evp_type, \ + ( OSSL_KEYMGMT_SELECT_ALL_PARAMETERS ), \ + NULL, \ + NULL, \ + keytype##_d2i_key_params, \ + NULL, \ + NULL, \ + keytype##_check, \ + keytype##_adjust, \ + keytype##_free + +#define DO_DSA(keytype) \ + "DSA", keytype##_evp_type, \ + ( OSSL_KEYMGMT_SELECT_ALL ), \ + keytype##_d2i_private_key, \ + keytype##_d2i_public_key, \ + keytype##_d2i_key_params, \ + NULL, \ + NULL, \ + keytype##_check, \ + keytype##_adjust, \ + keytype##_free + +#define DO_EC(keytype) \ + "EC", keytype##_evp_type, \ + ( OSSL_KEYMGMT_SELECT_PRIVATE_KEY \ + | OSSL_KEYMGMT_SELECT_ALL_PARAMETERS ), \ + keytype##_d2i_private_key, \ + NULL, \ + keytype##_d2i_key_params, \ + NULL, \ + NULL, \ + keytype##_check, \ + keytype##_adjust, \ + keytype##_free + +#define DO_RSA(keytype) \ + "RSA", keytype##_evp_type, \ + ( OSSL_KEYMGMT_SELECT_KEYPAIR ), \ + keytype##_d2i_private_key, \ + keytype##_d2i_public_key, \ + NULL, \ + NULL, \ + NULL, \ + keytype##_check, \ + keytype##_adjust, \ + keytype##_free + +/* + * MAKE_DECODER is the single driver for creating OSSL_DISPATCH tables. + * It takes the following arguments: + * + * keytype_name The implementation key type as a string. + * keytype The implementation key type. This must correspond exactly + * to our existing keymgmt keytype names... in other words, + * there must exist an ossl_##keytype##_keymgmt_functions. + * type The type name for the set of functions that implement the + * decoder for the key type. This isn't necessarily the same + * as keytype. For example, the key types ed25519, ed448, + * x25519 and x448 are all handled by the same functions with + * the common type name ecx. + * kind The kind of support to implement. This translates into + * the DO_##kind macros above, to populate the keytype_desc_st + * structure. + */ +#define MAKE_DECODER(keytype_name, keytype, type, kind) \ + static const struct keytype_desc_st kind##_##keytype##_desc = \ + { keytype_name, ossl_##keytype##_keymgmt_functions, \ + DO_##kind(keytype) }; \ + \ + static OSSL_FUNC_decoder_newctx_fn kind##_der2##keytype##_newctx; \ + \ + static void *kind##_der2##keytype##_newctx(void *provctx) \ + { \ + return der2key_newctx(provctx, &kind##_##keytype##_desc); \ + } \ + static int kind##_der2##keytype##_does_selection(void *provctx, \ + int selection) \ + { \ + return der2key_check_selection(selection, \ + &kind##_##keytype##_desc); \ + } \ + const OSSL_DISPATCH \ + ossl_##kind##_der_to_##keytype##_decoder_functions[] = { \ + { OSSL_FUNC_DECODER_NEWCTX, \ + (void (*)(void))kind##_der2##keytype##_newctx }, \ + { OSSL_FUNC_DECODER_FREECTX, \ + (void (*)(void))der2key_freectx }, \ + { OSSL_FUNC_DECODER_DOES_SELECTION, \ + (void (*)(void))kind##_der2##keytype##_does_selection }, \ + { OSSL_FUNC_DECODER_DECODE, \ + (void (*)(void))der2key_decode }, \ + { OSSL_FUNC_DECODER_EXPORT_OBJECT, \ + (void (*)(void))der2key_export_object }, \ + { OSSL_FUNC_DECODER_SETTABLE_CTX_PARAMS, \ + (void (*)(void))der2key_settable_ctx_params }, \ + { OSSL_FUNC_DECODER_SET_CTX_PARAMS, \ + (void (*)(void))der2key_set_ctx_params }, \ + OSSL_DISPATCH_END \ + } + +#ifndef OPENSSL_NO_DH +MAKE_DECODER("DH", dh, dh, PrivateKeyInfo); +MAKE_DECODER("DH", dh, dh, SubjectPublicKeyInfo); +MAKE_DECODER("DH", dh, dh, type_specific_params); +MAKE_DECODER("DH", dh, dh, DH); +MAKE_DECODER("DHX", dhx, dhx, PrivateKeyInfo); +MAKE_DECODER("DHX", dhx, dhx, SubjectPublicKeyInfo); +MAKE_DECODER("DHX", dhx, dhx, type_specific_params); +MAKE_DECODER("DHX", dhx, dhx, DHX); +#endif +#ifndef OPENSSL_NO_DSA +MAKE_DECODER("DSA", dsa, dsa, PrivateKeyInfo); +MAKE_DECODER("DSA", dsa, dsa, SubjectPublicKeyInfo); +MAKE_DECODER("DSA", dsa, dsa, type_specific); +MAKE_DECODER("DSA", dsa, dsa, DSA); +#endif +#ifndef OPENSSL_NO_EC +MAKE_DECODER("EC", ec, ec, PrivateKeyInfo); +MAKE_DECODER("EC", ec, ec, SubjectPublicKeyInfo); +MAKE_DECODER("EC", ec, ec, type_specific_no_pub); +MAKE_DECODER("EC", ec, ec, EC); +# ifndef OPENSSL_NO_ECX +MAKE_DECODER("X25519", x25519, ecx, PrivateKeyInfo); +MAKE_DECODER("X25519", x25519, ecx, SubjectPublicKeyInfo); +MAKE_DECODER("X448", x448, ecx, PrivateKeyInfo); +MAKE_DECODER("X448", x448, ecx, SubjectPublicKeyInfo); +MAKE_DECODER("ED25519", ed25519, ecx, PrivateKeyInfo); +MAKE_DECODER("ED25519", ed25519, ecx, SubjectPublicKeyInfo); +MAKE_DECODER("ED448", ed448, ecx, PrivateKeyInfo); +MAKE_DECODER("ED448", ed448, ecx, SubjectPublicKeyInfo); +# endif +# ifndef OPENSSL_NO_SM2 +MAKE_DECODER("SM2", sm2, ec, PrivateKeyInfo); +MAKE_DECODER("SM2", sm2, ec, SubjectPublicKeyInfo); +MAKE_DECODER("SM2", sm2, sm2, type_specific_no_pub); +# endif +#endif +#ifndef OPENSSL_NO_ML_KEM +MAKE_DECODER("ML-KEM-512", ml_kem_512, ml_kem_512, PrivateKeyInfo); +MAKE_DECODER("ML-KEM-512", ml_kem_512, ml_kem_512, SubjectPublicKeyInfo); +MAKE_DECODER("ML-KEM-768", ml_kem_768, ml_kem_768, PrivateKeyInfo); +MAKE_DECODER("ML-KEM-768", ml_kem_768, ml_kem_768, SubjectPublicKeyInfo); +MAKE_DECODER("ML-KEM-1024", ml_kem_1024, ml_kem_1024, PrivateKeyInfo); +MAKE_DECODER("ML-KEM-1024", ml_kem_1024, ml_kem_1024, SubjectPublicKeyInfo); +#endif +#ifndef OPENSSL_NO_SLH_DSA +MAKE_DECODER("SLH-DSA-SHA2-128s", slh_dsa_sha2_128s, slh_dsa, PrivateKeyInfo); +MAKE_DECODER("SLH-DSA-SHA2-128f", slh_dsa_sha2_128f, slh_dsa, PrivateKeyInfo); +MAKE_DECODER("SLH-DSA-SHA2-192s", slh_dsa_sha2_192s, slh_dsa, PrivateKeyInfo); +MAKE_DECODER("SLH-DSA-SHA2-192f", slh_dsa_sha2_192f, slh_dsa, PrivateKeyInfo); +MAKE_DECODER("SLH-DSA-SHA2-256s", slh_dsa_sha2_256s, slh_dsa, PrivateKeyInfo); +MAKE_DECODER("SLH-DSA-SHA2-256f", slh_dsa_sha2_256f, slh_dsa, PrivateKeyInfo); +MAKE_DECODER("SLH-DSA-SHAKE-128s", slh_dsa_shake_128s, slh_dsa, PrivateKeyInfo); +MAKE_DECODER("SLH-DSA-SHAKE-128f", slh_dsa_shake_128f, slh_dsa, PrivateKeyInfo); +MAKE_DECODER("SLH-DSA-SHAKE-192s", slh_dsa_shake_192s, slh_dsa, PrivateKeyInfo); +MAKE_DECODER("SLH-DSA-SHAKE-192f", slh_dsa_shake_192f, slh_dsa, PrivateKeyInfo); +MAKE_DECODER("SLH-DSA-SHAKE-256s", slh_dsa_shake_256s, slh_dsa, PrivateKeyInfo); +MAKE_DECODER("SLH-DSA-SHAKE-256f", slh_dsa_shake_256f, slh_dsa, PrivateKeyInfo); + +MAKE_DECODER("SLH-DSA-SHA2-128s", slh_dsa_sha2_128s, slh_dsa, SubjectPublicKeyInfo); +MAKE_DECODER("SLH-DSA-SHA2-128f", slh_dsa_sha2_128f, slh_dsa, SubjectPublicKeyInfo); +MAKE_DECODER("SLH-DSA-SHA2-192s", slh_dsa_sha2_192s, slh_dsa, SubjectPublicKeyInfo); +MAKE_DECODER("SLH-DSA-SHA2-192f", slh_dsa_sha2_192f, slh_dsa, SubjectPublicKeyInfo); +MAKE_DECODER("SLH-DSA-SHA2-256s", slh_dsa_sha2_256s, slh_dsa, SubjectPublicKeyInfo); +MAKE_DECODER("SLH-DSA-SHA2-256f", slh_dsa_sha2_256f, slh_dsa, SubjectPublicKeyInfo); +MAKE_DECODER("SLH-DSA-SHAKE-128s", slh_dsa_shake_128s, slh_dsa, SubjectPublicKeyInfo); +MAKE_DECODER("SLH-DSA-SHAKE-128f", slh_dsa_shake_128f, slh_dsa, SubjectPublicKeyInfo); +MAKE_DECODER("SLH-DSA-SHAKE-192s", slh_dsa_shake_192s, slh_dsa, SubjectPublicKeyInfo); +MAKE_DECODER("SLH-DSA-SHAKE-192f", slh_dsa_shake_192f, slh_dsa, SubjectPublicKeyInfo); +MAKE_DECODER("SLH-DSA-SHAKE-256s", slh_dsa_shake_256s, slh_dsa, SubjectPublicKeyInfo); +MAKE_DECODER("SLH-DSA-SHAKE-256f", slh_dsa_shake_256f, slh_dsa, SubjectPublicKeyInfo); +#endif /* OPENSSL_NO_SLH_DSA */ +MAKE_DECODER("RSA", rsa, rsa, PrivateKeyInfo); +MAKE_DECODER("RSA", rsa, rsa, SubjectPublicKeyInfo); +MAKE_DECODER("RSA", rsa, rsa, type_specific_keypair); +MAKE_DECODER("RSA", rsa, rsa, RSA); +MAKE_DECODER("RSA-PSS", rsapss, rsapss, PrivateKeyInfo); +MAKE_DECODER("RSA-PSS", rsapss, rsapss, SubjectPublicKeyInfo); + +#ifndef OPENSSL_NO_ML_DSA +MAKE_DECODER("ML-DSA-44", ml_dsa_44, ml_dsa_44, PrivateKeyInfo); +MAKE_DECODER("ML-DSA-44", ml_dsa_44, ml_dsa_44, SubjectPublicKeyInfo); +MAKE_DECODER("ML-DSA-65", ml_dsa_65, ml_dsa_65, PrivateKeyInfo); +MAKE_DECODER("ML-DSA-65", ml_dsa_65, ml_dsa_65, SubjectPublicKeyInfo); +MAKE_DECODER("ML-DSA-87", ml_dsa_87, ml_dsa_87, PrivateKeyInfo); +MAKE_DECODER("ML-DSA-87", ml_dsa_87, ml_dsa_87, SubjectPublicKeyInfo); +#endif diff --git a/crypto/openssl/providers/implementations/encode_decode/decode_epki2pki.c b/crypto/openssl/providers/implementations/encode_decode/decode_epki2pki.c new file mode 100644 index 000000000000..aecf2eb4f2b4 --- /dev/null +++ b/crypto/openssl/providers/implementations/encode_decode/decode_epki2pki.c @@ -0,0 +1,202 @@ +/* + * Copyright 2020-2025 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 + */ + +#include <openssl/core.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/core_object.h> +#include <openssl/asn1.h> +#include <openssl/err.h> +#include <openssl/objects.h> +#include <openssl/pkcs12.h> +#include <openssl/x509.h> +#include <openssl/proverr.h> +#include "internal/asn1.h" +#include "internal/sizes.h" +#include "prov/bio.h" +#include "prov/decoders.h" +#include "prov/implementations.h" +#include "endecoder_local.h" + +static OSSL_FUNC_decoder_newctx_fn epki2pki_newctx; +static OSSL_FUNC_decoder_freectx_fn epki2pki_freectx; +static OSSL_FUNC_decoder_decode_fn epki2pki_decode; +static OSSL_FUNC_decoder_settable_ctx_params_fn epki2pki_settable_ctx_params; +static OSSL_FUNC_decoder_set_ctx_params_fn epki2pki_set_ctx_params; + +/* + * Context used for EncryptedPrivateKeyInfo to PrivateKeyInfo decoding. + */ +struct epki2pki_ctx_st { + PROV_CTX *provctx; + char propq[OSSL_MAX_PROPQUERY_SIZE]; +}; + +static void *epki2pki_newctx(void *provctx) +{ + struct epki2pki_ctx_st *ctx = OPENSSL_zalloc(sizeof(*ctx)); + + if (ctx != NULL) + ctx->provctx = provctx; + return ctx; +} + +static void epki2pki_freectx(void *vctx) +{ + struct epki2pki_ctx_st *ctx = vctx; + + OPENSSL_free(ctx); +} + +static const OSSL_PARAM *epki2pki_settable_ctx_params(ossl_unused void *provctx) +{ + static const OSSL_PARAM settables[] = { + OSSL_PARAM_utf8_string(OSSL_DECODER_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_END + }; + return settables; +} + +static int epki2pki_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + struct epki2pki_ctx_st *ctx = vctx; + const OSSL_PARAM *p; + char *str = ctx->propq; + + p = OSSL_PARAM_locate_const(params, OSSL_DECODER_PARAM_PROPERTIES); + if (p != NULL && !OSSL_PARAM_get_utf8_string(p, &str, sizeof(ctx->propq))) + return 0; + + return 1; +} + +/* + * The selection parameter in epki2pki_decode() is not used by this function + * because it's not relevant just to decode EncryptedPrivateKeyInfo to + * PrivateKeyInfo. + */ +static int epki2pki_decode(void *vctx, OSSL_CORE_BIO *cin, int selection, + OSSL_CALLBACK *data_cb, void *data_cbarg, + OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg) +{ + struct epki2pki_ctx_st *ctx = vctx; + BUF_MEM *mem = NULL; + unsigned char *der = NULL; + long der_len = 0; + BIO *in = ossl_bio_new_from_core_bio(ctx->provctx, cin); + int ok = 0; + + if (in == NULL) + return 0; + + ok = (asn1_d2i_read_bio(in, &mem) >= 0); + BIO_free(in); + + /* We return "empty handed". This is not an error. */ + if (!ok) + return 1; + + der = (unsigned char *)mem->data; + der_len = (long)mem->length; + OPENSSL_free(mem); + + ok = ossl_epki2pki_der_decode(der, der_len, selection, data_cb, data_cbarg, + pw_cb, pw_cbarg, PROV_LIBCTX_OF(ctx->provctx), + ctx->propq); + OPENSSL_free(der); + return ok; +} + +int ossl_epki2pki_der_decode(unsigned char *der, long der_len, int selection, + OSSL_CALLBACK *data_cb, void *data_cbarg, + OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg, + OSSL_LIB_CTX *libctx, const char *propq) +{ + const unsigned char *pder = der; + unsigned char *new_der = NULL; + X509_SIG *p8 = NULL; + PKCS8_PRIV_KEY_INFO *p8inf = NULL; + const X509_ALGOR *alg = NULL; + int ok = 1; /* Assume good */ + + ERR_set_mark(); + if ((p8 = d2i_X509_SIG(NULL, &pder, der_len)) != NULL) { + char pbuf[1024]; + size_t plen = 0; + + ERR_clear_last_mark(); + + if (!pw_cb(pbuf, sizeof(pbuf), &plen, NULL, pw_cbarg)) { + ERR_raise(ERR_LIB_PROV, PROV_R_UNABLE_TO_GET_PASSPHRASE); + ok = 0; + } else { + const ASN1_OCTET_STRING *oct; + int new_der_len = 0; + + X509_SIG_get0(p8, &alg, &oct); + if (!PKCS12_pbe_crypt_ex(alg, pbuf, plen, + oct->data, oct->length, + &new_der, &new_der_len, 0, + libctx, propq)) { + ok = 0; + } else { + der = new_der; + der_len = new_der_len; + } + alg = NULL; + } + X509_SIG_free(p8); + } else { + ERR_pop_to_mark(); + } + + ERR_set_mark(); + pder = der; + p8inf = d2i_PKCS8_PRIV_KEY_INFO(NULL, &pder, der_len); + ERR_pop_to_mark(); + + if (p8inf != NULL && PKCS8_pkey_get0(NULL, NULL, NULL, &alg, p8inf)) { + /* + * We have something and recognised it as PrivateKeyInfo, so let's + * pass all the applicable data to the callback. + */ + char keytype[OSSL_MAX_NAME_SIZE]; + OSSL_PARAM params[6], *p = params; + int objtype = OSSL_OBJECT_PKEY; + + OBJ_obj2txt(keytype, sizeof(keytype), alg->algorithm, 0); + + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE, + keytype, 0); + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_INPUT_TYPE, + "DER", 0); + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_STRUCTURE, + "PrivateKeyInfo", 0); + *p++ = OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_DATA, + der, der_len); + *p++ = OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, &objtype); + *p = OSSL_PARAM_construct_end(); + + ok = data_cb(params, data_cbarg); + } + PKCS8_PRIV_KEY_INFO_free(p8inf); + OPENSSL_free(new_der); + return ok; +} + +const OSSL_DISPATCH ossl_EncryptedPrivateKeyInfo_der_to_der_decoder_functions[] = { + { OSSL_FUNC_DECODER_NEWCTX, (void (*)(void))epki2pki_newctx }, + { OSSL_FUNC_DECODER_FREECTX, (void (*)(void))epki2pki_freectx }, + { OSSL_FUNC_DECODER_DECODE, (void (*)(void))epki2pki_decode }, + { OSSL_FUNC_DECODER_SETTABLE_CTX_PARAMS, + (void (*)(void))epki2pki_settable_ctx_params }, + { OSSL_FUNC_DECODER_SET_CTX_PARAMS, + (void (*)(void))epki2pki_set_ctx_params }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/encode_decode/decode_msblob2key.c b/crypto/openssl/providers/implementations/encode_decode/decode_msblob2key.c new file mode 100644 index 000000000000..df327210f114 --- /dev/null +++ b/crypto/openssl/providers/implementations/encode_decode/decode_msblob2key.c @@ -0,0 +1,289 @@ +/* + * Copyright 2020-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 + */ + +/* + * low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include <string.h> + +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/core_object.h> +#include <openssl/crypto.h> +#include <openssl/params.h> +#include <openssl/pem.h> /* For public PVK functions */ +#include <openssl/x509.h> +#include <openssl/err.h> +#include "internal/passphrase.h" +#include "crypto/pem.h" /* For internal PVK and "blob" headers */ +#include "crypto/rsa.h" +#include "prov/bio.h" +#include "prov/implementations.h" +#include "endecoder_local.h" + +struct msblob2key_ctx_st; /* Forward declaration */ +typedef void *b2i_of_void_fn(const unsigned char **in, unsigned int bitlen, + int ispub); +typedef void adjust_key_fn(void *, struct msblob2key_ctx_st *ctx); +typedef void free_key_fn(void *); +struct keytype_desc_st { + int type; /* EVP key type */ + const char *name; /* Keytype */ + const OSSL_DISPATCH *fns; /* Keymgmt (to pilfer functions from) */ + + b2i_of_void_fn *read_private_key; + b2i_of_void_fn *read_public_key; + adjust_key_fn *adjust_key; + free_key_fn *free_key; +}; + +static OSSL_FUNC_decoder_freectx_fn msblob2key_freectx; +static OSSL_FUNC_decoder_decode_fn msblob2key_decode; +static OSSL_FUNC_decoder_export_object_fn msblob2key_export_object; + +/* + * Context used for DER to key decoding. + */ +struct msblob2key_ctx_st { + PROV_CTX *provctx; + const struct keytype_desc_st *desc; + /* The selection that is passed to msblob2key_decode() */ + int selection; +}; + +static struct msblob2key_ctx_st * +msblob2key_newctx(void *provctx, const struct keytype_desc_st *desc) +{ + struct msblob2key_ctx_st *ctx = OPENSSL_zalloc(sizeof(*ctx)); + + if (ctx != NULL) { + ctx->provctx = provctx; + ctx->desc = desc; + } + return ctx; +} + +static void msblob2key_freectx(void *vctx) +{ + struct msblob2key_ctx_st *ctx = vctx; + + OPENSSL_free(ctx); +} + +static int msblob2key_does_selection(void *provctx, int selection) +{ + if (selection == 0) + return 1; + + if ((selection & (OSSL_KEYMGMT_SELECT_PRIVATE_KEY + | OSSL_KEYMGMT_SELECT_PUBLIC_KEY)) != 0) + return 1; + + return 0; +} + +static int msblob2key_decode(void *vctx, OSSL_CORE_BIO *cin, int selection, + OSSL_CALLBACK *data_cb, void *data_cbarg, + OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg) +{ + struct msblob2key_ctx_st *ctx = vctx; + BIO *in = ossl_bio_new_from_core_bio(ctx->provctx, cin); + const unsigned char *p; + unsigned char hdr_buf[16], *buf = NULL; + unsigned int bitlen, magic, length; + int isdss = -1; + int ispub = -1; + void *key = NULL; + int ok = 0; + + if (in == NULL) + return 0; + + if (BIO_read(in, hdr_buf, 16) != 16) { + ERR_raise(ERR_LIB_PEM, PEM_R_KEYBLOB_TOO_SHORT); + goto next; + } + ERR_set_mark(); + p = hdr_buf; + ok = ossl_do_blob_header(&p, 16, &magic, &bitlen, &isdss, &ispub) > 0; + ERR_pop_to_mark(); + if (!ok) + goto next; + + ctx->selection = selection; + ok = 0; /* Assume that we fail */ + + if ((isdss && ctx->desc->type != EVP_PKEY_DSA) + || (!isdss && ctx->desc->type != EVP_PKEY_RSA)) + goto next; + + length = ossl_blob_length(bitlen, isdss, ispub); + if (length > BLOB_MAX_LENGTH) { + ERR_raise(ERR_LIB_PEM, PEM_R_HEADER_TOO_LONG); + goto next; + } + buf = OPENSSL_malloc(length); + if (buf == NULL) + goto end; + p = buf; + if (BIO_read(in, buf, length) != (int)length) { + ERR_raise(ERR_LIB_PEM, PEM_R_KEYBLOB_TOO_SHORT); + goto next; + } + + if ((selection == 0 + || (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + && !ispub + && ctx->desc->read_private_key != NULL) { + struct ossl_passphrase_data_st pwdata; + + memset(&pwdata, 0, sizeof(pwdata)); + if (!ossl_pw_set_ossl_passphrase_cb(&pwdata, pw_cb, pw_cbarg)) + goto end; + p = buf; + key = ctx->desc->read_private_key(&p, bitlen, ispub); + if (selection != 0 && key == NULL) + goto next; + } + if (key == NULL && (selection == 0 + || (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) + && ispub + && ctx->desc->read_public_key != NULL) { + p = buf; + key = ctx->desc->read_public_key(&p, bitlen, ispub); + if (selection != 0 && key == NULL) + goto next; + } + + if (key != NULL && ctx->desc->adjust_key != NULL) + ctx->desc->adjust_key(key, ctx); + + next: + /* + * Indicated that we successfully decoded something, or not at all. + * Ending up "empty handed" is not an error. + */ + ok = 1; + + /* + * We free resources here so it's not held up during the callback, because + * we know the process is recursive and the allocated chunks of memory + * add up. + */ + OPENSSL_free(buf); + BIO_free(in); + buf = NULL; + in = NULL; + + if (key != NULL) { + OSSL_PARAM params[4]; + int object_type = OSSL_OBJECT_PKEY; + + params[0] = + OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, &object_type); + params[1] = + OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE, + (char *)ctx->desc->name, 0); + /* The address of the key becomes the octet string */ + params[2] = + OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_REFERENCE, + &key, sizeof(key)); + params[3] = OSSL_PARAM_construct_end(); + + ok = data_cb(params, data_cbarg); + } + + end: + BIO_free(in); + OPENSSL_free(buf); + ctx->desc->free_key(key); + + return ok; +} + +static int +msblob2key_export_object(void *vctx, + const void *reference, size_t reference_sz, + OSSL_CALLBACK *export_cb, void *export_cbarg) +{ + struct msblob2key_ctx_st *ctx = vctx; + OSSL_FUNC_keymgmt_export_fn *export = + ossl_prov_get_keymgmt_export(ctx->desc->fns); + void *keydata; + + if (reference_sz == sizeof(keydata) && export != NULL) { + int selection = ctx->selection; + + if (selection == 0) + selection = OSSL_KEYMGMT_SELECT_ALL; + /* The contents of the reference is the address to our object */ + keydata = *(void **)reference; + + return export(keydata, selection, export_cb, export_cbarg); + } + return 0; +} + +/* ---------------------------------------------------------------------- */ + +#define dsa_decode_private_key (b2i_of_void_fn *)ossl_b2i_DSA_after_header +#define dsa_decode_public_key (b2i_of_void_fn *)ossl_b2i_DSA_after_header +#define dsa_adjust NULL +#define dsa_free (void (*)(void *))DSA_free + +/* ---------------------------------------------------------------------- */ + +#define rsa_decode_private_key (b2i_of_void_fn *)ossl_b2i_RSA_after_header +#define rsa_decode_public_key (b2i_of_void_fn *)ossl_b2i_RSA_after_header + +static void rsa_adjust(void *key, struct msblob2key_ctx_st *ctx) +{ + ossl_rsa_set0_libctx(key, PROV_LIBCTX_OF(ctx->provctx)); +} + +#define rsa_free (void (*)(void *))RSA_free + +/* ---------------------------------------------------------------------- */ + +#define IMPLEMENT_MSBLOB(KEYTYPE, keytype) \ + static const struct keytype_desc_st mstype##2##keytype##_desc = { \ + EVP_PKEY_##KEYTYPE, #KEYTYPE, \ + ossl_##keytype##_keymgmt_functions, \ + keytype##_decode_private_key, \ + keytype##_decode_public_key, \ + keytype##_adjust, \ + keytype##_free \ + }; \ + static OSSL_FUNC_decoder_newctx_fn msblob2##keytype##_newctx; \ + static void *msblob2##keytype##_newctx(void *provctx) \ + { \ + return msblob2key_newctx(provctx, &mstype##2##keytype##_desc); \ + } \ + const OSSL_DISPATCH \ + ossl_msblob_to_##keytype##_decoder_functions[] = { \ + { OSSL_FUNC_DECODER_NEWCTX, \ + (void (*)(void))msblob2##keytype##_newctx }, \ + { OSSL_FUNC_DECODER_FREECTX, \ + (void (*)(void))msblob2key_freectx }, \ + { OSSL_FUNC_DECODER_DOES_SELECTION, \ + (void (*)(void))msblob2key_does_selection }, \ + { OSSL_FUNC_DECODER_DECODE, \ + (void (*)(void))msblob2key_decode }, \ + { OSSL_FUNC_DECODER_EXPORT_OBJECT, \ + (void (*)(void))msblob2key_export_object }, \ + OSSL_DISPATCH_END \ + } + +#ifndef OPENSSL_NO_DSA +IMPLEMENT_MSBLOB(DSA, dsa); +#endif +IMPLEMENT_MSBLOB(RSA, rsa); diff --git a/crypto/openssl/providers/implementations/encode_decode/decode_pem2der.c b/crypto/openssl/providers/implementations/encode_decode/decode_pem2der.c new file mode 100644 index 000000000000..a38c71883dd1 --- /dev/null +++ b/crypto/openssl/providers/implementations/encode_decode/decode_pem2der.c @@ -0,0 +1,282 @@ +/* + * Copyright 2020-2025 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 + */ + +/* + * RSA low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include <string.h> + +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/core_object.h> +#include <openssl/crypto.h> +#include <openssl/err.h> +#include <openssl/params.h> +#include <openssl/pem.h> +#include <openssl/proverr.h> +#include "internal/nelem.h" +#include "internal/sizes.h" +#include "prov/bio.h" +#include "prov/decoders.h" +#include "prov/implementations.h" +#include "endecoder_local.h" + +static int read_pem(PROV_CTX *provctx, OSSL_CORE_BIO *cin, + char **pem_name, char **pem_header, + unsigned char **data, long *len) +{ + BIO *in = ossl_bio_new_from_core_bio(provctx, cin); + int ok; + + if (in == NULL) + return 0; + ok = (PEM_read_bio(in, pem_name, pem_header, data, len) > 0); + + BIO_free(in); + return ok; +} + +static OSSL_FUNC_decoder_newctx_fn pem2der_newctx; +static OSSL_FUNC_decoder_freectx_fn pem2der_freectx; +static OSSL_FUNC_decoder_decode_fn pem2der_decode; + +/* + * Context used for PEM to DER decoding. + */ +struct pem2der_ctx_st { + PROV_CTX *provctx; + char data_structure[OSSL_MAX_CODEC_STRUCT_SIZE]; + char propq[OSSL_MAX_PROPQUERY_SIZE]; +}; + +static void *pem2der_newctx(void *provctx) +{ + struct pem2der_ctx_st *ctx = OPENSSL_zalloc(sizeof(*ctx)); + + if (ctx != NULL) + ctx->provctx = provctx; + return ctx; +} + +static void pem2der_freectx(void *vctx) +{ + struct pem2der_ctx_st *ctx = vctx; + + OPENSSL_free(ctx); +} + +static const OSSL_PARAM *pem2der_settable_ctx_params(ossl_unused void *provctx) +{ + static const OSSL_PARAM settables[] = { + OSSL_PARAM_utf8_string(OSSL_DECODER_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_OBJECT_PARAM_DATA_STRUCTURE, NULL, 0), + OSSL_PARAM_END + }; + return settables; +} + +static int pem2der_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + struct pem2der_ctx_st *ctx = vctx; + const OSSL_PARAM *p; + char *str; + + p = OSSL_PARAM_locate_const(params, OSSL_DECODER_PARAM_PROPERTIES); + str = ctx->propq; + if (p != NULL + && !OSSL_PARAM_get_utf8_string(p, &str, sizeof(ctx->propq))) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_OBJECT_PARAM_DATA_STRUCTURE); + str = ctx->data_structure; + if (p != NULL + && !OSSL_PARAM_get_utf8_string(p, &str, sizeof(ctx->data_structure))) + return 0; + + return 1; +} + +/* pem_password_cb compatible function */ +struct pem2der_pass_data_st { + OSSL_PASSPHRASE_CALLBACK *cb; + void *cbarg; +}; + +static int pem2der_pass_helper(char *buf, int num, int w, void *data) +{ + struct pem2der_pass_data_st *pass_data = data; + size_t plen; + + if (pass_data == NULL + || pass_data->cb == NULL + || !pass_data->cb(buf, num, &plen, NULL, pass_data->cbarg)) + return -1; + return (int)plen; +} + +static int pem2der_decode(void *vctx, OSSL_CORE_BIO *cin, int selection, + OSSL_CALLBACK *data_cb, void *data_cbarg, + OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg) +{ + /* + * PEM names we recognise. Other PEM names should be recognised by + * other decoder implementations. + */ + static struct pem_name_map_st { + const char *pem_name; + int object_type; + const char *data_type; + const char *data_structure; + } pem_name_map[] = { + /* PKCS#8 and SubjectPublicKeyInfo */ + { PEM_STRING_PKCS8, OSSL_OBJECT_PKEY, NULL, "EncryptedPrivateKeyInfo" }, + { PEM_STRING_PKCS8INF, OSSL_OBJECT_PKEY, NULL, "PrivateKeyInfo" }, +#define PKCS8_LAST_IDX 1 + { PEM_STRING_PUBLIC, OSSL_OBJECT_PKEY, NULL, "SubjectPublicKeyInfo" }, +#define SPKI_LAST_IDX 2 + /* Our set of type specific PEM types */ + { PEM_STRING_DHPARAMS, OSSL_OBJECT_PKEY, "DH", "type-specific" }, + { PEM_STRING_DHXPARAMS, OSSL_OBJECT_PKEY, "X9.42 DH", "type-specific" }, + { PEM_STRING_DSA, OSSL_OBJECT_PKEY, "DSA", "type-specific" }, + { PEM_STRING_DSA_PUBLIC, OSSL_OBJECT_PKEY, "DSA", "type-specific" }, + { PEM_STRING_DSAPARAMS, OSSL_OBJECT_PKEY, "DSA", "type-specific" }, + { PEM_STRING_ECPRIVATEKEY, OSSL_OBJECT_PKEY, "EC", "type-specific" }, + { PEM_STRING_ECPARAMETERS, OSSL_OBJECT_PKEY, "EC", "type-specific" }, + { PEM_STRING_SM2PRIVATEKEY, OSSL_OBJECT_PKEY, "SM2", "type-specific" }, + { PEM_STRING_SM2PARAMETERS, OSSL_OBJECT_PKEY, "SM2", "type-specific" }, + { PEM_STRING_RSA, OSSL_OBJECT_PKEY, "RSA", "type-specific" }, + { PEM_STRING_RSA_PUBLIC, OSSL_OBJECT_PKEY, "RSA", "type-specific" }, + + /* + * A few others that there is at least have an object type for, even + * though there is no provider interface to handle such objects, yet. + * However, this is beneficial for the OSSL_STORE result handler. + */ + { PEM_STRING_X509, OSSL_OBJECT_CERT, NULL, "Certificate" }, + { PEM_STRING_X509_TRUSTED, OSSL_OBJECT_CERT, NULL, "Certificate" }, + { PEM_STRING_X509_OLD, OSSL_OBJECT_CERT, NULL, "Certificate" }, + { PEM_STRING_X509_CRL, OSSL_OBJECT_CRL, NULL, "CertificateList" } + }; + struct pem2der_ctx_st *ctx = vctx; + char *pem_name = NULL, *pem_header = NULL; + size_t i; + unsigned char *der = NULL; + long der_len = 0; + int ok = 0; + int objtype = OSSL_OBJECT_UNKNOWN; + + ok = read_pem(ctx->provctx, cin, &pem_name, &pem_header, + &der, &der_len) > 0; + /* We return "empty handed". This is not an error. */ + if (!ok) + return 1; + + /* + * 10 is the number of characters in "Proc-Type:", which + * PEM_get_EVP_CIPHER_INFO() requires to be present. + * If the PEM header has less characters than that, it's + * not worth spending cycles on it. + */ + if (strlen(pem_header) > 10) { + EVP_CIPHER_INFO cipher; + struct pem2der_pass_data_st pass_data; + + ok = 0; /* Assume that we fail */ + pass_data.cb = pw_cb; + pass_data.cbarg = pw_cbarg; + if (!PEM_get_EVP_CIPHER_INFO(pem_header, &cipher) + || !PEM_do_header(&cipher, der, &der_len, + pem2der_pass_helper, &pass_data)) + goto end; + } + + /* + * Indicated that we successfully decoded something, or not at all. + * Ending up "empty handed" is not an error. + */ + ok = 1; + + /* Have a look to see if we recognise anything */ + for (i = 0; i < OSSL_NELEM(pem_name_map); i++) + if (strcmp(pem_name, pem_name_map[i].pem_name) == 0) + break; + + if (i < OSSL_NELEM(pem_name_map)) { + OSSL_PARAM params[5], *p = params; + /* We expect these to be read only so casting away the const is ok */ + char *data_type = (char *)pem_name_map[i].data_type; + char *data_structure = (char *)pem_name_map[i].data_structure; + + /* + * Since this may perform decryption, we need to check the selection to + * avoid password prompts for objects of no interest. + */ + if (i <= PKCS8_LAST_IDX + && ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) + || OPENSSL_strcasecmp(ctx->data_structure, "EncryptedPrivateKeyInfo") == 0 + || OPENSSL_strcasecmp(ctx->data_structure, "PrivateKeyInfo") == 0)) { + ok = ossl_epki2pki_der_decode(der, der_len, selection, data_cb, + data_cbarg, pw_cb, pw_cbarg, + PROV_LIBCTX_OF(ctx->provctx), + ctx->propq); + goto end; + } + + if (i <= SPKI_LAST_IDX + && ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) + || OPENSSL_strcasecmp(ctx->data_structure, "SubjectPublicKeyInfo") == 0)) { + ok = ossl_spki2typespki_der_decode(der, der_len, selection, data_cb, + data_cbarg, pw_cb, pw_cbarg, + PROV_LIBCTX_OF(ctx->provctx), + ctx->propq); + goto end; + } + + objtype = pem_name_map[i].object_type; + if (data_type != NULL) + *p++ = + OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE, + data_type, 0); + + /* We expect this to be read only so casting away the const is ok */ + if (data_structure != NULL) + *p++ = + OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_STRUCTURE, + data_structure, 0); + *p++ = + OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_DATA, + der, der_len); + *p++ = + OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, &objtype); + + *p = OSSL_PARAM_construct_end(); + + ok = data_cb(params, data_cbarg); + } + + end: + OPENSSL_free(pem_name); + OPENSSL_free(pem_header); + OPENSSL_free(der); + return ok; +} + +const OSSL_DISPATCH ossl_pem_to_der_decoder_functions[] = { + { OSSL_FUNC_DECODER_NEWCTX, (void (*)(void))pem2der_newctx }, + { OSSL_FUNC_DECODER_FREECTX, (void (*)(void))pem2der_freectx }, + { OSSL_FUNC_DECODER_DECODE, (void (*)(void))pem2der_decode }, + { OSSL_FUNC_DECODER_SETTABLE_CTX_PARAMS, + (void (*)(void))pem2der_settable_ctx_params }, + { OSSL_FUNC_DECODER_SET_CTX_PARAMS, + (void (*)(void))pem2der_set_ctx_params }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/encode_decode/decode_pvk2key.c b/crypto/openssl/providers/implementations/encode_decode/decode_pvk2key.c new file mode 100644 index 000000000000..ea4585d93c5c --- /dev/null +++ b/crypto/openssl/providers/implementations/encode_decode/decode_pvk2key.c @@ -0,0 +1,287 @@ +/* + * Copyright 2020-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 + */ + +/* + * low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include <string.h> + +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/core_object.h> +#include <openssl/crypto.h> +#include <openssl/params.h> +#include <openssl/err.h> +#include <openssl/pem.h> /* For public PVK functions */ +#include <openssl/x509.h> +#include "internal/passphrase.h" +#include "internal/sizes.h" +#include "crypto/pem.h" /* For internal PVK and "blob" headers */ +#include "crypto/rsa.h" +#include "prov/bio.h" +#include "prov/implementations.h" +#include "endecoder_local.h" + +struct pvk2key_ctx_st; /* Forward declaration */ +typedef int check_key_fn(void *, struct pvk2key_ctx_st *ctx); +typedef void adjust_key_fn(void *, struct pvk2key_ctx_st *ctx); +typedef void *b2i_PVK_of_bio_pw_fn(BIO *in, pem_password_cb *cb, void *u, + OSSL_LIB_CTX *libctx, const char *propq); +typedef void free_key_fn(void *); +struct keytype_desc_st { + int type; /* EVP key type */ + const char *name; /* Keytype */ + const OSSL_DISPATCH *fns; /* Keymgmt (to pilfer functions from) */ + + b2i_PVK_of_bio_pw_fn *read_private_key; + adjust_key_fn *adjust_key; + free_key_fn *free_key; +}; + +static OSSL_FUNC_decoder_freectx_fn pvk2key_freectx; +static OSSL_FUNC_decoder_decode_fn pvk2key_decode; +static OSSL_FUNC_decoder_export_object_fn pvk2key_export_object; +static OSSL_FUNC_decoder_settable_ctx_params_fn pvk2key_settable_ctx_params; +static OSSL_FUNC_decoder_set_ctx_params_fn pvk2key_set_ctx_params; + +/* + * Context used for DER to key decoding. + */ +struct pvk2key_ctx_st { + PROV_CTX *provctx; + char propq[OSSL_MAX_PROPQUERY_SIZE]; + const struct keytype_desc_st *desc; + /* The selection that is passed to der2key_decode() */ + int selection; +}; + +static struct pvk2key_ctx_st * +pvk2key_newctx(void *provctx, const struct keytype_desc_st *desc) +{ + struct pvk2key_ctx_st *ctx = OPENSSL_zalloc(sizeof(*ctx)); + + if (ctx != NULL) { + ctx->provctx = provctx; + ctx->desc = desc; + } + return ctx; +} + +static void pvk2key_freectx(void *vctx) +{ + struct pvk2key_ctx_st *ctx = vctx; + + OPENSSL_free(ctx); +} + +static const OSSL_PARAM *pvk2key_settable_ctx_params(ossl_unused void *provctx) +{ + static const OSSL_PARAM settables[] = { + OSSL_PARAM_utf8_string(OSSL_DECODER_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_END, + }; + return settables; +} + +static int pvk2key_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + struct pvk2key_ctx_st *ctx = vctx; + const OSSL_PARAM *p; + char *str = ctx->propq; + + p = OSSL_PARAM_locate_const(params, OSSL_DECODER_PARAM_PROPERTIES); + if (p != NULL && !OSSL_PARAM_get_utf8_string(p, &str, sizeof(ctx->propq))) + return 0; + + return 1; +} + +static int pvk2key_does_selection(void *provctx, int selection) +{ + if (selection == 0) + return 1; + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + return 1; + + return 0; +} + +static int pvk2key_decode(void *vctx, OSSL_CORE_BIO *cin, int selection, + OSSL_CALLBACK *data_cb, void *data_cbarg, + OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg) +{ + struct pvk2key_ctx_st *ctx = vctx; + BIO *in = ossl_bio_new_from_core_bio(ctx->provctx, cin); + void *key = NULL; + int ok = 0; + + if (in == NULL) + return 0; + + ctx->selection = selection; + + if ((selection == 0 + || (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + && ctx->desc->read_private_key != NULL) { + struct ossl_passphrase_data_st pwdata; + int err, lib, reason; + + memset(&pwdata, 0, sizeof(pwdata)); + if (!ossl_pw_set_ossl_passphrase_cb(&pwdata, pw_cb, pw_cbarg)) + goto end; + + key = ctx->desc->read_private_key(in, ossl_pw_pvk_password, &pwdata, + PROV_LIBCTX_OF(ctx->provctx), + ctx->propq); + + /* + * Because the PVK API doesn't have a separate decrypt call, we need + * to check the error queue for certain well known errors that are + * considered fatal and which we pass through, while the rest gets + * thrown away. + */ + err = ERR_peek_last_error(); + lib = ERR_GET_LIB(err); + reason = ERR_GET_REASON(err); + if (lib == ERR_LIB_PEM + && (reason == PEM_R_BAD_PASSWORD_READ + || reason == PEM_R_BAD_DECRYPT)) { + ERR_clear_last_mark(); + goto end; + } + + if (selection != 0 && key == NULL) + goto next; + } + + if (key != NULL && ctx->desc->adjust_key != NULL) + ctx->desc->adjust_key(key, ctx); + + next: + /* + * Indicated that we successfully decoded something, or not at all. + * Ending up "empty handed" is not an error. + */ + ok = 1; + + /* + * We free resources here so it's not held up during the callback, because + * we know the process is recursive and the allocated chunks of memory + * add up. + */ + BIO_free(in); + in = NULL; + + if (key != NULL) { + OSSL_PARAM params[4]; + int object_type = OSSL_OBJECT_PKEY; + + params[0] = + OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, &object_type); + params[1] = + OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE, + (char *)ctx->desc->name, 0); + /* The address of the key becomes the octet string */ + params[2] = + OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_REFERENCE, + &key, sizeof(key)); + params[3] = OSSL_PARAM_construct_end(); + + ok = data_cb(params, data_cbarg); + } + + end: + BIO_free(in); + ctx->desc->free_key(key); + + return ok; +} + +static int pvk2key_export_object(void *vctx, + const void *reference, size_t reference_sz, + OSSL_CALLBACK *export_cb, void *export_cbarg) +{ + struct pvk2key_ctx_st *ctx = vctx; + OSSL_FUNC_keymgmt_export_fn *export = + ossl_prov_get_keymgmt_export(ctx->desc->fns); + void *keydata; + + if (reference_sz == sizeof(keydata) && export != NULL) { + int selection = ctx->selection; + + if (selection == 0) + selection = OSSL_KEYMGMT_SELECT_ALL; + /* The contents of the reference is the address to our object */ + keydata = *(void **)reference; + + return export(keydata, selection, export_cb, export_cbarg); + } + return 0; +} + +/* ---------------------------------------------------------------------- */ + +#define dsa_private_key_bio (b2i_PVK_of_bio_pw_fn *)b2i_DSA_PVK_bio_ex +#define dsa_adjust NULL +#define dsa_free (void (*)(void *))DSA_free + +/* ---------------------------------------------------------------------- */ + +#define rsa_private_key_bio (b2i_PVK_of_bio_pw_fn *)b2i_RSA_PVK_bio_ex + +static void rsa_adjust(void *key, struct pvk2key_ctx_st *ctx) +{ + ossl_rsa_set0_libctx(key, PROV_LIBCTX_OF(ctx->provctx)); +} + +#define rsa_free (void (*)(void *))RSA_free + +/* ---------------------------------------------------------------------- */ + +#define IMPLEMENT_MS(KEYTYPE, keytype) \ + static const struct keytype_desc_st \ + pvk2##keytype##_desc = { \ + EVP_PKEY_##KEYTYPE, #KEYTYPE, \ + ossl_##keytype##_keymgmt_functions, \ + keytype##_private_key_bio, \ + keytype##_adjust, \ + keytype##_free \ + }; \ + static OSSL_FUNC_decoder_newctx_fn pvk2##keytype##_newctx; \ + static void *pvk2##keytype##_newctx(void *provctx) \ + { \ + return pvk2key_newctx(provctx, &pvk2##keytype##_desc); \ + } \ + const OSSL_DISPATCH \ + ossl_##pvk_to_##keytype##_decoder_functions[] = { \ + { OSSL_FUNC_DECODER_NEWCTX, \ + (void (*)(void))pvk2##keytype##_newctx }, \ + { OSSL_FUNC_DECODER_FREECTX, \ + (void (*)(void))pvk2key_freectx }, \ + { OSSL_FUNC_DECODER_DOES_SELECTION, \ + (void (*)(void))pvk2key_does_selection }, \ + { OSSL_FUNC_DECODER_DECODE, \ + (void (*)(void))pvk2key_decode }, \ + { OSSL_FUNC_DECODER_EXPORT_OBJECT, \ + (void (*)(void))pvk2key_export_object }, \ + { OSSL_FUNC_DECODER_SETTABLE_CTX_PARAMS, \ + (void (*)(void))pvk2key_settable_ctx_params }, \ + { OSSL_FUNC_DECODER_SET_CTX_PARAMS, \ + (void (*)(void))pvk2key_set_ctx_params }, \ + OSSL_DISPATCH_END \ + } + +#ifndef OPENSSL_NO_DSA +IMPLEMENT_MS(DSA, dsa); +#endif +IMPLEMENT_MS(RSA, rsa); diff --git a/crypto/openssl/providers/implementations/encode_decode/decode_spki2typespki.c b/crypto/openssl/providers/implementations/encode_decode/decode_spki2typespki.c new file mode 100644 index 000000000000..8cf6d7d41ce3 --- /dev/null +++ b/crypto/openssl/providers/implementations/encode_decode/decode_spki2typespki.c @@ -0,0 +1,169 @@ +/* + * Copyright 2020-2025 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 + */ + +#include <string.h> +#include <openssl/asn1t.h> +#include <openssl/core_names.h> +#include <openssl/core_object.h> +#include <openssl/params.h> +#include <openssl/x509.h> +#include "internal/sizes.h" +#include "crypto/x509.h" +#include "crypto/ec.h" +#include "prov/bio.h" +#include "prov/decoders.h" +#include "prov/implementations.h" +#include "endecoder_local.h" + +static OSSL_FUNC_decoder_newctx_fn spki2typespki_newctx; +static OSSL_FUNC_decoder_freectx_fn spki2typespki_freectx; +static OSSL_FUNC_decoder_decode_fn spki2typespki_decode; +static OSSL_FUNC_decoder_settable_ctx_params_fn spki2typespki_settable_ctx_params; +static OSSL_FUNC_decoder_set_ctx_params_fn spki2typespki_set_ctx_params; + +/* + * Context used for SubjectPublicKeyInfo to Type specific SubjectPublicKeyInfo + * decoding. + */ +struct spki2typespki_ctx_st { + PROV_CTX *provctx; + char propq[OSSL_MAX_PROPQUERY_SIZE]; +}; + +static void *spki2typespki_newctx(void *provctx) +{ + struct spki2typespki_ctx_st *ctx = OPENSSL_zalloc(sizeof(*ctx)); + + if (ctx != NULL) + ctx->provctx = provctx; + return ctx; +} + +static void spki2typespki_freectx(void *vctx) +{ + struct spki2typespki_ctx_st *ctx = vctx; + + OPENSSL_free(ctx); +} + +static const OSSL_PARAM *spki2typespki_settable_ctx_params(ossl_unused void *provctx) +{ + static const OSSL_PARAM settables[] = { + OSSL_PARAM_utf8_string(OSSL_DECODER_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_END + }; + return settables; +} + +static int spki2typespki_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + struct spki2typespki_ctx_st *ctx = vctx; + const OSSL_PARAM *p; + char *str = ctx->propq; + + p = OSSL_PARAM_locate_const(params, OSSL_DECODER_PARAM_PROPERTIES); + if (p != NULL && !OSSL_PARAM_get_utf8_string(p, &str, sizeof(ctx->propq))) + return 0; + + return 1; +} + +static int spki2typespki_decode(void *vctx, OSSL_CORE_BIO *cin, int selection, + OSSL_CALLBACK *data_cb, void *data_cbarg, + OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg) +{ + struct spki2typespki_ctx_st *ctx = vctx; + unsigned char *der; + long len; + int ok = 0; + + if (!ossl_read_der(ctx->provctx, cin, &der, &len)) + return 1; + + ok = ossl_spki2typespki_der_decode(der, len, selection, data_cb, data_cbarg, + pw_cb, pw_cbarg, + PROV_LIBCTX_OF(ctx->provctx), ctx->propq); + OPENSSL_free(der); + return ok; +} + +int ossl_spki2typespki_der_decode(unsigned char *der, long len, int selection, + OSSL_CALLBACK *data_cb, void *data_cbarg, + OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg, + OSSL_LIB_CTX *libctx, const char *propq) +{ + const unsigned char *derp = der; + X509_PUBKEY *xpub = NULL; + X509_ALGOR *algor = NULL; + const ASN1_OBJECT *oid = NULL; + char dataname[OSSL_MAX_NAME_SIZE]; + OSSL_PARAM params[6], *p = params; + int objtype = OSSL_OBJECT_PKEY; + int ok = 0; + + xpub = ossl_d2i_X509_PUBKEY_INTERNAL(&derp, len, libctx, propq); + + if (xpub == NULL) { + /* We return "empty handed". This is not an error. */ + ok = 1; + goto end; + } + + if (!X509_PUBKEY_get0_param(NULL, NULL, NULL, &algor, xpub)) + goto end; + X509_ALGOR_get0(&oid, NULL, NULL, algor); + +#ifndef OPENSSL_NO_EC + /* SM2 abuses the EC oid, so this could actually be SM2 */ + if (OBJ_obj2nid(oid) == NID_X9_62_id_ecPublicKey + && ossl_x509_algor_is_sm2(algor)) + strcpy(dataname, "SM2"); + else +#endif + if (OBJ_obj2txt(dataname, sizeof(dataname), oid, 0) <= 0) + goto end; + + ossl_X509_PUBKEY_INTERNAL_free(xpub); + xpub = NULL; + + *p++ = + OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE, + dataname, 0); + + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_INPUT_TYPE, + "DER", 0); + + *p++ = + OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_STRUCTURE, + "SubjectPublicKeyInfo", + 0); + *p++ = + OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_DATA, der, len); + *p++ = + OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, &objtype); + + *p = OSSL_PARAM_construct_end(); + + ok = data_cb(params, data_cbarg); + + end: + ossl_X509_PUBKEY_INTERNAL_free(xpub); + return ok; +} + +const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_der_decoder_functions[] = { + { OSSL_FUNC_DECODER_NEWCTX, (void (*)(void))spki2typespki_newctx }, + { OSSL_FUNC_DECODER_FREECTX, (void (*)(void))spki2typespki_freectx }, + { OSSL_FUNC_DECODER_DECODE, (void (*)(void))spki2typespki_decode }, + { OSSL_FUNC_DECODER_SETTABLE_CTX_PARAMS, + (void (*)(void))spki2typespki_settable_ctx_params }, + { OSSL_FUNC_DECODER_SET_CTX_PARAMS, + (void (*)(void))spki2typespki_set_ctx_params }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/encode_decode/encode_key2any.c b/crypto/openssl/providers/implementations/encode_decode/encode_key2any.c new file mode 100644 index 000000000000..67a11590034a --- /dev/null +++ b/crypto/openssl/providers/implementations/encode_decode/encode_key2any.c @@ -0,0 +1,1764 @@ +/* + * Copyright 2020-2025 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 + */ + +/* + * Low level APIs are deprecated for public use, but still ok for internal use. + */ +#include "internal/deprecated.h" + +#include <openssl/byteorder.h> +#include <openssl/core.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/crypto.h> +#include <openssl/params.h> +#include <openssl/asn1.h> +#include <openssl/err.h> +#include <openssl/pem.h> +#include <openssl/x509.h> +#include <openssl/pkcs12.h> /* PKCS8_encrypt() */ +#include <openssl/dh.h> +#include <openssl/dsa.h> +#include <openssl/ec.h> +#include <openssl/proverr.h> +#include "internal/passphrase.h" +#include "internal/cryptlib.h" +#include "crypto/ecx.h" +#include "crypto/ml_kem.h" +#include "crypto/rsa.h" +#include "crypto/ml_dsa.h" +#include "crypto/slh_dsa.h" +#include "prov/implementations.h" +#include "prov/bio.h" +#include "prov/provider_ctx.h" +#include "prov/der_rsa.h" +#include "endecoder_local.h" +#include "ml_dsa_codecs.h" +#include "ml_kem_codecs.h" + +#if defined(OPENSSL_NO_DH) && defined(OPENSSL_NO_DSA) && defined(OPENSSL_NO_EC) +# define OPENSSL_NO_KEYPARAMS +#endif + +typedef struct key2any_ctx_st { + PROV_CTX *provctx; + + /* Set to 0 if parameters should not be saved (dsa only) */ + int save_parameters; + + /* Set to 1 if intending to encrypt/decrypt, otherwise 0 */ + int cipher_intent; + + EVP_CIPHER *cipher; + + struct ossl_passphrase_data_st pwdata; +} KEY2ANY_CTX; + +typedef int check_key_type_fn(const void *key, int nid); +typedef int key_to_paramstring_fn(const void *key, int nid, int save, + void **str, int *strtype); +typedef int key_to_der_fn(BIO *out, const void *key, + int key_nid, const char *pemname, + key_to_paramstring_fn *p2s, + OSSL_i2d_of_void_ctx *k2d, KEY2ANY_CTX *ctx); +typedef int write_bio_of_void_fn(BIO *bp, const void *x); + + +/* Free the blob allocated during key_to_paramstring_fn */ +static void free_asn1_data(int type, void *data) +{ + switch (type) { + case V_ASN1_OBJECT: + ASN1_OBJECT_free(data); + break; + case V_ASN1_SEQUENCE: + ASN1_STRING_free(data); + break; + } +} + +static PKCS8_PRIV_KEY_INFO *key_to_p8info(const void *key, int key_nid, + void *params, int params_type, + OSSL_i2d_of_void_ctx *k2d, + KEY2ANY_CTX *ctx) +{ + /* der, derlen store the key DER output and its length */ + unsigned char *der = NULL; + int derlen; + /* The final PKCS#8 info */ + PKCS8_PRIV_KEY_INFO *p8info = NULL; + + if ((p8info = PKCS8_PRIV_KEY_INFO_new()) == NULL + || (derlen = k2d(key, &der, (void *)ctx)) <= 0 + || !PKCS8_pkey_set0(p8info, OBJ_nid2obj(key_nid), 0, + params_type, params, der, derlen)) { + ERR_raise(ERR_LIB_PROV, ERR_R_ASN1_LIB); + PKCS8_PRIV_KEY_INFO_free(p8info); + OPENSSL_free(der); + p8info = NULL; + } + + return p8info; +} + +static X509_SIG *p8info_to_encp8(PKCS8_PRIV_KEY_INFO *p8info, + KEY2ANY_CTX *ctx) +{ + X509_SIG *p8 = NULL; + char kstr[PEM_BUFSIZE]; + size_t klen = 0; + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + + if (ctx->cipher == NULL) + return NULL; + + if (!ossl_pw_get_passphrase(kstr, sizeof(kstr), &klen, NULL, 1, + &ctx->pwdata)) { + ERR_raise(ERR_LIB_PROV, PROV_R_UNABLE_TO_GET_PASSPHRASE); + return NULL; + } + /* First argument == -1 means "standard" */ + p8 = PKCS8_encrypt_ex(-1, ctx->cipher, kstr, klen, NULL, 0, 0, p8info, libctx, NULL); + OPENSSL_cleanse(kstr, klen); + return p8; +} + +static X509_SIG *key_to_encp8(const void *key, int key_nid, + void *params, int params_type, + OSSL_i2d_of_void_ctx *k2d, + KEY2ANY_CTX *ctx) +{ + PKCS8_PRIV_KEY_INFO *p8info = + key_to_p8info(key, key_nid, params, params_type, k2d, ctx); + X509_SIG *p8 = NULL; + + if (p8info == NULL) { + free_asn1_data(params_type, params); + } else { + p8 = p8info_to_encp8(p8info, ctx); + PKCS8_PRIV_KEY_INFO_free(p8info); + } + return p8; +} + +static X509_PUBKEY *key_to_pubkey(const void *key, int key_nid, + void *params, int params_type, + OSSL_i2d_of_void_ctx *k2d, + KEY2ANY_CTX *ctx) +{ + /* der, derlen store the key DER output and its length */ + unsigned char *der = NULL; + int derlen; + /* The final X509_PUBKEY */ + X509_PUBKEY *xpk = NULL; + + + if ((xpk = X509_PUBKEY_new()) == NULL + || (derlen = k2d(key, &der, (void *)ctx)) <= 0 + || !X509_PUBKEY_set0_param(xpk, OBJ_nid2obj(key_nid), + params_type, params, der, derlen)) { + ERR_raise(ERR_LIB_PROV, ERR_R_X509_LIB); + X509_PUBKEY_free(xpk); + OPENSSL_free(der); + xpk = NULL; + } + + return xpk; +} + +/* + * key_to_epki_* produce encoded output with the private key data in a + * EncryptedPrivateKeyInfo structure (defined by PKCS#8). They require + * that there's an intent to encrypt, anything else is an error. + * + * key_to_pki_* primarily produce encoded output with the private key data + * in a PrivateKeyInfo structure (also defined by PKCS#8). However, if + * there is an intent to encrypt the data, the corresponding key_to_epki_* + * function is used instead. + * + * key_to_spki_* produce encoded output with the public key data in an + * X.509 SubjectPublicKeyInfo. + * + * Key parameters don't have any defined envelopment of this kind, but are + * included in some manner in the output from the functions described above, + * either in the AlgorithmIdentifier's parameter field, or as part of the + * key data itself. + */ + +static int key_to_epki_der_priv_bio(BIO *out, const void *key, + int key_nid, + ossl_unused const char *pemname, + key_to_paramstring_fn *p2s, + OSSL_i2d_of_void_ctx *k2d, + KEY2ANY_CTX *ctx) +{ + int ret = 0; + void *str = NULL; + int strtype = V_ASN1_UNDEF; + X509_SIG *p8; + + if (!ctx->cipher_intent) + return 0; + + if (p2s != NULL && !p2s(key, key_nid, ctx->save_parameters, + &str, &strtype)) + return 0; + + p8 = key_to_encp8(key, key_nid, str, strtype, k2d, ctx); + if (p8 != NULL) + ret = i2d_PKCS8_bio(out, p8); + + X509_SIG_free(p8); + + return ret; +} + +static int key_to_epki_pem_priv_bio(BIO *out, const void *key, + int key_nid, + ossl_unused const char *pemname, + key_to_paramstring_fn *p2s, + OSSL_i2d_of_void_ctx *k2d, + KEY2ANY_CTX *ctx) +{ + int ret = 0; + void *str = NULL; + int strtype = V_ASN1_UNDEF; + X509_SIG *p8; + + if (!ctx->cipher_intent) + return 0; + + if (p2s != NULL && !p2s(key, key_nid, ctx->save_parameters, + &str, &strtype)) + return 0; + + p8 = key_to_encp8(key, key_nid, str, strtype, k2d, ctx); + if (p8 != NULL) + ret = PEM_write_bio_PKCS8(out, p8); + + X509_SIG_free(p8); + + return ret; +} + +static int key_to_pki_der_priv_bio(BIO *out, const void *key, + int key_nid, + ossl_unused const char *pemname, + key_to_paramstring_fn *p2s, + OSSL_i2d_of_void_ctx *k2d, + KEY2ANY_CTX *ctx) +{ + int ret = 0; + void *str = NULL; + int strtype = V_ASN1_UNDEF; + PKCS8_PRIV_KEY_INFO *p8info; + + if (ctx->cipher_intent) + return key_to_epki_der_priv_bio(out, key, key_nid, pemname, + p2s, k2d, ctx); + + if (p2s != NULL && !p2s(key, key_nid, ctx->save_parameters, + &str, &strtype)) + return 0; + + p8info = key_to_p8info(key, key_nid, str, strtype, k2d, ctx); + + if (p8info != NULL) + ret = i2d_PKCS8_PRIV_KEY_INFO_bio(out, p8info); + else + free_asn1_data(strtype, str); + + PKCS8_PRIV_KEY_INFO_free(p8info); + + return ret; +} + +static int key_to_pki_pem_priv_bio(BIO *out, const void *key, + int key_nid, + ossl_unused const char *pemname, + key_to_paramstring_fn *p2s, + OSSL_i2d_of_void_ctx *k2d, + KEY2ANY_CTX *ctx) +{ + int ret = 0; + void *str = NULL; + int strtype = V_ASN1_UNDEF; + PKCS8_PRIV_KEY_INFO *p8info; + + if (ctx->cipher_intent) + return key_to_epki_pem_priv_bio(out, key, key_nid, pemname, + p2s, k2d, ctx); + + if (p2s != NULL && !p2s(key, key_nid, ctx->save_parameters, + &str, &strtype)) + return 0; + + p8info = key_to_p8info(key, key_nid, str, strtype, k2d, ctx); + + if (p8info != NULL) + ret = PEM_write_bio_PKCS8_PRIV_KEY_INFO(out, p8info); + else + free_asn1_data(strtype, str); + + PKCS8_PRIV_KEY_INFO_free(p8info); + + return ret; +} + +static int key_to_spki_der_pub_bio(BIO *out, const void *key, + int key_nid, + ossl_unused const char *pemname, + key_to_paramstring_fn *p2s, + OSSL_i2d_of_void_ctx *k2d, + KEY2ANY_CTX *ctx) +{ + int ret = 0; + void *str = NULL; + int strtype = V_ASN1_UNDEF; + X509_PUBKEY *xpk = NULL; + + if (p2s != NULL && !p2s(key, key_nid, ctx->save_parameters, + &str, &strtype)) + return 0; + + xpk = key_to_pubkey(key, key_nid, str, strtype, k2d, ctx); + + if (xpk != NULL) + ret = i2d_X509_PUBKEY_bio(out, xpk); + + /* Also frees |str| */ + X509_PUBKEY_free(xpk); + return ret; +} + +static int key_to_spki_pem_pub_bio(BIO *out, const void *key, + int key_nid, + ossl_unused const char *pemname, + key_to_paramstring_fn *p2s, + OSSL_i2d_of_void_ctx *k2d, + KEY2ANY_CTX *ctx) +{ + int ret = 0; + void *str = NULL; + int strtype = V_ASN1_UNDEF; + X509_PUBKEY *xpk = NULL; + + if (p2s != NULL && !p2s(key, key_nid, ctx->save_parameters, + &str, &strtype)) + return 0; + + xpk = key_to_pubkey(key, key_nid, str, strtype, k2d, ctx); + + if (xpk != NULL) + ret = PEM_write_bio_X509_PUBKEY(out, xpk); + else + free_asn1_data(strtype, str); + + /* Also frees |str| */ + X509_PUBKEY_free(xpk); + return ret; +} + +/* + * key_to_type_specific_* produce encoded output with type specific key data, + * no envelopment; the same kind of output as the type specific i2d_ and + * PEM_write_ functions, which is often a simple SEQUENCE of INTEGER. + * + * OpenSSL tries to discourage production of new keys in this form, because + * of the ambiguity when trying to recognise them, but can't deny that PKCS#1 + * et al still are live standards. + * + * Note that these functions completely ignore p2s, and rather rely entirely + * on k2d to do the complete work. + */ +static int key_to_type_specific_der_bio(BIO *out, const void *key, + int key_nid, + ossl_unused const char *pemname, + key_to_paramstring_fn *p2s, + OSSL_i2d_of_void_ctx *k2d, + KEY2ANY_CTX *ctx) +{ + unsigned char *der = NULL; + int derlen; + int ret; + + if ((derlen = k2d(key, &der, (void *)ctx)) <= 0) { + ERR_raise(ERR_LIB_PROV, ERR_R_PROV_LIB); + return 0; + } + + ret = BIO_write(out, der, derlen); + OPENSSL_free(der); + return ret > 0; +} +#define key_to_type_specific_der_priv_bio key_to_type_specific_der_bio +#define key_to_type_specific_der_pub_bio key_to_type_specific_der_bio +#define key_to_type_specific_der_param_bio key_to_type_specific_der_bio + +static int key_to_type_specific_pem_bio_cb(BIO *out, const void *key, + int key_nid, const char *pemname, + key_to_paramstring_fn *p2s, + OSSL_i2d_of_void_ctx *k2d, + KEY2ANY_CTX *ctx, + pem_password_cb *cb, void *cbarg) +{ + return PEM_ASN1_write_bio_ctx(k2d, (void *)ctx, pemname, out, key, + ctx->cipher, NULL, 0, cb, cbarg) > 0; +} + +static int key_to_type_specific_pem_priv_bio(BIO *out, const void *key, + int key_nid, const char *pemname, + key_to_paramstring_fn *p2s, + OSSL_i2d_of_void_ctx *k2d, + KEY2ANY_CTX *ctx) +{ + return key_to_type_specific_pem_bio_cb(out, key, key_nid, pemname, + p2s, k2d, ctx, + ossl_pw_pem_password, &ctx->pwdata); +} + +static int key_to_type_specific_pem_pub_bio(BIO *out, const void *key, + int key_nid, const char *pemname, + key_to_paramstring_fn *p2s, + OSSL_i2d_of_void_ctx *k2d, + KEY2ANY_CTX *ctx) +{ + return key_to_type_specific_pem_bio_cb(out, key, key_nid, pemname, + p2s, k2d, ctx, NULL, NULL); +} + +#ifndef OPENSSL_NO_KEYPARAMS +static int key_to_type_specific_pem_param_bio(BIO *out, const void *key, + int key_nid, const char *pemname, + key_to_paramstring_fn *p2s, + OSSL_i2d_of_void_ctx *k2d, + KEY2ANY_CTX *ctx) +{ + return key_to_type_specific_pem_bio_cb(out, key, key_nid, pemname, + p2s, k2d, ctx, NULL, NULL); +} +#endif + +/* ---------------------------------------------------------------------- */ + +#define k2d_NOCTX(n, f) \ + static int \ + n##_k2d(const void *key, unsigned char **pder, \ + ossl_unused void *ctx) \ + { \ + return f(key, pder); \ + } + +/* ---------------------------------------------------------------------- */ + +#ifndef OPENSSL_NO_DH +static int prepare_dh_params(const void *dh, int nid, int save, + void **pstr, int *pstrtype) +{ + ASN1_STRING *params = ASN1_STRING_new(); + + if (params == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_ASN1_LIB); + return 0; + } + + if (nid == EVP_PKEY_DHX) + params->length = i2d_DHxparams(dh, ¶ms->data); + else + params->length = i2d_DHparams(dh, ¶ms->data); + + if (params->length <= 0) { + ERR_raise(ERR_LIB_PROV, ERR_R_ASN1_LIB); + ASN1_STRING_free(params); + return 0; + } + params->type = V_ASN1_SEQUENCE; + + *pstr = params; + *pstrtype = V_ASN1_SEQUENCE; + return 1; +} + +static int dh_spki_pub_to_der(const void *dh, unsigned char **pder, + ossl_unused void *ctx) +{ + const BIGNUM *bn = NULL; + ASN1_INTEGER *pub_key = NULL; + int ret; + + if ((bn = DH_get0_pub_key(dh)) == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY); + return 0; + } + if ((pub_key = BN_to_ASN1_INTEGER(bn, NULL)) == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_BN_ERROR); + return 0; + } + + ret = i2d_ASN1_INTEGER(pub_key, pder); + + ASN1_STRING_clear_free(pub_key); + return ret; +} + +static int dh_pki_priv_to_der(const void *dh, unsigned char **pder, + ossl_unused void *ctx) +{ + const BIGNUM *bn = NULL; + ASN1_INTEGER *priv_key = NULL; + int ret; + + if ((bn = DH_get0_priv_key(dh)) == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PRIVATE_KEY); + return 0; + } + if ((priv_key = BN_to_ASN1_INTEGER(bn, NULL)) == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_BN_ERROR); + return 0; + } + + ret = i2d_ASN1_INTEGER(priv_key, pder); + + ASN1_STRING_clear_free(priv_key); + return ret; +} + +# define dh_epki_priv_to_der dh_pki_priv_to_der + +static int +dh_type_specific_params_to_der(const void *dh, unsigned char **pder, + ossl_unused void *ctx) +{ + if (DH_test_flags(dh, DH_FLAG_TYPE_DHX)) + return i2d_DHxparams(dh, pder); + return i2d_DHparams(dh, pder); +} + +/* + * DH doesn't have i2d_DHPrivateKey or i2d_DHPublicKey, so we can't make + * corresponding functions here. + */ +# define dh_type_specific_priv_to_der NULL +# define dh_type_specific_pub_to_der NULL + +static int dh_check_key_type(const void *dh, int expected_type) +{ + int type = + DH_test_flags(dh, DH_FLAG_TYPE_DHX) ? EVP_PKEY_DHX : EVP_PKEY_DH; + + return type == expected_type; +} + +# define dh_evp_type EVP_PKEY_DH +# define dhx_evp_type EVP_PKEY_DHX +# define dh_pem_type "DH" +# define dhx_pem_type "X9.42 DH" +#endif + +/* ---------------------------------------------------------------------- */ + +#ifndef OPENSSL_NO_DSA +static int encode_dsa_params(const void *dsa, int nid, + void **pstr, int *pstrtype) +{ + ASN1_STRING *params = ASN1_STRING_new(); + + if (params == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_ASN1_LIB); + return 0; + } + + params->length = i2d_DSAparams(dsa, ¶ms->data); + + if (params->length <= 0) { + ERR_raise(ERR_LIB_PROV, ERR_R_ASN1_LIB); + ASN1_STRING_free(params); + return 0; + } + + *pstrtype = V_ASN1_SEQUENCE; + *pstr = params; + return 1; +} + +static int prepare_dsa_params(const void *dsa, int nid, int save, + void **pstr, int *pstrtype) +{ + const BIGNUM *p = DSA_get0_p(dsa); + const BIGNUM *q = DSA_get0_q(dsa); + const BIGNUM *g = DSA_get0_g(dsa); + + if (save && p != NULL && q != NULL && g != NULL) + return encode_dsa_params(dsa, nid, pstr, pstrtype); + + *pstr = NULL; + *pstrtype = V_ASN1_UNDEF; + return 1; +} + +static int dsa_spki_pub_to_der(const void *dsa, unsigned char **pder, + ossl_unused void *ctx) +{ + const BIGNUM *bn = NULL; + ASN1_INTEGER *pub_key = NULL; + int ret; + + if ((bn = DSA_get0_pub_key(dsa)) == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY); + return 0; + } + if ((pub_key = BN_to_ASN1_INTEGER(bn, NULL)) == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_BN_ERROR); + return 0; + } + + ret = i2d_ASN1_INTEGER(pub_key, pder); + + ASN1_STRING_clear_free(pub_key); + return ret; +} + +static int dsa_pki_priv_to_der(const void *dsa, unsigned char **pder, + ossl_unused void *ctx) +{ + const BIGNUM *bn = NULL; + ASN1_INTEGER *priv_key = NULL; + int ret; + + if ((bn = DSA_get0_priv_key(dsa)) == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PRIVATE_KEY); + return 0; + } + if ((priv_key = BN_to_ASN1_INTEGER(bn, NULL)) == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_BN_ERROR); + return 0; + } + + ret = i2d_ASN1_INTEGER(priv_key, pder); + + ASN1_STRING_clear_free(priv_key); + return ret; +} + +k2d_NOCTX(dsa_prv, i2d_DSAPrivateKey) +k2d_NOCTX(dsa_pub, i2d_DSAPublicKey) +k2d_NOCTX(dsa_param, i2d_DSAparams) + +# define dsa_epki_priv_to_der dsa_pki_priv_to_der + +# define dsa_type_specific_priv_to_der dsa_prv_k2d +# define dsa_type_specific_pub_to_der dsa_pub_k2d +# define dsa_type_specific_params_to_der dsa_param_k2d + +# define dsa_check_key_type NULL +# define dsa_evp_type EVP_PKEY_DSA +# define dsa_pem_type "DSA" +#endif + +/* ---------------------------------------------------------------------- */ + +#ifndef OPENSSL_NO_EC +static int prepare_ec_explicit_params(const void *eckey, + void **pstr, int *pstrtype) +{ + ASN1_STRING *params = ASN1_STRING_new(); + + if (params == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_ASN1_LIB); + return 0; + } + + params->length = i2d_ECParameters(eckey, ¶ms->data); + if (params->length <= 0) { + ERR_raise(ERR_LIB_PROV, ERR_R_ASN1_LIB); + ASN1_STRING_free(params); + return 0; + } + + *pstrtype = V_ASN1_SEQUENCE; + *pstr = params; + return 1; +} + +/* + * This implements EcpkParameters, where the CHOICE is based on whether there + * is a curve name (curve nid) to be found or not. See RFC 3279 for details. + */ +static int prepare_ec_params(const void *eckey, int nid, int save, + void **pstr, int *pstrtype) +{ + int curve_nid; + const EC_GROUP *group = EC_KEY_get0_group(eckey); + ASN1_OBJECT *params = NULL; + + if (group == NULL) + return 0; + curve_nid = EC_GROUP_get_curve_name(group); + if (curve_nid != NID_undef) { + params = OBJ_nid2obj(curve_nid); + if (params == NULL) + return 0; + } + + if (curve_nid != NID_undef + && (EC_GROUP_get_asn1_flag(group) & OPENSSL_EC_NAMED_CURVE)) { + /* The CHOICE came to namedCurve */ + if (OBJ_length(params) == 0) { + /* Some curves might not have an associated OID */ + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_OID); + ASN1_OBJECT_free(params); + return 0; + } + *pstr = params; + *pstrtype = V_ASN1_OBJECT; + return 1; + } else { + /* The CHOICE came to ecParameters */ + return prepare_ec_explicit_params(eckey, pstr, pstrtype); + } +} + +static int ec_spki_pub_to_der(const void *eckey, unsigned char **pder, + ossl_unused void *ctx) +{ + if (EC_KEY_get0_public_key(eckey) == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY); + return 0; + } + return i2o_ECPublicKey(eckey, pder); +} + +static int ec_pki_priv_to_der(const void *veckey, unsigned char **pder, + ossl_unused void *ctx) +{ + EC_KEY *eckey = (EC_KEY *)veckey; + unsigned int old_flags; + int ret = 0; + + /* + * For PKCS8 the curve name appears in the PKCS8_PRIV_KEY_INFO object + * as the pkeyalg->parameter field. (For a named curve this is an OID) + * The pkey field is an octet string that holds the encoded + * ECPrivateKey SEQUENCE with the optional parameters field omitted. + * We omit this by setting the EC_PKEY_NO_PARAMETERS flag. + */ + old_flags = EC_KEY_get_enc_flags(eckey); /* save old flags */ + EC_KEY_set_enc_flags(eckey, old_flags | EC_PKEY_NO_PARAMETERS); + ret = i2d_ECPrivateKey(eckey, pder); + EC_KEY_set_enc_flags(eckey, old_flags); /* restore old flags */ + return ret; /* return the length of the der encoded data */ +} + +k2d_NOCTX(ec_param, i2d_ECParameters) +k2d_NOCTX(ec_prv, i2d_ECPrivateKey) + +# define ec_epki_priv_to_der ec_pki_priv_to_der + +# define ec_type_specific_params_to_der ec_param_k2d +/* No ec_type_specific_pub_to_der, there simply is no such thing */ +# define ec_type_specific_priv_to_der ec_prv_k2d + +# define ec_check_key_type NULL +# define ec_evp_type EVP_PKEY_EC +# define ec_pem_type "EC" + +# ifndef OPENSSL_NO_SM2 +/* + * Albeit SM2 is a slightly different algorithm than ECDSA, the key type + * encoding (in all places where an AlgorithmIdentifier is produced, such + * as PrivateKeyInfo and SubjectPublicKeyInfo) is the same as for ECC keys + * according to the example in GM/T 0015-2012, appendix D.2. + * This leaves the distinction of SM2 keys to the EC group (which is found + * in AlgorithmIdentified.params). + */ +# define sm2_evp_type ec_evp_type +# define sm2_pem_type "SM2" +# endif +#endif + +/* ---------------------------------------------------------------------- */ + +#ifndef OPENSSL_NO_ECX +# define prepare_ecx_params NULL + +static int ecx_spki_pub_to_der(const void *vecxkey, unsigned char **pder, + ossl_unused void *ctx) +{ + const ECX_KEY *ecxkey = vecxkey; + unsigned char *keyblob; + + if (ecxkey == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + keyblob = OPENSSL_memdup(ecxkey->pubkey, ecxkey->keylen); + if (keyblob == NULL) + return 0; + + *pder = keyblob; + return ecxkey->keylen; +} + +static int ecx_pki_priv_to_der(const void *vecxkey, unsigned char **pder, + ossl_unused void *ctx) +{ + const ECX_KEY *ecxkey = vecxkey; + ASN1_OCTET_STRING oct; + int keybloblen; + + if (ecxkey == NULL || ecxkey->privkey == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + oct.data = ecxkey->privkey; + oct.length = ecxkey->keylen; + oct.flags = 0; + + keybloblen = i2d_ASN1_OCTET_STRING(&oct, pder); + if (keybloblen < 0) { + ERR_raise(ERR_LIB_PROV, ERR_R_ASN1_LIB); + return 0; + } + + return keybloblen; +} + +# define ecx_epki_priv_to_der ecx_pki_priv_to_der + +/* + * ED25519, ED448, X25519 and X448 only has PKCS#8 / SubjectPublicKeyInfo + * representation, so we don't define ecx_type_specific_[priv,pub,params]_to_der. + */ + +# define ecx_check_key_type NULL + +# define ed25519_evp_type EVP_PKEY_ED25519 +# define ed448_evp_type EVP_PKEY_ED448 +# define x25519_evp_type EVP_PKEY_X25519 +# define x448_evp_type EVP_PKEY_X448 +# define ed25519_pem_type "ED25519" +# define ed448_pem_type "ED448" +# define x25519_pem_type "X25519" +# define x448_pem_type "X448" +#endif + +/* ---------------------------------------------------------------------- */ + +#ifndef OPENSSL_NO_ML_DSA +static int ml_dsa_spki_pub_to_der(const void *vkey, unsigned char **pder, + ossl_unused void *ctx) +{ + return ossl_ml_dsa_i2d_pubkey(vkey, pder); +} + +static int ml_dsa_pki_priv_to_der(const void *vkey, unsigned char **pder, + void *vctx) +{ + KEY2ANY_CTX *ctx = vctx; + + return ossl_ml_dsa_i2d_prvkey(vkey, pder, ctx->provctx); +} + +# define ml_dsa_epki_priv_to_der ml_dsa_pki_priv_to_der +# define prepare_ml_dsa_params NULL +# define ml_dsa_check_key_type NULL + +# define ml_dsa_44_evp_type EVP_PKEY_ML_DSA_44 +# define ml_dsa_44_pem_type "ML-DSA-44" +# define ml_dsa_65_evp_type EVP_PKEY_ML_DSA_65 +# define ml_dsa_65_pem_type "ML-DSA-65" +# define ml_dsa_87_evp_type EVP_PKEY_ML_DSA_87 +# define ml_dsa_87_pem_type "ML-DSA-87" +#endif /* OPENSSL_NO_ML_DSA */ + +/* ---------------------------------------------------------------------- */ + +#ifndef OPENSSL_NO_ML_KEM + +static int ml_kem_spki_pub_to_der(const void *vkey, unsigned char **pder, + ossl_unused void *ctx) +{ + return ossl_ml_kem_i2d_pubkey(vkey, pder); +} + +static int ml_kem_pki_priv_to_der(const void *vkey, unsigned char **pder, + void *vctx) +{ + KEY2ANY_CTX *ctx = vctx; + + return ossl_ml_kem_i2d_prvkey(vkey, pder, ctx->provctx); +} + +# define ml_kem_epki_priv_to_der ml_kem_pki_priv_to_der +# define prepare_ml_kem_params NULL +# define ml_kem_check_key_type NULL + +# define ml_kem_512_evp_type EVP_PKEY_ML_KEM_512 +# define ml_kem_512_pem_type "ML-KEM-512" +# define ml_kem_768_evp_type EVP_PKEY_ML_KEM_768 +# define ml_kem_768_pem_type "ML-KEM-768" +# define ml_kem_1024_evp_type EVP_PKEY_ML_KEM_1024 +# define ml_kem_1024_pem_type "ML-KEM-1024" +#endif + +/* ---------------------------------------------------------------------- */ + +/* + * Helper functions to prepare RSA-PSS params for encoding. We would + * have simply written the whole AlgorithmIdentifier, but existing libcrypto + * functionality doesn't allow that. + */ + +static int prepare_rsa_params(const void *rsa, int nid, int save, + void **pstr, int *pstrtype) +{ + const RSA_PSS_PARAMS_30 *pss = ossl_rsa_get0_pss_params_30((RSA *)rsa); + + *pstr = NULL; + + switch (RSA_test_flags(rsa, RSA_FLAG_TYPE_MASK)) { + case RSA_FLAG_TYPE_RSA: + /* If plain RSA, the parameters shall be NULL */ + *pstrtype = V_ASN1_NULL; + return 1; + case RSA_FLAG_TYPE_RSASSAPSS: + if (ossl_rsa_pss_params_30_is_unrestricted(pss)) { + *pstrtype = V_ASN1_UNDEF; + return 1; + } else { + ASN1_STRING *astr = NULL; + WPACKET pkt; + unsigned char *str = NULL; + size_t str_sz = 0; + int i; + + for (i = 0; i < 2; i++) { + switch (i) { + case 0: + if (!WPACKET_init_null_der(&pkt)) + goto err; + break; + case 1: + if ((str = OPENSSL_malloc(str_sz)) == NULL + || !WPACKET_init_der(&pkt, str, str_sz)) { + WPACKET_cleanup(&pkt); + goto err; + } + break; + } + if (!ossl_DER_w_RSASSA_PSS_params(&pkt, -1, pss) + || !WPACKET_finish(&pkt) + || !WPACKET_get_total_written(&pkt, &str_sz)) { + WPACKET_cleanup(&pkt); + goto err; + } + WPACKET_cleanup(&pkt); + + /* + * If no PSS parameters are going to be written, there's no + * point going for another iteration. + * This saves us from getting |str| allocated just to have it + * immediately de-allocated. + */ + if (str_sz == 0) + break; + } + + if ((astr = ASN1_STRING_new()) == NULL) + goto err; + *pstrtype = V_ASN1_SEQUENCE; + ASN1_STRING_set0(astr, str, (int)str_sz); + *pstr = astr; + + return 1; + err: + OPENSSL_free(str); + return 0; + } + } + + /* Currently unsupported RSA key type */ + return 0; +} + +k2d_NOCTX(rsa_prv, i2d_RSAPrivateKey) +k2d_NOCTX(rsa_pub, i2d_RSAPublicKey) + +/* + * RSA is extremely simple, as PKCS#1 is used for the PKCS#8 |privateKey| + * field as well as the SubjectPublicKeyInfo |subjectPublicKey| field. + */ +#define rsa_pki_priv_to_der rsa_type_specific_priv_to_der +#define rsa_epki_priv_to_der rsa_type_specific_priv_to_der +#define rsa_spki_pub_to_der rsa_type_specific_pub_to_der +#define rsa_type_specific_priv_to_der rsa_prv_k2d +#define rsa_type_specific_pub_to_der rsa_pub_k2d +#define rsa_type_specific_params_to_der NULL + +static int rsa_check_key_type(const void *rsa, int expected_type) +{ + switch (RSA_test_flags(rsa, RSA_FLAG_TYPE_MASK)) { + case RSA_FLAG_TYPE_RSA: + return expected_type == EVP_PKEY_RSA; + case RSA_FLAG_TYPE_RSASSAPSS: + return expected_type == EVP_PKEY_RSA_PSS; + } + + /* Currently unsupported RSA key type */ + return EVP_PKEY_NONE; +} + +#define rsa_evp_type EVP_PKEY_RSA +#define rsapss_evp_type EVP_PKEY_RSA_PSS +#define rsa_pem_type "RSA" +#define rsapss_pem_type "RSA-PSS" + +/* ---------------------------------------------------------------------- */ + +#ifndef OPENSSL_NO_SLH_DSA +# define prepare_slh_dsa_params NULL + +static int slh_dsa_spki_pub_to_der(const void *vkey, unsigned char **pder, + ossl_unused void *ctx) +{ + const SLH_DSA_KEY *key = vkey; + uint8_t *key_blob; + size_t key_len; + + if (key == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + key_len = ossl_slh_dsa_key_get_pub_len(key); + key_blob = OPENSSL_memdup(ossl_slh_dsa_key_get_pub(key), key_len); + if (key_blob == NULL) + return 0; + + *pder = key_blob; + return key_len; +} + +static int slh_dsa_pki_priv_to_der(const void *vkey, unsigned char **pder, + ossl_unused void *ctx) +{ + const SLH_DSA_KEY *key = vkey; + size_t len; + + if (ossl_slh_dsa_key_get_priv(key) == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + len = ossl_slh_dsa_key_get_priv_len(key); + + if (pder != NULL + && ((*pder = OPENSSL_memdup(ossl_slh_dsa_key_get_priv(key), len)) == NULL)) + return 0; + + return len; +} +# define slh_dsa_epki_priv_to_der slh_dsa_pki_priv_to_der + +/* SLH_DSA only has PKCS#8 / SubjectPublicKeyInfo representations. */ + +# define slh_dsa_check_key_type NULL +# define slh_dsa_sha2_128s_evp_type EVP_PKEY_SLH_DSA_SHA2_128S +# define slh_dsa_sha2_128f_evp_type EVP_PKEY_SLH_DSA_SHA2_128F +# define slh_dsa_sha2_192s_evp_type EVP_PKEY_SLH_DSA_SHA2_192S +# define slh_dsa_sha2_192f_evp_type EVP_PKEY_SLH_DSA_SHA2_192F +# define slh_dsa_sha2_256s_evp_type EVP_PKEY_SLH_DSA_SHA2_256S +# define slh_dsa_sha2_256f_evp_type EVP_PKEY_SLH_DSA_SHA2_256F +# define slh_dsa_shake_128s_evp_type EVP_PKEY_SLH_DSA_SHAKE_128S +# define slh_dsa_shake_128f_evp_type EVP_PKEY_SLH_DSA_SHAKE_128F +# define slh_dsa_shake_192s_evp_type EVP_PKEY_SLH_DSA_SHAKE_192S +# define slh_dsa_shake_192f_evp_type EVP_PKEY_SLH_DSA_SHAKE_192F +# define slh_dsa_shake_256s_evp_type EVP_PKEY_SLH_DSA_SHAKE_256S +# define slh_dsa_shake_256f_evp_type EVP_PKEY_SLH_DSA_SHAKE_256F +# define slh_dsa_sha2_128s_input_type "SLH-DSA-SHA2-128s" +# define slh_dsa_sha2_128f_input_type "SLH-DSA-SHA2-128f" +# define slh_dsa_sha2_192s_input_type "SLH-DSA-SHA2-192s" +# define slh_dsa_sha2_192f_input_type "SLH-DSA-SHA2-192f" +# define slh_dsa_sha2_256s_input_type "SLH-DSA-SHA2-256s" +# define slh_dsa_sha2_256f_input_type "SLH-DSA-SHA2-256f" +# define slh_dsa_shake_128s_input_type "SLH-DSA-SHAKE-128s" +# define slh_dsa_shake_128f_input_type "SLH-DSA-SHAKE-128f" +# define slh_dsa_shake_192s_input_type "SLH-DSA-SHAKE-192s" +# define slh_dsa_shake_192f_input_type "SLH-DSA-SHAKE-192f" +# define slh_dsa_shake_256s_input_type "SLH-DSA-SHAKE-256s" +# define slh_dsa_shake_256f_input_type "SLH-DSA-SHAKE-256f" +# define slh_dsa_sha2_128s_pem_type "SLH-DSA-SHA2-128s" +# define slh_dsa_sha2_128f_pem_type "SLH-DSA-SHA2-128f" +# define slh_dsa_sha2_192s_pem_type "SLH-DSA-SHA2-192s" +# define slh_dsa_sha2_192f_pem_type "SLH-DSA-SHA2-192f" +# define slh_dsa_sha2_256s_pem_type "SLH-DSA-SHA2-256s" +# define slh_dsa_sha2_256f_pem_type "SLH-DSA-SHA2-256f" +# define slh_dsa_shake_128s_pem_type "SLH-DSA-SHAKE-128s" +# define slh_dsa_shake_128f_pem_type "SLH-DSA-SHAKE-128f" +# define slh_dsa_shake_192s_pem_type "SLH-DSA-SHAKE-192s" +# define slh_dsa_shake_192f_pem_type "SLH-DSA-SHAKE-192f" +# define slh_dsa_shake_256s_pem_type "SLH-DSA-SHAKE-256s" +# define slh_dsa_shake_256f_pem_type "SLH-DSA-SHAKE-256f" +#endif /* OPENSSL_NO_SLH_DSA */ + +/* ---------------------------------------------------------------------- */ + +static OSSL_FUNC_decoder_newctx_fn key2any_newctx; +static OSSL_FUNC_decoder_freectx_fn key2any_freectx; + +static void *key2any_newctx(void *provctx) +{ + KEY2ANY_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx)); + + if (ctx != NULL) { + ctx->provctx = provctx; + ctx->save_parameters = 1; + } + + return ctx; +} + +static void key2any_freectx(void *vctx) +{ + KEY2ANY_CTX *ctx = vctx; + + ossl_pw_clear_passphrase_data(&ctx->pwdata); + EVP_CIPHER_free(ctx->cipher); + OPENSSL_free(ctx); +} + +static const OSSL_PARAM *key2any_settable_ctx_params(ossl_unused void *provctx) +{ + static const OSSL_PARAM settables[] = { + OSSL_PARAM_utf8_string(OSSL_ENCODER_PARAM_CIPHER, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_ENCODER_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_END, + }; + + return settables; +} + +static int key2any_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + KEY2ANY_CTX *ctx = vctx; + OSSL_LIB_CTX *libctx = ossl_prov_ctx_get0_libctx(ctx->provctx); + const OSSL_PARAM *cipherp = + OSSL_PARAM_locate_const(params, OSSL_ENCODER_PARAM_CIPHER); + const OSSL_PARAM *propsp = + OSSL_PARAM_locate_const(params, OSSL_ENCODER_PARAM_PROPERTIES); + const OSSL_PARAM *save_paramsp = + OSSL_PARAM_locate_const(params, OSSL_ENCODER_PARAM_SAVE_PARAMETERS); + + if (cipherp != NULL) { + const char *ciphername = NULL; + const char *props = NULL; + + if (!OSSL_PARAM_get_utf8_string_ptr(cipherp, &ciphername)) + return 0; + if (propsp != NULL && !OSSL_PARAM_get_utf8_string_ptr(propsp, &props)) + return 0; + + EVP_CIPHER_free(ctx->cipher); + ctx->cipher = NULL; + ctx->cipher_intent = ciphername != NULL; + if (ciphername != NULL + && ((ctx->cipher = + EVP_CIPHER_fetch(libctx, ciphername, props)) == NULL)) + return 0; + } + + if (save_paramsp != NULL) { + if (!OSSL_PARAM_get_int(save_paramsp, &ctx->save_parameters)) + return 0; + } + return 1; +} + +static int key2any_check_selection(int selection, int selection_mask) +{ + /* + * The selections are kinda sorta "levels", i.e. each selection given + * here is assumed to include those following. + */ + int checks[] = { + OSSL_KEYMGMT_SELECT_PRIVATE_KEY, + OSSL_KEYMGMT_SELECT_PUBLIC_KEY, + OSSL_KEYMGMT_SELECT_ALL_PARAMETERS + }; + size_t i; + + /* The decoder implementations made here support guessing */ + if (selection == 0) + return 1; + + for (i = 0; i < OSSL_NELEM(checks); i++) { + int check1 = (selection & checks[i]) != 0; + int check2 = (selection_mask & checks[i]) != 0; + + /* + * If the caller asked for the currently checked bit(s), return + * whether the decoder description says it's supported. + */ + if (check1) + return check2; + } + + /* This should be dead code, but just to be safe... */ + return 0; +} + +static int key2any_encode(KEY2ANY_CTX *ctx, OSSL_CORE_BIO *cout, + const void *key, int type, const char *pemname, + check_key_type_fn *checker, + key_to_der_fn *writer, + OSSL_PASSPHRASE_CALLBACK *pwcb, void *pwcbarg, + key_to_paramstring_fn *key2paramstring, + OSSL_i2d_of_void_ctx *key2der) +{ + int ret = 0; + + if (key == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER); + } else if (writer != NULL + && (checker == NULL || checker(key, type))) { + BIO *out = ossl_bio_new_from_core_bio(ctx->provctx, cout); + + if (out != NULL + && (pwcb == NULL + || ossl_pw_set_ossl_passphrase_cb(&ctx->pwdata, pwcb, pwcbarg))) + ret = + writer(out, key, type, pemname, key2paramstring, key2der, ctx); + + BIO_free(out); + } else { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT); + } + return ret; +} + +#define DO_PRIVATE_KEY_selection_mask OSSL_KEYMGMT_SELECT_PRIVATE_KEY +#define DO_PRIVATE_KEY(impl, type, kind, output) \ + if ((selection & DO_PRIVATE_KEY_selection_mask) != 0) \ + return key2any_encode(ctx, cout, key, impl##_evp_type, \ + impl##_pem_type " PRIVATE KEY", \ + type##_check_key_type, \ + key_to_##kind##_##output##_priv_bio, \ + cb, cbarg, prepare_##type##_params, \ + type##_##kind##_priv_to_der); + +#define DO_PUBLIC_KEY_selection_mask OSSL_KEYMGMT_SELECT_PUBLIC_KEY +#define DO_PUBLIC_KEY(impl, type, kind, output) \ + if ((selection & DO_PUBLIC_KEY_selection_mask) != 0) \ + return key2any_encode(ctx, cout, key, impl##_evp_type, \ + impl##_pem_type " PUBLIC KEY", \ + type##_check_key_type, \ + key_to_##kind##_##output##_pub_bio, \ + cb, cbarg, prepare_##type##_params, \ + type##_##kind##_pub_to_der); + +#define DO_PARAMETERS_selection_mask OSSL_KEYMGMT_SELECT_ALL_PARAMETERS +#define DO_PARAMETERS(impl, type, kind, output) \ + if ((selection & DO_PARAMETERS_selection_mask) != 0) \ + return key2any_encode(ctx, cout, key, impl##_evp_type, \ + impl##_pem_type " PARAMETERS", \ + type##_check_key_type, \ + key_to_##kind##_##output##_param_bio, \ + NULL, NULL, NULL, \ + type##_##kind##_params_to_der); + +/*- + * Implement the kinds of output structure that can be produced. They are + * referred to by name, and for each name, the following macros are defined + * (braces not included): + * + * DO_{kind}_selection_mask + * + * A mask of selection bits that must not be zero. This is used as a + * selection criterion for each implementation. + * This mask must never be zero. + * + * DO_{kind} + * + * The performing macro. It must use the DO_ macros defined above, + * always in this order: + * + * - DO_PRIVATE_KEY + * - DO_PUBLIC_KEY + * - DO_PARAMETERS + * + * Any of those may be omitted, but the relative order must still be + * the same. + */ + +/* + * PKCS#8 defines two structures for private keys only: + * - PrivateKeyInfo (raw unencrypted form) + * - EncryptedPrivateKeyInfo (encrypted wrapping) + * + * To allow a certain amount of flexibility, we allow the routines + * for PrivateKeyInfo to also produce EncryptedPrivateKeyInfo if a + * passphrase callback has been passed to them. + */ +#define DO_PrivateKeyInfo_selection_mask DO_PRIVATE_KEY_selection_mask +#define DO_PrivateKeyInfo(impl, type, output) \ + DO_PRIVATE_KEY(impl, type, pki, output) + +#define DO_EncryptedPrivateKeyInfo_selection_mask DO_PRIVATE_KEY_selection_mask +#define DO_EncryptedPrivateKeyInfo(impl, type, output) \ + DO_PRIVATE_KEY(impl, type, epki, output) + +/* SubjectPublicKeyInfo is a structure for public keys only */ +#define DO_SubjectPublicKeyInfo_selection_mask DO_PUBLIC_KEY_selection_mask +#define DO_SubjectPublicKeyInfo(impl, type, output) \ + DO_PUBLIC_KEY(impl, type, spki, output) + +/* + * "type-specific" is a uniform name for key type specific output for private + * and public keys as well as key parameters. This is used internally in + * libcrypto so it doesn't have to have special knowledge about select key + * types, but also when no better name has been found. If there are more + * expressive DO_ names above, those are preferred. + * + * Three forms exist: + * + * - type_specific_keypair Only supports private and public key + * - type_specific_params Only supports parameters + * - type_specific Supports all parts of an EVP_PKEY + * - type_specific_no_pub Supports all parts of an EVP_PKEY + * except public key + */ +#define DO_type_specific_params_selection_mask DO_PARAMETERS_selection_mask +#define DO_type_specific_params(impl, type, output) \ + DO_PARAMETERS(impl, type, type_specific, output) +#define DO_type_specific_keypair_selection_mask \ + ( DO_PRIVATE_KEY_selection_mask | DO_PUBLIC_KEY_selection_mask ) +#define DO_type_specific_keypair(impl, type, output) \ + DO_PRIVATE_KEY(impl, type, type_specific, output) \ + DO_PUBLIC_KEY(impl, type, type_specific, output) +#define DO_type_specific_selection_mask \ + ( DO_type_specific_keypair_selection_mask \ + | DO_type_specific_params_selection_mask ) +#define DO_type_specific(impl, type, output) \ + DO_type_specific_keypair(impl, type, output) \ + DO_type_specific_params(impl, type, output) +#define DO_type_specific_no_pub_selection_mask \ + ( DO_PRIVATE_KEY_selection_mask | DO_PARAMETERS_selection_mask) +#define DO_type_specific_no_pub(impl, type, output) \ + DO_PRIVATE_KEY(impl, type, type_specific, output) \ + DO_type_specific_params(impl, type, output) + +/* + * Type specific aliases for the cases where we need to refer to them by + * type name. + * This only covers key types that are represented with i2d_{TYPE}PrivateKey, + * i2d_{TYPE}PublicKey and i2d_{TYPE}params / i2d_{TYPE}Parameters. + */ +#define DO_RSA_selection_mask DO_type_specific_keypair_selection_mask +#define DO_RSA(impl, type, output) DO_type_specific_keypair(impl, type, output) + +#define DO_DH_selection_mask DO_type_specific_params_selection_mask +#define DO_DH(impl, type, output) DO_type_specific_params(impl, type, output) + +#define DO_DHX_selection_mask DO_type_specific_params_selection_mask +#define DO_DHX(impl, type, output) DO_type_specific_params(impl, type, output) + +#define DO_DSA_selection_mask DO_type_specific_selection_mask +#define DO_DSA(impl, type, output) DO_type_specific(impl, type, output) + +#define DO_EC_selection_mask DO_type_specific_no_pub_selection_mask +#define DO_EC(impl, type, output) DO_type_specific_no_pub(impl, type, output) + +#define DO_SM2_selection_mask DO_type_specific_no_pub_selection_mask +#define DO_SM2(impl, type, output) DO_type_specific_no_pub(impl, type, output) + +/* PKCS#1 defines a structure for RSA private and public keys */ +#define DO_PKCS1_selection_mask DO_RSA_selection_mask +#define DO_PKCS1(impl, type, output) DO_RSA(impl, type, output) + +/* PKCS#3 defines a structure for DH parameters */ +#define DO_PKCS3_selection_mask DO_DH_selection_mask +#define DO_PKCS3(impl, type, output) DO_DH(impl, type, output) +/* X9.42 defines a structure for DHx parameters */ +#define DO_X9_42_selection_mask DO_DHX_selection_mask +#define DO_X9_42(impl, type, output) DO_DHX(impl, type, output) + +/* X9.62 defines a structure for EC keys and parameters */ +#define DO_X9_62_selection_mask DO_EC_selection_mask +#define DO_X9_62(impl, type, output) DO_EC(impl, type, output) + +/* + * MAKE_ENCODER is the single driver for creating OSSL_DISPATCH tables. + * It takes the following arguments: + * + * impl This is the key type name that's being implemented. + * type This is the type name for the set of functions that implement + * the key type. For example, ed25519, ed448, x25519 and x448 + * are all implemented with the exact same set of functions. + * kind What kind of support to implement. These translate into + * the DO_##kind macros above. + * output The output type to implement. may be der or pem. + * + * The resulting OSSL_DISPATCH array gets the following name (expressed in + * C preprocessor terms) from those arguments: + * + * ossl_##impl##_to_##kind##_##output##_encoder_functions + */ +#define MAKE_ENCODER(impl, type, kind, output) \ + static OSSL_FUNC_encoder_import_object_fn \ + impl##_to_##kind##_##output##_import_object; \ + static OSSL_FUNC_encoder_free_object_fn \ + impl##_to_##kind##_##output##_free_object; \ + static OSSL_FUNC_encoder_encode_fn \ + impl##_to_##kind##_##output##_encode; \ + \ + static void * \ + impl##_to_##kind##_##output##_import_object(void *vctx, int selection, \ + const OSSL_PARAM params[]) \ + { \ + KEY2ANY_CTX *ctx = vctx; \ + \ + return ossl_prov_import_key(ossl_##impl##_keymgmt_functions, \ + ctx->provctx, selection, params); \ + } \ + static void impl##_to_##kind##_##output##_free_object(void *key) \ + { \ + ossl_prov_free_key(ossl_##impl##_keymgmt_functions, key); \ + } \ + static int impl##_to_##kind##_##output##_does_selection(void *ctx, \ + int selection) \ + { \ + return key2any_check_selection(selection, \ + DO_##kind##_selection_mask); \ + } \ + static int \ + impl##_to_##kind##_##output##_encode(void *ctx, OSSL_CORE_BIO *cout, \ + const void *key, \ + const OSSL_PARAM key_abstract[], \ + int selection, \ + OSSL_PASSPHRASE_CALLBACK *cb, \ + void *cbarg) \ + { \ + /* We don't deal with abstract objects */ \ + if (key_abstract != NULL) { \ + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT); \ + return 0; \ + } \ + DO_##kind(impl, type, output) \ + \ + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT); \ + return 0; \ + } \ + const OSSL_DISPATCH \ + ossl_##impl##_to_##kind##_##output##_encoder_functions[] = { \ + { OSSL_FUNC_ENCODER_NEWCTX, \ + (void (*)(void))key2any_newctx }, \ + { OSSL_FUNC_ENCODER_FREECTX, \ + (void (*)(void))key2any_freectx }, \ + { OSSL_FUNC_ENCODER_SETTABLE_CTX_PARAMS, \ + (void (*)(void))key2any_settable_ctx_params }, \ + { OSSL_FUNC_ENCODER_SET_CTX_PARAMS, \ + (void (*)(void))key2any_set_ctx_params }, \ + { OSSL_FUNC_ENCODER_DOES_SELECTION, \ + (void (*)(void))impl##_to_##kind##_##output##_does_selection }, \ + { OSSL_FUNC_ENCODER_IMPORT_OBJECT, \ + (void (*)(void))impl##_to_##kind##_##output##_import_object }, \ + { OSSL_FUNC_ENCODER_FREE_OBJECT, \ + (void (*)(void))impl##_to_##kind##_##output##_free_object }, \ + { OSSL_FUNC_ENCODER_ENCODE, \ + (void (*)(void))impl##_to_##kind##_##output##_encode }, \ + OSSL_DISPATCH_END \ + } + +/* + * Replacements for i2d_{TYPE}PrivateKey, i2d_{TYPE}PublicKey, + * i2d_{TYPE}params, as they exist. + */ +MAKE_ENCODER(rsa, rsa, type_specific_keypair, der); +#ifndef OPENSSL_NO_DH +MAKE_ENCODER(dh, dh, type_specific_params, der); +MAKE_ENCODER(dhx, dh, type_specific_params, der); +#endif +#ifndef OPENSSL_NO_DSA +MAKE_ENCODER(dsa, dsa, type_specific, der); +#endif +#ifndef OPENSSL_NO_EC +MAKE_ENCODER(ec, ec, type_specific_no_pub, der); +# ifndef OPENSSL_NO_SM2 +MAKE_ENCODER(sm2, ec, type_specific_no_pub, der); +# endif +#endif + +/* + * Replacements for PEM_write_bio_{TYPE}PrivateKey, + * PEM_write_bio_{TYPE}PublicKey, PEM_write_bio_{TYPE}params, as they exist. + */ +MAKE_ENCODER(rsa, rsa, type_specific_keypair, pem); +#ifndef OPENSSL_NO_DH +MAKE_ENCODER(dh, dh, type_specific_params, pem); +MAKE_ENCODER(dhx, dh, type_specific_params, pem); +#endif +#ifndef OPENSSL_NO_DSA +MAKE_ENCODER(dsa, dsa, type_specific, pem); +#endif +#ifndef OPENSSL_NO_EC +MAKE_ENCODER(ec, ec, type_specific_no_pub, pem); +# ifndef OPENSSL_NO_SM2 +MAKE_ENCODER(sm2, ec, type_specific_no_pub, pem); +# endif +#endif + +/* + * PKCS#8 and SubjectPublicKeyInfo support. This may duplicate some of the + * implementations specified above, but are more specific. + * The SubjectPublicKeyInfo implementations also replace the + * PEM_write_bio_{TYPE}_PUBKEY functions. + * For PEM, these are expected to be used by PEM_write_bio_PrivateKey(), + * PEM_write_bio_PUBKEY() and PEM_write_bio_Parameters(). + */ +MAKE_ENCODER(rsa, rsa, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(rsa, rsa, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(rsa, rsa, PrivateKeyInfo, der); +MAKE_ENCODER(rsa, rsa, PrivateKeyInfo, pem); +MAKE_ENCODER(rsa, rsa, SubjectPublicKeyInfo, der); +MAKE_ENCODER(rsa, rsa, SubjectPublicKeyInfo, pem); +MAKE_ENCODER(rsapss, rsa, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(rsapss, rsa, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(rsapss, rsa, PrivateKeyInfo, der); +MAKE_ENCODER(rsapss, rsa, PrivateKeyInfo, pem); +MAKE_ENCODER(rsapss, rsa, SubjectPublicKeyInfo, der); +MAKE_ENCODER(rsapss, rsa, SubjectPublicKeyInfo, pem); +#ifndef OPENSSL_NO_DH +MAKE_ENCODER(dh, dh, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(dh, dh, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(dh, dh, PrivateKeyInfo, der); +MAKE_ENCODER(dh, dh, PrivateKeyInfo, pem); +MAKE_ENCODER(dh, dh, SubjectPublicKeyInfo, der); +MAKE_ENCODER(dh, dh, SubjectPublicKeyInfo, pem); +MAKE_ENCODER(dhx, dh, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(dhx, dh, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(dhx, dh, PrivateKeyInfo, der); +MAKE_ENCODER(dhx, dh, PrivateKeyInfo, pem); +MAKE_ENCODER(dhx, dh, SubjectPublicKeyInfo, der); +MAKE_ENCODER(dhx, dh, SubjectPublicKeyInfo, pem); +#endif +#ifndef OPENSSL_NO_DSA +MAKE_ENCODER(dsa, dsa, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(dsa, dsa, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(dsa, dsa, PrivateKeyInfo, der); +MAKE_ENCODER(dsa, dsa, PrivateKeyInfo, pem); +MAKE_ENCODER(dsa, dsa, SubjectPublicKeyInfo, der); +MAKE_ENCODER(dsa, dsa, SubjectPublicKeyInfo, pem); +#endif +#ifndef OPENSSL_NO_EC +MAKE_ENCODER(ec, ec, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(ec, ec, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(ec, ec, PrivateKeyInfo, der); +MAKE_ENCODER(ec, ec, PrivateKeyInfo, pem); +MAKE_ENCODER(ec, ec, SubjectPublicKeyInfo, der); +MAKE_ENCODER(ec, ec, SubjectPublicKeyInfo, pem); +# ifndef OPENSSL_NO_SM2 +MAKE_ENCODER(sm2, ec, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(sm2, ec, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(sm2, ec, PrivateKeyInfo, der); +MAKE_ENCODER(sm2, ec, PrivateKeyInfo, pem); +MAKE_ENCODER(sm2, ec, SubjectPublicKeyInfo, der); +MAKE_ENCODER(sm2, ec, SubjectPublicKeyInfo, pem); +# endif +# ifndef OPENSSL_NO_ECX +MAKE_ENCODER(ed25519, ecx, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(ed25519, ecx, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(ed25519, ecx, PrivateKeyInfo, der); +MAKE_ENCODER(ed25519, ecx, PrivateKeyInfo, pem); +MAKE_ENCODER(ed25519, ecx, SubjectPublicKeyInfo, der); +MAKE_ENCODER(ed25519, ecx, SubjectPublicKeyInfo, pem); +MAKE_ENCODER(ed448, ecx, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(ed448, ecx, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(ed448, ecx, PrivateKeyInfo, der); +MAKE_ENCODER(ed448, ecx, PrivateKeyInfo, pem); +MAKE_ENCODER(ed448, ecx, SubjectPublicKeyInfo, der); +MAKE_ENCODER(ed448, ecx, SubjectPublicKeyInfo, pem); +MAKE_ENCODER(x25519, ecx, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(x25519, ecx, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(x25519, ecx, PrivateKeyInfo, der); +MAKE_ENCODER(x25519, ecx, PrivateKeyInfo, pem); +MAKE_ENCODER(x25519, ecx, SubjectPublicKeyInfo, der); +MAKE_ENCODER(x25519, ecx, SubjectPublicKeyInfo, pem); +MAKE_ENCODER(x448, ecx, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(x448, ecx, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(x448, ecx, PrivateKeyInfo, der); +MAKE_ENCODER(x448, ecx, PrivateKeyInfo, pem); +MAKE_ENCODER(x448, ecx, SubjectPublicKeyInfo, der); +MAKE_ENCODER(x448, ecx, SubjectPublicKeyInfo, pem); +# endif +#endif +#ifndef OPENSSL_NO_SLH_DSA +MAKE_ENCODER(slh_dsa_sha2_128s, slh_dsa, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(slh_dsa_sha2_128f, slh_dsa, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(slh_dsa_sha2_192s, slh_dsa, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(slh_dsa_sha2_192f, slh_dsa, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(slh_dsa_sha2_256s, slh_dsa, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(slh_dsa_sha2_256f, slh_dsa, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(slh_dsa_sha2_128s, slh_dsa, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(slh_dsa_sha2_128f, slh_dsa, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(slh_dsa_sha2_192s, slh_dsa, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(slh_dsa_sha2_192f, slh_dsa, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(slh_dsa_sha2_256s, slh_dsa, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(slh_dsa_sha2_256f, slh_dsa, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(slh_dsa_shake_128s, slh_dsa, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(slh_dsa_shake_128f, slh_dsa, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(slh_dsa_shake_192s, slh_dsa, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(slh_dsa_shake_192f, slh_dsa, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(slh_dsa_shake_256s, slh_dsa, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(slh_dsa_shake_256f, slh_dsa, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(slh_dsa_shake_128s, slh_dsa, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(slh_dsa_shake_128f, slh_dsa, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(slh_dsa_shake_192s, slh_dsa, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(slh_dsa_shake_192f, slh_dsa, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(slh_dsa_shake_256s, slh_dsa, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(slh_dsa_shake_256f, slh_dsa, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(slh_dsa_sha2_128s, slh_dsa, PrivateKeyInfo, der); +MAKE_ENCODER(slh_dsa_sha2_128f, slh_dsa, PrivateKeyInfo, der); +MAKE_ENCODER(slh_dsa_sha2_192s, slh_dsa, PrivateKeyInfo, der); +MAKE_ENCODER(slh_dsa_sha2_192f, slh_dsa, PrivateKeyInfo, der); +MAKE_ENCODER(slh_dsa_sha2_256s, slh_dsa, PrivateKeyInfo, der); +MAKE_ENCODER(slh_dsa_sha2_256f, slh_dsa, PrivateKeyInfo, der); +MAKE_ENCODER(slh_dsa_sha2_128s, slh_dsa, PrivateKeyInfo, pem); +MAKE_ENCODER(slh_dsa_sha2_128f, slh_dsa, PrivateKeyInfo, pem); +MAKE_ENCODER(slh_dsa_sha2_192s, slh_dsa, PrivateKeyInfo, pem); +MAKE_ENCODER(slh_dsa_sha2_192f, slh_dsa, PrivateKeyInfo, pem); +MAKE_ENCODER(slh_dsa_sha2_256s, slh_dsa, PrivateKeyInfo, pem); +MAKE_ENCODER(slh_dsa_sha2_256f, slh_dsa, PrivateKeyInfo, pem); +MAKE_ENCODER(slh_dsa_shake_128s, slh_dsa, PrivateKeyInfo, der); +MAKE_ENCODER(slh_dsa_shake_128f, slh_dsa, PrivateKeyInfo, der); +MAKE_ENCODER(slh_dsa_shake_192s, slh_dsa, PrivateKeyInfo, der); +MAKE_ENCODER(slh_dsa_shake_192f, slh_dsa, PrivateKeyInfo, der); +MAKE_ENCODER(slh_dsa_shake_256s, slh_dsa, PrivateKeyInfo, der); +MAKE_ENCODER(slh_dsa_shake_256f, slh_dsa, PrivateKeyInfo, der); +MAKE_ENCODER(slh_dsa_shake_128s, slh_dsa, PrivateKeyInfo, pem); +MAKE_ENCODER(slh_dsa_shake_128f, slh_dsa, PrivateKeyInfo, pem); +MAKE_ENCODER(slh_dsa_shake_192s, slh_dsa, PrivateKeyInfo, pem); +MAKE_ENCODER(slh_dsa_shake_192f, slh_dsa, PrivateKeyInfo, pem); +MAKE_ENCODER(slh_dsa_shake_256s, slh_dsa, PrivateKeyInfo, pem); +MAKE_ENCODER(slh_dsa_shake_256f, slh_dsa, PrivateKeyInfo, pem); +MAKE_ENCODER(slh_dsa_sha2_128s, slh_dsa, SubjectPublicKeyInfo, der); +MAKE_ENCODER(slh_dsa_sha2_128f, slh_dsa, SubjectPublicKeyInfo, der); +MAKE_ENCODER(slh_dsa_sha2_192s, slh_dsa, SubjectPublicKeyInfo, der); +MAKE_ENCODER(slh_dsa_sha2_192f, slh_dsa, SubjectPublicKeyInfo, der); +MAKE_ENCODER(slh_dsa_sha2_256s, slh_dsa, SubjectPublicKeyInfo, der); +MAKE_ENCODER(slh_dsa_sha2_256f, slh_dsa, SubjectPublicKeyInfo, der); +MAKE_ENCODER(slh_dsa_sha2_128s, slh_dsa, SubjectPublicKeyInfo, pem); +MAKE_ENCODER(slh_dsa_sha2_128f, slh_dsa, SubjectPublicKeyInfo, pem); +MAKE_ENCODER(slh_dsa_sha2_192s, slh_dsa, SubjectPublicKeyInfo, pem); +MAKE_ENCODER(slh_dsa_sha2_192f, slh_dsa, SubjectPublicKeyInfo, pem); +MAKE_ENCODER(slh_dsa_sha2_256s, slh_dsa, SubjectPublicKeyInfo, pem); +MAKE_ENCODER(slh_dsa_sha2_256f, slh_dsa, SubjectPublicKeyInfo, pem); +MAKE_ENCODER(slh_dsa_shake_128s, slh_dsa, SubjectPublicKeyInfo, der); +MAKE_ENCODER(slh_dsa_shake_128f, slh_dsa, SubjectPublicKeyInfo, der); +MAKE_ENCODER(slh_dsa_shake_192s, slh_dsa, SubjectPublicKeyInfo, der); +MAKE_ENCODER(slh_dsa_shake_192f, slh_dsa, SubjectPublicKeyInfo, der); +MAKE_ENCODER(slh_dsa_shake_256s, slh_dsa, SubjectPublicKeyInfo, der); +MAKE_ENCODER(slh_dsa_shake_256f, slh_dsa, SubjectPublicKeyInfo, der); +MAKE_ENCODER(slh_dsa_shake_128s, slh_dsa, SubjectPublicKeyInfo, pem); +MAKE_ENCODER(slh_dsa_shake_128f, slh_dsa, SubjectPublicKeyInfo, pem); +MAKE_ENCODER(slh_dsa_shake_192s, slh_dsa, SubjectPublicKeyInfo, pem); +MAKE_ENCODER(slh_dsa_shake_192f, slh_dsa, SubjectPublicKeyInfo, pem); +MAKE_ENCODER(slh_dsa_shake_256s, slh_dsa, SubjectPublicKeyInfo, pem); +MAKE_ENCODER(slh_dsa_shake_256f, slh_dsa, SubjectPublicKeyInfo, pem); +#endif /* OPENSSL_NO_SLH_DSA */ + +#ifndef OPENSSL_NO_ML_KEM +MAKE_ENCODER(ml_kem_512, ml_kem, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(ml_kem_512, ml_kem, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(ml_kem_512, ml_kem, PrivateKeyInfo, der); +MAKE_ENCODER(ml_kem_512, ml_kem, PrivateKeyInfo, pem); +MAKE_ENCODER(ml_kem_512, ml_kem, SubjectPublicKeyInfo, der); +MAKE_ENCODER(ml_kem_512, ml_kem, SubjectPublicKeyInfo, pem); + +MAKE_ENCODER(ml_kem_768, ml_kem, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(ml_kem_768, ml_kem, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(ml_kem_768, ml_kem, PrivateKeyInfo, der); +MAKE_ENCODER(ml_kem_768, ml_kem, PrivateKeyInfo, pem); +MAKE_ENCODER(ml_kem_768, ml_kem, SubjectPublicKeyInfo, der); +MAKE_ENCODER(ml_kem_768, ml_kem, SubjectPublicKeyInfo, pem); + +MAKE_ENCODER(ml_kem_1024, ml_kem, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(ml_kem_1024, ml_kem, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(ml_kem_1024, ml_kem, PrivateKeyInfo, der); +MAKE_ENCODER(ml_kem_1024, ml_kem, PrivateKeyInfo, pem); +MAKE_ENCODER(ml_kem_1024, ml_kem, SubjectPublicKeyInfo, der); +MAKE_ENCODER(ml_kem_1024, ml_kem, SubjectPublicKeyInfo, pem); +#endif + +/* + * Support for key type specific output formats. Not all key types have + * this, we only aim to duplicate what is available in 1.1.1 as + * i2d_TYPEPrivateKey(), i2d_TYPEPublicKey() and i2d_TYPEparams(). + * For example, there are no publicly available i2d_ function for + * ED25519, ED448, X25519 or X448, and they therefore only have PKCS#8 + * and SubjectPublicKeyInfo implementations as implemented above. + */ +MAKE_ENCODER(rsa, rsa, RSA, der); +MAKE_ENCODER(rsa, rsa, RSA, pem); +#ifndef OPENSSL_NO_DH +MAKE_ENCODER(dh, dh, DH, der); +MAKE_ENCODER(dh, dh, DH, pem); +MAKE_ENCODER(dhx, dh, DHX, der); +MAKE_ENCODER(dhx, dh, DHX, pem); +#endif +#ifndef OPENSSL_NO_DSA +MAKE_ENCODER(dsa, dsa, DSA, der); +MAKE_ENCODER(dsa, dsa, DSA, pem); +#endif +#ifndef OPENSSL_NO_EC +MAKE_ENCODER(ec, ec, EC, der); +MAKE_ENCODER(ec, ec, EC, pem); +# ifndef OPENSSL_NO_SM2 +MAKE_ENCODER(sm2, ec, SM2, der); +MAKE_ENCODER(sm2, ec, SM2, pem); +# endif +#endif + +/* Convenience structure names */ +MAKE_ENCODER(rsa, rsa, PKCS1, der); +MAKE_ENCODER(rsa, rsa, PKCS1, pem); +MAKE_ENCODER(rsapss, rsa, PKCS1, der); +MAKE_ENCODER(rsapss, rsa, PKCS1, pem); +#ifndef OPENSSL_NO_DH +MAKE_ENCODER(dh, dh, PKCS3, der); /* parameters only */ +MAKE_ENCODER(dh, dh, PKCS3, pem); /* parameters only */ +MAKE_ENCODER(dhx, dh, X9_42, der); /* parameters only */ +MAKE_ENCODER(dhx, dh, X9_42, pem); /* parameters only */ +#endif +#ifndef OPENSSL_NO_EC +MAKE_ENCODER(ec, ec, X9_62, der); +MAKE_ENCODER(ec, ec, X9_62, pem); +#endif + +#ifndef OPENSSL_NO_ML_DSA +MAKE_ENCODER(ml_dsa_44, ml_dsa, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(ml_dsa_44, ml_dsa, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(ml_dsa_44, ml_dsa, PrivateKeyInfo, der); +MAKE_ENCODER(ml_dsa_44, ml_dsa, PrivateKeyInfo, pem); +MAKE_ENCODER(ml_dsa_44, ml_dsa, SubjectPublicKeyInfo, der); +MAKE_ENCODER(ml_dsa_44, ml_dsa, SubjectPublicKeyInfo, pem); + +MAKE_ENCODER(ml_dsa_65, ml_dsa, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(ml_dsa_65, ml_dsa, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(ml_dsa_65, ml_dsa, PrivateKeyInfo, der); +MAKE_ENCODER(ml_dsa_65, ml_dsa, PrivateKeyInfo, pem); +MAKE_ENCODER(ml_dsa_65, ml_dsa, SubjectPublicKeyInfo, der); +MAKE_ENCODER(ml_dsa_65, ml_dsa, SubjectPublicKeyInfo, pem); + +MAKE_ENCODER(ml_dsa_87, ml_dsa, EncryptedPrivateKeyInfo, der); +MAKE_ENCODER(ml_dsa_87, ml_dsa, EncryptedPrivateKeyInfo, pem); +MAKE_ENCODER(ml_dsa_87, ml_dsa, PrivateKeyInfo, der); +MAKE_ENCODER(ml_dsa_87, ml_dsa, PrivateKeyInfo, pem); +MAKE_ENCODER(ml_dsa_87, ml_dsa, SubjectPublicKeyInfo, der); +MAKE_ENCODER(ml_dsa_87, ml_dsa, SubjectPublicKeyInfo, pem); +#endif /* OPENSSL_NO_ML_DSA */ diff --git a/crypto/openssl/providers/implementations/encode_decode/encode_key2blob.c b/crypto/openssl/providers/implementations/encode_decode/encode_key2blob.c new file mode 100644 index 000000000000..29e72faa63de --- /dev/null +++ b/crypto/openssl/providers/implementations/encode_decode/encode_key2blob.c @@ -0,0 +1,179 @@ +/* + * Copyright 2021-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 + */ + +/* + * Low level APIs are deprecated for public use, but still ok for internal use. + */ +#include "internal/deprecated.h" + +#include <openssl/core.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/params.h> +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/ec.h> +#include "internal/passphrase.h" +#include "internal/nelem.h" +#include "prov/implementations.h" +#include "prov/bio.h" +#include "prov/provider_ctx.h" +#include "endecoder_local.h" + +static int write_blob(void *provctx, OSSL_CORE_BIO *cout, + void *data, int len) +{ + BIO *out = ossl_bio_new_from_core_bio(provctx, cout); + int ret; + + if (out == NULL) + return 0; + ret = BIO_write(out, data, len); + + BIO_free(out); + return ret; +} + +static OSSL_FUNC_encoder_newctx_fn key2blob_newctx; +static OSSL_FUNC_encoder_freectx_fn key2blob_freectx; + +static void *key2blob_newctx(void *provctx) +{ + return provctx; +} + +static void key2blob_freectx(void *vctx) +{ +} + +static int key2blob_check_selection(int selection, int selection_mask) +{ + /* + * The selections are kinda sorta "levels", i.e. each selection given + * here is assumed to include those following. + */ + int checks[] = { + OSSL_KEYMGMT_SELECT_PRIVATE_KEY, + OSSL_KEYMGMT_SELECT_PUBLIC_KEY, + OSSL_KEYMGMT_SELECT_ALL_PARAMETERS + }; + size_t i; + + /* The decoder implementations made here support guessing */ + if (selection == 0) + return 1; + + for (i = 0; i < OSSL_NELEM(checks); i++) { + int check1 = (selection & checks[i]) != 0; + int check2 = (selection_mask & checks[i]) != 0; + + /* + * If the caller asked for the currently checked bit(s), return + * whether the decoder description says it's supported. + */ + if (check1) + return check2; + } + + /* This should be dead code, but just to be safe... */ + return 0; +} + +static int key2blob_encode(void *vctx, const void *key, int selection, + OSSL_CORE_BIO *cout) +{ + int pubkey_len = 0, ok = 0; + unsigned char *pubkey = NULL; + + pubkey_len = i2o_ECPublicKey(key, &pubkey); + if (pubkey_len > 0 && pubkey != NULL) + ok = write_blob(vctx, cout, pubkey, pubkey_len); + OPENSSL_free(pubkey); + return ok; +} + +/* + * MAKE_BLOB_ENCODER() Makes an OSSL_DISPATCH table for a particular key->blob + * encoder + * + * impl: The keytype to encode + * type: The C structure type holding the key data + * selection_name: The acceptable selections. This translates into + * the macro EVP_PKEY_##selection_name. + * + * The selection is understood as a "level" rather than an exact set of + * requests from the caller. The encoder has to decide what contents fit + * the encoded format. For example, the EC public key blob will only contain + * the encoded public key itself, no matter if the selection bits include + * OSSL_KEYMGMT_SELECT_PARAMETERS or not. However, if the selection includes + * OSSL_KEYMGMT_SELECT_PRIVATE_KEY, the same encoder will simply refuse to + * cooperate, because it cannot output the private key. + * + * EVP_PKEY_##selection_name are convenience macros that combine "typical" + * OSSL_KEYMGMT_SELECT_ macros for a certain type of EVP_PKEY content. + */ +#define MAKE_BLOB_ENCODER(impl, type, selection_name) \ + static OSSL_FUNC_encoder_import_object_fn \ + impl##2blob_import_object; \ + static OSSL_FUNC_encoder_free_object_fn impl##2blob_free_object; \ + static OSSL_FUNC_encoder_does_selection_fn \ + impl##2blob_does_selection; \ + static OSSL_FUNC_encoder_encode_fn impl##2blob_encode; \ + \ + static void *impl##2blob_import_object(void *ctx, int selection, \ + const OSSL_PARAM params[]) \ + { \ + return ossl_prov_import_key(ossl_##impl##_keymgmt_functions, \ + ctx, selection, params); \ + } \ + static void impl##2blob_free_object(void *key) \ + { \ + ossl_prov_free_key(ossl_##impl##_keymgmt_functions, key); \ + } \ + static int impl##2blob_does_selection(void *ctx, int selection) \ + { \ + return key2blob_check_selection(selection, \ + EVP_PKEY_##selection_name); \ + } \ + static int impl##2blob_encode(void *vctx, OSSL_CORE_BIO *cout, \ + const void *key, \ + const OSSL_PARAM key_abstract[], \ + int selection, \ + OSSL_PASSPHRASE_CALLBACK *cb, \ + void *cbarg) \ + { \ + /* We don't deal with abstract objects */ \ + if (key_abstract != NULL) { \ + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT); \ + return 0; \ + } \ + return key2blob_encode(vctx, key, selection, cout); \ + } \ + const OSSL_DISPATCH ossl_##impl##_to_blob_encoder_functions[] = { \ + { OSSL_FUNC_ENCODER_NEWCTX, \ + (void (*)(void))key2blob_newctx }, \ + { OSSL_FUNC_ENCODER_FREECTX, \ + (void (*)(void))key2blob_freectx }, \ + { OSSL_FUNC_ENCODER_DOES_SELECTION, \ + (void (*)(void))impl##2blob_does_selection }, \ + { OSSL_FUNC_ENCODER_IMPORT_OBJECT, \ + (void (*)(void))impl##2blob_import_object }, \ + { OSSL_FUNC_ENCODER_FREE_OBJECT, \ + (void (*)(void))impl##2blob_free_object }, \ + { OSSL_FUNC_ENCODER_ENCODE, \ + (void (*)(void))impl##2blob_encode }, \ + OSSL_DISPATCH_END \ + } + +#ifndef OPENSSL_NO_EC +MAKE_BLOB_ENCODER(ec, ec, PUBLIC_KEY); +# ifndef OPENSSL_NO_SM2 +MAKE_BLOB_ENCODER(sm2, ec, PUBLIC_KEY); +# endif +#endif diff --git a/crypto/openssl/providers/implementations/encode_decode/encode_key2ms.c b/crypto/openssl/providers/implementations/encode_decode/encode_key2ms.c new file mode 100644 index 000000000000..1f21a5129615 --- /dev/null +++ b/crypto/openssl/providers/implementations/encode_decode/encode_key2ms.c @@ -0,0 +1,234 @@ +/* + * Copyright 2020-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 + */ + +/* + * Low level APIs are deprecated for public use, but still ok for internal use. + */ +#include "internal/deprecated.h" + +#include <string.h> +#include <openssl/core.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/params.h> +#include <openssl/err.h> +#include <openssl/pem.h> /* Functions for writing MSBLOB and PVK */ +#include <openssl/dsa.h> +#include "internal/passphrase.h" +#include "crypto/rsa.h" +#include "prov/implementations.h" +#include "prov/bio.h" +#include "prov/provider_ctx.h" +#include "endecoder_local.h" + +struct key2ms_ctx_st { + PROV_CTX *provctx; + + int pvk_encr_level; + + struct ossl_passphrase_data_st pwdata; +}; + +static int write_msblob(struct key2ms_ctx_st *ctx, OSSL_CORE_BIO *cout, + EVP_PKEY *pkey, int ispub) +{ + BIO *out = ossl_bio_new_from_core_bio(ctx->provctx, cout); + int ret; + + if (out == NULL) + return 0; + ret = ispub ? i2b_PublicKey_bio(out, pkey) : i2b_PrivateKey_bio(out, pkey); + + BIO_free(out); + return ret; +} + +static int write_pvk(struct key2ms_ctx_st *ctx, OSSL_CORE_BIO *cout, + EVP_PKEY *pkey) +{ + BIO *out = NULL; + int ret; + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + + out = ossl_bio_new_from_core_bio(ctx->provctx, cout); + if (out == NULL) + return 0; + ret = i2b_PVK_bio_ex(out, pkey, ctx->pvk_encr_level, + ossl_pw_pvk_password, &ctx->pwdata, libctx, NULL); + BIO_free(out); + return ret; +} + +static OSSL_FUNC_encoder_freectx_fn key2ms_freectx; +static OSSL_FUNC_encoder_does_selection_fn key2ms_does_selection; + +static struct key2ms_ctx_st *key2ms_newctx(void *provctx) +{ + struct key2ms_ctx_st *ctx = OPENSSL_zalloc(sizeof(*ctx)); + + if (ctx != NULL) { + ctx->provctx = provctx; + /* This is the strongest encryption level */ + ctx->pvk_encr_level = 2; + } + return ctx; +} + +static void key2ms_freectx(void *vctx) +{ + struct key2ms_ctx_st *ctx = vctx; + + ossl_pw_clear_passphrase_data(&ctx->pwdata); + OPENSSL_free(ctx); +} + +static const OSSL_PARAM *key2pvk_settable_ctx_params(ossl_unused void *provctx) +{ + static const OSSL_PARAM settables[] = { + OSSL_PARAM_int(OSSL_ENCODER_PARAM_ENCRYPT_LEVEL, NULL), + OSSL_PARAM_END, + }; + + return settables; +} + +static int key2pvk_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + struct key2ms_ctx_st *ctx = vctx; + const OSSL_PARAM *p; + + p = OSSL_PARAM_locate_const(params, OSSL_ENCODER_PARAM_ENCRYPT_LEVEL); + if (p != NULL && !OSSL_PARAM_get_int(p, &ctx->pvk_encr_level)) + return 0; + return 1; +} + +static int key2ms_does_selection(void *vctx, int selection) +{ + return (selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0; +} + +/* + * The real EVP_PKEY_set1_TYPE() functions take a non-const key, while the key + * pointer in the encode function is a const pointer. We violate the constness + * knowingly, since we know that the key comes from the same provider, is never + * actually const, and the implied reference count change is safe. + * + * EVP_PKEY_assign() can't be used, because there's no way to clear the internal + * key using that function without freeing the existing internal key. + */ +typedef int evp_pkey_set1_fn(EVP_PKEY *, const void *key); + +static int key2msblob_encode(void *vctx, const void *key, int selection, + OSSL_CORE_BIO *cout, evp_pkey_set1_fn *set1_key, + OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg) +{ + struct key2ms_ctx_st *ctx = vctx; + int ispub = -1; + EVP_PKEY *pkey = NULL; + int ok = 0; + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + ispub = 0; + else if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) + ispub = 1; + else + return 0; /* Error */ + + if ((pkey = EVP_PKEY_new()) != NULL && set1_key(pkey, key)) + ok = write_msblob(ctx, cout, pkey, ispub); + EVP_PKEY_free(pkey); + return ok; +} + +static int key2pvk_encode(void *vctx, const void *key, int selection, + OSSL_CORE_BIO *cout, evp_pkey_set1_fn *set1_key, + OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg) +{ + struct key2ms_ctx_st *ctx = vctx; + EVP_PKEY *pkey = NULL; + int ok = 0; + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) == 0) + return 0; /* Error */ + + if ((pkey = EVP_PKEY_new()) != NULL && set1_key(pkey, key) + && (pw_cb == NULL + || ossl_pw_set_ossl_passphrase_cb(&ctx->pwdata, pw_cb, pw_cbarg))) + ok = write_pvk(ctx, cout, pkey); + EVP_PKEY_free(pkey); + return ok; +} + +#define dsa_set1 (evp_pkey_set1_fn *)EVP_PKEY_set1_DSA +#define rsa_set1 (evp_pkey_set1_fn *)EVP_PKEY_set1_RSA + +#define msblob_set_params +#define pvk_set_params \ + { OSSL_FUNC_ENCODER_SETTABLE_CTX_PARAMS, \ + (void (*)(void))key2pvk_settable_ctx_params }, \ + { OSSL_FUNC_ENCODER_SET_CTX_PARAMS, \ + (void (*)(void))key2pvk_set_ctx_params }, + +#define MAKE_MS_ENCODER(impl, output, type) \ + static OSSL_FUNC_encoder_import_object_fn \ + impl##2##output##_import_object; \ + static OSSL_FUNC_encoder_free_object_fn impl##2##output##_free_object; \ + static OSSL_FUNC_encoder_encode_fn impl##2##output##_encode; \ + \ + static void * \ + impl##2##output##_import_object(void *ctx, int selection, \ + const OSSL_PARAM params[]) \ + { \ + return ossl_prov_import_key(ossl_##impl##_keymgmt_functions, \ + ctx, selection, params); \ + } \ + static void impl##2##output##_free_object(void *key) \ + { \ + ossl_prov_free_key(ossl_##impl##_keymgmt_functions, key); \ + } \ + static int impl##2##output##_encode(void *vctx, OSSL_CORE_BIO *cout, \ + const void *key, \ + const OSSL_PARAM key_abstract[], \ + int selection, \ + OSSL_PASSPHRASE_CALLBACK *cb, \ + void *cbarg) \ + { \ + /* We don't deal with abstract objects */ \ + if (key_abstract != NULL) { \ + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT); \ + return 0; \ + } \ + return key2##output##_encode(vctx, key, selection, cout, type##_set1, \ + cb, cbarg); \ + } \ + const OSSL_DISPATCH ossl_##impl##_to_##output##_encoder_functions[] = { \ + { OSSL_FUNC_ENCODER_NEWCTX, \ + (void (*)(void))key2ms_newctx }, \ + { OSSL_FUNC_ENCODER_FREECTX, \ + (void (*)(void))key2ms_freectx }, \ + output##_set_params \ + { OSSL_FUNC_ENCODER_DOES_SELECTION, \ + (void (*)(void))key2ms_does_selection }, \ + { OSSL_FUNC_ENCODER_IMPORT_OBJECT, \ + (void (*)(void))impl##2##output##_import_object }, \ + { OSSL_FUNC_ENCODER_FREE_OBJECT, \ + (void (*)(void))impl##2##output##_free_object }, \ + { OSSL_FUNC_ENCODER_ENCODE, \ + (void (*)(void))impl##2##output##_encode }, \ + OSSL_DISPATCH_END \ + } + +#ifndef OPENSSL_NO_DSA +MAKE_MS_ENCODER(dsa, pvk, dsa); +MAKE_MS_ENCODER(dsa, msblob, dsa); +#endif + +MAKE_MS_ENCODER(rsa, pvk, rsa); +MAKE_MS_ENCODER(rsa, msblob, rsa); diff --git a/crypto/openssl/providers/implementations/encode_decode/encode_key2text.c b/crypto/openssl/providers/implementations/encode_decode/encode_key2text.c new file mode 100644 index 000000000000..7a564807326f --- /dev/null +++ b/crypto/openssl/providers/implementations/encode_decode/encode_key2text.c @@ -0,0 +1,737 @@ +/* + * Copyright 2020-2025 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 + */ + +/* + * Low level APIs are deprecated for public use, but still ok for internal use. + */ +#include "internal/deprecated.h" + +#include <openssl/core.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/bn.h> +#include <openssl/err.h> +#include <openssl/safestack.h> +#include <openssl/proverr.h> +#include "crypto/dh.h" /* ossl_dh_get0_params() */ +#include "crypto/dsa.h" /* ossl_dsa_get0_params() */ +#include "crypto/ec.h" /* ossl_ec_key_get_libctx */ +#include "crypto/ecx.h" /* ECX_KEY, etc... */ +#include "crypto/ml_kem.h" /* ML_KEM_KEY, etc... */ +#include "crypto/rsa.h" /* RSA_PSS_PARAMS_30, etc... */ +#include "crypto/ml_dsa.h" +#include "crypto/slh_dsa.h" +#include "prov/bio.h" +#include "prov/implementations.h" +#include "internal/encoder.h" +#include "endecoder_local.h" +#include "ml_dsa_codecs.h" +#include "ml_kem_codecs.h" + +DEFINE_SPECIAL_STACK_OF_CONST(BIGNUM_const, BIGNUM) + +/* ---------------------------------------------------------------------- */ + +#ifndef OPENSSL_NO_DH +static int dh_to_text(BIO *out, const void *key, int selection) +{ + const DH *dh = key; + const char *type_label = NULL; + const BIGNUM *priv_key = NULL, *pub_key = NULL; + const FFC_PARAMS *params = NULL; + const BIGNUM *p = NULL; + long length; + + if (out == NULL || dh == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + type_label = "DH Private-Key"; + else if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) + type_label = "DH Public-Key"; + else if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) + type_label = "DH Parameters"; + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) { + priv_key = DH_get0_priv_key(dh); + if (priv_key == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PRIVATE_KEY); + return 0; + } + } + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) { + pub_key = DH_get0_pub_key(dh); + if (pub_key == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY); + return 0; + } + } + if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) { + params = ossl_dh_get0_params((DH *)dh); + if (params == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_PARAMETERS); + return 0; + } + } + + p = DH_get0_p(dh); + if (p == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY); + return 0; + } + + if (BIO_printf(out, "%s: (%d bit)\n", type_label, BN_num_bits(p)) <= 0) + return 0; + if (priv_key != NULL + && !ossl_bio_print_labeled_bignum(out, "private-key:", priv_key)) + return 0; + if (pub_key != NULL + && !ossl_bio_print_labeled_bignum(out, "public-key:", pub_key)) + return 0; + if (params != NULL + && !ossl_bio_print_ffc_params(out, params)) + return 0; + length = DH_get_length(dh); + if (length > 0 + && BIO_printf(out, "recommended-private-length: %ld bits\n", + length) <= 0) + return 0; + + return 1; +} +#endif + +/* ---------------------------------------------------------------------- */ + +#ifndef OPENSSL_NO_DSA +static int dsa_to_text(BIO *out, const void *key, int selection) +{ + const DSA *dsa = key; + const char *type_label = NULL; + const BIGNUM *priv_key = NULL, *pub_key = NULL; + const FFC_PARAMS *params = NULL; + const BIGNUM *p = NULL; + + if (out == NULL || dsa == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + type_label = "Private-Key"; + else if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) + type_label = "Public-Key"; + else if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) + type_label = "DSA-Parameters"; + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) { + priv_key = DSA_get0_priv_key(dsa); + if (priv_key == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PRIVATE_KEY); + return 0; + } + } + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) { + pub_key = DSA_get0_pub_key(dsa); + if (pub_key == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY); + return 0; + } + } + if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) { + params = ossl_dsa_get0_params((DSA *)dsa); + if (params == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_PARAMETERS); + return 0; + } + } + + p = DSA_get0_p(dsa); + if (p == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY); + return 0; + } + + if (BIO_printf(out, "%s: (%d bit)\n", type_label, BN_num_bits(p)) <= 0) + return 0; + if (priv_key != NULL + && !ossl_bio_print_labeled_bignum(out, "priv:", priv_key)) + return 0; + if (pub_key != NULL + && !ossl_bio_print_labeled_bignum(out, "pub: ", pub_key)) + return 0; + if (params != NULL + && !ossl_bio_print_ffc_params(out, params)) + return 0; + + return 1; +} +#endif + +/* ---------------------------------------------------------------------- */ + +#ifndef OPENSSL_NO_EC +static int ec_param_explicit_curve_to_text(BIO *out, const EC_GROUP *group, + BN_CTX *ctx) +{ + const char *plabel = "Prime:"; + BIGNUM *p = NULL, *a = NULL, *b = NULL; + + p = BN_CTX_get(ctx); + a = BN_CTX_get(ctx); + b = BN_CTX_get(ctx); + if (b == NULL + || !EC_GROUP_get_curve(group, p, a, b, ctx)) + return 0; + + if (EC_GROUP_get_field_type(group) == NID_X9_62_characteristic_two_field) { + int basis_type = EC_GROUP_get_basis_type(group); + + /* print the 'short name' of the base type OID */ + if (basis_type == NID_undef + || BIO_printf(out, "Basis Type: %s\n", OBJ_nid2sn(basis_type)) <= 0) + return 0; + plabel = "Polynomial:"; + } + return ossl_bio_print_labeled_bignum(out, plabel, p) + && ossl_bio_print_labeled_bignum(out, "A: ", a) + && ossl_bio_print_labeled_bignum(out, "B: ", b); +} + +static int ec_param_explicit_gen_to_text(BIO *out, const EC_GROUP *group, + BN_CTX *ctx) +{ + int ret; + size_t buflen; + point_conversion_form_t form; + const EC_POINT *point = NULL; + const char *glabel = NULL; + unsigned char *buf = NULL; + + form = EC_GROUP_get_point_conversion_form(group); + point = EC_GROUP_get0_generator(group); + + if (point == NULL) + return 0; + + switch (form) { + case POINT_CONVERSION_COMPRESSED: + glabel = "Generator (compressed):"; + break; + case POINT_CONVERSION_UNCOMPRESSED: + glabel = "Generator (uncompressed):"; + break; + case POINT_CONVERSION_HYBRID: + glabel = "Generator (hybrid):"; + break; + default: + return 0; + } + + buflen = EC_POINT_point2buf(group, point, form, &buf, ctx); + if (buflen == 0) + return 0; + + ret = ossl_bio_print_labeled_buf(out, glabel, buf, buflen); + OPENSSL_clear_free(buf, buflen); + return ret; +} + +/* Print explicit parameters */ +static int ec_param_explicit_to_text(BIO *out, const EC_GROUP *group, + OSSL_LIB_CTX *libctx) +{ + int ret = 0, tmp_nid; + BN_CTX *ctx = NULL; + const BIGNUM *order = NULL, *cofactor = NULL; + const unsigned char *seed; + size_t seed_len = 0; + + ctx = BN_CTX_new_ex(libctx); + if (ctx == NULL) + return 0; + BN_CTX_start(ctx); + + tmp_nid = EC_GROUP_get_field_type(group); + order = EC_GROUP_get0_order(group); + if (order == NULL) + goto err; + + seed = EC_GROUP_get0_seed(group); + if (seed != NULL) + seed_len = EC_GROUP_get_seed_len(group); + cofactor = EC_GROUP_get0_cofactor(group); + + /* print the 'short name' of the field type */ + if (BIO_printf(out, "Field Type: %s\n", OBJ_nid2sn(tmp_nid)) <= 0 + || !ec_param_explicit_curve_to_text(out, group, ctx) + || !ec_param_explicit_gen_to_text(out, group, ctx) + || !ossl_bio_print_labeled_bignum(out, "Order: ", order) + || (cofactor != NULL + && !ossl_bio_print_labeled_bignum(out, "Cofactor: ", cofactor)) + || (seed != NULL + && !ossl_bio_print_labeled_buf(out, "Seed:", seed, seed_len))) + goto err; + ret = 1; +err: + BN_CTX_end(ctx); + BN_CTX_free(ctx); + return ret; +} + +static int ec_param_to_text(BIO *out, const EC_GROUP *group, + OSSL_LIB_CTX *libctx) +{ + if (EC_GROUP_get_asn1_flag(group) & OPENSSL_EC_NAMED_CURVE) { + const char *curve_name; + int curve_nid = EC_GROUP_get_curve_name(group); + + /* Explicit parameters */ + if (curve_nid == NID_undef) + return 0; + + if (BIO_printf(out, "%s: %s\n", "ASN1 OID", OBJ_nid2sn(curve_nid)) <= 0) + return 0; + + curve_name = EC_curve_nid2nist(curve_nid); + return (curve_name == NULL + || BIO_printf(out, "%s: %s\n", "NIST CURVE", curve_name) > 0); + } else { + return ec_param_explicit_to_text(out, group, libctx); + } +} + +static int ec_to_text(BIO *out, const void *key, int selection) +{ + const EC_KEY *ec = key; + const char *type_label = NULL; + unsigned char *priv = NULL, *pub = NULL; + size_t priv_len = 0, pub_len = 0; + const EC_GROUP *group; + int ret = 0; + + if (out == NULL || ec == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if ((group = EC_KEY_get0_group(ec)) == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY); + return 0; + } + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + type_label = "Private-Key"; + else if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) + type_label = "Public-Key"; + else if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) + if (EC_GROUP_get_curve_name(group) != NID_sm2) + type_label = "EC-Parameters"; + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) { + const BIGNUM *priv_key = EC_KEY_get0_private_key(ec); + + if (priv_key == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PRIVATE_KEY); + goto err; + } + priv_len = EC_KEY_priv2buf(ec, &priv); + if (priv_len == 0) + goto err; + } + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) { + const EC_POINT *pub_pt = EC_KEY_get0_public_key(ec); + + if (pub_pt == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY); + goto err; + } + + pub_len = EC_KEY_key2buf(ec, EC_KEY_get_conv_form(ec), &pub, NULL); + if (pub_len == 0) + goto err; + } + + if (type_label != NULL + && BIO_printf(out, "%s: (%d bit)\n", type_label, + EC_GROUP_order_bits(group)) <= 0) + goto err; + if (priv != NULL + && !ossl_bio_print_labeled_buf(out, "priv:", priv, priv_len)) + goto err; + if (pub != NULL + && !ossl_bio_print_labeled_buf(out, "pub:", pub, pub_len)) + goto err; + if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) + ret = ec_param_to_text(out, group, ossl_ec_key_get_libctx(ec)); +err: + OPENSSL_clear_free(priv, priv_len); + OPENSSL_free(pub); + return ret; +} +#endif + +/* ---------------------------------------------------------------------- */ + +#ifndef OPENSSL_NO_ECX +static int ecx_to_text(BIO *out, const void *key, int selection) +{ + const ECX_KEY *ecx = key; + const char *type_label = NULL; + + if (out == NULL || ecx == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + switch (ecx->type) { + case ECX_KEY_TYPE_X25519: + type_label = "X25519"; + break; + case ECX_KEY_TYPE_X448: + type_label = "X448"; + break; + case ECX_KEY_TYPE_ED25519: + type_label = "ED25519"; + break; + case ECX_KEY_TYPE_ED448: + type_label = "ED448"; + break; + } + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) { + if (ecx->privkey == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PRIVATE_KEY); + return 0; + } + + if (BIO_printf(out, "%s Private-Key:\n", type_label) <= 0) + return 0; + if (!ossl_bio_print_labeled_buf(out, "priv:", ecx->privkey, ecx->keylen)) + return 0; + } else if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) { + /* ecx->pubkey is an array, not a pointer... */ + if (!ecx->haspubkey) { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY); + return 0; + } + + if (BIO_printf(out, "%s Public-Key:\n", type_label) <= 0) + return 0; + } + + if (!ossl_bio_print_labeled_buf(out, "pub:", ecx->pubkey, ecx->keylen)) + return 0; + + return 1; +} +#endif + +/* ---------------------------------------------------------------------- */ + +#ifndef OPENSSL_NO_ML_KEM +static int ml_kem_to_text(BIO *out, const void *vkey, int selection) +{ + return ossl_ml_kem_key_to_text(out, (ML_KEM_KEY *)vkey, selection); +} +#endif + +/* ---------------------------------------------------------------------- */ + +#ifndef OPENSSL_NO_SLH_DSA +static int slh_dsa_to_text(BIO *out, const void *key, int selection) +{ + return ossl_slh_dsa_key_to_text(out, (SLH_DSA_KEY *)key, selection); +} +#endif /* OPENSSL_NO_SLH_DSA */ + +static int rsa_to_text(BIO *out, const void *key, int selection) +{ + const RSA *rsa = key; + const char *type_label = "RSA key"; + const char *modulus_label = NULL; + const char *exponent_label = NULL; + const BIGNUM *rsa_d = NULL, *rsa_n = NULL, *rsa_e = NULL; + STACK_OF(BIGNUM_const) *factors = NULL; + STACK_OF(BIGNUM_const) *exps = NULL; + STACK_OF(BIGNUM_const) *coeffs = NULL; + int primes; + const RSA_PSS_PARAMS_30 *pss_params = ossl_rsa_get0_pss_params_30((RSA *)rsa); + int ret = 0; + + if (out == NULL || rsa == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER); + goto err; + } + + factors = sk_BIGNUM_const_new_null(); + exps = sk_BIGNUM_const_new_null(); + coeffs = sk_BIGNUM_const_new_null(); + + if (factors == NULL || exps == NULL || coeffs == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_CRYPTO_LIB); + goto err; + } + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) { + type_label = "Private-Key"; + modulus_label = "modulus:"; + exponent_label = "publicExponent:"; + } else if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) { + type_label = "Public-Key"; + modulus_label = "Modulus:"; + exponent_label = "Exponent:"; + } + + RSA_get0_key(rsa, &rsa_n, &rsa_e, &rsa_d); + ossl_rsa_get0_all_params((RSA *)rsa, factors, exps, coeffs); + primes = sk_BIGNUM_const_num(factors); + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) { + if (BIO_printf(out, "%s: (%d bit, %d primes)\n", + type_label, BN_num_bits(rsa_n), primes) <= 0) + goto err; + } else { + if (BIO_printf(out, "%s: (%d bit)\n", + type_label, BN_num_bits(rsa_n)) <= 0) + goto err; + } + + if (!ossl_bio_print_labeled_bignum(out, modulus_label, rsa_n)) + goto err; + if (!ossl_bio_print_labeled_bignum(out, exponent_label, rsa_e)) + goto err; + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) { + int i; + + if (!ossl_bio_print_labeled_bignum(out, "privateExponent:", rsa_d)) + goto err; + if (!ossl_bio_print_labeled_bignum(out, "prime1:", + sk_BIGNUM_const_value(factors, 0))) + goto err; + if (!ossl_bio_print_labeled_bignum(out, "prime2:", + sk_BIGNUM_const_value(factors, 1))) + goto err; + if (!ossl_bio_print_labeled_bignum(out, "exponent1:", + sk_BIGNUM_const_value(exps, 0))) + goto err; + if (!ossl_bio_print_labeled_bignum(out, "exponent2:", + sk_BIGNUM_const_value(exps, 1))) + goto err; + if (!ossl_bio_print_labeled_bignum(out, "coefficient:", + sk_BIGNUM_const_value(coeffs, 0))) + goto err; + for (i = 2; i < sk_BIGNUM_const_num(factors); i++) { + if (BIO_printf(out, "prime%d:", i + 1) <= 0) + goto err; + if (!ossl_bio_print_labeled_bignum(out, NULL, + sk_BIGNUM_const_value(factors, i))) + goto err; + if (BIO_printf(out, "exponent%d:", i + 1) <= 0) + goto err; + if (!ossl_bio_print_labeled_bignum(out, NULL, + sk_BIGNUM_const_value(exps, i))) + goto err; + if (BIO_printf(out, "coefficient%d:", i + 1) <= 0) + goto err; + if (!ossl_bio_print_labeled_bignum(out, NULL, + sk_BIGNUM_const_value(coeffs, i - 1))) + goto err; + } + } + + if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0) { + switch (RSA_test_flags(rsa, RSA_FLAG_TYPE_MASK)) { + case RSA_FLAG_TYPE_RSA: + if (!ossl_rsa_pss_params_30_is_unrestricted(pss_params)) { + if (BIO_printf(out, "(INVALID PSS PARAMETERS)\n") <= 0) + goto err; + } + break; + case RSA_FLAG_TYPE_RSASSAPSS: + if (ossl_rsa_pss_params_30_is_unrestricted(pss_params)) { + if (BIO_printf(out, "No PSS parameter restrictions\n") <= 0) + goto err; + } else { + int hashalg_nid = ossl_rsa_pss_params_30_hashalg(pss_params); + int maskgenalg_nid = + ossl_rsa_pss_params_30_maskgenalg(pss_params); + int maskgenhashalg_nid = + ossl_rsa_pss_params_30_maskgenhashalg(pss_params); + int saltlen = ossl_rsa_pss_params_30_saltlen(pss_params); + int trailerfield = + ossl_rsa_pss_params_30_trailerfield(pss_params); + + if (BIO_printf(out, "PSS parameter restrictions:\n") <= 0) + goto err; + if (BIO_printf(out, " Hash Algorithm: %s%s\n", + ossl_rsa_oaeppss_nid2name(hashalg_nid), + (hashalg_nid == NID_sha1 + ? " (default)" : "")) <= 0) + goto err; + if (BIO_printf(out, " Mask Algorithm: %s with %s%s\n", + ossl_rsa_mgf_nid2name(maskgenalg_nid), + ossl_rsa_oaeppss_nid2name(maskgenhashalg_nid), + (maskgenalg_nid == NID_mgf1 + && maskgenhashalg_nid == NID_sha1 + ? " (default)" : "")) <= 0) + goto err; + if (BIO_printf(out, " Minimum Salt Length: %d%s\n", + saltlen, + (saltlen == 20 ? " (default)" : "")) <= 0) + goto err; + if (BIO_printf(out, " Trailer Field: 0x%x%s\n", + trailerfield, + (trailerfield == 1 ? " (default)" : "")) <= 0) + goto err; + } + break; + } + } + + ret = 1; + err: + sk_BIGNUM_const_free(factors); + sk_BIGNUM_const_free(exps); + sk_BIGNUM_const_free(coeffs); + return ret; +} + +/* ---------------------------------------------------------------------- */ + +#ifndef OPENSSL_NO_ML_DSA +static int ml_dsa_to_text(BIO *out, const void *key, int selection) +{ + return ossl_ml_dsa_key_to_text(out, (ML_DSA_KEY *)key, selection); +} +#endif /* OPENSSL_NO_ML_DSA */ +/* ---------------------------------------------------------------------- */ + +static void *key2text_newctx(void *provctx) +{ + return provctx; +} + +static void key2text_freectx(ossl_unused void *vctx) +{ +} + +static int key2text_encode(void *vctx, const void *key, int selection, + OSSL_CORE_BIO *cout, + int (*key2text)(BIO *out, const void *key, + int selection), + OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg) +{ + BIO *out = ossl_bio_new_from_core_bio(vctx, cout); + int ret; + + if (out == NULL) + return 0; + + ret = key2text(out, key, selection); + BIO_free(out); + + return ret; +} + +#define MAKE_TEXT_ENCODER(impl, type) \ + static OSSL_FUNC_encoder_import_object_fn \ + impl##2text_import_object; \ + static OSSL_FUNC_encoder_free_object_fn \ + impl##2text_free_object; \ + static OSSL_FUNC_encoder_encode_fn impl##2text_encode; \ + \ + static void *impl##2text_import_object(void *ctx, int selection, \ + const OSSL_PARAM params[]) \ + { \ + return ossl_prov_import_key(ossl_##impl##_keymgmt_functions, \ + ctx, selection, params); \ + } \ + static void impl##2text_free_object(void *key) \ + { \ + ossl_prov_free_key(ossl_##impl##_keymgmt_functions, key); \ + } \ + static int impl##2text_encode(void *vctx, OSSL_CORE_BIO *cout, \ + const void *key, \ + const OSSL_PARAM key_abstract[], \ + int selection, \ + OSSL_PASSPHRASE_CALLBACK *cb, \ + void *cbarg) \ + { \ + /* We don't deal with abstract objects */ \ + if (key_abstract != NULL) { \ + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT); \ + return 0; \ + } \ + return key2text_encode(vctx, key, selection, cout, \ + type##_to_text, cb, cbarg); \ + } \ + const OSSL_DISPATCH ossl_##impl##_to_text_encoder_functions[] = { \ + { OSSL_FUNC_ENCODER_NEWCTX, \ + (void (*)(void))key2text_newctx }, \ + { OSSL_FUNC_ENCODER_FREECTX, \ + (void (*)(void))key2text_freectx }, \ + { OSSL_FUNC_ENCODER_IMPORT_OBJECT, \ + (void (*)(void))impl##2text_import_object }, \ + { OSSL_FUNC_ENCODER_FREE_OBJECT, \ + (void (*)(void))impl##2text_free_object }, \ + { OSSL_FUNC_ENCODER_ENCODE, \ + (void (*)(void))impl##2text_encode }, \ + OSSL_DISPATCH_END \ + } + +#ifndef OPENSSL_NO_DH +MAKE_TEXT_ENCODER(dh, dh); +MAKE_TEXT_ENCODER(dhx, dh); +#endif +#ifndef OPENSSL_NO_DSA +MAKE_TEXT_ENCODER(dsa, dsa); +#endif +#ifndef OPENSSL_NO_EC +MAKE_TEXT_ENCODER(ec, ec); +# ifndef OPENSSL_NO_SM2 +MAKE_TEXT_ENCODER(sm2, ec); +# endif +# ifndef OPENSSL_NO_ECX +MAKE_TEXT_ENCODER(ed25519, ecx); +MAKE_TEXT_ENCODER(ed448, ecx); +MAKE_TEXT_ENCODER(x25519, ecx); +MAKE_TEXT_ENCODER(x448, ecx); +# endif +#endif +#ifndef OPENSSL_NO_ML_KEM +MAKE_TEXT_ENCODER(ml_kem_512, ml_kem); +MAKE_TEXT_ENCODER(ml_kem_768, ml_kem); +MAKE_TEXT_ENCODER(ml_kem_1024, ml_kem); +#endif +MAKE_TEXT_ENCODER(rsa, rsa); +MAKE_TEXT_ENCODER(rsapss, rsa); + +#ifndef OPENSSL_NO_ML_DSA +MAKE_TEXT_ENCODER(ml_dsa_44, ml_dsa); +MAKE_TEXT_ENCODER(ml_dsa_65, ml_dsa); +MAKE_TEXT_ENCODER(ml_dsa_87, ml_dsa); +#endif + +#ifndef OPENSSL_NO_SLH_DSA +MAKE_TEXT_ENCODER(slh_dsa_sha2_128s, slh_dsa); +MAKE_TEXT_ENCODER(slh_dsa_sha2_128f, slh_dsa); +MAKE_TEXT_ENCODER(slh_dsa_sha2_192s, slh_dsa); +MAKE_TEXT_ENCODER(slh_dsa_sha2_192f, slh_dsa); +MAKE_TEXT_ENCODER(slh_dsa_sha2_256s, slh_dsa); +MAKE_TEXT_ENCODER(slh_dsa_sha2_256f, slh_dsa); +MAKE_TEXT_ENCODER(slh_dsa_shake_128s, slh_dsa); +MAKE_TEXT_ENCODER(slh_dsa_shake_128f, slh_dsa); +MAKE_TEXT_ENCODER(slh_dsa_shake_192s, slh_dsa); +MAKE_TEXT_ENCODER(slh_dsa_shake_192f, slh_dsa); +MAKE_TEXT_ENCODER(slh_dsa_shake_256s, slh_dsa); +MAKE_TEXT_ENCODER(slh_dsa_shake_256f, slh_dsa); +#endif diff --git a/crypto/openssl/providers/implementations/encode_decode/endecoder_common.c b/crypto/openssl/providers/implementations/encode_decode/endecoder_common.c new file mode 100644 index 000000000000..c4ea2f853cfc --- /dev/null +++ b/crypto/openssl/providers/implementations/encode_decode/endecoder_common.c @@ -0,0 +1,104 @@ +/* + * Copyright 2020-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 + */ + +#include <openssl/core.h> +#include <openssl/buffer.h> +#include "internal/asn1.h" +#include "prov/bio.h" +#include "endecoder_local.h" + +OSSL_FUNC_keymgmt_new_fn * +ossl_prov_get_keymgmt_new(const OSSL_DISPATCH *fns) +{ + /* Pilfer the keymgmt dispatch table */ + for (; fns->function_id != 0; fns++) + if (fns->function_id == OSSL_FUNC_KEYMGMT_NEW) + return OSSL_FUNC_keymgmt_new(fns); + + return NULL; +} + +OSSL_FUNC_keymgmt_free_fn * +ossl_prov_get_keymgmt_free(const OSSL_DISPATCH *fns) +{ + /* Pilfer the keymgmt dispatch table */ + for (; fns->function_id != 0; fns++) + if (fns->function_id == OSSL_FUNC_KEYMGMT_FREE) + return OSSL_FUNC_keymgmt_free(fns); + + return NULL; +} + +OSSL_FUNC_keymgmt_import_fn * +ossl_prov_get_keymgmt_import(const OSSL_DISPATCH *fns) +{ + /* Pilfer the keymgmt dispatch table */ + for (; fns->function_id != 0; fns++) + if (fns->function_id == OSSL_FUNC_KEYMGMT_IMPORT) + return OSSL_FUNC_keymgmt_import(fns); + + return NULL; +} + +OSSL_FUNC_keymgmt_export_fn * +ossl_prov_get_keymgmt_export(const OSSL_DISPATCH *fns) +{ + /* Pilfer the keymgmt dispatch table */ + for (; fns->function_id != 0; fns++) + if (fns->function_id == OSSL_FUNC_KEYMGMT_EXPORT) + return OSSL_FUNC_keymgmt_export(fns); + + return NULL; +} + +void *ossl_prov_import_key(const OSSL_DISPATCH *fns, void *provctx, + int selection, const OSSL_PARAM params[]) +{ + OSSL_FUNC_keymgmt_new_fn *kmgmt_new = ossl_prov_get_keymgmt_new(fns); + OSSL_FUNC_keymgmt_free_fn *kmgmt_free = ossl_prov_get_keymgmt_free(fns); + OSSL_FUNC_keymgmt_import_fn *kmgmt_import = + ossl_prov_get_keymgmt_import(fns); + void *key = NULL; + + if (kmgmt_new != NULL && kmgmt_import != NULL && kmgmt_free != NULL) { + if ((key = kmgmt_new(provctx)) == NULL + || !kmgmt_import(key, selection, params)) { + kmgmt_free(key); + key = NULL; + } + } + return key; +} + +void ossl_prov_free_key(const OSSL_DISPATCH *fns, void *key) +{ + OSSL_FUNC_keymgmt_free_fn *kmgmt_free = ossl_prov_get_keymgmt_free(fns); + + if (kmgmt_free != NULL) + kmgmt_free(key); +} + +int ossl_read_der(PROV_CTX *provctx, OSSL_CORE_BIO *cin, unsigned char **data, + long *len) +{ + BUF_MEM *mem = NULL; + BIO *in = ossl_bio_new_from_core_bio(provctx, cin); + int ok; + + if (in == NULL) + return 0; + ok = (asn1_d2i_read_bio(in, &mem) >= 0); + if (ok) { + *data = (unsigned char *)mem->data; + *len = (long)mem->length; + OPENSSL_free(mem); + } + BIO_free(in); + return ok; +} diff --git a/crypto/openssl/providers/implementations/encode_decode/endecoder_local.h b/crypto/openssl/providers/implementations/encode_decode/endecoder_local.h new file mode 100644 index 000000000000..a65d05ffaeac --- /dev/null +++ b/crypto/openssl/providers/implementations/encode_decode/endecoder_local.h @@ -0,0 +1,28 @@ +/* + * Copyright 2020-2021 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 + */ + +#include <openssl/core.h> +#include <openssl/core_dispatch.h> +#include <openssl/types.h> +#include "prov/provider_ctx.h" + +OSSL_FUNC_keymgmt_new_fn *ossl_prov_get_keymgmt_new(const OSSL_DISPATCH *fns); +OSSL_FUNC_keymgmt_free_fn *ossl_prov_get_keymgmt_free(const OSSL_DISPATCH *fns); +OSSL_FUNC_keymgmt_import_fn *ossl_prov_get_keymgmt_import(const OSSL_DISPATCH *fns); +OSSL_FUNC_keymgmt_export_fn *ossl_prov_get_keymgmt_export(const OSSL_DISPATCH *fns); + +int ossl_prov_der_from_p8(unsigned char **new_der, long *new_der_len, + unsigned char *input_der, long input_der_len, + OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg); + +void *ossl_prov_import_key(const OSSL_DISPATCH *fns, void *provctx, + int selection, const OSSL_PARAM params[]); +void ossl_prov_free_key(const OSSL_DISPATCH *fns, void *key); +int ossl_read_der(PROV_CTX *provctx, OSSL_CORE_BIO *cin, unsigned char **data, + long *len); diff --git a/crypto/openssl/providers/implementations/encode_decode/ml_common_codecs.c b/crypto/openssl/providers/implementations/encode_decode/ml_common_codecs.c new file mode 100644 index 000000000000..773550c9fb93 --- /dev/null +++ b/crypto/openssl/providers/implementations/encode_decode/ml_common_codecs.c @@ -0,0 +1,92 @@ +/* + * Copyright 2025 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 + */ + +#include <string.h> +#include <openssl/crypto.h> +#include <openssl/err.h> +#include <openssl/proverr.h> +#include "ml_common_codecs.h" + +static int pref_cmp(const void *va, const void *vb) +{ + const ML_COMMON_PKCS8_FMT_PREF *a = va; + const ML_COMMON_PKCS8_FMT_PREF *b = vb; + + /* + * Zeros sort last, otherwise the sort is in increasing order. + * + * The preferences are small enough to ensure the comparison is transitive + * as required by qsort(3). When overflow or underflow is possible, the + * correct transitive comparison would be: (b < a) - (a < b). + */ + if (a->pref > 0 && b->pref > 0) + return a->pref - b->pref; + /* A preference of 0 is "larger" than (sorts after) any nonzero value. */ + return b->pref - a->pref; +} + +ML_COMMON_PKCS8_FMT_PREF * +ossl_ml_common_pkcs8_fmt_order(const char *algorithm_name, + const ML_COMMON_PKCS8_FMT *p8fmt, + const char *direction, const char *formats) +{ + ML_COMMON_PKCS8_FMT_PREF *ret; + int i, count = 0; + const char *fmt = formats, *end; + const char *sep = "\t ,"; + + /* Reserve an extra terminal slot with fmt == NULL */ + if ((ret = OPENSSL_zalloc((NUM_PKCS8_FORMATS + 1) * sizeof(*ret))) == NULL) + return NULL; + + /* Entries that match a format will get a non-zero preference. */ + for (i = 0; i < NUM_PKCS8_FORMATS; ++i) { + ret[i].fmt = &p8fmt[i]; + ret[i].pref = 0; + } + + /* Default to compile-time table order when none specified. */ + if (formats == NULL) + return ret; + + /* + * Formats are case-insensitive, separated by spaces, tabs or commas. + * Duplicate formats are allowed, the first occurence determines the order. + */ + do { + if (*(fmt += strspn(fmt, sep)) == '\0') + break; + end = fmt + strcspn(fmt, sep); + for (i = 0; i < NUM_PKCS8_FORMATS; ++i) { + /* Skip slots already selected or with a different name. */ + if (ret[i].pref > 0 + || OPENSSL_strncasecmp(ret[i].fmt->p8_name, + fmt, (end - fmt)) != 0) + continue; + /* First time match */ + ret[i].pref = ++count; + break; + } + fmt = end; + } while (count < NUM_PKCS8_FORMATS); + + /* No formats matched, raise an error */ + if (count == 0) { + OPENSSL_free(ret); + ERR_raise_data(ERR_LIB_PROV, PROV_R_ML_DSA_NO_FORMAT, + "no %s private key %s formats are enabled", + algorithm_name, direction); + return NULL; + } + /* Sort by preference, with 0's last */ + qsort(ret, NUM_PKCS8_FORMATS, sizeof(*ret), pref_cmp); + /* Terminate the list at first unselected entry, perhaps reserved slot. */ + ret[count].fmt = NULL; + return ret; +} diff --git a/crypto/openssl/providers/implementations/encode_decode/ml_common_codecs.h b/crypto/openssl/providers/implementations/encode_decode/ml_common_codecs.h new file mode 100644 index 000000000000..4bf618ea0b79 --- /dev/null +++ b/crypto/openssl/providers/implementations/encode_decode/ml_common_codecs.h @@ -0,0 +1,98 @@ +/* + * Copyright 2025 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 + */ + +#ifndef PROV_ML_COMMON_CODECS_H +# define PROV_ML_COMMON_CODECS_H +# pragma once + +# include <openssl/e_os2.h> +# include "crypto/ml_dsa.h" +# include "prov/provider_ctx.h" + + /*- + * The DER ASN.1 encoding of ML-DSA and ML-KEM public keys prepends 22 bytes + * to the encoded public key: + * + * - 4 byte outer sequence tag and length + * - 2 byte algorithm sequence tag and length + * - 2 byte algorithm OID tag and length + * - 9 byte algorithm OID (from NIST CSOR OID arc) + * - 4 byte bit string tag and length + * - 1 bitstring lead byte + */ +# define ML_COMMON_SPKI_OVERHEAD 22 +typedef struct { + const uint8_t asn1_prefix[ML_COMMON_SPKI_OVERHEAD]; +} ML_COMMON_SPKI_FMT; + +/*- +* For each parameter set we support a few PKCS#8 input formats, three + * corresponding to the "either or both" variants of: + * + * ML-DSA-PrivateKey ::= CHOICE { + * seed [0] IMPLICIT OCTET STRING (SIZE (32)), + * expandedKey OCTET STRING (SIZE (2560 | 4032 | 4896)), + * both SEQUENCE { + * seed OCTET STRING (SIZE (32)), + * expandedKey OCTET STRING (SIZE (2560 | 4032 | 4896)) } } + * + * ML-KEM-PrivateKey ::= CHOICE { + * seed [0] IMPLICIT OCTET STRING (SIZE (64)), + * expandedKey OCTET STRING (SIZE (1632 | 2400 | 3168)), + * both SEQUENCE { + * seed OCTET STRING (SIZE (64)), + * expandedKey OCTET STRING SIZE ((1632 | 2400 | 3168)) } } + * + * one more for a historical OQS encoding: + * + * - OQS private + public key: OCTET STRING + * (The public key is ignored, just as with PKCS#8 v2.) + * + * and two more that are the minimal IETF non-ASN.1 seed encoding: + * + * - Bare seed (just the 32 or 64 bytes) + * - Bare priv (just the key bytes) + * + * A length of zero means that particular field is absent. + * + * The p8_shift is 0 when the top-level tag+length occupy four bytes, 2 when + * they occupy two by†es, and 4 when no tag is used at all. + */ +#define NUM_PKCS8_FORMATS 6 + +typedef struct { + const char *p8_name; /* Format name */ + size_t p8_bytes; /* Total P8 encoding length */ + int p8_shift; /* 4 - (top-level tag + len) */ + uint32_t p8_magic; /* The tag + len value */ + uint16_t seed_magic; /* Interior tag + len for the seed */ + size_t seed_offset; /* Seed offset from start */ + size_t seed_length; /* Seed bytes */ + uint32_t priv_magic; /* Interior tag + len for the key */ + size_t priv_offset; /* Key offset from start */ + size_t priv_length; /* Key bytes */ + size_t pub_offset; /* Pubkey offset */ + size_t pub_length; /* Pubkey bytes */ +} ML_COMMON_PKCS8_FMT; + +typedef struct { + const ML_COMMON_SPKI_FMT *spkifmt; + const ML_COMMON_PKCS8_FMT *p8fmt; +} ML_COMMON_CODEC; + +typedef struct { + const ML_COMMON_PKCS8_FMT *fmt; + int pref; +} ML_COMMON_PKCS8_FMT_PREF; + +ML_COMMON_PKCS8_FMT_PREF * +ossl_ml_common_pkcs8_fmt_order(const char *algorithm_name, + const ML_COMMON_PKCS8_FMT *p8fmt, + const char *direction, const char *formats); +#endif diff --git a/crypto/openssl/providers/implementations/encode_decode/ml_dsa_codecs.c b/crypto/openssl/providers/implementations/encode_decode/ml_dsa_codecs.c new file mode 100644 index 000000000000..dd54137fe500 --- /dev/null +++ b/crypto/openssl/providers/implementations/encode_decode/ml_dsa_codecs.c @@ -0,0 +1,449 @@ +/* + * Copyright 2025 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 + */ + +#include <string.h> +#include <openssl/byteorder.h> +#include <openssl/err.h> +#include <openssl/proverr.h> +#include <openssl/x509.h> +#include <openssl/core_names.h> +#include "internal/encoder.h" +#include "prov/ml_dsa.h" +#include "ml_dsa_codecs.h" + +/*- + * Tables describing supported ASN.1 input/output formats. + */ + +/*- + * ML-DSA-44: + * Public key bytes: 1312 (0x0520) + * Private key bytes: 2560 (0x0a00) + */ +static const ML_COMMON_SPKI_FMT ml_dsa_44_spkifmt = { + { 0x30, 0x82, 0x05, 0x32, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, + 0x01, 0x65, 0x03, 0x04, 0x03, 0x11, 0x03, 0x82, 0x05, 0x21, 0x00, } +}; +static const ML_COMMON_PKCS8_FMT ml_dsa_44_p8fmt[NUM_PKCS8_FORMATS] = { + { "seed-priv", 0x0a2a, 0, 0x30820a26, 0x0420, 6, 0x20, 0x04820a00, 0x2a, 0x0a00, 0, 0, }, + { "priv-only", 0x0a04, 0, 0x04820a00, 0, 0, 0, 0, 0x04, 0x0a00, 0, 0, }, + { "oqskeypair", 0x0f24, 0, 0x04820f20, 0, 0, 0, 0, 0x04, 0x0a00, 0x0a04, 0x0520 }, + { "seed-only", 0x0022, 2, 0x8020, 0, 2, 0x20, 0, 0, 0, 0, 0, }, + { "bare-priv", 0x0a00, 4, 0, 0, 0, 0, 0, 0, 0x0a00, 0, 0, }, + { "bare-seed", 0x0020, 4, 0, 0, 0, 0x20, 0, 0, 0, 0, 0, }, +}; + +/* + * ML-DSA-65: + * Public key bytes: 1952 (0x07a0) + * Private key bytes: 4032 (0x0fc0) + */ +static const ML_COMMON_SPKI_FMT ml_dsa_65_spkifmt = { + { 0x30, 0x82, 0x07, 0xb2, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, + 0x01, 0x65, 0x03, 0x04, 0x03, 0x12, 0x03, 0x82, 0x07, 0xa1, 0x00, } +}; +static const ML_COMMON_PKCS8_FMT ml_dsa_65_p8fmt[NUM_PKCS8_FORMATS] = { + { "seed-priv", 0x0fea, 0, 0x30820fe6, 0x0420, 6, 0x20, 0x04820fc0, 0x2a, 0x0fc0, 0, 0, }, + { "priv-only", 0x0fc4, 0, 0x04820fc0, 0, 0, 0, 0, 0x04, 0x0fc0, 0, 0, }, + { "oqskeypair", 0x1764, 0, 0x04821760, 0, 0, 0, 0, 0x04, 0x0fc0, 0x0fc4, 0x07a0 }, + { "seed-only", 0x0022, 2, 0x8020, 0, 2, 0x20, 0, 0, 0, 0, 0, }, + { "bare-priv", 0x0fc0, 4, 0, 0, 0, 0, 0, 0, 0x0fc0, 0, 0, }, + { "bare-seed", 0x0020, 4, 0, 0, 0, 0x20, 0, 0, 0, 0, 0, }, +}; + +/*- + * ML-DSA-87: + * Public key bytes: 2592 (0x0a20) + * Private key bytes: 4896 (0x1320) + */ +static const ML_COMMON_SPKI_FMT ml_dsa_87_spkifmt = { + { 0x30, 0x82, 0x0a, 0x32, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, + 0x01, 0x65, 0x03, 0x04, 0x03, 0x13, 0x03, 0x82, 0x0a, 0x21, 0x00, } +}; +static const ML_COMMON_PKCS8_FMT ml_dsa_87_p8fmt[NUM_PKCS8_FORMATS] = { + { "seed-priv", 0x134a, 0, 0x30821346, 0x0420, 6, 0x20, 0x04821320, 0x2a, 0x1320, 0, 0, }, + { "priv-only", 0x1324, 0, 0x04821320, 0, 0, 0, 0, 0x04, 0x1320, 0, 0, }, + { "oqskeypair", 0x1d44, 0, 0x04821d40, 0, 0, 0, 0, 0x04, 0x1320, 0x1324, 0x0a20 }, + { "seed-only", 0x0022, 2, 0x8020, 0, 2, 0x20, 0, 0, 0, 0, 0, }, + { "bare-priv", 0x1320, 4, 0, 0, 0, 0, 0, 0, 0x1320, 0, 0, }, + { "bare-seed", 0x0020, 4, 0, 0, 0, 0x20, 0, 0, 0, 0, 0, }, +}; + +/* Indices of slots in the codec table below */ +#define ML_DSA_44_CODEC 0 +#define ML_DSA_65_CODEC 1 +#define ML_DSA_87_CODEC 2 + +/* + * Per-variant fixed parameters + */ +static const ML_COMMON_CODEC codecs[3] = { + { &ml_dsa_44_spkifmt, ml_dsa_44_p8fmt }, + { &ml_dsa_65_spkifmt, ml_dsa_65_p8fmt }, + { &ml_dsa_87_spkifmt, ml_dsa_87_p8fmt } +}; + +/* Retrieve the parameters of one of the ML-DSA variants */ +static const ML_COMMON_CODEC *ml_dsa_get_codec(int evp_type) +{ + switch (evp_type) { + case EVP_PKEY_ML_DSA_44: + return &codecs[ML_DSA_44_CODEC]; + case EVP_PKEY_ML_DSA_65: + return &codecs[ML_DSA_65_CODEC]; + case EVP_PKEY_ML_DSA_87: + return &codecs[ML_DSA_87_CODEC]; + } + return NULL; +} + +ML_DSA_KEY * +ossl_ml_dsa_d2i_PUBKEY(const uint8_t *pk, int pk_len, int evp_type, + PROV_CTX *provctx, const char *propq) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx); + const ML_COMMON_CODEC *codec; + const ML_DSA_PARAMS *params; + ML_DSA_KEY *ret; + + if ((params = ossl_ml_dsa_params_get(evp_type)) == NULL + || (codec = ml_dsa_get_codec(evp_type)) == NULL) + return NULL; + if (pk_len != ML_COMMON_SPKI_OVERHEAD + (ossl_ssize_t) params->pk_len + || memcmp(pk, codec->spkifmt->asn1_prefix, ML_COMMON_SPKI_OVERHEAD) != 0) + return NULL; + pk_len -= ML_COMMON_SPKI_OVERHEAD; + pk += ML_COMMON_SPKI_OVERHEAD; + + if ((ret = ossl_ml_dsa_key_new(libctx, propq, evp_type)) == NULL) + return NULL; + + if (!ossl_ml_dsa_pk_decode(ret, pk, (size_t) pk_len)) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_ENCODING, + "errror parsing %s public key from input SPKI", + params->alg); + ossl_ml_dsa_key_free(ret); + return NULL; + } + + return ret; +} + +ML_DSA_KEY * +ossl_ml_dsa_d2i_PKCS8(const uint8_t *prvenc, int prvlen, + int evp_type, PROV_CTX *provctx, + const char *propq) +{ + const ML_DSA_PARAMS *v; + const ML_COMMON_CODEC *codec; + ML_COMMON_PKCS8_FMT_PREF *fmt_slots = NULL, *slot; + const ML_COMMON_PKCS8_FMT *p8fmt; + ML_DSA_KEY *key = NULL, *ret = NULL; + PKCS8_PRIV_KEY_INFO *p8inf = NULL; + const uint8_t *buf, *pos; + const X509_ALGOR *alg = NULL; + const char *formats; + int len, ptype; + uint32_t magic; + uint16_t seed_magic; + const uint8_t *seed = NULL; + const uint8_t *priv = NULL; + + /* Which ML-DSA variant? */ + if ((v = ossl_ml_dsa_params_get(evp_type)) == NULL + || (codec = ml_dsa_get_codec(evp_type)) == NULL) + return 0; + + /* Extract the key OID and any parameters. */ + if ((p8inf = d2i_PKCS8_PRIV_KEY_INFO(NULL, &prvenc, prvlen)) == NULL) + return 0; + /* Shortest prefix is 4 bytes: seq tag/len + octet string tag/len */ + if (!PKCS8_pkey_get0(NULL, &buf, &len, &alg, p8inf)) + goto end; + /* Bail out early if this is some other key type. */ + if (OBJ_obj2nid(alg->algorithm) != evp_type) + goto end; + + /* Get the list of enabled decoders. Their order is not important here. */ + formats = ossl_prov_ctx_get_param( + provctx, OSSL_PKEY_PARAM_ML_DSA_INPUT_FORMATS, NULL); + fmt_slots = ossl_ml_common_pkcs8_fmt_order(v->alg, codec->p8fmt, + "input", formats); + if (fmt_slots == NULL) + goto end; + + /* Parameters must be absent. */ + X509_ALGOR_get0(NULL, &ptype, NULL, alg); + if (ptype != V_ASN1_UNDEF) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_UNEXPECTED_KEY_PARAMETERS, + "unexpected parameters with a PKCS#8 %s private key", + v->alg); + goto end; + } + if ((ossl_ssize_t)len < (ossl_ssize_t)sizeof(magic)) + goto end; + + /* Find the matching p8 info slot, that also has the expected length. */ + pos = OPENSSL_load_u32_be(&magic, buf); + for (slot = fmt_slots; (p8fmt = slot->fmt) != NULL; ++slot) { + if (len != (ossl_ssize_t)p8fmt->p8_bytes) + continue; + if (p8fmt->p8_shift == sizeof(magic) + || (magic >> (p8fmt->p8_shift * 8)) == p8fmt->p8_magic) { + pos -= p8fmt->p8_shift; + break; + } + } + if (p8fmt == NULL + || (p8fmt->seed_length > 0 && p8fmt->seed_length != ML_DSA_SEED_BYTES) + || (p8fmt->priv_length > 0 && p8fmt->priv_length != v->sk_len) + || (p8fmt->pub_length > 0 && p8fmt->pub_length != v->pk_len)) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_ML_DSA_NO_FORMAT, + "no matching enabled %s private key input formats", + v->alg); + goto end; + } + + if (p8fmt->seed_length > 0) { + /* Check |seed| tag/len, if not subsumed by |magic|. */ + if (pos + sizeof(uint16_t) == buf + p8fmt->seed_offset) { + pos = OPENSSL_load_u16_be(&seed_magic, pos); + if (seed_magic != p8fmt->seed_magic) + goto end; + } else if (pos != buf + p8fmt->seed_offset) { + goto end; + } + pos += ML_DSA_SEED_BYTES; + } + if (p8fmt->priv_length > 0) { + /* Check |priv| tag/len */ + if (pos + sizeof(uint32_t) == buf + p8fmt->priv_offset) { + pos = OPENSSL_load_u32_be(&magic, pos); + if (magic != p8fmt->priv_magic) + goto end; + } else if (pos != buf + p8fmt->priv_offset) { + goto end; + } + pos += v->sk_len; + } + if (p8fmt->pub_length > 0) { + if (pos != buf + p8fmt->pub_offset) + goto end; + pos += v->pk_len; + } + if (pos != buf + len) + goto end; + + /* + * Collect the seed and/or key into a "decoded" private key object, + * to be turned into a real key on provider "load" or "import". + */ + if ((key = ossl_prov_ml_dsa_new(provctx, propq, evp_type)) == NULL) + goto end; + if (p8fmt->seed_length > 0) + seed = buf + p8fmt->seed_offset; + if (p8fmt->priv_length > 0) + priv = buf + p8fmt->priv_offset; + /* Any OQS public key content is ignored */ + + if (ossl_ml_dsa_set_prekey(key, 0, 0, + seed, ML_DSA_SEED_BYTES, priv, v->sk_len)) + ret = key; + + end: + OPENSSL_free(fmt_slots); + PKCS8_PRIV_KEY_INFO_free(p8inf); + if (ret == NULL) + ossl_ml_dsa_key_free(key); + return ret; +} + +/* Same as ossl_ml_dsa_encode_pubkey, but allocates the output buffer. */ +int ossl_ml_dsa_i2d_pubkey(const ML_DSA_KEY *key, unsigned char **out) +{ + const ML_DSA_PARAMS *params = ossl_ml_dsa_key_params(key); + const uint8_t *pk = ossl_ml_dsa_key_get_pub(key); + + if (pk == NULL) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY, + "no %s public key data available", params->alg); + return 0; + } + if (out != NULL + && (*out = OPENSSL_memdup(pk, params->pk_len)) == NULL) + return 0; + return (int)params->pk_len; +} + +/* Allocate and encode PKCS#8 private key payload. */ +int ossl_ml_dsa_i2d_prvkey(const ML_DSA_KEY *key, uint8_t **out, + PROV_CTX *provctx) +{ + const ML_DSA_PARAMS *params = ossl_ml_dsa_key_params(key); + const ML_COMMON_CODEC *codec; + ML_COMMON_PKCS8_FMT_PREF *fmt_slots, *slot; + const ML_COMMON_PKCS8_FMT *p8fmt; + uint8_t *buf = NULL, *pos; + const char *formats; + int len = ML_DSA_SEED_BYTES; + int ret = 0; + const uint8_t *seed = ossl_ml_dsa_key_get_seed(key); + const uint8_t *sk = ossl_ml_dsa_key_get_priv(key); + + /* Not ours to handle */ + if ((codec = ml_dsa_get_codec(params->evp_type)) == NULL) + return 0; + + if (sk == NULL) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_NOT_A_PRIVATE_KEY, + "no %s private key data available", + params->alg); + return 0; + } + + formats = ossl_prov_ctx_get_param( + provctx, OSSL_PKEY_PARAM_ML_DSA_OUTPUT_FORMATS, NULL); + fmt_slots = ossl_ml_common_pkcs8_fmt_order(params->alg, codec->p8fmt, + "output", formats); + if (fmt_slots == NULL) + return 0; + + /* If we don't have a seed, skip seedful entries */ + for (slot = fmt_slots; (p8fmt = slot->fmt) != NULL; ++slot) + if (seed != NULL || p8fmt->seed_length == 0) + break; + /* No matching table entries, give up */ + if (p8fmt == NULL + || (p8fmt->seed_length > 0 && p8fmt->seed_length != ML_DSA_SEED_BYTES) + || (p8fmt->priv_length > 0 && p8fmt->priv_length != params->sk_len) + || (p8fmt->pub_length > 0 && p8fmt->pub_length != params->pk_len)) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_ML_DSA_NO_FORMAT, + "no matching enabled %s private key output formats", + params->alg); + goto end; + } + len = p8fmt->p8_bytes; + + if (out == NULL) { + ret = len; + goto end; + } + + if ((pos = buf = OPENSSL_malloc((size_t) len)) == NULL) + goto end; + + switch (p8fmt->p8_shift) { + case 0: + pos = OPENSSL_store_u32_be(pos, p8fmt->p8_magic); + break; + case 2: + pos = OPENSSL_store_u16_be(pos, (uint16_t)p8fmt->p8_magic); + break; + case 4: + break; + default: + ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR, + "error encoding %s private key", params->alg); + goto end; + } + + if (p8fmt->seed_length != 0) { + /* + * Either the tag/len were already included in |magic| or they require + * us to write two bytes now. + */ + if (pos + sizeof(uint16_t) == buf + p8fmt->seed_offset) + pos = OPENSSL_store_u16_be(pos, p8fmt->seed_magic); + if (pos != buf + p8fmt->seed_offset) { + ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR, + "error encoding %s private key", params->alg); + goto end; + } + memcpy(pos, seed, ML_DSA_SEED_BYTES); + pos += ML_DSA_SEED_BYTES; + } + if (p8fmt->priv_length != 0) { + if (pos + sizeof(uint32_t) == buf + p8fmt->priv_offset) + pos = OPENSSL_store_u32_be(pos, p8fmt->priv_magic); + if (pos != buf + p8fmt->priv_offset) { + ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR, + "error encoding %s private key", params->alg); + goto end; + } + memcpy(pos, sk, params->sk_len); + pos += params->sk_len; + } + /* OQS form output with tacked-on public key */ + if (p8fmt->pub_length != 0) { + /* The OQS pubkey is never separately DER-wrapped */ + if (pos != buf + p8fmt->pub_offset) { + ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR, + "error encoding %s private key", params->alg); + goto end; + } + memcpy(pos, ossl_ml_dsa_key_get_pub(key), params->pk_len); + pos += params->pk_len; + } + + if (pos == buf + len) { + *out = buf; + ret = len; + } + + end: + OPENSSL_free(fmt_slots); + if (ret == 0) + OPENSSL_free(buf); + return ret; +} + +int ossl_ml_dsa_key_to_text(BIO *out, const ML_DSA_KEY *key, int selection) +{ + const ML_DSA_PARAMS *params; + const uint8_t *seed, *sk, *pk; + + if (out == NULL || key == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + params = ossl_ml_dsa_key_params(key); + pk = ossl_ml_dsa_key_get_pub(key); + sk = ossl_ml_dsa_key_get_priv(key); + seed = ossl_ml_dsa_key_get_seed(key); + + if (pk == NULL) { + /* Regardless of the |selection|, there must be a public key */ + ERR_raise_data(ERR_LIB_PROV, PROV_R_MISSING_KEY, + "no %s key material available", params->alg); + return 0; + } + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) { + if (sk == NULL) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_MISSING_KEY, + "no %s key material available", params->alg); + return 0; + } + if (BIO_printf(out, "%s Private-Key:\n", params->alg) <= 0) + return 0; + if (seed != NULL && !ossl_bio_print_labeled_buf(out, "seed:", seed, + ML_DSA_SEED_BYTES)) + return 0; + if (!ossl_bio_print_labeled_buf(out, "priv:", sk, params->sk_len)) + return 0; + } else if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) { + if (BIO_printf(out, "%s Public-Key:\n", params->alg) <= 0) + return 0; + } + + if (!ossl_bio_print_labeled_buf(out, "pub:", pk, params->pk_len)) + return 0; + + return 1; +} diff --git a/crypto/openssl/providers/implementations/encode_decode/ml_dsa_codecs.h b/crypto/openssl/providers/implementations/encode_decode/ml_dsa_codecs.h new file mode 100644 index 000000000000..c0c2e842a242 --- /dev/null +++ b/crypto/openssl/providers/implementations/encode_decode/ml_dsa_codecs.h @@ -0,0 +1,39 @@ +/* + * Copyright 2025 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 + */ + +#ifndef PROV_ML_DSA_CODECS_H +# define PROV_ML_DSA_CODECS_H +# pragma once + +# ifndef OPENSSL_NO_ML_DSA +# include <openssl/e_os2.h> +# include "crypto/ml_dsa.h" +# include "prov/provider_ctx.h" +# include "ml_common_codecs.h" + +__owur +ML_DSA_KEY *ossl_ml_dsa_d2i_PUBKEY(const uint8_t *pubenc, int publen, + int evp_type, PROV_CTX *provctx, + const char *propq); +__owur +ML_DSA_KEY *ossl_ml_dsa_d2i_PKCS8(const uint8_t *prvenc, int prvlen, + int evp_type, PROV_CTX *provctx, + const char *propq); +__owur +int ossl_ml_dsa_key_to_text(BIO *out, const ML_DSA_KEY *key, int selection); +__owur +__owur +int ossl_ml_dsa_i2d_pubkey(const ML_DSA_KEY *key, unsigned char **out); +__owur +__owur +int ossl_ml_dsa_i2d_prvkey(const ML_DSA_KEY *key, unsigned char **out, + PROV_CTX *provctx); + +# endif /* OPENSSL_NO_ML_DSA */ +#endif /* PROV_ML_DSA_CODECS_H */ diff --git a/crypto/openssl/providers/implementations/encode_decode/ml_kem_codecs.c b/crypto/openssl/providers/implementations/encode_decode/ml_kem_codecs.c new file mode 100644 index 000000000000..fe0c8acc7e63 --- /dev/null +++ b/crypto/openssl/providers/implementations/encode_decode/ml_kem_codecs.c @@ -0,0 +1,488 @@ +/* + * Copyright 2025 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 + */ + +#include <string.h> +#include <openssl/byteorder.h> +#include <openssl/proverr.h> +#include <openssl/x509.h> +#include <openssl/core_names.h> +#include "internal/encoder.h" +#include "prov/ml_kem.h" +#include "ml_kem_codecs.h" + +/* Tables describing supported ASN.1 input/output formats. */ + +/*- + * ML-KEM-512: + * Public key bytes: 800 (0x0320) + * Private key bytes: 1632 (0x0660) + */ +static const ML_COMMON_SPKI_FMT ml_kem_512_spkifmt = { + { 0x30, 0x82, 0x03, 0x32, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, + 0x01, 0x65, 0x03, 0x04, 0x04, 0x01, 0x03, 0x82, 0x03, 0x21, 0x00, } +}; +static const ML_COMMON_PKCS8_FMT ml_kem_512_p8fmt[NUM_PKCS8_FORMATS] = { + { "seed-priv", 0x06aa, 0, 0x308206a6, 0x0440, 6, 0x40, 0x04820660, 0x4a, 0x0660, 0, 0 }, + { "priv-only", 0x0664, 0, 0x04820660, 0, 0, 0, 0, 0x04, 0x0660, 0, 0 }, + { "oqskeypair", 0x0984, 0, 0x04820980, 0, 0, 0, 0, 0x04, 0x0660, 0x0664, 0x0320 }, + { "seed-only", 0x0042, 2, 0x8040, 0, 2, 0x40, 0, 0, 0, 0, 0 }, + { "bare-priv", 0x0660, 4, 0, 0, 0, 0, 0, 0, 0x0660, 0, 0 }, + { "bare-seed", 0x0040, 4, 0, 0, 0, 0x40, 0, 0, 0, 0, 0 }, +}; + +/*- + * ML-KEM-768: + * Public key bytes: 1184 (0x04a0) + * Private key bytes: 2400 (0x0960) + */ +static const ML_COMMON_SPKI_FMT ml_kem_768_spkifmt = { + { 0x30, 0x82, 0x04, 0xb2, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, + 0x01, 0x65, 0x03, 0x04, 0x04, 0x02, 0x03, 0x82, 0x04, 0xa1, 0x00, } +}; +static const ML_COMMON_PKCS8_FMT ml_kem_768_p8fmt[NUM_PKCS8_FORMATS] = { + { "seed-priv", 0x09aa, 0, 0x308209a6, 0x0440, 6, 0x40, 0x04820960, 0x4a, 0x0960, 0, 0, }, + { "priv-only", 0x0964, 0, 0x04820960, 0, 0, 0, 0, 0x04, 0x0960, 0, 0, }, + { "oqskeypair", 0x0e04, 0, 0x04820e00, 0, 0, 0, 0, 0x04, 0x0960, 0x0964, 0x04a0 }, + { "seed-only", 0x0042, 2, 0x8040, 0, 2, 0x40, 0, 0, 0, 0, 0, }, + { "bare-priv", 0x0960, 4, 0, 0, 0, 0, 0, 0, 0x0960, 0, 0, }, + { "bare-seed", 0x0040, 4, 0, 0, 0, 0x40, 0, 0, 0, 0, 0, }, +}; + +/*- + * ML-KEM-1024: + * Private key bytes: 3168 (0x0c60) + * Public key bytes: 1568 (0x0620) + */ +static const ML_COMMON_SPKI_FMT ml_kem_1024_spkifmt = { + { 0x30, 0x82, 0x06, 0x32, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, + 0x01, 0x65, 0x03, 0x04, 0x04, 0x03, 0x03, 0x82, 0x06, 0x21, 0x00, } +}; +static const ML_COMMON_PKCS8_FMT ml_kem_1024_p8fmt[NUM_PKCS8_FORMATS] = { + { "seed-priv", 0x0caa, 0, 0x30820ca6, 0x0440, 6, 0x40, 0x04820c60, 0x4a, 0x0c60, 0, 0 }, + { "priv-only", 0x0c64, 0, 0x04820c60, 0, 0, 0, 0, 0x04, 0x0c60, 0, 0 }, + { "oqskeypair", 0x1284, 0, 0x04821280, 0, 0, 0, 0, 0x04, 0x0c60, 0x0c64, 0x0620 }, + { "seed-only", 0x0042, 2, 0x8040, 0, 2, 0x40, 0, 0, 0, 0, 0 }, + { "bare-priv", 0x0c60, 4, 0, 0, 0, 0, 0, 0, 0x0c60, 0, 0 }, + { "bare-seed", 0x0040, 4, 0, 0, 0, 0x40, 0, 0, 0, 0, 0 }, +}; + +/* Indices of slots in the `codecs` table below */ +#define ML_KEM_512_CODEC 0 +#define ML_KEM_768_CODEC 1 +#define ML_KEM_1024_CODEC 2 + +/* + * Per-variant fixed parameters + */ +static const ML_COMMON_CODEC codecs[3] = { + { &ml_kem_512_spkifmt, ml_kem_512_p8fmt }, + { &ml_kem_768_spkifmt, ml_kem_768_p8fmt }, + { &ml_kem_1024_spkifmt, ml_kem_1024_p8fmt } +}; + +/* Retrieve the parameters of one of the ML-KEM variants */ +static const ML_COMMON_CODEC *ml_kem_get_codec(int evp_type) +{ + switch (evp_type) { + case EVP_PKEY_ML_KEM_512: + return &codecs[ML_KEM_512_CODEC]; + case EVP_PKEY_ML_KEM_768: + return &codecs[ML_KEM_768_CODEC]; + case EVP_PKEY_ML_KEM_1024: + return &codecs[ML_KEM_1024_CODEC]; + } + return NULL; +} + +ML_KEM_KEY * +ossl_ml_kem_d2i_PUBKEY(const uint8_t *pubenc, int publen, int evp_type, + PROV_CTX *provctx, const char *propq) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx); + const ML_KEM_VINFO *v; + const ML_COMMON_CODEC *codec; + const ML_COMMON_SPKI_FMT *vspki; + ML_KEM_KEY *ret; + + if ((v = ossl_ml_kem_get_vinfo(evp_type)) == NULL + || (codec = ml_kem_get_codec(evp_type)) == NULL) + return NULL; + vspki = codec->spkifmt; + if (publen != ML_COMMON_SPKI_OVERHEAD + (ossl_ssize_t) v->pubkey_bytes + || memcmp(pubenc, vspki->asn1_prefix, ML_COMMON_SPKI_OVERHEAD) != 0) + return NULL; + publen -= ML_COMMON_SPKI_OVERHEAD; + pubenc += ML_COMMON_SPKI_OVERHEAD; + + if ((ret = ossl_ml_kem_key_new(libctx, propq, evp_type)) == NULL) + return NULL; + + if (!ossl_ml_kem_parse_public_key(pubenc, (size_t) publen, ret)) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_ENCODING, + "errror parsing %s public key from input SPKI", + v->algorithm_name); + ossl_ml_kem_key_free(ret); + return NULL; + } + + return ret; +} + +ML_KEM_KEY * +ossl_ml_kem_d2i_PKCS8(const uint8_t *prvenc, int prvlen, + int evp_type, PROV_CTX *provctx, + const char *propq) +{ + const ML_KEM_VINFO *v; + const ML_COMMON_CODEC *codec; + ML_COMMON_PKCS8_FMT_PREF *fmt_slots = NULL, *slot; + const ML_COMMON_PKCS8_FMT *p8fmt; + ML_KEM_KEY *key = NULL, *ret = NULL; + PKCS8_PRIV_KEY_INFO *p8inf = NULL; + const uint8_t *buf, *pos; + const X509_ALGOR *alg = NULL; + const char *formats; + int len, ptype; + uint32_t magic; + uint16_t seed_magic; + + /* Which ML-KEM variant? */ + if ((v = ossl_ml_kem_get_vinfo(evp_type)) == NULL + || (codec = ml_kem_get_codec(evp_type)) == NULL) + return 0; + + /* Extract the key OID and any parameters. */ + if ((p8inf = d2i_PKCS8_PRIV_KEY_INFO(NULL, &prvenc, prvlen)) == NULL) + return 0; + /* Shortest prefix is 4 bytes: seq tag/len + octet string tag/len */ + if (!PKCS8_pkey_get0(NULL, &buf, &len, &alg, p8inf)) + goto end; + /* Bail out early if this is some other key type. */ + if (OBJ_obj2nid(alg->algorithm) != evp_type) + goto end; + + /* Get the list of enabled decoders. Their order is not important here. */ + formats = ossl_prov_ctx_get_param( + provctx, OSSL_PKEY_PARAM_ML_KEM_INPUT_FORMATS, NULL); + fmt_slots = ossl_ml_common_pkcs8_fmt_order(v->algorithm_name, codec->p8fmt, + "input", formats); + if (fmt_slots == NULL) + goto end; + + /* Parameters must be absent. */ + X509_ALGOR_get0(NULL, &ptype, NULL, alg); + if (ptype != V_ASN1_UNDEF) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_UNEXPECTED_KEY_PARAMETERS, + "unexpected parameters with a PKCS#8 %s private key", + v->algorithm_name); + goto end; + } + if ((ossl_ssize_t)len < (ossl_ssize_t)sizeof(magic)) + goto end; + + /* Find the matching p8 info slot, that also has the expected length. */ + pos = OPENSSL_load_u32_be(&magic, buf); + for (slot = fmt_slots; (p8fmt = slot->fmt) != NULL; ++slot) { + if (len != (ossl_ssize_t)p8fmt->p8_bytes) + continue; + if (p8fmt->p8_shift == sizeof(magic) + || (magic >> (p8fmt->p8_shift * 8)) == p8fmt->p8_magic) { + pos -= p8fmt->p8_shift; + break; + } + } + if (p8fmt == NULL + || (p8fmt->seed_length > 0 && p8fmt->seed_length != ML_KEM_SEED_BYTES) + || (p8fmt->priv_length > 0 && p8fmt->priv_length != v->prvkey_bytes) + || (p8fmt->pub_length > 0 && p8fmt->pub_length != v->pubkey_bytes)) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_ML_KEM_NO_FORMAT, + "no matching enabled %s private key input formats", + v->algorithm_name); + goto end; + } + + if (p8fmt->seed_length > 0) { + /* Check |seed| tag/len, if not subsumed by |magic|. */ + if (pos + sizeof(uint16_t) == buf + p8fmt->seed_offset) { + pos = OPENSSL_load_u16_be(&seed_magic, pos); + if (seed_magic != p8fmt->seed_magic) + goto end; + } else if (pos != buf + p8fmt->seed_offset) { + goto end; + } + pos += ML_KEM_SEED_BYTES; + } + if (p8fmt->priv_length > 0) { + /* Check |priv| tag/len */ + if (pos + sizeof(uint32_t) == buf + p8fmt->priv_offset) { + pos = OPENSSL_load_u32_be(&magic, pos); + if (magic != p8fmt->priv_magic) + goto end; + } else if (pos != buf + p8fmt->priv_offset) { + goto end; + } + pos += v->prvkey_bytes; + } + if (p8fmt->pub_length > 0) { + if (pos != buf + p8fmt->pub_offset) + goto end; + pos += v->pubkey_bytes; + } + if (pos != buf + len) + goto end; + + /* + * Collect the seed and/or key into a "decoded" private key object, + * to be turned into a real key on provider "load" or "import". + */ + if ((key = ossl_prov_ml_kem_new(provctx, propq, evp_type)) == NULL) + goto end; + + if (p8fmt->seed_length > 0) { + if (!ossl_ml_kem_set_seed(buf + p8fmt->seed_offset, + ML_KEM_SEED_BYTES, key)) { + ERR_raise_data(ERR_LIB_OSSL_DECODER, ERR_R_INTERNAL_ERROR, + "error storing %s private key seed", + v->algorithm_name); + goto end; + } + } + if (p8fmt->priv_length > 0) { + if ((key->encoded_dk = OPENSSL_malloc(p8fmt->priv_length)) == NULL) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, + "error parsing %s private key", + v->algorithm_name); + goto end; + } + memcpy(key->encoded_dk, buf + p8fmt->priv_offset, p8fmt->priv_length); + } + /* Any OQS public key content is ignored */ + ret = key; + + end: + OPENSSL_free(fmt_slots); + PKCS8_PRIV_KEY_INFO_free(p8inf); + if (ret == NULL) + ossl_ml_kem_key_free(key); + return ret; +} + +/* Same as ossl_ml_kem_encode_pubkey, but allocates the output buffer. */ +int ossl_ml_kem_i2d_pubkey(const ML_KEM_KEY *key, unsigned char **out) +{ + size_t publen; + + if (!ossl_ml_kem_have_pubkey(key)) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY, + "no %s public key data available", + key->vinfo->algorithm_name); + return 0; + } + publen = key->vinfo->pubkey_bytes; + + if (out != NULL + && (*out = OPENSSL_malloc(publen)) == NULL) + return 0; + if (!ossl_ml_kem_encode_public_key(*out, publen, key)) { + ERR_raise_data(ERR_LIB_OSSL_ENCODER, ERR_R_INTERNAL_ERROR, + "error encoding %s public key", + key->vinfo->algorithm_name); + OPENSSL_free(*out); + return 0; + } + + return (int)publen; +} + +/* Allocate and encode PKCS#8 private key payload. */ +int ossl_ml_kem_i2d_prvkey(const ML_KEM_KEY *key, uint8_t **out, + PROV_CTX *provctx) +{ + const ML_KEM_VINFO *v = key->vinfo; + const ML_COMMON_CODEC *codec; + ML_COMMON_PKCS8_FMT_PREF *fmt_slots, *slot; + const ML_COMMON_PKCS8_FMT *p8fmt; + uint8_t *buf = NULL, *pos; + const char *formats; + int len = ML_KEM_SEED_BYTES; + int ret = 0; + + /* Not ours to handle */ + if ((codec = ml_kem_get_codec(v->evp_type)) == NULL) + return 0; + + if (!ossl_ml_kem_have_prvkey(key)) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_NOT_A_PRIVATE_KEY, + "no %s private key data available", + key->vinfo->algorithm_name); + return 0; + } + + formats = ossl_prov_ctx_get_param( + provctx, OSSL_PKEY_PARAM_ML_KEM_OUTPUT_FORMATS, NULL); + fmt_slots = ossl_ml_common_pkcs8_fmt_order(v->algorithm_name, codec->p8fmt, + "output", formats); + if (fmt_slots == NULL) + return 0; + + /* If we don't have a seed, skip seedful entries */ + for (slot = fmt_slots; (p8fmt = slot->fmt) != NULL; ++slot) + if (ossl_ml_kem_have_seed(key) || p8fmt->seed_length == 0) + break; + /* No matching table entries, give up */ + if (p8fmt == NULL + || (p8fmt->seed_length > 0 && p8fmt->seed_length != ML_KEM_SEED_BYTES) + || (p8fmt->priv_length > 0 && p8fmt->priv_length != v->prvkey_bytes) + || (p8fmt->pub_length > 0 && p8fmt->pub_length != v->pubkey_bytes)) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_ML_KEM_NO_FORMAT, + "no matching enabled %s private key output formats", + v->algorithm_name); + goto end; + } + len = p8fmt->p8_bytes; + + if (out == NULL) { + ret = len; + goto end; + } + + if ((pos = buf = OPENSSL_malloc((size_t) len)) == NULL) + goto end; + + switch (p8fmt->p8_shift) { + case 0: + pos = OPENSSL_store_u32_be(pos, p8fmt->p8_magic); + break; + case 2: + pos = OPENSSL_store_u16_be(pos, (uint16_t)p8fmt->p8_magic); + break; + case 4: + break; + default: + ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR, + "error encoding %s private key", + v->algorithm_name); + goto end; + } + + if (p8fmt->seed_length != 0) { + /* + * Either the tag/len were already included in |magic| or they require + * us to write two bytes now. + */ + if (pos + sizeof(uint16_t) == buf + p8fmt->seed_offset) + pos = OPENSSL_store_u16_be(pos, p8fmt->seed_magic); + if (pos != buf + p8fmt->seed_offset + || !ossl_ml_kem_encode_seed(pos, ML_KEM_SEED_BYTES, key)) { + ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR, + "error encoding %s private key", + v->algorithm_name); + goto end; + } + pos += ML_KEM_SEED_BYTES; + } + if (p8fmt->priv_length != 0) { + if (pos + sizeof(uint32_t) == buf + p8fmt->priv_offset) + pos = OPENSSL_store_u32_be(pos, p8fmt->priv_magic); + if (pos != buf + p8fmt->priv_offset + || !ossl_ml_kem_encode_private_key(pos, v->prvkey_bytes, key)) { + ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR, + "error encoding %s private key", + v->algorithm_name); + goto end; + } + pos += v->prvkey_bytes; + } + /* OQS form output with tacked-on public key */ + if (p8fmt->pub_length != 0) { + /* The OQS pubkey is never separately DER-wrapped */ + if (pos != buf + p8fmt->pub_offset + || !ossl_ml_kem_encode_public_key(pos, v->pubkey_bytes, key)) { + ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR, + "error encoding %s private key", + v->algorithm_name); + goto end; + } + pos += v->pubkey_bytes; + } + + if (pos == buf + len) { + *out = buf; + ret = len; + } + + end: + OPENSSL_free(fmt_slots); + if (ret == 0) + OPENSSL_free(buf); + return ret; +} + +int ossl_ml_kem_key_to_text(BIO *out, const ML_KEM_KEY *key, int selection) +{ + uint8_t seed[ML_KEM_SEED_BYTES], *prvenc = NULL, *pubenc = NULL; + size_t publen, prvlen; + const char *type_label = NULL; + int ret = 0; + + if (out == NULL || key == NULL) { + ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + type_label = key->vinfo->algorithm_name; + publen = key->vinfo->pubkey_bytes; + prvlen = key->vinfo->prvkey_bytes; + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0 + && (ossl_ml_kem_have_prvkey(key) + || ossl_ml_kem_have_seed(key))) { + if (BIO_printf(out, "%s Private-Key:\n", type_label) <= 0) + return 0; + + if (ossl_ml_kem_have_seed(key)) { + if (!ossl_ml_kem_encode_seed(seed, sizeof(seed), key)) + goto end; + if (!ossl_bio_print_labeled_buf(out, "seed:", seed, sizeof(seed))) + goto end; + } + if (ossl_ml_kem_have_prvkey(key)) { + if ((prvenc = OPENSSL_malloc(prvlen)) == NULL) + return 0; + if (!ossl_ml_kem_encode_private_key(prvenc, prvlen, key)) + goto end; + if (!ossl_bio_print_labeled_buf(out, "dk:", prvenc, prvlen)) + goto end; + } + ret = 1; + } + + /* The public key is output regardless of the selection */ + if (ossl_ml_kem_have_pubkey(key)) { + /* If we did not output private key bits, this is a public key */ + if (ret == 0 && BIO_printf(out, "%s Public-Key:\n", type_label) <= 0) + goto end; + + if ((pubenc = OPENSSL_malloc(key->vinfo->pubkey_bytes)) == NULL + || !ossl_ml_kem_encode_public_key(pubenc, publen, key) + || !ossl_bio_print_labeled_buf(out, "ek:", pubenc, publen)) + goto end; + ret = 1; + } + + /* If we got here, and ret == 0, there was no key material */ + if (ret == 0) + ERR_raise_data(ERR_LIB_PROV, PROV_R_MISSING_KEY, + "no %s key material available", + type_label); + + end: + OPENSSL_free(pubenc); + OPENSSL_free(prvenc); + return ret; +} diff --git a/crypto/openssl/providers/implementations/encode_decode/ml_kem_codecs.h b/crypto/openssl/providers/implementations/encode_decode/ml_kem_codecs.h new file mode 100644 index 000000000000..b8a22201ab17 --- /dev/null +++ b/crypto/openssl/providers/implementations/encode_decode/ml_kem_codecs.h @@ -0,0 +1,39 @@ +/* + * Copyright 2025 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 + */ + +#ifndef PROV_ML_KEM_CODECS_H +# define PROV_ML_KEM_CODECS_H +# pragma once + +# ifndef OPENSSL_NO_ML_KEM +# include <openssl/e_os2.h> +# include "crypto/ml_kem.h" +# include "prov/provider_ctx.h" +# include "ml_common_codecs.h" + +__owur +ML_KEM_KEY *ossl_ml_kem_d2i_PUBKEY(const uint8_t *pubenc, int publen, + int evp_type, PROV_CTX *provctx, + const char *propq); +__owur +ML_KEM_KEY *ossl_ml_kem_d2i_PKCS8(const uint8_t *prvenc, int prvlen, + int evp_type, PROV_CTX *provctx, + const char *propq); +__owur +int ossl_ml_kem_key_to_text(BIO *out, const ML_KEM_KEY *key, int selection); +__owur +__owur +int ossl_ml_kem_i2d_pubkey(const ML_KEM_KEY *key, unsigned char **out); +__owur +__owur +int ossl_ml_kem_i2d_prvkey(const ML_KEM_KEY *key, unsigned char **out, + PROV_CTX *provctx); + +# endif /* OPENSSL_NO_ML_KEM */ +#endif /* PROV_ML_KEM_CODECS_H */ diff --git a/crypto/openssl/providers/implementations/exchange/build.info b/crypto/openssl/providers/implementations/exchange/build.info new file mode 100644 index 000000000000..0333c9f49c2c --- /dev/null +++ b/crypto/openssl/providers/implementations/exchange/build.info @@ -0,0 +1,31 @@ +# We make separate GOAL variables for each algorithm, to make it easy to +# switch each to the Legacy provider when needed. + +$DH_GOAL=../../libdefault.a ../../libfips.a +$ECDH_GOAL=../../libdefault.a ../../libfips.a +$ECX_GOAL=../../libdefault.a ../../libfips.a +$KDF_GOAL=../../libdefault.a ../../libfips.a + +IF[{- !$disabled{dh} -}] + SOURCE[$DH_GOAL]=dh_exch.c +ENDIF + +IF[{- !$disabled{asm} -}] + $ECDEF_s390x=S390X_EC_ASM + + # Now that we have defined all the arch specific variables, use the + # appropriate one, and define the appropriate macros + IF[$ECASM_{- $target{asm_arch} -}] + $ECDEF=$ECDEF_{- $target{asm_arch} -} + ENDIF +ENDIF + +IF[{- !$disabled{ec} -}] + IF[{- !$disabled{ecx} -}] + SOURCE[$ECX_GOAL]=ecx_exch.c + DEFINE[$ECX_GOAL]=$ECDEF + ENDIF + SOURCE[$ECDH_GOAL]=ecdh_exch.c +ENDIF + +SOURCE[$KDF_GOAL]=kdf_exch.c diff --git a/crypto/openssl/providers/implementations/exchange/dh_exch.c b/crypto/openssl/providers/implementations/exchange/dh_exch.c new file mode 100644 index 000000000000..cfb3938810b8 --- /dev/null +++ b/crypto/openssl/providers/implementations/exchange/dh_exch.c @@ -0,0 +1,562 @@ +/* + * Copyright 2019-2024 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 + */ + +/* + * DH low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include <string.h> +#include <openssl/crypto.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/dh.h> +#include <openssl/err.h> +#include <openssl/proverr.h> +#include <openssl/params.h> +#include "prov/providercommon.h" +#include "prov/implementations.h" +#include "prov/provider_ctx.h" +#include "prov/securitycheck.h" +#include "crypto/dh.h" + +static OSSL_FUNC_keyexch_newctx_fn dh_newctx; +static OSSL_FUNC_keyexch_init_fn dh_init; +static OSSL_FUNC_keyexch_set_peer_fn dh_set_peer; +static OSSL_FUNC_keyexch_derive_fn dh_derive; +static OSSL_FUNC_keyexch_freectx_fn dh_freectx; +static OSSL_FUNC_keyexch_dupctx_fn dh_dupctx; +static OSSL_FUNC_keyexch_set_ctx_params_fn dh_set_ctx_params; +static OSSL_FUNC_keyexch_settable_ctx_params_fn dh_settable_ctx_params; +static OSSL_FUNC_keyexch_get_ctx_params_fn dh_get_ctx_params; +static OSSL_FUNC_keyexch_gettable_ctx_params_fn dh_gettable_ctx_params; + +/* + * This type is only really used to handle some legacy related functionality. + * If you need to use other KDF's (such as SSKDF) just use PROV_DH_KDF_NONE + * here and then create and run a KDF after the key is derived. + * Note that X942 has 2 variants of key derivation: + * (1) DH_KDF_X9_42_ASN1 - which contains an ANS1 encoded object that has + * the counter embedded in it. + * (2) DH_KDF_X941_CONCAT - which is the same as ECDH_X963_KDF (which can be + * done by creating a "X963KDF". + */ +enum kdf_type { + PROV_DH_KDF_NONE = 0, + PROV_DH_KDF_X9_42_ASN1 +}; + +/* + * What's passed as an actual key is defined by the KEYMGMT interface. + * We happen to know that our KEYMGMT simply passes DH structures, so + * we use that here too. + */ + +typedef struct { + OSSL_LIB_CTX *libctx; + DH *dh; + DH *dhpeer; + unsigned int pad : 1; + + /* DH KDF */ + /* KDF (if any) to use for DH */ + enum kdf_type kdf_type; + /* Message digest to use for key derivation */ + EVP_MD *kdf_md; + /* User key material */ + unsigned char *kdf_ukm; + size_t kdf_ukmlen; + /* KDF output length */ + size_t kdf_outlen; + char *kdf_cekalg; + OSSL_FIPS_IND_DECLARE +} PROV_DH_CTX; + +static void *dh_newctx(void *provctx) +{ + PROV_DH_CTX *pdhctx; + + if (!ossl_prov_is_running()) + return NULL; + + pdhctx = OPENSSL_zalloc(sizeof(PROV_DH_CTX)); + if (pdhctx == NULL) + return NULL; + OSSL_FIPS_IND_INIT(pdhctx) + pdhctx->libctx = PROV_LIBCTX_OF(provctx); + pdhctx->kdf_type = PROV_DH_KDF_NONE; + return pdhctx; +} + +#ifdef FIPS_MODULE +static int dh_check_key(PROV_DH_CTX *ctx) +{ + int key_approved = ossl_dh_check_key(ctx->dh); + + if (!key_approved) { + if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE0, + ctx->libctx, "DH Init", "DH Key", + ossl_fips_config_securitycheck_enabled)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + } + return 1; +} + +static int digest_check(PROV_DH_CTX *ctx, const EVP_MD *md) +{ + return ossl_fips_ind_digest_exch_check(OSSL_FIPS_IND_GET(ctx), + OSSL_FIPS_IND_SETTABLE1, ctx->libctx, + md, "DH Set Ctx"); +} +#endif + +static int dh_init(void *vpdhctx, void *vdh, const OSSL_PARAM params[]) +{ + PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx; + + if (!ossl_prov_is_running() + || pdhctx == NULL + || vdh == NULL + || !DH_up_ref(vdh)) + return 0; + DH_free(pdhctx->dh); + pdhctx->dh = vdh; + pdhctx->kdf_type = PROV_DH_KDF_NONE; + + OSSL_FIPS_IND_SET_APPROVED(pdhctx) + if (!dh_set_ctx_params(pdhctx, params)) + return 0; +#ifdef FIPS_MODULE + if (!dh_check_key(pdhctx)) + return 0; +#endif + return 1; +} + +/* The 2 parties must share the same domain parameters */ +static int dh_match_params(DH *priv, DH *peer) +{ + int ret; + FFC_PARAMS *dhparams_priv = ossl_dh_get0_params(priv); + FFC_PARAMS *dhparams_peer = ossl_dh_get0_params(peer); + + ret = dhparams_priv != NULL + && dhparams_peer != NULL + && ossl_ffc_params_cmp(dhparams_priv, dhparams_peer, 1); + if (!ret) + ERR_raise(ERR_LIB_PROV, PROV_R_MISMATCHING_DOMAIN_PARAMETERS); + return ret; +} + +static int dh_set_peer(void *vpdhctx, void *vdh) +{ + PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx; + + if (!ossl_prov_is_running() + || pdhctx == NULL + || vdh == NULL + || !dh_match_params(vdh, pdhctx->dh) + || !DH_up_ref(vdh)) + return 0; + DH_free(pdhctx->dhpeer); + pdhctx->dhpeer = vdh; + return 1; +} + +static int dh_plain_derive(void *vpdhctx, + unsigned char *secret, size_t *secretlen, + size_t outlen, unsigned int pad) +{ + PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx; + int ret; + size_t dhsize; + const BIGNUM *pub_key = NULL; + + if (pdhctx->dh == NULL || pdhctx->dhpeer == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + return 0; + } + + dhsize = (size_t)DH_size(pdhctx->dh); + if (secret == NULL) { + *secretlen = dhsize; + return 1; + } + if (outlen < dhsize) { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + return 0; + } + + DH_get0_key(pdhctx->dhpeer, &pub_key, NULL); + if (pad) + ret = DH_compute_key_padded(secret, pub_key, pdhctx->dh); + else + ret = DH_compute_key(secret, pub_key, pdhctx->dh); + if (ret <= 0) + return 0; + + *secretlen = ret; + return 1; +} + +static int dh_X9_42_kdf_derive(void *vpdhctx, unsigned char *secret, + size_t *secretlen, size_t outlen) +{ + PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx; + unsigned char *stmp = NULL; + size_t stmplen; + int ret = 0; + + if (secret == NULL) { + *secretlen = pdhctx->kdf_outlen; + return 1; + } + + if (pdhctx->kdf_outlen > outlen) { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + return 0; + } + if (!dh_plain_derive(pdhctx, NULL, &stmplen, 0, 1)) + return 0; + if ((stmp = OPENSSL_secure_malloc(stmplen)) == NULL) + return 0; + if (!dh_plain_derive(pdhctx, stmp, &stmplen, stmplen, 1)) + goto err; + + /* Do KDF stuff */ + if (pdhctx->kdf_type == PROV_DH_KDF_X9_42_ASN1) { + if (!ossl_dh_kdf_X9_42_asn1(secret, pdhctx->kdf_outlen, + stmp, stmplen, + pdhctx->kdf_cekalg, + pdhctx->kdf_ukm, + pdhctx->kdf_ukmlen, + pdhctx->kdf_md, + pdhctx->libctx, NULL)) + goto err; + } + *secretlen = pdhctx->kdf_outlen; + ret = 1; +err: + OPENSSL_secure_clear_free(stmp, stmplen); + return ret; +} + +static int dh_derive(void *vpdhctx, unsigned char *secret, + size_t *psecretlen, size_t outlen) +{ + PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx; + + if (!ossl_prov_is_running()) + return 0; + + switch (pdhctx->kdf_type) { + case PROV_DH_KDF_NONE: + return dh_plain_derive(pdhctx, secret, psecretlen, outlen, + pdhctx->pad); + case PROV_DH_KDF_X9_42_ASN1: + return dh_X9_42_kdf_derive(pdhctx, secret, psecretlen, outlen); + default: + break; + } + return 0; +} + +static void dh_freectx(void *vpdhctx) +{ + PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx; + + OPENSSL_free(pdhctx->kdf_cekalg); + DH_free(pdhctx->dh); + DH_free(pdhctx->dhpeer); + EVP_MD_free(pdhctx->kdf_md); + OPENSSL_clear_free(pdhctx->kdf_ukm, pdhctx->kdf_ukmlen); + + OPENSSL_free(pdhctx); +} + +static void *dh_dupctx(void *vpdhctx) +{ + PROV_DH_CTX *srcctx = (PROV_DH_CTX *)vpdhctx; + PROV_DH_CTX *dstctx; + + if (!ossl_prov_is_running()) + return NULL; + + dstctx = OPENSSL_zalloc(sizeof(*srcctx)); + if (dstctx == NULL) + return NULL; + + *dstctx = *srcctx; + dstctx->dh = NULL; + dstctx->dhpeer = NULL; + dstctx->kdf_md = NULL; + dstctx->kdf_ukm = NULL; + dstctx->kdf_cekalg = NULL; + + if (srcctx->dh != NULL && !DH_up_ref(srcctx->dh)) + goto err; + else + dstctx->dh = srcctx->dh; + + if (srcctx->dhpeer != NULL && !DH_up_ref(srcctx->dhpeer)) + goto err; + else + dstctx->dhpeer = srcctx->dhpeer; + + if (srcctx->kdf_md != NULL && !EVP_MD_up_ref(srcctx->kdf_md)) + goto err; + else + dstctx->kdf_md = srcctx->kdf_md; + + /* Duplicate UKM data if present */ + if (srcctx->kdf_ukm != NULL && srcctx->kdf_ukmlen > 0) { + dstctx->kdf_ukm = OPENSSL_memdup(srcctx->kdf_ukm, + srcctx->kdf_ukmlen); + if (dstctx->kdf_ukm == NULL) + goto err; + } + + if (srcctx->kdf_cekalg != NULL) { + dstctx->kdf_cekalg = OPENSSL_strdup(srcctx->kdf_cekalg); + if (dstctx->kdf_cekalg == NULL) + goto err; + } + + return dstctx; +err: + dh_freectx(dstctx); + return NULL; +} + +static int dh_set_ctx_params(void *vpdhctx, const OSSL_PARAM params[]) +{ + PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx; + const OSSL_PARAM *p; + unsigned int pad; + char name[80] = { '\0' }; /* should be big enough */ + char *str = NULL; + + if (pdhctx == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 1; + + if (!OSSL_FIPS_IND_SET_CTX_PARAM(pdhctx, OSSL_FIPS_IND_SETTABLE0, params, + OSSL_EXCHANGE_PARAM_FIPS_KEY_CHECK)) + return 0; + if (!OSSL_FIPS_IND_SET_CTX_PARAM(pdhctx, OSSL_FIPS_IND_SETTABLE1, params, + OSSL_EXCHANGE_PARAM_FIPS_DIGEST_CHECK)) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_TYPE); + if (p != NULL) { + str = name; + if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(name))) + return 0; + + if (name[0] == '\0') + pdhctx->kdf_type = PROV_DH_KDF_NONE; + else if (strcmp(name, OSSL_KDF_NAME_X942KDF_ASN1) == 0) + pdhctx->kdf_type = PROV_DH_KDF_X9_42_ASN1; + else + return 0; + } + p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_DIGEST); + if (p != NULL) { + char mdprops[80] = { '\0' }; /* should be big enough */ + + str = name; + if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(name))) + return 0; + + str = mdprops; + p = OSSL_PARAM_locate_const(params, + OSSL_EXCHANGE_PARAM_KDF_DIGEST_PROPS); + + if (p != NULL) { + if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(mdprops))) + return 0; + } + + EVP_MD_free(pdhctx->kdf_md); + pdhctx->kdf_md = EVP_MD_fetch(pdhctx->libctx, name, mdprops); + if (pdhctx->kdf_md == NULL) + return 0; + /* XOF digests are not allowed */ + if (EVP_MD_xof(pdhctx->kdf_md)) { + ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED); + return 0; + } +#ifdef FIPS_MODULE + if (!digest_check(pdhctx, pdhctx->kdf_md)) { + EVP_MD_free(pdhctx->kdf_md); + pdhctx->kdf_md = NULL; + return 0; + } +#endif + } + + p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_OUTLEN); + if (p != NULL) { + size_t outlen; + + if (!OSSL_PARAM_get_size_t(p, &outlen)) + return 0; + pdhctx->kdf_outlen = outlen; + } + + p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_UKM); + if (p != NULL) { + void *tmp_ukm = NULL; + size_t tmp_ukmlen; + + OPENSSL_free(pdhctx->kdf_ukm); + pdhctx->kdf_ukm = NULL; + pdhctx->kdf_ukmlen = 0; + /* ukm is an optional field so it can be NULL */ + if (p->data != NULL && p->data_size != 0) { + if (!OSSL_PARAM_get_octet_string(p, &tmp_ukm, 0, &tmp_ukmlen)) + return 0; + pdhctx->kdf_ukm = tmp_ukm; + pdhctx->kdf_ukmlen = tmp_ukmlen; + } + } + + p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_PAD); + if (p != NULL) { + if (!OSSL_PARAM_get_uint(p, &pad)) + return 0; + pdhctx->pad = pad ? 1 : 0; + } + + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_CEK_ALG); + if (p != NULL) { + str = name; + + OPENSSL_free(pdhctx->kdf_cekalg); + pdhctx->kdf_cekalg = NULL; + if (p->data != NULL && p->data_size != 0) { + if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(name))) + return 0; + pdhctx->kdf_cekalg = OPENSSL_strdup(name); + if (pdhctx->kdf_cekalg == NULL) + return 0; + } + } + return 1; +} + +static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_int(OSSL_EXCHANGE_PARAM_PAD, NULL), + OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST_PROPS, NULL, 0), + OSSL_PARAM_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN, NULL), + OSSL_PARAM_octet_string(OSSL_EXCHANGE_PARAM_KDF_UKM, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_CEK_ALG, NULL, 0), + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_EXCHANGE_PARAM_FIPS_KEY_CHECK) + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_EXCHANGE_PARAM_FIPS_DIGEST_CHECK) + OSSL_PARAM_END +}; + +static const OSSL_PARAM *dh_settable_ctx_params(ossl_unused void *vpdhctx, + ossl_unused void *provctx) +{ + return known_settable_ctx_params; +} + +static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST, NULL, 0), + OSSL_PARAM_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN, NULL), + OSSL_PARAM_DEFN(OSSL_EXCHANGE_PARAM_KDF_UKM, OSSL_PARAM_OCTET_PTR, + NULL, 0), + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_CEK_ALG, NULL, 0), + OSSL_FIPS_IND_GETTABLE_CTX_PARAM() + OSSL_PARAM_END +}; + +static const OSSL_PARAM *dh_gettable_ctx_params(ossl_unused void *vpdhctx, + ossl_unused void *provctx) +{ + return known_gettable_ctx_params; +} + +static int dh_get_ctx_params(void *vpdhctx, OSSL_PARAM params[]) +{ + PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx; + OSSL_PARAM *p; + + if (pdhctx == NULL) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_TYPE); + if (p != NULL) { + const char *kdf_type = NULL; + + switch (pdhctx->kdf_type) { + case PROV_DH_KDF_NONE: + kdf_type = ""; + break; + case PROV_DH_KDF_X9_42_ASN1: + kdf_type = OSSL_KDF_NAME_X942KDF_ASN1; + break; + default: + return 0; + } + + if (!OSSL_PARAM_set_utf8_string(p, kdf_type)) + return 0; + } + + p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_DIGEST); + if (p != NULL + && !OSSL_PARAM_set_utf8_string(p, pdhctx->kdf_md == NULL + ? "" + : EVP_MD_get0_name(pdhctx->kdf_md))) { + return 0; + } + + p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_OUTLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, pdhctx->kdf_outlen)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_UKM); + if (p != NULL + && !OSSL_PARAM_set_octet_ptr(p, pdhctx->kdf_ukm, pdhctx->kdf_ukmlen)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_CEK_ALG); + if (p != NULL + && !OSSL_PARAM_set_utf8_string(p, pdhctx->kdf_cekalg == NULL + ? "" : pdhctx->kdf_cekalg)) + return 0; + if (!OSSL_FIPS_IND_GET_CTX_PARAM(pdhctx, params)) + return 0; + return 1; +} + +const OSSL_DISPATCH ossl_dh_keyexch_functions[] = { + { OSSL_FUNC_KEYEXCH_NEWCTX, (void (*)(void))dh_newctx }, + { OSSL_FUNC_KEYEXCH_INIT, (void (*)(void))dh_init }, + { OSSL_FUNC_KEYEXCH_DERIVE, (void (*)(void))dh_derive }, + { OSSL_FUNC_KEYEXCH_SET_PEER, (void (*)(void))dh_set_peer }, + { OSSL_FUNC_KEYEXCH_FREECTX, (void (*)(void))dh_freectx }, + { OSSL_FUNC_KEYEXCH_DUPCTX, (void (*)(void))dh_dupctx }, + { OSSL_FUNC_KEYEXCH_SET_CTX_PARAMS, (void (*)(void))dh_set_ctx_params }, + { OSSL_FUNC_KEYEXCH_SETTABLE_CTX_PARAMS, + (void (*)(void))dh_settable_ctx_params }, + { OSSL_FUNC_KEYEXCH_GET_CTX_PARAMS, (void (*)(void))dh_get_ctx_params }, + { OSSL_FUNC_KEYEXCH_GETTABLE_CTX_PARAMS, + (void (*)(void))dh_gettable_ctx_params }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/exchange/ecdh_exch.c b/crypto/openssl/providers/implementations/exchange/ecdh_exch.c new file mode 100644 index 000000000000..58fbc7bc09f0 --- /dev/null +++ b/crypto/openssl/providers/implementations/exchange/ecdh_exch.c @@ -0,0 +1,650 @@ +/* + * Copyright 2020-2024 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 + */ + +/* + * ECDH low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include <string.h> +#include <openssl/crypto.h> +#include <openssl/evp.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/ec.h> +#include <openssl/params.h> +#include <openssl/err.h> +#include <openssl/proverr.h> +#include "prov/provider_ctx.h" +#include "prov/providercommon.h" +#include "prov/implementations.h" +#include "prov/securitycheck.h" +#include "crypto/ec.h" /* ossl_ecdh_kdf_X9_63() */ + +static OSSL_FUNC_keyexch_newctx_fn ecdh_newctx; +static OSSL_FUNC_keyexch_init_fn ecdh_init; +static OSSL_FUNC_keyexch_set_peer_fn ecdh_set_peer; +static OSSL_FUNC_keyexch_derive_fn ecdh_derive; +static OSSL_FUNC_keyexch_freectx_fn ecdh_freectx; +static OSSL_FUNC_keyexch_dupctx_fn ecdh_dupctx; +static OSSL_FUNC_keyexch_set_ctx_params_fn ecdh_set_ctx_params; +static OSSL_FUNC_keyexch_settable_ctx_params_fn ecdh_settable_ctx_params; +static OSSL_FUNC_keyexch_get_ctx_params_fn ecdh_get_ctx_params; +static OSSL_FUNC_keyexch_gettable_ctx_params_fn ecdh_gettable_ctx_params; + +enum kdf_type { + PROV_ECDH_KDF_NONE = 0, + PROV_ECDH_KDF_X9_63 +}; + +/* + * What's passed as an actual key is defined by the KEYMGMT interface. + * We happen to know that our KEYMGMT simply passes EC_KEY structures, so + * we use that here too. + */ + +typedef struct { + OSSL_LIB_CTX *libctx; + + EC_KEY *k; + EC_KEY *peerk; + + /* + * ECDH cofactor mode: + * + * . 0 disabled + * . 1 enabled + * . -1 use cofactor mode set for k + */ + int cofactor_mode; + + /************ + * ECDH KDF * + ************/ + /* KDF (if any) to use for ECDH */ + enum kdf_type kdf_type; + /* Message digest to use for key derivation */ + EVP_MD *kdf_md; + /* User key material */ + unsigned char *kdf_ukm; + size_t kdf_ukmlen; + /* KDF output length */ + size_t kdf_outlen; + OSSL_FIPS_IND_DECLARE +} PROV_ECDH_CTX; + +static +void *ecdh_newctx(void *provctx) +{ + PROV_ECDH_CTX *pectx; + + if (!ossl_prov_is_running()) + return NULL; + + pectx = OPENSSL_zalloc(sizeof(*pectx)); + if (pectx == NULL) + return NULL; + + pectx->libctx = PROV_LIBCTX_OF(provctx); + pectx->cofactor_mode = -1; + pectx->kdf_type = PROV_ECDH_KDF_NONE; + OSSL_FIPS_IND_INIT(pectx) + + return (void *)pectx; +} + +static +int ecdh_init(void *vpecdhctx, void *vecdh, const OSSL_PARAM params[]) +{ + PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx; + + if (!ossl_prov_is_running() + || pecdhctx == NULL + || vecdh == NULL + || (EC_KEY_get0_group(vecdh) == NULL) + || !EC_KEY_up_ref(vecdh)) + return 0; + EC_KEY_free(pecdhctx->k); + pecdhctx->k = vecdh; + pecdhctx->cofactor_mode = -1; + pecdhctx->kdf_type = PROV_ECDH_KDF_NONE; + + OSSL_FIPS_IND_SET_APPROVED(pecdhctx) + if (!ecdh_set_ctx_params(pecdhctx, params)) + return 0; +#ifdef FIPS_MODULE + if (!ossl_fips_ind_ec_key_check(OSSL_FIPS_IND_GET(pecdhctx), + OSSL_FIPS_IND_SETTABLE0, pecdhctx->libctx, + EC_KEY_get0_group(vecdh), "ECDH Init", 1)) + return 0; +#endif + return 1; +} + +static +int ecdh_match_params(const EC_KEY *priv, const EC_KEY *peer) +{ + int ret; + BN_CTX *ctx = NULL; + const EC_GROUP *group_priv = EC_KEY_get0_group(priv); + const EC_GROUP *group_peer = EC_KEY_get0_group(peer); + + ctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(priv)); + if (ctx == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_BN_LIB); + return 0; + } + ret = group_priv != NULL + && group_peer != NULL + && EC_GROUP_cmp(group_priv, group_peer, ctx) == 0; + if (!ret) + ERR_raise(ERR_LIB_PROV, PROV_R_MISMATCHING_DOMAIN_PARAMETERS); + BN_CTX_free(ctx); + return ret; +} + +static +int ecdh_set_peer(void *vpecdhctx, void *vecdh) +{ + PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx; + + if (!ossl_prov_is_running() + || pecdhctx == NULL + || vecdh == NULL + || !ecdh_match_params(pecdhctx->k, vecdh)) + return 0; +#ifdef FIPS_MODULE + if (!ossl_fips_ind_ec_key_check(OSSL_FIPS_IND_GET(pecdhctx), + OSSL_FIPS_IND_SETTABLE0, pecdhctx->libctx, + EC_KEY_get0_group(vecdh), "ECDH Set Peer", + 1)) + return 0; +#endif + if (!EC_KEY_up_ref(vecdh)) + return 0; + + EC_KEY_free(pecdhctx->peerk); + pecdhctx->peerk = vecdh; + return 1; +} + +static +void ecdh_freectx(void *vpecdhctx) +{ + PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx; + + EC_KEY_free(pecdhctx->k); + EC_KEY_free(pecdhctx->peerk); + + EVP_MD_free(pecdhctx->kdf_md); + OPENSSL_clear_free(pecdhctx->kdf_ukm, pecdhctx->kdf_ukmlen); + + OPENSSL_free(pecdhctx); +} + +static +void *ecdh_dupctx(void *vpecdhctx) +{ + PROV_ECDH_CTX *srcctx = (PROV_ECDH_CTX *)vpecdhctx; + PROV_ECDH_CTX *dstctx; + + if (!ossl_prov_is_running()) + return NULL; + + dstctx = OPENSSL_zalloc(sizeof(*srcctx)); + if (dstctx == NULL) + return NULL; + + *dstctx = *srcctx; + + /* clear all pointers */ + + dstctx->k= NULL; + dstctx->peerk = NULL; + dstctx->kdf_md = NULL; + dstctx->kdf_ukm = NULL; + + /* up-ref all ref-counted objects referenced in dstctx */ + + if (srcctx->k != NULL && !EC_KEY_up_ref(srcctx->k)) + goto err; + else + dstctx->k = srcctx->k; + + if (srcctx->peerk != NULL && !EC_KEY_up_ref(srcctx->peerk)) + goto err; + else + dstctx->peerk = srcctx->peerk; + + if (srcctx->kdf_md != NULL && !EVP_MD_up_ref(srcctx->kdf_md)) + goto err; + else + dstctx->kdf_md = srcctx->kdf_md; + + /* Duplicate UKM data if present */ + if (srcctx->kdf_ukm != NULL && srcctx->kdf_ukmlen > 0) { + dstctx->kdf_ukm = OPENSSL_memdup(srcctx->kdf_ukm, + srcctx->kdf_ukmlen); + if (dstctx->kdf_ukm == NULL) + goto err; + } + + return dstctx; + + err: + ecdh_freectx(dstctx); + return NULL; +} + +static +int ecdh_set_ctx_params(void *vpecdhctx, const OSSL_PARAM params[]) +{ + char name[80] = { '\0' }; /* should be big enough */ + char *str = NULL; + PROV_ECDH_CTX *pectx = (PROV_ECDH_CTX *)vpecdhctx; + const OSSL_PARAM *p; + + if (pectx == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 1; + + if (!OSSL_FIPS_IND_SET_CTX_PARAM(pectx, OSSL_FIPS_IND_SETTABLE0, params, + OSSL_EXCHANGE_PARAM_FIPS_KEY_CHECK)) + return 0; + if (!OSSL_FIPS_IND_SET_CTX_PARAM(pectx, OSSL_FIPS_IND_SETTABLE1, params, + OSSL_EXCHANGE_PARAM_FIPS_DIGEST_CHECK)) + return 0; + if (!OSSL_FIPS_IND_SET_CTX_PARAM(pectx, OSSL_FIPS_IND_SETTABLE2, params, + OSSL_EXCHANGE_PARAM_FIPS_ECDH_COFACTOR_CHECK)) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE); + if (p != NULL) { + int mode; + + if (!OSSL_PARAM_get_int(p, &mode)) + return 0; + + if (mode < -1 || mode > 1) + return 0; + + pectx->cofactor_mode = mode; + } + + p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_TYPE); + if (p != NULL) { + str = name; + if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(name))) + return 0; + + if (name[0] == '\0') + pectx->kdf_type = PROV_ECDH_KDF_NONE; + else if (strcmp(name, OSSL_KDF_NAME_X963KDF) == 0) + pectx->kdf_type = PROV_ECDH_KDF_X9_63; + else + return 0; + } + + p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_DIGEST); + if (p != NULL) { + char mdprops[80] = { '\0' }; /* should be big enough */ + + str = name; + if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(name))) + return 0; + + str = mdprops; + p = OSSL_PARAM_locate_const(params, + OSSL_EXCHANGE_PARAM_KDF_DIGEST_PROPS); + + if (p != NULL) { + if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(mdprops))) + return 0; + } + + EVP_MD_free(pectx->kdf_md); + pectx->kdf_md = EVP_MD_fetch(pectx->libctx, name, mdprops); + if (pectx->kdf_md == NULL) + return 0; + /* XOF digests are not allowed */ + if (EVP_MD_xof(pectx->kdf_md)) { + ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED); + return 0; + } +#ifdef FIPS_MODULE + if (!ossl_fips_ind_digest_exch_check(OSSL_FIPS_IND_GET(pectx), + OSSL_FIPS_IND_SETTABLE1, pectx->libctx, + pectx->kdf_md, "ECDH Set Ctx")) { + EVP_MD_free(pectx->kdf_md); + pectx->kdf_md = NULL; + return 0; + } +#endif + } + + p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_OUTLEN); + if (p != NULL) { + size_t outlen; + + if (!OSSL_PARAM_get_size_t(p, &outlen)) + return 0; + pectx->kdf_outlen = outlen; + } + + p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_UKM); + if (p != NULL) { + void *tmp_ukm = NULL; + size_t tmp_ukmlen; + + if (!OSSL_PARAM_get_octet_string(p, &tmp_ukm, 0, &tmp_ukmlen)) + return 0; + OPENSSL_free(pectx->kdf_ukm); + pectx->kdf_ukm = tmp_ukm; + pectx->kdf_ukmlen = tmp_ukmlen; + } + + return 1; +} + +static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_int(OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE, NULL), + OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST_PROPS, NULL, 0), + OSSL_PARAM_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN, NULL), + OSSL_PARAM_octet_string(OSSL_EXCHANGE_PARAM_KDF_UKM, NULL, 0), + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_EXCHANGE_PARAM_FIPS_KEY_CHECK) + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_EXCHANGE_PARAM_FIPS_DIGEST_CHECK) + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_EXCHANGE_PARAM_FIPS_ECDH_COFACTOR_CHECK) + OSSL_PARAM_END +}; + +static +const OSSL_PARAM *ecdh_settable_ctx_params(ossl_unused void *vpecdhctx, + ossl_unused void *provctx) +{ + return known_settable_ctx_params; +} + +static +int ecdh_get_ctx_params(void *vpecdhctx, OSSL_PARAM params[]) +{ + PROV_ECDH_CTX *pectx = (PROV_ECDH_CTX *)vpecdhctx; + OSSL_PARAM *p; + + if (pectx == NULL) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE); + if (p != NULL) { + int mode = pectx->cofactor_mode; + + if (mode == -1) { + /* check what is the default for pecdhctx->k */ + mode = EC_KEY_get_flags(pectx->k) & EC_FLAG_COFACTOR_ECDH ? 1 : 0; + } + + if (!OSSL_PARAM_set_int(p, mode)) + return 0; + } + + p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_TYPE); + if (p != NULL) { + const char *kdf_type = NULL; + + switch (pectx->kdf_type) { + case PROV_ECDH_KDF_NONE: + kdf_type = ""; + break; + case PROV_ECDH_KDF_X9_63: + kdf_type = OSSL_KDF_NAME_X963KDF; + break; + default: + return 0; + } + + if (!OSSL_PARAM_set_utf8_string(p, kdf_type)) + return 0; + } + + p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_DIGEST); + if (p != NULL + && !OSSL_PARAM_set_utf8_string(p, pectx->kdf_md == NULL + ? "" + : EVP_MD_get0_name(pectx->kdf_md))) { + return 0; + } + + p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_OUTLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, pectx->kdf_outlen)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_UKM); + if (p != NULL && + !OSSL_PARAM_set_octet_ptr(p, pectx->kdf_ukm, pectx->kdf_ukmlen)) + return 0; + if (!OSSL_FIPS_IND_GET_CTX_PARAM(pectx, params)) + return 0; + return 1; +} + +static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_PARAM_int(OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE, NULL), + OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST, NULL, 0), + OSSL_PARAM_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN, NULL), + OSSL_PARAM_DEFN(OSSL_EXCHANGE_PARAM_KDF_UKM, OSSL_PARAM_OCTET_PTR, + NULL, 0), + OSSL_FIPS_IND_GETTABLE_CTX_PARAM() + OSSL_PARAM_END +}; + +static +const OSSL_PARAM *ecdh_gettable_ctx_params(ossl_unused void *vpecdhctx, + ossl_unused void *provctx) +{ + return known_gettable_ctx_params; +} + +static ossl_inline +size_t ecdh_size(const EC_KEY *k) +{ + size_t degree = 0; + const EC_GROUP *group; + + if (k == NULL + || (group = EC_KEY_get0_group(k)) == NULL) + return 0; + + degree = EC_GROUP_get_degree(group); + + return (degree + 7) / 8; +} + +static ossl_inline +int ecdh_plain_derive(void *vpecdhctx, unsigned char *secret, + size_t *psecretlen, size_t outlen) +{ + PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx; + int retlen, ret = 0; + size_t ecdhsize, size; + const EC_POINT *ppubkey = NULL; + EC_KEY *privk = NULL; + const EC_GROUP *group; + const BIGNUM *cofactor; + int key_cofactor_mode; + int has_cofactor; +#ifdef FIPS_MODULE + int cofactor_approved = 0; +#endif + + if (pecdhctx->k == NULL || pecdhctx->peerk == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + return 0; + } + + ecdhsize = ecdh_size(pecdhctx->k); + if (secret == NULL) { + *psecretlen = ecdhsize; + return 1; + } + + if ((group = EC_KEY_get0_group(pecdhctx->k)) == NULL + || (cofactor = EC_GROUP_get0_cofactor(group)) == NULL) + return 0; + + has_cofactor = !BN_is_one(cofactor); + + /* + * NB: unlike PKCS#3 DH, if outlen is less than maximum size this is not + * an error, the result is truncated. + */ + size = outlen < ecdhsize ? outlen : ecdhsize; + + /* + * The ctx->cofactor_mode flag has precedence over the + * cofactor_mode flag set on ctx->k. + * + * - if ctx->cofactor_mode == -1, use ctx->k directly + * - if ctx->cofactor_mode == key_cofactor_mode, use ctx->k directly + * - if ctx->cofactor_mode != key_cofactor_mode: + * - if ctx->k->cofactor == 1, the cofactor_mode flag is irrelevant, use + * ctx->k directly + * - if ctx->k->cofactor != 1, use a duplicate of ctx->k with the flag + * set to ctx->cofactor_mode + */ + key_cofactor_mode = + (EC_KEY_get_flags(pecdhctx->k) & EC_FLAG_COFACTOR_ECDH) ? 1 : 0; + if (pecdhctx->cofactor_mode != -1 + && pecdhctx->cofactor_mode != key_cofactor_mode + && has_cofactor) { + if ((privk = EC_KEY_dup(pecdhctx->k)) == NULL) + return 0; + + if (pecdhctx->cofactor_mode == 1) { + EC_KEY_set_flags(privk, EC_FLAG_COFACTOR_ECDH); +#ifdef FIPS_MODULE + cofactor_approved = 1; +#endif + } else { + EC_KEY_clear_flags(privk, EC_FLAG_COFACTOR_ECDH); + } + } else { + privk = pecdhctx->k; +#ifdef FIPS_MODULE + cofactor_approved = key_cofactor_mode; +#endif + } + +#ifdef FIPS_MODULE + /* + * SP800-56A r3 Section 5.7.1.2 requires ECC Cofactor DH to be used. + * This applies to the 'B' and 'K' curves that have cofactors that are not 1. + */ + if (has_cofactor && !cofactor_approved) { + if (!OSSL_FIPS_IND_ON_UNAPPROVED(pecdhctx, OSSL_FIPS_IND_SETTABLE2, + pecdhctx->libctx, "ECDH", "Cofactor", + ossl_fips_config_ecdh_cofactor_check)) { + ERR_raise(ERR_LIB_PROV, PROV_R_COFACTOR_REQUIRED); + goto end; + } + } +#endif + + ppubkey = EC_KEY_get0_public_key(pecdhctx->peerk); + + retlen = ECDH_compute_key(secret, size, ppubkey, privk, NULL); + + if (retlen <= 0) + goto end; + + *psecretlen = retlen; + ret = 1; + + end: + if (privk != pecdhctx->k) + EC_KEY_free(privk); + return ret; +} + +static ossl_inline +int ecdh_X9_63_kdf_derive(void *vpecdhctx, unsigned char *secret, + size_t *psecretlen, size_t outlen) +{ + PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx; + unsigned char *stmp = NULL; + size_t stmplen; + int ret = 0; + + if (secret == NULL) { + *psecretlen = pecdhctx->kdf_outlen; + return 1; + } + + if (pecdhctx->kdf_outlen > outlen) { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + return 0; + } + if (!ecdh_plain_derive(vpecdhctx, NULL, &stmplen, 0)) + return 0; + if ((stmp = OPENSSL_secure_malloc(stmplen)) == NULL) + return 0; + if (!ecdh_plain_derive(vpecdhctx, stmp, &stmplen, stmplen)) + goto err; + + /* Do KDF stuff */ + if (!ossl_ecdh_kdf_X9_63(secret, pecdhctx->kdf_outlen, + stmp, stmplen, + pecdhctx->kdf_ukm, + pecdhctx->kdf_ukmlen, + pecdhctx->kdf_md, + pecdhctx->libctx, NULL)) + goto err; + *psecretlen = pecdhctx->kdf_outlen; + ret = 1; + + err: + OPENSSL_secure_clear_free(stmp, stmplen); + return ret; +} + +static +int ecdh_derive(void *vpecdhctx, unsigned char *secret, + size_t *psecretlen, size_t outlen) +{ + PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx; + + switch (pecdhctx->kdf_type) { + case PROV_ECDH_KDF_NONE: + return ecdh_plain_derive(vpecdhctx, secret, psecretlen, outlen); + case PROV_ECDH_KDF_X9_63: + return ecdh_X9_63_kdf_derive(vpecdhctx, secret, psecretlen, outlen); + default: + break; + } + return 0; +} + +const OSSL_DISPATCH ossl_ecdh_keyexch_functions[] = { + { OSSL_FUNC_KEYEXCH_NEWCTX, (void (*)(void))ecdh_newctx }, + { OSSL_FUNC_KEYEXCH_INIT, (void (*)(void))ecdh_init }, + { OSSL_FUNC_KEYEXCH_DERIVE, (void (*)(void))ecdh_derive }, + { OSSL_FUNC_KEYEXCH_SET_PEER, (void (*)(void))ecdh_set_peer }, + { OSSL_FUNC_KEYEXCH_FREECTX, (void (*)(void))ecdh_freectx }, + { OSSL_FUNC_KEYEXCH_DUPCTX, (void (*)(void))ecdh_dupctx }, + { OSSL_FUNC_KEYEXCH_SET_CTX_PARAMS, (void (*)(void))ecdh_set_ctx_params }, + { OSSL_FUNC_KEYEXCH_SETTABLE_CTX_PARAMS, + (void (*)(void))ecdh_settable_ctx_params }, + { OSSL_FUNC_KEYEXCH_GET_CTX_PARAMS, (void (*)(void))ecdh_get_ctx_params }, + { OSSL_FUNC_KEYEXCH_GETTABLE_CTX_PARAMS, + (void (*)(void))ecdh_gettable_ctx_params }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/exchange/ecx_exch.c b/crypto/openssl/providers/implementations/exchange/ecx_exch.c new file mode 100644 index 000000000000..28e2ff61c7cc --- /dev/null +++ b/crypto/openssl/providers/implementations/exchange/ecx_exch.c @@ -0,0 +1,227 @@ +/* + * Copyright 2020-2024 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 + */ + +#include <openssl/crypto.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/params.h> +#include <openssl/err.h> +#include <openssl/proverr.h> +#include "internal/cryptlib.h" +#include "crypto/ecx.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" +#include "prov/securitycheck.h" + +static OSSL_FUNC_keyexch_newctx_fn x25519_newctx; +static OSSL_FUNC_keyexch_newctx_fn x448_newctx; +static OSSL_FUNC_keyexch_init_fn x25519_init; +static OSSL_FUNC_keyexch_init_fn x448_init; +static OSSL_FUNC_keyexch_set_peer_fn ecx_set_peer; +static OSSL_FUNC_keyexch_derive_fn ecx_derive; +static OSSL_FUNC_keyexch_freectx_fn ecx_freectx; +static OSSL_FUNC_keyexch_dupctx_fn ecx_dupctx; +static OSSL_FUNC_keyexch_gettable_ctx_params_fn ecx_gettable_ctx_params; +static OSSL_FUNC_keyexch_get_ctx_params_fn ecx_get_ctx_params; + +/* + * What's passed as an actual key is defined by the KEYMGMT interface. + * We happen to know that our KEYMGMT simply passes ECX_KEY structures, so + * we use that here too. + */ + +typedef struct { + size_t keylen; + ECX_KEY *key; + ECX_KEY *peerkey; +} PROV_ECX_CTX; + +static void *ecx_newctx(void *provctx, size_t keylen) +{ + PROV_ECX_CTX *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + ctx = OPENSSL_zalloc(sizeof(PROV_ECX_CTX)); + if (ctx == NULL) + return NULL; + + ctx->keylen = keylen; + + return ctx; +} + +static void *x25519_newctx(void *provctx) +{ + return ecx_newctx(provctx, X25519_KEYLEN); +} + +static void *x448_newctx(void *provctx) +{ + return ecx_newctx(provctx, X448_KEYLEN); +} + +static int ecx_init(void *vecxctx, void *vkey, const char *algname) +{ + PROV_ECX_CTX *ecxctx = (PROV_ECX_CTX *)vecxctx; + ECX_KEY *key = vkey; + + if (!ossl_prov_is_running()) + return 0; + + if (ecxctx == NULL + || key == NULL + || key->keylen != ecxctx->keylen + || !ossl_ecx_key_up_ref(key)) { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + return 0; + } + + ossl_ecx_key_free(ecxctx->key); + ecxctx->key = key; + +#ifdef FIPS_MODULE + if (!ossl_FIPS_IND_callback(key->libctx, algname, "Init")) + return 0; +#endif + return 1; +} + +static int x25519_init(void *vecxctx, void *vkey, + ossl_unused const OSSL_PARAM params[]) +{ + return ecx_init(vecxctx, vkey, "X25519"); +} + +static int x448_init(void *vecxctx, void *vkey, + ossl_unused const OSSL_PARAM params[]) +{ + return ecx_init(vecxctx, vkey, "X448"); +} + +static int ecx_set_peer(void *vecxctx, void *vkey) +{ + PROV_ECX_CTX *ecxctx = (PROV_ECX_CTX *)vecxctx; + ECX_KEY *key = vkey; + + if (!ossl_prov_is_running()) + return 0; + + if (ecxctx == NULL + || key == NULL + || key->keylen != ecxctx->keylen + || !ossl_ecx_key_up_ref(key)) { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + return 0; + } + ossl_ecx_key_free(ecxctx->peerkey); + ecxctx->peerkey = key; + + return 1; +} + +static int ecx_derive(void *vecxctx, unsigned char *secret, size_t *secretlen, + size_t outlen) +{ + PROV_ECX_CTX *ecxctx = (PROV_ECX_CTX *)vecxctx; + + if (!ossl_prov_is_running()) + return 0; + return ossl_ecx_compute_key(ecxctx->peerkey, ecxctx->key, ecxctx->keylen, + secret, secretlen, outlen); +} + +static void ecx_freectx(void *vecxctx) +{ + PROV_ECX_CTX *ecxctx = (PROV_ECX_CTX *)vecxctx; + + ossl_ecx_key_free(ecxctx->key); + ossl_ecx_key_free(ecxctx->peerkey); + + OPENSSL_free(ecxctx); +} + +static void *ecx_dupctx(void *vecxctx) +{ + PROV_ECX_CTX *srcctx = (PROV_ECX_CTX *)vecxctx; + PROV_ECX_CTX *dstctx; + + if (!ossl_prov_is_running()) + return NULL; + + dstctx = OPENSSL_zalloc(sizeof(*srcctx)); + if (dstctx == NULL) + return NULL; + + *dstctx = *srcctx; + if (dstctx->key != NULL && !ossl_ecx_key_up_ref(dstctx->key)) { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + OPENSSL_free(dstctx); + return NULL; + } + + if (dstctx->peerkey != NULL && !ossl_ecx_key_up_ref(dstctx->peerkey)) { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + ossl_ecx_key_free(dstctx->key); + OPENSSL_free(dstctx); + return NULL; + } + + return dstctx; +} + +static const OSSL_PARAM *ecx_gettable_ctx_params(ossl_unused void *vctx, + ossl_unused void *provctx) +{ + static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_FIPS_IND_GETTABLE_CTX_PARAM() + OSSL_PARAM_END + }; + return known_gettable_ctx_params; +} + +static int ecx_get_ctx_params(ossl_unused void *vctx, OSSL_PARAM params[]) +{ +#ifdef FIPS_MODULE + int approved = 0; + OSSL_PARAM *p = OSSL_PARAM_locate(params, + OSSL_ALG_PARAM_FIPS_APPROVED_INDICATOR); + + if (p != NULL && !OSSL_PARAM_set_int(p, approved)) + return 0; +#endif + return 1; +} + +const OSSL_DISPATCH ossl_x25519_keyexch_functions[] = { + { OSSL_FUNC_KEYEXCH_NEWCTX, (void (*)(void))x25519_newctx }, + { OSSL_FUNC_KEYEXCH_INIT, (void (*)(void))x25519_init }, + { OSSL_FUNC_KEYEXCH_DERIVE, (void (*)(void))ecx_derive }, + { OSSL_FUNC_KEYEXCH_SET_PEER, (void (*)(void))ecx_set_peer }, + { OSSL_FUNC_KEYEXCH_FREECTX, (void (*)(void))ecx_freectx }, + { OSSL_FUNC_KEYEXCH_DUPCTX, (void (*)(void))ecx_dupctx }, + { OSSL_FUNC_KEYEXCH_GET_CTX_PARAMS, (void (*)(void))ecx_get_ctx_params }, + { OSSL_FUNC_KEYEXCH_GETTABLE_CTX_PARAMS, + (void (*)(void))ecx_gettable_ctx_params }, + OSSL_DISPATCH_END +}; + +const OSSL_DISPATCH ossl_x448_keyexch_functions[] = { + { OSSL_FUNC_KEYEXCH_NEWCTX, (void (*)(void))x448_newctx }, + { OSSL_FUNC_KEYEXCH_INIT, (void (*)(void))x448_init }, + { OSSL_FUNC_KEYEXCH_DERIVE, (void (*)(void))ecx_derive }, + { OSSL_FUNC_KEYEXCH_SET_PEER, (void (*)(void))ecx_set_peer }, + { OSSL_FUNC_KEYEXCH_FREECTX, (void (*)(void))ecx_freectx }, + { OSSL_FUNC_KEYEXCH_DUPCTX, (void (*)(void))ecx_dupctx }, + { OSSL_FUNC_KEYEXCH_GET_CTX_PARAMS, (void (*)(void))ecx_get_ctx_params }, + { OSSL_FUNC_KEYEXCH_GETTABLE_CTX_PARAMS, + (void (*)(void))ecx_gettable_ctx_params }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/exchange/kdf_exch.c b/crypto/openssl/providers/implementations/exchange/kdf_exch.c new file mode 100644 index 000000000000..340a2663c588 --- /dev/null +++ b/crypto/openssl/providers/implementations/exchange/kdf_exch.c @@ -0,0 +1,257 @@ +/* + * Copyright 2020-2024 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 + */ + +#include <openssl/crypto.h> +#include <openssl/kdf.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/err.h> +#include <openssl/proverr.h> +#include <openssl/params.h> +#include "internal/numbers.h" +#include "prov/implementations.h" +#include "prov/provider_ctx.h" +#include "prov/kdfexchange.h" +#include "prov/providercommon.h" + +static OSSL_FUNC_keyexch_newctx_fn kdf_tls1_prf_newctx; +static OSSL_FUNC_keyexch_newctx_fn kdf_hkdf_newctx; +static OSSL_FUNC_keyexch_newctx_fn kdf_scrypt_newctx; +static OSSL_FUNC_keyexch_init_fn kdf_init; +static OSSL_FUNC_keyexch_derive_fn kdf_derive; +static OSSL_FUNC_keyexch_freectx_fn kdf_freectx; +static OSSL_FUNC_keyexch_dupctx_fn kdf_dupctx; +static OSSL_FUNC_keyexch_set_ctx_params_fn kdf_set_ctx_params; +static OSSL_FUNC_keyexch_get_ctx_params_fn kdf_get_ctx_params; +static OSSL_FUNC_keyexch_settable_ctx_params_fn kdf_tls1_prf_settable_ctx_params; +static OSSL_FUNC_keyexch_settable_ctx_params_fn kdf_hkdf_settable_ctx_params; +static OSSL_FUNC_keyexch_settable_ctx_params_fn kdf_scrypt_settable_ctx_params; +static OSSL_FUNC_keyexch_gettable_ctx_params_fn kdf_tls1_prf_gettable_ctx_params; +static OSSL_FUNC_keyexch_gettable_ctx_params_fn kdf_hkdf_gettable_ctx_params; +static OSSL_FUNC_keyexch_gettable_ctx_params_fn kdf_scrypt_gettable_ctx_params; + +typedef struct { + void *provctx; + EVP_KDF_CTX *kdfctx; + KDF_DATA *kdfdata; +} PROV_KDF_CTX; + +static void *kdf_newctx(const char *kdfname, void *provctx) +{ + PROV_KDF_CTX *kdfctx; + EVP_KDF *kdf = NULL; + + if (!ossl_prov_is_running()) + return NULL; + + kdfctx = OPENSSL_zalloc(sizeof(PROV_KDF_CTX)); + if (kdfctx == NULL) + return NULL; + + kdfctx->provctx = provctx; + + kdf = EVP_KDF_fetch(PROV_LIBCTX_OF(provctx), kdfname, NULL); + if (kdf == NULL) + goto err; + kdfctx->kdfctx = EVP_KDF_CTX_new(kdf); + EVP_KDF_free(kdf); + + if (kdfctx->kdfctx == NULL) + goto err; + + return kdfctx; +err: + OPENSSL_free(kdfctx); + return NULL; +} + +#define KDF_NEWCTX(funcname, kdfname) \ + static void *kdf_##funcname##_newctx(void *provctx) \ + { \ + return kdf_newctx(kdfname, provctx); \ + } + +KDF_NEWCTX(tls1_prf, "TLS1-PRF") +KDF_NEWCTX(hkdf, "HKDF") +KDF_NEWCTX(scrypt, "SCRYPT") + +static int kdf_init(void *vpkdfctx, void *vkdf, const OSSL_PARAM params[]) +{ + PROV_KDF_CTX *pkdfctx = (PROV_KDF_CTX *)vpkdfctx; + + if (!ossl_prov_is_running() + || pkdfctx == NULL + || vkdf == NULL + || !ossl_kdf_data_up_ref(vkdf)) + return 0; + pkdfctx->kdfdata = vkdf; + + return kdf_set_ctx_params(pkdfctx, params); +} + +static int kdf_derive(void *vpkdfctx, unsigned char *secret, size_t *secretlen, + size_t outlen) +{ + PROV_KDF_CTX *pkdfctx = (PROV_KDF_CTX *)vpkdfctx; + size_t kdfsize; + int ret; + + if (!ossl_prov_is_running()) + return 0; + + kdfsize = EVP_KDF_CTX_get_kdf_size(pkdfctx->kdfctx); + + if (secret == NULL) { + *secretlen = kdfsize; + return 1; + } + + if (kdfsize != SIZE_MAX) { + if (outlen < kdfsize) { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + return 0; + } + outlen = kdfsize; + } + + ret = EVP_KDF_derive(pkdfctx->kdfctx, secret, outlen, NULL); + if (ret <= 0) + return 0; + + *secretlen = outlen; + return 1; +} + +static void kdf_freectx(void *vpkdfctx) +{ + PROV_KDF_CTX *pkdfctx = (PROV_KDF_CTX *)vpkdfctx; + + EVP_KDF_CTX_free(pkdfctx->kdfctx); + ossl_kdf_data_free(pkdfctx->kdfdata); + + OPENSSL_free(pkdfctx); +} + +static void *kdf_dupctx(void *vpkdfctx) +{ + PROV_KDF_CTX *srcctx = (PROV_KDF_CTX *)vpkdfctx; + PROV_KDF_CTX *dstctx; + + if (!ossl_prov_is_running()) + return NULL; + + dstctx = OPENSSL_zalloc(sizeof(*srcctx)); + if (dstctx == NULL) + return NULL; + + *dstctx = *srcctx; + + dstctx->kdfctx = EVP_KDF_CTX_dup(srcctx->kdfctx); + if (dstctx->kdfctx == NULL) { + OPENSSL_free(dstctx); + return NULL; + } + if (!ossl_kdf_data_up_ref(dstctx->kdfdata)) { + EVP_KDF_CTX_free(dstctx->kdfctx); + OPENSSL_free(dstctx); + return NULL; + } + + return dstctx; +} + +static int kdf_set_ctx_params(void *vpkdfctx, const OSSL_PARAM params[]) +{ + PROV_KDF_CTX *pkdfctx = (PROV_KDF_CTX *)vpkdfctx; + + return EVP_KDF_CTX_set_params(pkdfctx->kdfctx, params); +} + +static int kdf_get_ctx_params(void *vpkdfctx, OSSL_PARAM params[]) +{ + PROV_KDF_CTX *pkdfctx = (PROV_KDF_CTX *)vpkdfctx; + + return EVP_KDF_CTX_get_params(pkdfctx->kdfctx, params); +} + +static const OSSL_PARAM *kdf_settable_ctx_params(ossl_unused void *vpkdfctx, + void *provctx, + const char *kdfname) +{ + EVP_KDF *kdf = EVP_KDF_fetch(PROV_LIBCTX_OF(provctx), kdfname, + NULL); + const OSSL_PARAM *params; + + if (kdf == NULL) + return NULL; + + params = EVP_KDF_settable_ctx_params(kdf); + EVP_KDF_free(kdf); + + return params; +} + +#define KDF_SETTABLE_CTX_PARAMS(funcname, kdfname) \ + static const OSSL_PARAM *kdf_##funcname##_settable_ctx_params(void *vpkdfctx, \ + void *provctx) \ + { \ + return kdf_settable_ctx_params(vpkdfctx, provctx, kdfname); \ + } + +KDF_SETTABLE_CTX_PARAMS(tls1_prf, "TLS1-PRF") +KDF_SETTABLE_CTX_PARAMS(hkdf, "HKDF") +KDF_SETTABLE_CTX_PARAMS(scrypt, "SCRYPT") + +static const OSSL_PARAM *kdf_gettable_ctx_params(ossl_unused void *vpkdfctx, + void *provctx, + const char *kdfname) +{ + EVP_KDF *kdf = EVP_KDF_fetch(PROV_LIBCTX_OF(provctx), kdfname, + NULL); + const OSSL_PARAM *params; + + if (kdf == NULL) + return NULL; + + params = EVP_KDF_gettable_ctx_params(kdf); + EVP_KDF_free(kdf); + + return params; +} + +#define KDF_GETTABLE_CTX_PARAMS(funcname, kdfname) \ + static const OSSL_PARAM *kdf_##funcname##_gettable_ctx_params(void *vpkdfctx, \ + void *provctx) \ + { \ + return kdf_gettable_ctx_params(vpkdfctx, provctx, kdfname); \ + } + +KDF_GETTABLE_CTX_PARAMS(tls1_prf, "TLS1-PRF") +KDF_GETTABLE_CTX_PARAMS(hkdf, "HKDF") +KDF_GETTABLE_CTX_PARAMS(scrypt, "SCRYPT") + +#define KDF_KEYEXCH_FUNCTIONS(funcname) \ + const OSSL_DISPATCH ossl_kdf_##funcname##_keyexch_functions[] = { \ + { OSSL_FUNC_KEYEXCH_NEWCTX, (void (*)(void))kdf_##funcname##_newctx }, \ + { OSSL_FUNC_KEYEXCH_INIT, (void (*)(void))kdf_init }, \ + { OSSL_FUNC_KEYEXCH_DERIVE, (void (*)(void))kdf_derive }, \ + { OSSL_FUNC_KEYEXCH_FREECTX, (void (*)(void))kdf_freectx }, \ + { OSSL_FUNC_KEYEXCH_DUPCTX, (void (*)(void))kdf_dupctx }, \ + { OSSL_FUNC_KEYEXCH_SET_CTX_PARAMS, (void (*)(void))kdf_set_ctx_params }, \ + { OSSL_FUNC_KEYEXCH_GET_CTX_PARAMS, (void (*)(void))kdf_get_ctx_params }, \ + { OSSL_FUNC_KEYEXCH_SETTABLE_CTX_PARAMS, \ + (void (*)(void))kdf_##funcname##_settable_ctx_params }, \ + { OSSL_FUNC_KEYEXCH_GETTABLE_CTX_PARAMS, \ + (void (*)(void))kdf_##funcname##_gettable_ctx_params }, \ + OSSL_DISPATCH_END \ + }; + +KDF_KEYEXCH_FUNCTIONS(tls1_prf) +KDF_KEYEXCH_FUNCTIONS(hkdf) +KDF_KEYEXCH_FUNCTIONS(scrypt) diff --git a/crypto/openssl/providers/implementations/include/prov/__DECC_INCLUDE_EPILOGUE.H b/crypto/openssl/providers/implementations/include/prov/__DECC_INCLUDE_EPILOGUE.H new file mode 100644 index 000000000000..2ab493330675 --- /dev/null +++ b/crypto/openssl/providers/implementations/include/prov/__DECC_INCLUDE_EPILOGUE.H @@ -0,0 +1,22 @@ +/* + * Copyright 2016-2021 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 + */ + +/* + * This file is only used by HP C/C++ on VMS, and is included automatically + * after each header file from this directory + */ + +/* + * The C++ compiler doesn't understand these pragmas, even though it + * understands the corresponding command line qualifier. + */ +#ifndef __cplusplus +/* restore state. Must correspond to the save in __decc_include_prologue.h */ +# pragma names restore +#endif diff --git a/crypto/openssl/providers/implementations/include/prov/__DECC_INCLUDE_PROLOGUE.H b/crypto/openssl/providers/implementations/include/prov/__DECC_INCLUDE_PROLOGUE.H new file mode 100644 index 000000000000..8e95fa975488 --- /dev/null +++ b/crypto/openssl/providers/implementations/include/prov/__DECC_INCLUDE_PROLOGUE.H @@ -0,0 +1,26 @@ +/* + * Copyright 2016-2021 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 + */ + +/* + * This file is only used by HP C/C++ on VMS, and is included automatically + * after each header file from this directory + */ + +/* + * The C++ compiler doesn't understand these pragmas, even though it + * understands the corresponding command line qualifier. + */ +#ifndef __cplusplus +/* save state */ +# pragma names save +/* have the compiler shorten symbols larger than 31 chars to 23 chars + * followed by a 8 hex char CRC + */ +# pragma names as_is,shortened +#endif diff --git a/crypto/openssl/providers/implementations/include/prov/blake2.h b/crypto/openssl/providers/implementations/include/prov/blake2.h new file mode 100644 index 000000000000..42229e2d7402 --- /dev/null +++ b/crypto/openssl/providers/implementations/include/prov/blake2.h @@ -0,0 +1,138 @@ +/* + * Copyright 2019-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 + */ + +#ifndef OSSL_PROV_BLAKE2_H +# define OSSL_PROV_BLAKE2_H + +# include <openssl/opensslconf.h> + +# include <openssl/e_os2.h> +# include <stddef.h> +# include <crypto/evp.h> + +# define BLAKE2S_BLOCKBYTES 64 +# define BLAKE2S_OUTBYTES 32 +# define BLAKE2S_KEYBYTES 32 +# define BLAKE2S_SALTBYTES 8 +# define BLAKE2S_PERSONALBYTES 8 + +# define BLAKE2B_BLOCKBYTES 128 +# define BLAKE2B_OUTBYTES 64 +# define BLAKE2B_KEYBYTES 64 +# define BLAKE2B_SALTBYTES 16 +# define BLAKE2B_PERSONALBYTES 16 + +struct blake2s_param_st { + uint8_t digest_length; /* 1 */ + uint8_t key_length; /* 2 */ + uint8_t fanout; /* 3 */ + uint8_t depth; /* 4 */ + uint8_t leaf_length[4];/* 8 */ + uint8_t node_offset[6];/* 14 */ + uint8_t node_depth; /* 15 */ + uint8_t inner_length; /* 16 */ + uint8_t salt[BLAKE2S_SALTBYTES]; /* 24 */ + uint8_t personal[BLAKE2S_PERSONALBYTES]; /* 32 */ +}; + +typedef struct blake2s_param_st BLAKE2S_PARAM; + +struct blake2s_ctx_st { + uint32_t h[8]; + uint32_t t[2]; + uint32_t f[2]; + uint8_t buf[BLAKE2S_BLOCKBYTES]; + size_t buflen; + size_t outlen; +}; + +struct blake2b_param_st { + uint8_t digest_length; /* 1 */ + uint8_t key_length; /* 2 */ + uint8_t fanout; /* 3 */ + uint8_t depth; /* 4 */ + uint8_t leaf_length[4];/* 8 */ + uint8_t node_offset[8];/* 16 */ + uint8_t node_depth; /* 17 */ + uint8_t inner_length; /* 18 */ + uint8_t reserved[14]; /* 32 */ + uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */ + uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */ +}; + +typedef struct blake2b_param_st BLAKE2B_PARAM; + +struct blake2b_ctx_st { + uint64_t h[8]; + uint64_t t[2]; + uint64_t f[2]; + uint8_t buf[BLAKE2B_BLOCKBYTES]; + size_t buflen; + size_t outlen; +}; + +#define BLAKE2B_DIGEST_LENGTH 64 +#define BLAKE2S_DIGEST_LENGTH 32 + +typedef struct blake2s_ctx_st BLAKE2S_CTX; +typedef struct blake2b_ctx_st BLAKE2B_CTX; + +struct blake2b_md_data_st { + BLAKE2B_CTX ctx; + BLAKE2B_PARAM params; +}; + +struct blake2s_md_data_st { + BLAKE2S_CTX ctx; + BLAKE2S_PARAM params; +}; + +int ossl_blake2b_init(BLAKE2B_CTX *c, const BLAKE2B_PARAM *P); +int ossl_blake2b_init_key(BLAKE2B_CTX *c, const BLAKE2B_PARAM *P, + const void *key); +int ossl_blake2b_update(BLAKE2B_CTX *c, const void *data, size_t datalen); +int ossl_blake2b_final(unsigned char *md, BLAKE2B_CTX *c); + +OSSL_FUNC_digest_get_ctx_params_fn ossl_blake2b_get_ctx_params; +OSSL_FUNC_digest_set_ctx_params_fn ossl_blake2b_set_ctx_params; +OSSL_FUNC_digest_gettable_ctx_params_fn ossl_blake2b_gettable_ctx_params; +OSSL_FUNC_digest_settable_ctx_params_fn ossl_blake2b_settable_ctx_params; + +/* + * These setters are internal and do not check the validity of their parameters. + * See blake2b_mac_ctrl for validation logic. + */ + +void ossl_blake2b_param_init(BLAKE2B_PARAM *P); +void ossl_blake2b_param_set_digest_length(BLAKE2B_PARAM *P, uint8_t outlen); +void ossl_blake2b_param_set_key_length(BLAKE2B_PARAM *P, uint8_t keylen); +void ossl_blake2b_param_set_personal(BLAKE2B_PARAM *P, const uint8_t *personal, + size_t length); +void ossl_blake2b_param_set_salt(BLAKE2B_PARAM *P, const uint8_t *salt, + size_t length); +int ossl_blake2s_init(BLAKE2S_CTX *c, const BLAKE2S_PARAM *P); +int ossl_blake2s_init_key(BLAKE2S_CTX *c, const BLAKE2S_PARAM *P, + const void *key); +int ossl_blake2s_update(BLAKE2S_CTX *c, const void *data, size_t datalen); +int ossl_blake2s_final(unsigned char *md, BLAKE2S_CTX *c); + +void ossl_blake2s_param_init(BLAKE2S_PARAM *P); +void ossl_blake2s_param_set_digest_length(BLAKE2S_PARAM *P, uint8_t outlen); +void ossl_blake2s_param_set_key_length(BLAKE2S_PARAM *P, uint8_t keylen); +void ossl_blake2s_param_set_personal(BLAKE2S_PARAM *P, const uint8_t *personal, + size_t length); +void ossl_blake2s_param_set_salt(BLAKE2S_PARAM *P, const uint8_t *salt, + size_t length); + +OSSL_FUNC_digest_get_ctx_params_fn ossl_blake2s_get_ctx_params; +OSSL_FUNC_digest_set_ctx_params_fn ossl_blake2s_set_ctx_params; +OSSL_FUNC_digest_gettable_ctx_params_fn ossl_blake2s_gettable_ctx_params; +OSSL_FUNC_digest_settable_ctx_params_fn ossl_blake2s_settable_ctx_params; + +#endif /* OSSL_PROV_BLAKE2_H */ diff --git a/crypto/openssl/providers/implementations/include/prov/ciphercommon.h b/crypto/openssl/providers/implementations/include/prov/ciphercommon.h new file mode 100644 index 000000000000..46efdc80033f --- /dev/null +++ b/crypto/openssl/providers/implementations/include/prov/ciphercommon.h @@ -0,0 +1,375 @@ +/* + * Copyright 2019-2025 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 + */ + +#ifndef OSSL_PROV_CIPHERCOMMON_H +# define OSSL_PROV_CIPHERCOMMON_H +# pragma once + +# include <openssl/params.h> +# include <openssl/core_dispatch.h> +# include <openssl/core_names.h> +# include <openssl/evp.h> +# include "internal/cryptlib.h" +# include "crypto/modes.h" + +# define MAXCHUNK ((size_t)1 << 30) +# define MAXBITCHUNK ((size_t)1 << (sizeof(size_t) * 8 - 4)) + +# define GENERIC_BLOCK_SIZE 16 +# define IV_STATE_UNINITIALISED 0 /* initial state is not initialized */ +# define IV_STATE_BUFFERED 1 /* iv has been copied to the iv buffer */ +# define IV_STATE_COPIED 2 /* iv has been copied from the iv buffer */ +# define IV_STATE_FINISHED 3 /* the iv has been used - so don't reuse it */ + +# define PROV_CIPHER_FUNC(type, name, args) typedef type (* OSSL_##name##_fn)args + +typedef struct prov_cipher_hw_st PROV_CIPHER_HW; +typedef struct prov_cipher_ctx_st PROV_CIPHER_CTX; + +typedef int (PROV_CIPHER_HW_FN)(PROV_CIPHER_CTX *dat, unsigned char *out, + const unsigned char *in, size_t len); + +/* Internal flags that can be queried */ +# define PROV_CIPHER_FLAG_AEAD 0x0001 +# define PROV_CIPHER_FLAG_CUSTOM_IV 0x0002 +# define PROV_CIPHER_FLAG_CTS 0x0004 +# define PROV_CIPHER_FLAG_TLS1_MULTIBLOCK 0x0008 +# define PROV_CIPHER_FLAG_RAND_KEY 0x0010 +/* Internal flags that are only used within the provider */ +# define PROV_CIPHER_FLAG_VARIABLE_LENGTH 0x0100 +# define PROV_CIPHER_FLAG_INVERSE_CIPHER 0x0200 + +struct prov_cipher_ctx_st { + /* place buffer at the beginning for memory alignment */ + /* The original value of the iv */ + unsigned char oiv[GENERIC_BLOCK_SIZE]; + /* Buffer of partial blocks processed via update calls */ + unsigned char buf[GENERIC_BLOCK_SIZE]; + unsigned char iv[GENERIC_BLOCK_SIZE]; + + block128_f block; + union { + cbc128_f cbc; + ctr128_f ctr; + ecb128_f ecb; + } stream; + + unsigned int mode; + size_t keylen; /* key size (in bytes) */ + size_t ivlen; + size_t blocksize; + size_t bufsz; /* Number of bytes in buf */ + unsigned int cts_mode; /* Use to set the type for CTS modes */ + unsigned int pad : 1; /* Whether padding should be used or not */ + unsigned int enc : 1; /* Set to 1 for encrypt, or 0 otherwise */ + unsigned int iv_set : 1; /* Set when the iv is copied to the iv/oiv buffers */ + unsigned int key_set : 1; /* Set when key is set on the context */ + unsigned int updated : 1; /* Set to 1 during update for one shot ciphers */ + unsigned int variable_keylength : 1; + unsigned int inverse_cipher : 1; /* set to 1 to use inverse cipher */ + unsigned int use_bits : 1; /* Set to 0 for cfb1 to use bits instead of bytes */ + + unsigned int tlsversion; /* If TLS padding is in use the TLS version number */ + unsigned char *tlsmac; /* tls MAC extracted from the last record */ + int alloced; /* + * Whether the tlsmac data has been allocated or + * points into the user buffer. + */ + size_t tlsmacsize; /* Size of the TLS MAC */ + int removetlspad; /* Whether TLS padding should be removed or not */ + size_t removetlsfixed; /* + * Length of the fixed size data to remove when + * processing TLS data (equals mac size plus + * IV size if applicable) + */ + + /* + * num contains the number of bytes of |iv| which are valid for modes that + * manage partial blocks themselves. + */ + unsigned int num; + const PROV_CIPHER_HW *hw; /* hardware specific functions */ + const void *ks; /* Pointer to algorithm specific key data */ + OSSL_LIB_CTX *libctx; +}; + +struct prov_cipher_hw_st { + int (*init)(PROV_CIPHER_CTX *dat, const uint8_t *key, size_t keylen); + PROV_CIPHER_HW_FN *cipher; + void (*copyctx)(PROV_CIPHER_CTX *dst, const PROV_CIPHER_CTX *src); +}; + +void ossl_cipher_generic_reset_ctx(PROV_CIPHER_CTX *ctx); +OSSL_FUNC_cipher_encrypt_init_fn ossl_cipher_generic_einit; +OSSL_FUNC_cipher_decrypt_init_fn ossl_cipher_generic_dinit; +OSSL_FUNC_cipher_update_fn ossl_cipher_generic_block_update; +OSSL_FUNC_cipher_final_fn ossl_cipher_generic_block_final; +OSSL_FUNC_cipher_update_fn ossl_cipher_generic_stream_update; +OSSL_FUNC_cipher_final_fn ossl_cipher_generic_stream_final; +OSSL_FUNC_cipher_cipher_fn ossl_cipher_generic_cipher; +OSSL_FUNC_cipher_get_ctx_params_fn ossl_cipher_generic_get_ctx_params; +OSSL_FUNC_cipher_set_ctx_params_fn ossl_cipher_generic_set_ctx_params; +OSSL_FUNC_cipher_gettable_params_fn ossl_cipher_generic_gettable_params; +OSSL_FUNC_cipher_gettable_ctx_params_fn ossl_cipher_generic_gettable_ctx_params; +OSSL_FUNC_cipher_settable_ctx_params_fn ossl_cipher_generic_settable_ctx_params; +OSSL_FUNC_cipher_set_ctx_params_fn ossl_cipher_var_keylen_set_ctx_params; +OSSL_FUNC_cipher_settable_ctx_params_fn ossl_cipher_var_keylen_settable_ctx_params; +OSSL_FUNC_cipher_gettable_ctx_params_fn ossl_cipher_aead_gettable_ctx_params; +OSSL_FUNC_cipher_settable_ctx_params_fn ossl_cipher_aead_settable_ctx_params; +OSSL_FUNC_cipher_encrypt_skey_init_fn ossl_cipher_generic_skey_einit; +OSSL_FUNC_cipher_decrypt_skey_init_fn ossl_cipher_generic_skey_dinit; + +int ossl_cipher_generic_get_params(OSSL_PARAM params[], unsigned int md, + uint64_t flags, + size_t kbits, size_t blkbits, size_t ivbits); +void ossl_cipher_generic_initkey(void *vctx, size_t kbits, size_t blkbits, + size_t ivbits, unsigned int mode, + uint64_t flags, + const PROV_CIPHER_HW *hw, void *provctx); + +# define IMPLEMENT_generic_cipher_func(alg, UCALG, lcmode, UCMODE, flags, kbits,\ + blkbits, ivbits, typ) \ +const OSSL_DISPATCH ossl_##alg##kbits##lcmode##_functions[] = { \ + { OSSL_FUNC_CIPHER_NEWCTX, \ + (void (*)(void)) alg##_##kbits##_##lcmode##_newctx }, \ + { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void)) alg##_freectx }, \ + { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void)) alg##_dupctx }, \ + { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))ossl_cipher_generic_einit }, \ + { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))ossl_cipher_generic_dinit }, \ + { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))ossl_cipher_generic_##typ##_update },\ + { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))ossl_cipher_generic_##typ##_final }, \ + { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))ossl_cipher_generic_cipher }, \ + { OSSL_FUNC_CIPHER_GET_PARAMS, \ + (void (*)(void)) alg##_##kbits##_##lcmode##_get_params }, \ + { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, \ + (void (*)(void))ossl_cipher_generic_get_ctx_params }, \ + { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \ + (void (*)(void))ossl_cipher_generic_set_ctx_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \ + (void (*)(void))ossl_cipher_generic_gettable_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \ + (void (*)(void))ossl_cipher_generic_gettable_ctx_params }, \ + { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \ + (void (*)(void))ossl_cipher_generic_settable_ctx_params }, \ + { OSSL_FUNC_CIPHER_ENCRYPT_SKEY_INIT, (void (*)(void))ossl_cipher_generic_skey_einit },\ + { OSSL_FUNC_CIPHER_DECRYPT_SKEY_INIT, (void (*)(void))ossl_cipher_generic_skey_dinit },\ + OSSL_DISPATCH_END \ +}; + +# define IMPLEMENT_var_keylen_cipher_func(alg, UCALG, lcmode, UCMODE, flags, \ + kbits, blkbits, ivbits, typ) \ +const OSSL_DISPATCH ossl_##alg##kbits##lcmode##_functions[] = { \ + { OSSL_FUNC_CIPHER_NEWCTX, \ + (void (*)(void)) alg##_##kbits##_##lcmode##_newctx }, \ + { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void)) alg##_freectx }, \ + { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void)) alg##_dupctx }, \ + { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))ossl_cipher_generic_einit },\ + { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))ossl_cipher_generic_dinit },\ + { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))ossl_cipher_generic_##typ##_update },\ + { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))ossl_cipher_generic_##typ##_final }, \ + { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))ossl_cipher_generic_cipher }, \ + { OSSL_FUNC_CIPHER_GET_PARAMS, \ + (void (*)(void)) alg##_##kbits##_##lcmode##_get_params }, \ + { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, \ + (void (*)(void))ossl_cipher_generic_get_ctx_params }, \ + { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \ + (void (*)(void))ossl_cipher_var_keylen_set_ctx_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \ + (void (*)(void))ossl_cipher_generic_gettable_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \ + (void (*)(void))ossl_cipher_generic_gettable_ctx_params }, \ + { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \ + (void (*)(void))ossl_cipher_var_keylen_settable_ctx_params }, \ + { OSSL_FUNC_CIPHER_ENCRYPT_SKEY_INIT, (void (*)(void))ossl_cipher_generic_skey_einit },\ + { OSSL_FUNC_CIPHER_DECRYPT_SKEY_INIT, (void (*)(void))ossl_cipher_generic_skey_dinit },\ + OSSL_DISPATCH_END \ +}; + + +# define IMPLEMENT_generic_cipher_genfn(alg, UCALG, lcmode, UCMODE, flags, \ + kbits, blkbits, ivbits, typ) \ +static OSSL_FUNC_cipher_get_params_fn alg##_##kbits##_##lcmode##_get_params; \ +static int alg##_##kbits##_##lcmode##_get_params(OSSL_PARAM params[]) \ +{ \ + return ossl_cipher_generic_get_params(params, EVP_CIPH_##UCMODE##_MODE, \ + flags, kbits, blkbits, ivbits); \ +} \ +static OSSL_FUNC_cipher_newctx_fn alg##_##kbits##_##lcmode##_newctx; \ +static void * alg##_##kbits##_##lcmode##_newctx(void *provctx) \ +{ \ + PROV_##UCALG##_CTX *ctx = ossl_prov_is_running() ? OPENSSL_zalloc(sizeof(*ctx))\ + : NULL; \ + if (ctx != NULL) { \ + ossl_cipher_generic_initkey(ctx, kbits, blkbits, ivbits, \ + EVP_CIPH_##UCMODE##_MODE, flags, \ + ossl_prov_cipher_hw_##alg##_##lcmode(kbits),\ + provctx); \ + } \ + return ctx; \ +} \ + +# define IMPLEMENT_generic_cipher(alg, UCALG, lcmode, UCMODE, flags, kbits, \ + blkbits, ivbits, typ) \ +IMPLEMENT_generic_cipher_genfn(alg, UCALG, lcmode, UCMODE, flags, kbits, \ + blkbits, ivbits, typ) \ +IMPLEMENT_generic_cipher_func(alg, UCALG, lcmode, UCMODE, flags, kbits, \ + blkbits, ivbits, typ) + +# define IMPLEMENT_var_keylen_cipher(alg, UCALG, lcmode, UCMODE, flags, kbits, \ + blkbits, ivbits, typ) \ +IMPLEMENT_generic_cipher_genfn(alg, UCALG, lcmode, UCMODE, flags, kbits, \ + blkbits, ivbits, typ) \ +IMPLEMENT_var_keylen_cipher_func(alg, UCALG, lcmode, UCMODE, flags, kbits, \ + blkbits, ivbits, typ) + +PROV_CIPHER_HW_FN ossl_cipher_hw_generic_cbc; +PROV_CIPHER_HW_FN ossl_cipher_hw_generic_ecb; +PROV_CIPHER_HW_FN ossl_cipher_hw_generic_ofb128; +PROV_CIPHER_HW_FN ossl_cipher_hw_generic_cfb128; +PROV_CIPHER_HW_FN ossl_cipher_hw_generic_cfb8; +PROV_CIPHER_HW_FN ossl_cipher_hw_generic_cfb1; +PROV_CIPHER_HW_FN ossl_cipher_hw_generic_ctr; +PROV_CIPHER_HW_FN ossl_cipher_hw_chunked_cbc; +PROV_CIPHER_HW_FN ossl_cipher_hw_chunked_cfb8; +PROV_CIPHER_HW_FN ossl_cipher_hw_chunked_cfb128; +PROV_CIPHER_HW_FN ossl_cipher_hw_chunked_ofb128; +# define ossl_cipher_hw_chunked_ecb ossl_cipher_hw_generic_ecb +# define ossl_cipher_hw_chunked_ctr ossl_cipher_hw_generic_ctr +# define ossl_cipher_hw_chunked_cfb1 ossl_cipher_hw_generic_cfb1 + +# define IMPLEMENT_CIPHER_HW_OFB(MODE, NAME, CTX_NAME, KEY_NAME, FUNC_PREFIX) \ +static int cipher_hw_##NAME##_##MODE##_cipher(PROV_CIPHER_CTX *ctx, \ + unsigned char *out, \ + const unsigned char *in, size_t len) \ +{ \ + int num = ctx->num; \ + KEY_NAME *key = &(((CTX_NAME *)ctx)->ks.ks); \ + \ + while (len >= MAXCHUNK) { \ + FUNC_PREFIX##_encrypt(in, out, MAXCHUNK, key, ctx->iv, &num); \ + len -= MAXCHUNK; \ + in += MAXCHUNK; \ + out += MAXCHUNK; \ + } \ + if (len > 0) { \ + FUNC_PREFIX##_encrypt(in, out, (long)len, key, ctx->iv, &num); \ + } \ + ctx->num = num; \ + return 1; \ +} + +# define IMPLEMENT_CIPHER_HW_ECB(MODE, NAME, CTX_NAME, KEY_NAME, FUNC_PREFIX) \ +static int cipher_hw_##NAME##_##MODE##_cipher(PROV_CIPHER_CTX *ctx, \ + unsigned char *out, \ + const unsigned char *in, size_t len) \ +{ \ + size_t i, bl = ctx->blocksize; \ + KEY_NAME *key = &(((CTX_NAME *)ctx)->ks.ks); \ + \ + if (len < bl) \ + return 1; \ + for (i = 0, len -= bl; i <= len; i += bl) \ + FUNC_PREFIX##_encrypt(in + i, out + i, key, ctx->enc); \ + return 1; \ +} + +# define IMPLEMENT_CIPHER_HW_CBC(MODE, NAME, CTX_NAME, KEY_NAME, FUNC_PREFIX) \ +static int cipher_hw_##NAME##_##MODE##_cipher(PROV_CIPHER_CTX *ctx, \ + unsigned char *out, \ + const unsigned char *in, size_t len) \ +{ \ + KEY_NAME *key = &(((CTX_NAME *)ctx)->ks.ks); \ + \ + while (len >= MAXCHUNK) { \ + FUNC_PREFIX##_encrypt(in, out, MAXCHUNK, key, ctx->iv, ctx->enc); \ + len -= MAXCHUNK; \ + in += MAXCHUNK; \ + out += MAXCHUNK; \ + } \ + if (len > 0) \ + FUNC_PREFIX##_encrypt(in, out, (long)len, key, ctx->iv, ctx->enc); \ + return 1; \ +} + +# define IMPLEMENT_CIPHER_HW_CFB(MODE, NAME, CTX_NAME, KEY_NAME, FUNC_PREFIX) \ +static int cipher_hw_##NAME##_##MODE##_cipher(PROV_CIPHER_CTX *ctx, \ + unsigned char *out, \ + const unsigned char *in, size_t len) \ +{ \ + size_t chunk = MAXCHUNK; \ + KEY_NAME *key = &(((CTX_NAME *)ctx)->ks.ks); \ + int num = ctx->num; \ + \ + if (len < chunk) \ + chunk = len; \ + while (len > 0 && len >= chunk) { \ + FUNC_PREFIX##_encrypt(in, out, (long)chunk, key, ctx->iv, &num, \ + ctx->enc); \ + len -= chunk; \ + in += chunk; \ + out += chunk; \ + if (len < chunk) \ + chunk = len; \ + } \ + ctx->num = num; \ + return 1; \ +} + +# define IMPLEMENT_CIPHER_HW_COPYCTX(name, CTX_TYPE) \ +static void name(PROV_CIPHER_CTX *dst, const PROV_CIPHER_CTX *src) \ +{ \ + CTX_TYPE *sctx = (CTX_TYPE *)src; \ + CTX_TYPE *dctx = (CTX_TYPE *)dst; \ + \ + *dctx = *sctx; \ + dst->ks = &dctx->ks.ks; \ +} + +# define CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_START(name) \ +static const OSSL_PARAM name##_known_gettable_ctx_params[] = { \ + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), \ + OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL), \ + OSSL_PARAM_uint(OSSL_CIPHER_PARAM_PADDING, NULL), \ + OSSL_PARAM_uint(OSSL_CIPHER_PARAM_NUM, NULL), \ + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_IV, NULL, 0), \ + OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_UPDATED_IV, NULL, 0), + +# define CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_END(name) \ + OSSL_PARAM_END \ +}; \ +const OSSL_PARAM * name##_gettable_ctx_params(ossl_unused void *cctx, \ + ossl_unused void *provctx) \ +{ \ + return name##_known_gettable_ctx_params; \ +} + +# define CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_START(name) \ +static const OSSL_PARAM name##_known_settable_ctx_params[] = { \ + OSSL_PARAM_uint(OSSL_CIPHER_PARAM_PADDING, NULL), \ + OSSL_PARAM_uint(OSSL_CIPHER_PARAM_NUM, NULL), +# define CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_END(name) \ + OSSL_PARAM_END \ +}; \ +const OSSL_PARAM * name##_settable_ctx_params(ossl_unused void *cctx, \ + ossl_unused void *provctx) \ +{ \ + return name##_known_settable_ctx_params; \ +} + +int ossl_cipher_generic_initiv(PROV_CIPHER_CTX *ctx, const unsigned char *iv, + size_t ivlen); + +size_t ossl_cipher_fillblock(unsigned char *buf, size_t *buflen, + size_t blocksize, + const unsigned char **in, size_t *inlen); +int ossl_cipher_trailingdata(unsigned char *buf, size_t *buflen, + size_t blocksize, + const unsigned char **in, size_t *inlen); + +#endif diff --git a/crypto/openssl/providers/implementations/include/prov/ciphercommon_aead.h b/crypto/openssl/providers/implementations/include/prov/ciphercommon_aead.h new file mode 100644 index 000000000000..8d709f10ea64 --- /dev/null +++ b/crypto/openssl/providers/implementations/include/prov/ciphercommon_aead.h @@ -0,0 +1,58 @@ +/* + * Copyright 2019-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 + */ + +#ifndef OSSL_PROV_CIPHERCOMMON_AEAD_H +# define OSSL_PROV_CIPHERCOMMON_AEAD_H +# pragma once + +# define UNINITIALISED_SIZET ((size_t)-1) + +# define AEAD_FLAGS (PROV_CIPHER_FLAG_AEAD | PROV_CIPHER_FLAG_CUSTOM_IV) + +# define IMPLEMENT_aead_cipher(alg, lc, UCMODE, flags, kbits, blkbits, ivbits) \ +static OSSL_FUNC_cipher_get_params_fn alg##_##kbits##_##lc##_get_params; \ +static int alg##_##kbits##_##lc##_get_params(OSSL_PARAM params[]) \ +{ \ + return ossl_cipher_generic_get_params(params, EVP_CIPH_##UCMODE##_MODE, \ + flags, kbits, blkbits, ivbits); \ +} \ +static OSSL_FUNC_cipher_newctx_fn alg##kbits##lc##_newctx; \ +static void * alg##kbits##lc##_newctx(void *provctx) \ +{ \ + return alg##_##lc##_newctx(provctx, kbits); \ +} \ +static void * alg##kbits##lc##_dupctx(void *src) \ +{ \ + return alg##_##lc##_dupctx(src); \ +} \ +const OSSL_DISPATCH ossl_##alg##kbits##lc##_functions[] = { \ + { OSSL_FUNC_CIPHER_NEWCTX, (void (*)(void))alg##kbits##lc##_newctx }, \ + { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))alg##_##lc##_freectx }, \ + { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void))alg##kbits##lc##_dupctx }, \ + { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))ossl_##lc##_einit }, \ + { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))ossl_##lc##_dinit }, \ + { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))ossl_##lc##_stream_update }, \ + { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))ossl_##lc##_stream_final }, \ + { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))ossl_##lc##_cipher }, \ + { OSSL_FUNC_CIPHER_GET_PARAMS, \ + (void (*)(void)) alg##_##kbits##_##lc##_get_params }, \ + { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, \ + (void (*)(void)) ossl_##lc##_get_ctx_params }, \ + { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \ + (void (*)(void)) ossl_##lc##_set_ctx_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \ + (void (*)(void))ossl_cipher_generic_gettable_params }, \ + { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \ + (void (*)(void))ossl_cipher_aead_gettable_ctx_params }, \ + { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \ + (void (*)(void))ossl_cipher_aead_settable_ctx_params }, \ + OSSL_DISPATCH_END \ +} + +#endif diff --git a/crypto/openssl/providers/implementations/include/prov/ciphercommon_ccm.h b/crypto/openssl/providers/implementations/include/prov/ciphercommon_ccm.h new file mode 100644 index 000000000000..ce1a2aa0e736 --- /dev/null +++ b/crypto/openssl/providers/implementations/include/prov/ciphercommon_ccm.h @@ -0,0 +1,106 @@ +/* + * Copyright 2019-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 + */ + +#ifndef OSSL_PROV_CIPHERCOMMON_CCM_H +# define OSSL_PROV_CIPHERCOMMON_CCM_H +# pragma once + +# include "ciphercommon_aead.h" + +typedef struct prov_ccm_hw_st PROV_CCM_HW; + +# if defined(OPENSSL_CPUID_OBJ) && defined(__s390__) +/*- + * KMAC-AES parameter block - begin + * (see z/Architecture Principles of Operation >= SA22-7832-08) + */ +typedef struct S390X_kmac_params_st { + union { + unsigned long long g[2]; + unsigned char b[16]; + } icv; + unsigned char k[32]; +} S390X_KMAC_PARAMS; +/* KMAC-AES parameter block - end */ +# endif + +/* Base structure that is shared by AES & ARIA for CCM MODE */ +typedef struct prov_ccm_st { + unsigned int enc : 1; + unsigned int key_set : 1; /* Set if key initialised */ + unsigned int iv_set : 1; /* Set if an iv is set */ + unsigned int tag_set : 1; /* Set if tag is valid */ + unsigned int len_set : 1; /* Set if message length set */ + size_t l, m; /* L and M parameters from RFC3610 */ + size_t keylen; + size_t tls_aad_len; /* TLS AAD length */ + size_t tls_aad_pad_sz; + unsigned char iv[GENERIC_BLOCK_SIZE]; + unsigned char buf[GENERIC_BLOCK_SIZE]; + CCM128_CONTEXT ccm_ctx; + ccm128_f str; + const PROV_CCM_HW *hw; /* hardware specific methods */ +} PROV_CCM_CTX; + +PROV_CIPHER_FUNC(int, CCM_cipher, (PROV_CCM_CTX *ctx, unsigned char *out, \ + size_t *padlen, const unsigned char *in, \ + size_t len)); +PROV_CIPHER_FUNC(int, CCM_setkey, (PROV_CCM_CTX *ctx, \ + const unsigned char *key, size_t keylen)); +PROV_CIPHER_FUNC(int, CCM_setiv, (PROV_CCM_CTX *dat, \ + const unsigned char *iv, size_t ivlen, \ + size_t mlen)); +PROV_CIPHER_FUNC(int, CCM_setaad, (PROV_CCM_CTX *ctx, \ + const unsigned char *aad, size_t aadlen)); +PROV_CIPHER_FUNC(int, CCM_auth_encrypt, (PROV_CCM_CTX *ctx, \ + const unsigned char *in, \ + unsigned char *out, size_t len, \ + unsigned char *tag, size_t taglen)); +PROV_CIPHER_FUNC(int, CCM_auth_decrypt, (PROV_CCM_CTX *ctx, \ + const unsigned char *in, \ + unsigned char *out, size_t len, \ + unsigned char *tag, size_t taglen)); +PROV_CIPHER_FUNC(int, CCM_gettag, (PROV_CCM_CTX *ctx, \ + unsigned char *tag, size_t taglen)); + +/* + * CCM Mode internal method table used to handle hardware specific differences, + * (and different algorithms). + */ +struct prov_ccm_hw_st { + OSSL_CCM_setkey_fn setkey; + OSSL_CCM_setiv_fn setiv; + OSSL_CCM_setaad_fn setaad; + OSSL_CCM_auth_encrypt_fn auth_encrypt; + OSSL_CCM_auth_decrypt_fn auth_decrypt; + OSSL_CCM_gettag_fn gettag; +}; + +OSSL_FUNC_cipher_encrypt_init_fn ossl_ccm_einit; +OSSL_FUNC_cipher_decrypt_init_fn ossl_ccm_dinit; +OSSL_FUNC_cipher_get_ctx_params_fn ossl_ccm_get_ctx_params; +OSSL_FUNC_cipher_set_ctx_params_fn ossl_ccm_set_ctx_params; +OSSL_FUNC_cipher_update_fn ossl_ccm_stream_update; +OSSL_FUNC_cipher_final_fn ossl_ccm_stream_final; +OSSL_FUNC_cipher_cipher_fn ossl_ccm_cipher; +void ossl_ccm_initctx(PROV_CCM_CTX *ctx, size_t keybits, const PROV_CCM_HW *hw); + +int ossl_ccm_generic_setiv(PROV_CCM_CTX *ctx, const unsigned char *nonce, + size_t nlen, size_t mlen); +int ossl_ccm_generic_setaad(PROV_CCM_CTX *ctx, const unsigned char *aad, + size_t alen); +int ossl_ccm_generic_gettag(PROV_CCM_CTX *ctx, unsigned char *tag, size_t tlen); +int ossl_ccm_generic_auth_encrypt(PROV_CCM_CTX *ctx, const unsigned char *in, + unsigned char *out, size_t len, + unsigned char *tag, size_t taglen); +int ossl_ccm_generic_auth_decrypt(PROV_CCM_CTX *ctx, const unsigned char *in, + unsigned char *out, size_t len, + unsigned char *expected_tag, size_t taglen); + +#endif diff --git a/crypto/openssl/providers/implementations/include/prov/ciphercommon_gcm.h b/crypto/openssl/providers/implementations/include/prov/ciphercommon_gcm.h new file mode 100644 index 000000000000..ee0b23b92785 --- /dev/null +++ b/crypto/openssl/providers/implementations/include/prov/ciphercommon_gcm.h @@ -0,0 +1,133 @@ + +/* + * Copyright 2019-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 + */ + +#ifndef OSSL_PROV_CIPHERCOMMON_GCM_H +# define OSSL_PROV_CIPHERCOMMON_GCM_H +# pragma once + +# include <openssl/aes.h> +# include "ciphercommon_aead.h" + +typedef struct prov_gcm_hw_st PROV_GCM_HW; + +# define GCM_IV_DEFAULT_SIZE 12 /* IV's for AES_GCM should normally be 12 bytes */ +# define GCM_IV_MAX_SIZE (1024 / 8) +# define GCM_TAG_MAX_SIZE 16 + +# if defined(OPENSSL_CPUID_OBJ) && defined(__s390__) +/*- + * KMA-GCM-AES parameter block - begin + * (see z/Architecture Principles of Operation >= SA22-7832-11) + */ +typedef struct S390X_kma_params_st { + unsigned char reserved[12]; + union { + unsigned int w; + unsigned char b[4]; + } cv; /* 32 bit counter value */ + union { + unsigned long long g[2]; + unsigned char b[16]; + } t; /* tag */ + unsigned char h[16]; /* hash subkey */ + unsigned long long taadl; /* total AAD length */ + unsigned long long tpcl; /* total plaintxt/ciphertxt len */ + union { + unsigned long long g[2]; + unsigned int w[4]; + } j0; /* initial counter value */ + unsigned char k[32]; /* key */ +} S390X_KMA_PARAMS; + +# endif + +typedef struct prov_gcm_ctx_st { + unsigned int mode; /* The mode that we are using */ + size_t keylen; + size_t ivlen; + size_t taglen; + size_t tls_aad_pad_sz; + size_t tls_aad_len; /* TLS AAD length */ + uint64_t tls_enc_records; /* Number of TLS records encrypted */ + + /* + * num contains the number of bytes of |iv| which are valid for modes that + * manage partial blocks themselves. + */ + size_t num; + size_t bufsz; /* Number of bytes in buf */ + uint64_t flags; + + unsigned int iv_state; /* set to one of IV_STATE_XXX */ + unsigned int enc:1; /* Set to 1 if we are encrypting or 0 otherwise */ + unsigned int pad:1; /* Whether padding should be used or not */ + unsigned int key_set:1; /* Set if key initialised */ + unsigned int iv_gen_rand:1; /* No IV was specified, so generate a rand IV */ + unsigned int iv_gen:1; /* It is OK to generate IVs */ + + unsigned char iv[GCM_IV_MAX_SIZE]; /* Buffer to use for IV's */ + unsigned char buf[AES_BLOCK_SIZE]; /* Buffer of partial blocks processed via update calls */ + + OSSL_LIB_CTX *libctx; /* needed for rand calls */ + const PROV_GCM_HW *hw; /* hardware specific methods */ + GCM128_CONTEXT gcm; + ctr128_f ctr; +} PROV_GCM_CTX; + +PROV_CIPHER_FUNC(int, GCM_setkey, (PROV_GCM_CTX *ctx, const unsigned char *key, + size_t keylen)); +PROV_CIPHER_FUNC(int, GCM_setiv, (PROV_GCM_CTX *dat, const unsigned char *iv, + size_t ivlen)); +PROV_CIPHER_FUNC(int, GCM_aadupdate, (PROV_GCM_CTX *ctx, + const unsigned char *aad, size_t aadlen)); +PROV_CIPHER_FUNC(int, GCM_cipherupdate, (PROV_GCM_CTX *ctx, + const unsigned char *in, size_t len, + unsigned char *out)); +PROV_CIPHER_FUNC(int, GCM_cipherfinal, (PROV_GCM_CTX *ctx, unsigned char *tag)); +PROV_CIPHER_FUNC(int, GCM_oneshot, (PROV_GCM_CTX *ctx, unsigned char *aad, + size_t aad_len, const unsigned char *in, + size_t in_len, unsigned char *out, + unsigned char *tag, size_t taglen)); +struct prov_gcm_hw_st { + OSSL_GCM_setkey_fn setkey; + OSSL_GCM_setiv_fn setiv; + OSSL_GCM_aadupdate_fn aadupdate; + OSSL_GCM_cipherupdate_fn cipherupdate; + OSSL_GCM_cipherfinal_fn cipherfinal; + OSSL_GCM_oneshot_fn oneshot; +}; + +OSSL_FUNC_cipher_encrypt_init_fn ossl_gcm_einit; +OSSL_FUNC_cipher_decrypt_init_fn ossl_gcm_dinit; +OSSL_FUNC_cipher_get_ctx_params_fn ossl_gcm_get_ctx_params; +OSSL_FUNC_cipher_set_ctx_params_fn ossl_gcm_set_ctx_params; +OSSL_FUNC_cipher_cipher_fn ossl_gcm_cipher; +OSSL_FUNC_cipher_update_fn ossl_gcm_stream_update; +OSSL_FUNC_cipher_final_fn ossl_gcm_stream_final; +void ossl_gcm_initctx(void *provctx, PROV_GCM_CTX *ctx, size_t keybits, + const PROV_GCM_HW *hw); + +int ossl_gcm_setiv(PROV_GCM_CTX *ctx, const unsigned char *iv, size_t ivlen); +int ossl_gcm_aad_update(PROV_GCM_CTX *ctx, const unsigned char *aad, + size_t aad_len); +int ossl_gcm_cipher_final(PROV_GCM_CTX *ctx, unsigned char *tag); +int ossl_gcm_one_shot(PROV_GCM_CTX *ctx, unsigned char *aad, size_t aad_len, + const unsigned char *in, size_t in_len, + unsigned char *out, unsigned char *tag, size_t tag_len); +int ossl_gcm_cipher_update(PROV_GCM_CTX *ctx, const unsigned char *in, + size_t len, unsigned char *out); + +# define GCM_HW_SET_KEY_CTR_FN(ks, fn_set_enc_key, fn_block, fn_ctr) \ + fn_set_enc_key(key, keylen * 8, ks); \ + CRYPTO_gcm128_init(&ctx->gcm, ks, (block128_f)fn_block); \ + ctx->ctr = (ctr128_f)fn_ctr; \ + ctx->key_set = 1; + +#endif diff --git a/crypto/openssl/providers/implementations/include/prov/decoders.h b/crypto/openssl/providers/implementations/include/prov/decoders.h new file mode 100644 index 000000000000..654efee607d1 --- /dev/null +++ b/crypto/openssl/providers/implementations/include/prov/decoders.h @@ -0,0 +1,20 @@ +/* + * Copyright 2025 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 + */ + +#include <openssl/core.h> + +int ossl_epki2pki_der_decode(unsigned char *der, long der_len, int selection, + OSSL_CALLBACK *data_cb, void *data_cbarg, + OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg, + OSSL_LIB_CTX *libctx, const char *propq); + +int ossl_spki2typespki_der_decode(unsigned char *der, long len, int selection, + OSSL_CALLBACK *data_cb, void *data_cbarg, + OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg, + OSSL_LIB_CTX *libctx, const char *propq); diff --git a/crypto/openssl/providers/implementations/include/prov/digestcommon.h b/crypto/openssl/providers/implementations/include/prov/digestcommon.h new file mode 100644 index 000000000000..332d47349027 --- /dev/null +++ b/crypto/openssl/providers/implementations/include/prov/digestcommon.h @@ -0,0 +1,133 @@ +/* + * Copyright 2019-2021 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 + */ + +#ifndef OSSL_PROVIDERS_DIGESTCOMMON_H +# define OSSL_PROVIDERS_DIGESTCOMMON_H + +# include <openssl/core_dispatch.h> +# include <openssl/core_names.h> +# include <openssl/params.h> +# include "prov/providercommon.h" + +/* Internal flags that can be queried */ +#define PROV_DIGEST_FLAG_XOF 0x0001 +#define PROV_DIGEST_FLAG_ALGID_ABSENT 0x0002 + +# ifdef __cplusplus +extern "C" { +# endif + +#define PROV_FUNC_DIGEST_GET_PARAM(name, blksize, dgstsize, flags) \ +static OSSL_FUNC_digest_get_params_fn name##_get_params; \ +static int name##_get_params(OSSL_PARAM params[]) \ +{ \ + return ossl_digest_default_get_params(params, blksize, dgstsize, flags); \ +} + +#define PROV_DISPATCH_FUNC_DIGEST_GET_PARAMS(name) \ +{ OSSL_FUNC_DIGEST_GET_PARAMS, (void (*)(void))name##_get_params }, \ +{ OSSL_FUNC_DIGEST_GETTABLE_PARAMS, \ + (void (*)(void))ossl_digest_default_gettable_params } + +# define PROV_FUNC_DIGEST_FINAL(name, dgstsize, fin) \ +static OSSL_FUNC_digest_final_fn name##_internal_final; \ +static int name##_internal_final(void *ctx, unsigned char *out, size_t *outl, \ + size_t outsz) \ +{ \ + if (ossl_prov_is_running() && outsz >= dgstsize && fin(out, ctx)) { \ + *outl = dgstsize; \ + return 1; \ + } \ + return 0; \ +} + +# define PROV_DISPATCH_FUNC_DIGEST_CONSTRUCT_START( \ + name, CTX, blksize, dgstsize, flags, upd, fin) \ +static OSSL_FUNC_digest_newctx_fn name##_newctx; \ +static OSSL_FUNC_digest_freectx_fn name##_freectx; \ +static OSSL_FUNC_digest_dupctx_fn name##_dupctx; \ +static void *name##_newctx(void *prov_ctx) \ +{ \ + CTX *ctx = ossl_prov_is_running() ? OPENSSL_zalloc(sizeof(*ctx)) : NULL; \ + return ctx; \ +} \ +static void name##_freectx(void *vctx) \ +{ \ + CTX *ctx = (CTX *)vctx; \ + OPENSSL_clear_free(ctx, sizeof(*ctx)); \ +} \ +static void *name##_dupctx(void *ctx) \ +{ \ + CTX *in = (CTX *)ctx; \ + CTX *ret = ossl_prov_is_running() ? OPENSSL_malloc(sizeof(*ret)) : NULL; \ + if (ret != NULL) \ + *ret = *in; \ + return ret; \ +} \ +static void name##_copyctx(void *voutctx, void *vinctx) \ +{ \ + CTX *outctx = (CTX *)voutctx; \ + CTX *inctx = (CTX *)vinctx; \ + *outctx = *inctx; \ +} \ +PROV_FUNC_DIGEST_FINAL(name, dgstsize, fin) \ +PROV_FUNC_DIGEST_GET_PARAM(name, blksize, dgstsize, flags) \ +const OSSL_DISPATCH ossl_##name##_functions[] = { \ + { OSSL_FUNC_DIGEST_NEWCTX, (void (*)(void))name##_newctx }, \ + { OSSL_FUNC_DIGEST_UPDATE, (void (*)(void))upd }, \ + { OSSL_FUNC_DIGEST_FINAL, (void (*)(void))name##_internal_final }, \ + { OSSL_FUNC_DIGEST_FREECTX, (void (*)(void))name##_freectx }, \ + { OSSL_FUNC_DIGEST_DUPCTX, (void (*)(void))name##_dupctx }, \ + { OSSL_FUNC_DIGEST_COPYCTX, (void (*)(void))name##_copyctx }, \ + PROV_DISPATCH_FUNC_DIGEST_GET_PARAMS(name) + +# define PROV_DISPATCH_FUNC_DIGEST_CONSTRUCT_END \ + { 0, NULL } \ +}; + +# define IMPLEMENT_digest_functions( \ + name, CTX, blksize, dgstsize, flags, init, upd, fin) \ +static OSSL_FUNC_digest_init_fn name##_internal_init; \ +static int name##_internal_init(void *ctx, \ + ossl_unused const OSSL_PARAM params[]) \ +{ \ + return ossl_prov_is_running() && init(ctx); \ +} \ +PROV_DISPATCH_FUNC_DIGEST_CONSTRUCT_START(name, CTX, blksize, dgstsize, flags, \ + upd, fin), \ + { OSSL_FUNC_DIGEST_INIT, (void (*)(void))name##_internal_init }, \ +PROV_DISPATCH_FUNC_DIGEST_CONSTRUCT_END + +# define IMPLEMENT_digest_functions_with_settable_ctx( \ + name, CTX, blksize, dgstsize, flags, init, upd, fin, \ + settable_ctx_params, set_ctx_params) \ +static OSSL_FUNC_digest_init_fn name##_internal_init; \ +static int name##_internal_init(void *ctx, const OSSL_PARAM params[]) \ +{ \ + return ossl_prov_is_running() \ + && init(ctx) \ + && set_ctx_params(ctx, params); \ +} \ +PROV_DISPATCH_FUNC_DIGEST_CONSTRUCT_START(name, CTX, blksize, dgstsize, flags, \ + upd, fin), \ + { OSSL_FUNC_DIGEST_INIT, (void (*)(void))name##_internal_init }, \ + { OSSL_FUNC_DIGEST_SETTABLE_CTX_PARAMS, (void (*)(void))settable_ctx_params }, \ + { OSSL_FUNC_DIGEST_SET_CTX_PARAMS, (void (*)(void))set_ctx_params }, \ +PROV_DISPATCH_FUNC_DIGEST_CONSTRUCT_END + + +const OSSL_PARAM *ossl_digest_default_gettable_params(void *provctx); +int ossl_digest_default_get_params(OSSL_PARAM params[], size_t blksz, + size_t paramsz, unsigned long flags); + +# ifdef __cplusplus +} +# endif + +#endif /* OSSL_PROVIDERS_DIGESTCOMMON_H */ diff --git a/crypto/openssl/providers/implementations/include/prov/ecx.h b/crypto/openssl/providers/implementations/include/prov/ecx.h new file mode 100644 index 000000000000..3427d154aa8a --- /dev/null +++ b/crypto/openssl/providers/implementations/include/prov/ecx.h @@ -0,0 +1,31 @@ +/* + * 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 + */ + +#include "crypto/types.h" + +#ifndef OPENSSL_NO_EC + +/* RFC 9180 Labels used for Extract and Expand operations */ + +/* ASCII: "eae_prk", in hex for EBCDIC compatibility */ +#define OSSL_DHKEM_LABEL_EAE_PRK "\x65\x61\x65\x5F\x70\x72\x6B" +/* ASCII: "shared_secret", in hex for EBCDIC compatibility */ +#define OSSL_DHKEM_LABEL_SHARED_SECRET "\x73\x68\x61\x72\x65\x64\x5F\x73\x65\x63\x72\x65\x74" +/* ASCII: "dkp_prk", in hex for EBCDIC compatibility */ +#define OSSL_DHKEM_LABEL_DKP_PRK "\x64\x6B\x70\x5F\x70\x72\x6B" +/* ASCII: "candidate", in hex for EBCDIC compatibility */ +#define OSSL_DHKEM_LABEL_CANDIDATE "\x63\x61\x6E\x64\x69\x64\x61\x74\x65" +/* ASCII: "sk", in hex for EBCDIC compatibility */ +#define OSSL_DHKEM_LABEL_SK "\x73\x6B" + +int ossl_ecx_dhkem_derive_private(ECX_KEY *ecx, unsigned char *privout, + const unsigned char *ikm, size_t ikmlen); +int ossl_ec_dhkem_derive_private(EC_KEY *ec, BIGNUM *privout, + const unsigned char *ikm, size_t ikmlen); +#endif diff --git a/crypto/openssl/providers/implementations/include/prov/hmac_drbg.h b/crypto/openssl/providers/implementations/include/prov/hmac_drbg.h new file mode 100644 index 000000000000..28aa5bc1adfc --- /dev/null +++ b/crypto/openssl/providers/implementations/include/prov/hmac_drbg.h @@ -0,0 +1,33 @@ +/* + * 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 + */ + +#ifndef OSSL_PROV_HMAC_DRBG_H +# define OSSL_PROV_HMAC_DRBG_H +# pragma once + +#include <openssl/evp.h> +#include "prov/provider_util.h" + +typedef struct drbg_hmac_st { + EVP_MAC_CTX *ctx; /* H(x) = HMAC_hash OR H(x) = KMAC */ + PROV_DIGEST digest; /* H(x) = hash(x) */ + size_t blocklen; + unsigned char K[EVP_MAX_MD_SIZE]; + unsigned char V[EVP_MAX_MD_SIZE]; +} PROV_DRBG_HMAC; + +int ossl_drbg_hmac_init(PROV_DRBG_HMAC *drbg, + const unsigned char *ent, size_t ent_len, + const unsigned char *nonce, size_t nonce_len, + const unsigned char *pstr, size_t pstr_len); +int ossl_drbg_hmac_generate(PROV_DRBG_HMAC *hmac, + unsigned char *out, size_t outlen, + const unsigned char *adin, size_t adin_len); + +#endif /* OSSL_PROV_HMAC_DRBG_H */ diff --git a/crypto/openssl/providers/implementations/include/prov/implementations.h b/crypto/openssl/providers/implementations/include/prov/implementations.h new file mode 100644 index 000000000000..35b0b0b97406 --- /dev/null +++ b/crypto/openssl/providers/implementations/include/prov/implementations.h @@ -0,0 +1,849 @@ +/* + * Copyright 2019-2025 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 + */ + +#include <openssl/core.h> +#include <openssl/types.h> + +/* Digests */ +extern const OSSL_DISPATCH ossl_sha1_functions[]; +extern const OSSL_DISPATCH ossl_sha224_functions[]; +extern const OSSL_DISPATCH ossl_sha256_functions[]; +extern const OSSL_DISPATCH ossl_sha256_192_functions[]; +extern const OSSL_DISPATCH ossl_sha384_functions[]; +extern const OSSL_DISPATCH ossl_sha512_functions[]; +extern const OSSL_DISPATCH ossl_sha512_224_functions[]; +extern const OSSL_DISPATCH ossl_sha512_256_functions[]; +extern const OSSL_DISPATCH ossl_sha3_224_functions[]; +extern const OSSL_DISPATCH ossl_sha3_256_functions[]; +extern const OSSL_DISPATCH ossl_sha3_384_functions[]; +extern const OSSL_DISPATCH ossl_sha3_512_functions[]; +extern const OSSL_DISPATCH ossl_keccak_224_functions[]; +extern const OSSL_DISPATCH ossl_keccak_256_functions[]; +extern const OSSL_DISPATCH ossl_keccak_384_functions[]; +extern const OSSL_DISPATCH ossl_keccak_512_functions[]; +extern const OSSL_DISPATCH ossl_keccak_kmac_128_functions[]; +extern const OSSL_DISPATCH ossl_keccak_kmac_256_functions[]; +extern const OSSL_DISPATCH ossl_shake_128_functions[]; +extern const OSSL_DISPATCH ossl_shake_256_functions[]; +extern const OSSL_DISPATCH ossl_blake2s256_functions[]; +extern const OSSL_DISPATCH ossl_blake2b512_functions[]; +extern const OSSL_DISPATCH ossl_md5_functions[]; +extern const OSSL_DISPATCH ossl_md5_sha1_functions[]; +extern const OSSL_DISPATCH ossl_sm3_functions[]; +extern const OSSL_DISPATCH ossl_md2_functions[]; +extern const OSSL_DISPATCH ossl_md4_functions[]; +extern const OSSL_DISPATCH ossl_mdc2_functions[]; +extern const OSSL_DISPATCH ossl_wp_functions[]; +extern const OSSL_DISPATCH ossl_ripemd160_functions[]; +extern const OSSL_DISPATCH ossl_nullmd_functions[]; + +/* Ciphers */ +extern const OSSL_DISPATCH ossl_null_functions[]; +extern const OSSL_DISPATCH ossl_aes256ecb_functions[]; +extern const OSSL_DISPATCH ossl_aes192ecb_functions[]; +extern const OSSL_DISPATCH ossl_aes128ecb_functions[]; +extern const OSSL_DISPATCH ossl_aes256cbc_functions[]; +extern const OSSL_DISPATCH ossl_aes192cbc_functions[]; +extern const OSSL_DISPATCH ossl_aes128cbc_functions[]; +extern const OSSL_DISPATCH ossl_aes256cbc_cts_functions[]; +extern const OSSL_DISPATCH ossl_aes192cbc_cts_functions[]; +extern const OSSL_DISPATCH ossl_aes128cbc_cts_functions[]; +extern const OSSL_DISPATCH ossl_aes256ofb_functions[]; +extern const OSSL_DISPATCH ossl_aes192ofb_functions[]; +extern const OSSL_DISPATCH ossl_aes128ofb_functions[]; +extern const OSSL_DISPATCH ossl_aes256cfb_functions[]; +extern const OSSL_DISPATCH ossl_aes192cfb_functions[]; +extern const OSSL_DISPATCH ossl_aes128cfb_functions[]; +extern const OSSL_DISPATCH ossl_aes256cfb1_functions[]; +extern const OSSL_DISPATCH ossl_aes192cfb1_functions[]; +extern const OSSL_DISPATCH ossl_aes128cfb1_functions[]; +extern const OSSL_DISPATCH ossl_aes256cfb8_functions[]; +extern const OSSL_DISPATCH ossl_aes192cfb8_functions[]; +extern const OSSL_DISPATCH ossl_aes128cfb8_functions[]; +extern const OSSL_DISPATCH ossl_aes256ctr_functions[]; +extern const OSSL_DISPATCH ossl_aes192ctr_functions[]; +extern const OSSL_DISPATCH ossl_aes128ctr_functions[]; +extern const OSSL_DISPATCH ossl_aes256xts_functions[]; +extern const OSSL_DISPATCH ossl_aes128xts_functions[]; +#ifndef OPENSSL_NO_OCB +extern const OSSL_DISPATCH ossl_aes256ocb_functions[]; +extern const OSSL_DISPATCH ossl_aes192ocb_functions[]; +extern const OSSL_DISPATCH ossl_aes128ocb_functions[]; +#endif /* OPENSSL_NO_OCB */ +extern const OSSL_DISPATCH ossl_aes256gcm_functions[]; +extern const OSSL_DISPATCH ossl_aes192gcm_functions[]; +extern const OSSL_DISPATCH ossl_aes128gcm_functions[]; +extern const OSSL_DISPATCH ossl_aes256ccm_functions[]; +extern const OSSL_DISPATCH ossl_aes192ccm_functions[]; +extern const OSSL_DISPATCH ossl_aes128ccm_functions[]; +extern const OSSL_DISPATCH ossl_aes256wrap_functions[]; +extern const OSSL_DISPATCH ossl_aes192wrap_functions[]; +extern const OSSL_DISPATCH ossl_aes128wrap_functions[]; +extern const OSSL_DISPATCH ossl_aes256wrappad_functions[]; +extern const OSSL_DISPATCH ossl_aes192wrappad_functions[]; +extern const OSSL_DISPATCH ossl_aes128wrappad_functions[]; +extern const OSSL_DISPATCH ossl_aes256wrapinv_functions[]; +extern const OSSL_DISPATCH ossl_aes192wrapinv_functions[]; +extern const OSSL_DISPATCH ossl_aes128wrapinv_functions[]; +extern const OSSL_DISPATCH ossl_aes256wrappadinv_functions[]; +extern const OSSL_DISPATCH ossl_aes192wrappadinv_functions[]; +extern const OSSL_DISPATCH ossl_aes128wrappadinv_functions[]; +extern const OSSL_DISPATCH ossl_aes256cbc_hmac_sha1_functions[]; +extern const OSSL_DISPATCH ossl_aes128cbc_hmac_sha1_functions[]; +extern const OSSL_DISPATCH ossl_aes256cbc_hmac_sha256_functions[]; +extern const OSSL_DISPATCH ossl_aes128cbc_hmac_sha256_functions[]; + +#ifndef OPENSSL_NO_ARIA +extern const OSSL_DISPATCH ossl_aria256gcm_functions[]; +extern const OSSL_DISPATCH ossl_aria192gcm_functions[]; +extern const OSSL_DISPATCH ossl_aria128gcm_functions[]; +extern const OSSL_DISPATCH ossl_aria256ccm_functions[]; +extern const OSSL_DISPATCH ossl_aria192ccm_functions[]; +extern const OSSL_DISPATCH ossl_aria128ccm_functions[]; +extern const OSSL_DISPATCH ossl_aria256ecb_functions[]; +extern const OSSL_DISPATCH ossl_aria192ecb_functions[]; +extern const OSSL_DISPATCH ossl_aria128ecb_functions[]; +extern const OSSL_DISPATCH ossl_aria256cbc_functions[]; +extern const OSSL_DISPATCH ossl_aria192cbc_functions[]; +extern const OSSL_DISPATCH ossl_aria128cbc_functions[]; +extern const OSSL_DISPATCH ossl_aria256ofb_functions[]; +extern const OSSL_DISPATCH ossl_aria192ofb_functions[]; +extern const OSSL_DISPATCH ossl_aria128ofb_functions[]; +extern const OSSL_DISPATCH ossl_aria256cfb_functions[]; +extern const OSSL_DISPATCH ossl_aria192cfb_functions[]; +extern const OSSL_DISPATCH ossl_aria128cfb_functions[]; +extern const OSSL_DISPATCH ossl_aria256cfb1_functions[]; +extern const OSSL_DISPATCH ossl_aria192cfb1_functions[]; +extern const OSSL_DISPATCH ossl_aria128cfb1_functions[]; +extern const OSSL_DISPATCH ossl_aria256cfb8_functions[]; +extern const OSSL_DISPATCH ossl_aria192cfb8_functions[]; +extern const OSSL_DISPATCH ossl_aria128cfb8_functions[]; +extern const OSSL_DISPATCH ossl_aria256ctr_functions[]; +extern const OSSL_DISPATCH ossl_aria192ctr_functions[]; +extern const OSSL_DISPATCH ossl_aria128ctr_functions[]; +#endif /* OPENSSL_NO_ARIA */ +#ifndef OPENSSL_NO_CAMELLIA +extern const OSSL_DISPATCH ossl_camellia256ecb_functions[]; +extern const OSSL_DISPATCH ossl_camellia192ecb_functions[]; +extern const OSSL_DISPATCH ossl_camellia128ecb_functions[]; +extern const OSSL_DISPATCH ossl_camellia256cbc_functions[]; +extern const OSSL_DISPATCH ossl_camellia192cbc_functions[]; +extern const OSSL_DISPATCH ossl_camellia128cbc_functions[]; +extern const OSSL_DISPATCH ossl_camellia256cbc_cts_functions[]; +extern const OSSL_DISPATCH ossl_camellia192cbc_cts_functions[]; +extern const OSSL_DISPATCH ossl_camellia128cbc_cts_functions[]; +extern const OSSL_DISPATCH ossl_camellia256ofb_functions[]; +extern const OSSL_DISPATCH ossl_camellia192ofb_functions[]; +extern const OSSL_DISPATCH ossl_camellia128ofb_functions[]; +extern const OSSL_DISPATCH ossl_camellia256cfb_functions[]; +extern const OSSL_DISPATCH ossl_camellia192cfb_functions[]; +extern const OSSL_DISPATCH ossl_camellia128cfb_functions[]; +extern const OSSL_DISPATCH ossl_camellia256cfb1_functions[]; +extern const OSSL_DISPATCH ossl_camellia192cfb1_functions[]; +extern const OSSL_DISPATCH ossl_camellia128cfb1_functions[]; +extern const OSSL_DISPATCH ossl_camellia256cfb8_functions[]; +extern const OSSL_DISPATCH ossl_camellia192cfb8_functions[]; +extern const OSSL_DISPATCH ossl_camellia128cfb8_functions[]; +extern const OSSL_DISPATCH ossl_camellia256ctr_functions[]; +extern const OSSL_DISPATCH ossl_camellia192ctr_functions[]; +extern const OSSL_DISPATCH ossl_camellia128ctr_functions[]; +#endif /* OPENSSL_NO_CAMELLIA */ +#ifndef OPENSSL_NO_BF +extern const OSSL_DISPATCH ossl_blowfish128ecb_functions[]; +extern const OSSL_DISPATCH ossl_blowfish128cbc_functions[]; +extern const OSSL_DISPATCH ossl_blowfish128ofb64_functions[]; +extern const OSSL_DISPATCH ossl_blowfish128cfb64_functions[]; +#endif /* OPENSSL_NO_BF */ +#ifndef OPENSSL_NO_IDEA +extern const OSSL_DISPATCH ossl_idea128ecb_functions[]; +extern const OSSL_DISPATCH ossl_idea128cbc_functions[]; +extern const OSSL_DISPATCH ossl_idea128ofb64_functions[]; +extern const OSSL_DISPATCH ossl_idea128cfb64_functions[]; +#endif /* OPENSSL_NO_IDEA */ +#ifndef OPENSSL_NO_CAST +extern const OSSL_DISPATCH ossl_cast5128ecb_functions[]; +extern const OSSL_DISPATCH ossl_cast5128cbc_functions[]; +extern const OSSL_DISPATCH ossl_cast5128ofb64_functions[]; +extern const OSSL_DISPATCH ossl_cast5128cfb64_functions[]; +#endif /* OPENSSL_NO_CAST */ +#ifndef OPENSSL_NO_SEED +extern const OSSL_DISPATCH ossl_seed128ecb_functions[]; +extern const OSSL_DISPATCH ossl_seed128cbc_functions[]; +extern const OSSL_DISPATCH ossl_seed128ofb128_functions[]; +extern const OSSL_DISPATCH ossl_seed128cfb128_functions[]; +#endif /* OPENSSL_NO_SEED */ +#ifndef OPENSSL_NO_SM4 +extern const OSSL_DISPATCH ossl_sm4128gcm_functions[]; +extern const OSSL_DISPATCH ossl_sm4128ccm_functions[]; +extern const OSSL_DISPATCH ossl_sm4128ecb_functions[]; +extern const OSSL_DISPATCH ossl_sm4128cbc_functions[]; +extern const OSSL_DISPATCH ossl_sm4128ctr_functions[]; +extern const OSSL_DISPATCH ossl_sm4128ofb128_functions[]; +extern const OSSL_DISPATCH ossl_sm4128cfb128_functions[]; +extern const OSSL_DISPATCH ossl_sm4128xts_functions[]; +#endif /* OPENSSL_NO_SM4 */ +#ifndef OPENSSL_NO_RC5 +extern const OSSL_DISPATCH ossl_rc5128ecb_functions[]; +extern const OSSL_DISPATCH ossl_rc5128cbc_functions[]; +extern const OSSL_DISPATCH ossl_rc5128ofb64_functions[]; +extern const OSSL_DISPATCH ossl_rc5128cfb64_functions[]; +#endif /* OPENSSL_NO_RC5 */ +#ifndef OPENSSL_NO_RC2 +extern const OSSL_DISPATCH ossl_rc2128ecb_functions[]; +extern const OSSL_DISPATCH ossl_rc2128cbc_functions[]; +extern const OSSL_DISPATCH ossl_rc240cbc_functions[]; +extern const OSSL_DISPATCH ossl_rc264cbc_functions[]; +extern const OSSL_DISPATCH ossl_rc2128cfb128_functions[]; +extern const OSSL_DISPATCH ossl_rc2128ofb128_functions[]; +#endif /* OPENSSL_NO_RC2 */ +#ifndef OPENSSL_NO_DES +extern const OSSL_DISPATCH ossl_tdes_ede3_ecb_functions[]; +extern const OSSL_DISPATCH ossl_tdes_ede3_cbc_functions[]; +# ifndef FIPS_MODULE +extern const OSSL_DISPATCH ossl_tdes_ede3_ofb_functions[]; +extern const OSSL_DISPATCH ossl_tdes_ede3_cfb_functions[]; +extern const OSSL_DISPATCH ossl_tdes_ede3_cfb8_functions[]; +extern const OSSL_DISPATCH ossl_tdes_ede3_cfb1_functions[]; + +extern const OSSL_DISPATCH ossl_tdes_ede2_ecb_functions[]; +extern const OSSL_DISPATCH ossl_tdes_ede2_cbc_functions[]; +extern const OSSL_DISPATCH ossl_tdes_ede2_ofb_functions[]; +extern const OSSL_DISPATCH ossl_tdes_ede2_cfb_functions[]; + +extern const OSSL_DISPATCH ossl_tdes_desx_cbc_functions[]; +extern const OSSL_DISPATCH ossl_tdes_wrap_cbc_functions[]; + +extern const OSSL_DISPATCH ossl_des_ecb_functions[]; +extern const OSSL_DISPATCH ossl_des_cbc_functions[]; +extern const OSSL_DISPATCH ossl_des_ofb64_functions[]; +extern const OSSL_DISPATCH ossl_des_cfb64_functions[]; +extern const OSSL_DISPATCH ossl_des_cfb1_functions[]; +extern const OSSL_DISPATCH ossl_des_cfb8_functions[]; +# endif /* FIPS_MODULE */ +#endif /* OPENSSL_NO_DES */ + +#ifndef OPENSSL_NO_RC4 +extern const OSSL_DISPATCH ossl_rc440_functions[]; +extern const OSSL_DISPATCH ossl_rc4128_functions[]; +# ifndef OPENSSL_NO_MD5 +extern const OSSL_DISPATCH ossl_rc4_hmac_ossl_md5_functions[]; +# endif /* OPENSSL_NO_MD5 */ +#endif /* OPENSSL_NO_RC4 */ +#ifndef OPENSSL_NO_CHACHA +extern const OSSL_DISPATCH ossl_chacha20_functions[]; +# ifndef OPENSSL_NO_POLY1305 +extern const OSSL_DISPATCH ossl_chacha20_ossl_poly1305_functions[]; +# endif /* OPENSSL_NO_POLY1305 */ +#endif /* OPENSSL_NO_CHACHA */ + + +#ifndef OPENSSL_NO_SIV +extern const OSSL_DISPATCH ossl_aes128siv_functions[]; +extern const OSSL_DISPATCH ossl_aes192siv_functions[]; +extern const OSSL_DISPATCH ossl_aes256siv_functions[]; +extern const OSSL_DISPATCH ossl_aes128gcm_siv_functions[]; +extern const OSSL_DISPATCH ossl_aes192gcm_siv_functions[]; +extern const OSSL_DISPATCH ossl_aes256gcm_siv_functions[]; +#endif /* OPENSSL_NO_SIV */ + +/* MACs */ +extern const OSSL_DISPATCH ossl_blake2bmac_functions[]; +extern const OSSL_DISPATCH ossl_blake2smac_functions[]; +extern const OSSL_DISPATCH ossl_cmac_functions[]; +extern const OSSL_DISPATCH ossl_gmac_functions[]; +extern const OSSL_DISPATCH ossl_hmac_functions[]; +#ifdef FIPS_MODULE +extern const OSSL_DISPATCH ossl_hmac_internal_functions[]; +extern const OSSL_DISPATCH ossl_kmac128_internal_functions[]; +extern const OSSL_DISPATCH ossl_kmac256_internal_functions[]; +#endif +extern const OSSL_DISPATCH ossl_kmac128_functions[]; +extern const OSSL_DISPATCH ossl_kmac256_functions[]; +extern const OSSL_DISPATCH ossl_siphash_functions[]; +extern const OSSL_DISPATCH ossl_poly1305_functions[]; + +/* KDFs / PRFs */ +extern const OSSL_DISPATCH ossl_kdf_pbkdf1_functions[]; +extern const OSSL_DISPATCH ossl_kdf_pbkdf2_functions[]; +extern const OSSL_DISPATCH ossl_kdf_pvk_functions[]; +extern const OSSL_DISPATCH ossl_kdf_pkcs12_functions[]; +#ifndef OPENSSL_NO_SCRYPT +extern const OSSL_DISPATCH ossl_kdf_scrypt_functions[]; +#endif +extern const OSSL_DISPATCH ossl_kdf_tls1_prf_functions[]; +extern const OSSL_DISPATCH ossl_kdf_hkdf_functions[]; +extern const OSSL_DISPATCH ossl_kdf_tls1_3_kdf_functions[]; +extern const OSSL_DISPATCH ossl_kdf_sshkdf_functions[]; +extern const OSSL_DISPATCH ossl_kdf_sskdf_functions[]; +extern const OSSL_DISPATCH ossl_kdf_x963_kdf_functions[]; +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[]; +extern const OSSL_DISPATCH ossl_seed_src_functions[]; +extern const OSSL_DISPATCH ossl_jitter_functions[]; +extern const OSSL_DISPATCH ossl_crng_test_functions[]; +extern const OSSL_DISPATCH ossl_drbg_hash_functions[]; +extern const OSSL_DISPATCH ossl_drbg_ossl_hmac_functions[]; +extern const OSSL_DISPATCH ossl_drbg_ctr_functions[]; +extern const OSSL_DISPATCH crngt_functions[]; + +/* Key management */ +extern const OSSL_DISPATCH ossl_dh_keymgmt_functions[]; +extern const OSSL_DISPATCH ossl_dhx_keymgmt_functions[]; +extern const OSSL_DISPATCH ossl_dsa_keymgmt_functions[]; +extern const OSSL_DISPATCH ossl_rsa_keymgmt_functions[]; +extern const OSSL_DISPATCH ossl_rsapss_keymgmt_functions[]; +extern const OSSL_DISPATCH ossl_kdf_keymgmt_functions[]; +extern const OSSL_DISPATCH ossl_mac_legacy_keymgmt_functions[]; +extern const OSSL_DISPATCH ossl_cmac_legacy_keymgmt_functions[]; +#ifndef OPENSSL_NO_EC +extern const OSSL_DISPATCH ossl_ec_keymgmt_functions[]; +# ifndef OPENSSL_NO_ECX +extern const OSSL_DISPATCH ossl_x25519_keymgmt_functions[]; +extern const OSSL_DISPATCH ossl_x448_keymgmt_functions[]; +extern const OSSL_DISPATCH ossl_ed25519_keymgmt_functions[]; +extern const OSSL_DISPATCH ossl_ed448_keymgmt_functions[]; +# endif +# ifndef OPENSSL_NO_SM2 +extern const OSSL_DISPATCH ossl_sm2_keymgmt_functions[]; +# endif +#endif +extern const OSSL_DISPATCH ossl_ml_dsa_44_keymgmt_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_65_keymgmt_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_87_keymgmt_functions[]; +#ifndef OPENSSL_NO_ML_KEM +extern const OSSL_DISPATCH ossl_ml_kem_512_keymgmt_functions[]; +extern const OSSL_DISPATCH ossl_ml_kem_768_keymgmt_functions[]; +extern const OSSL_DISPATCH ossl_ml_kem_1024_keymgmt_functions[]; +# ifndef OPENSSL_NO_EC +# ifndef OPENSSL_NO_ECX +extern const OSSL_DISPATCH ossl_mlx_x25519_kem_kmgmt_functions[]; +extern const OSSL_DISPATCH ossl_mlx_x448_kem_kmgmt_functions[]; +# endif +extern const OSSL_DISPATCH ossl_mlx_p256_kem_kmgmt_functions[]; +extern const OSSL_DISPATCH ossl_mlx_p384_kem_kmgmt_functions[]; +# endif +#endif +#ifndef OPENSSL_NO_SLH_DSA +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128s_keymgmt_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128f_keymgmt_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192s_keymgmt_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192f_keymgmt_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256s_keymgmt_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256f_keymgmt_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_128s_keymgmt_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_128f_keymgmt_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_192s_keymgmt_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_192f_keymgmt_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_256s_keymgmt_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_256f_keymgmt_functions[]; +#endif /* OPENSSL_NO_SLH_DSA */ + +/* Key Exchange */ +extern const OSSL_DISPATCH ossl_dh_keyexch_functions[]; +#ifndef OPENSSL_NO_EC +extern const OSSL_DISPATCH ossl_ecdh_keyexch_functions[]; +# ifndef OPENSSL_NO_ECX +extern const OSSL_DISPATCH ossl_x25519_keyexch_functions[]; +extern const OSSL_DISPATCH ossl_x448_keyexch_functions[]; +# endif +#endif +extern const OSSL_DISPATCH ossl_kdf_tls1_prf_keyexch_functions[]; +extern const OSSL_DISPATCH ossl_kdf_hkdf_keyexch_functions[]; +extern const OSSL_DISPATCH ossl_kdf_scrypt_keyexch_functions[]; + +/* Signature */ +extern const OSSL_DISPATCH ossl_dsa_signature_functions[]; +extern const OSSL_DISPATCH ossl_dsa_sha1_signature_functions[]; +extern const OSSL_DISPATCH ossl_dsa_sha224_signature_functions[]; +extern const OSSL_DISPATCH ossl_dsa_sha256_signature_functions[]; +extern const OSSL_DISPATCH ossl_dsa_sha384_signature_functions[]; +extern const OSSL_DISPATCH ossl_dsa_sha512_signature_functions[]; +extern const OSSL_DISPATCH ossl_dsa_sha3_224_signature_functions[]; +extern const OSSL_DISPATCH ossl_dsa_sha3_256_signature_functions[]; +extern const OSSL_DISPATCH ossl_dsa_sha3_384_signature_functions[]; +extern const OSSL_DISPATCH ossl_dsa_sha3_512_signature_functions[]; +extern const OSSL_DISPATCH ossl_rsa_signature_functions[]; +#ifndef OPENSSL_NO_MD5 +extern const OSSL_DISPATCH ossl_rsa_md5_signature_functions[]; +#endif +#ifndef OPENSSL_NO_RMD160 +extern const OSSL_DISPATCH ossl_rsa_ripemd160_signature_functions[]; +#endif +extern const OSSL_DISPATCH ossl_rsa_sha1_signature_functions[]; +extern const OSSL_DISPATCH ossl_rsa_sha224_signature_functions[]; +extern const OSSL_DISPATCH ossl_rsa_sha256_signature_functions[]; +extern const OSSL_DISPATCH ossl_rsa_sha384_signature_functions[]; +extern const OSSL_DISPATCH ossl_rsa_sha512_signature_functions[]; +extern const OSSL_DISPATCH ossl_rsa_sha512_224_signature_functions[]; +extern const OSSL_DISPATCH ossl_rsa_sha512_256_signature_functions[]; +extern const OSSL_DISPATCH ossl_rsa_sha3_224_signature_functions[]; +extern const OSSL_DISPATCH ossl_rsa_sha3_256_signature_functions[]; +extern const OSSL_DISPATCH ossl_rsa_sha3_384_signature_functions[]; +extern const OSSL_DISPATCH ossl_rsa_sha3_512_signature_functions[]; +#ifndef OPENSSL_NO_SM3 +extern const OSSL_DISPATCH ossl_rsa_sm3_signature_functions[]; +#endif +extern const OSSL_DISPATCH ossl_ed25519_signature_functions[]; +extern const OSSL_DISPATCH ossl_ed25519ph_signature_functions[]; +extern const OSSL_DISPATCH ossl_ed25519ctx_signature_functions[]; +extern const OSSL_DISPATCH ossl_ed448_signature_functions[]; +extern const OSSL_DISPATCH ossl_ed448ph_signature_functions[]; +extern const OSSL_DISPATCH ossl_ecdsa_signature_functions[]; +extern const OSSL_DISPATCH ossl_ecdsa_sha1_signature_functions[]; +extern const OSSL_DISPATCH ossl_ecdsa_sha224_signature_functions[]; +extern const OSSL_DISPATCH ossl_ecdsa_sha256_signature_functions[]; +extern const OSSL_DISPATCH ossl_ecdsa_sha384_signature_functions[]; +extern const OSSL_DISPATCH ossl_ecdsa_sha512_signature_functions[]; +extern const OSSL_DISPATCH ossl_ecdsa_sha3_224_signature_functions[]; +extern const OSSL_DISPATCH ossl_ecdsa_sha3_256_signature_functions[]; +extern const OSSL_DISPATCH ossl_ecdsa_sha3_384_signature_functions[]; +extern const OSSL_DISPATCH ossl_ecdsa_sha3_512_signature_functions[]; +extern const OSSL_DISPATCH ossl_mac_legacy_hmac_signature_functions[]; +extern const OSSL_DISPATCH ossl_mac_legacy_siphash_signature_functions[]; +extern const OSSL_DISPATCH ossl_mac_legacy_poly1305_signature_functions[]; +extern const OSSL_DISPATCH ossl_mac_legacy_cmac_signature_functions[]; +extern const OSSL_DISPATCH ossl_sm2_signature_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_44_signature_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_65_signature_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_87_signature_functions[]; +#ifndef OPENSSL_NO_SLH_DSA +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128s_signature_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128f_signature_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192s_signature_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192f_signature_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256s_signature_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256f_signature_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_128s_signature_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_128f_signature_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_192s_signature_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_192f_signature_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_256s_signature_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_256f_signature_functions[]; +#endif /* OPENSSL_NO_SLH_DSA */ + +/* Asym Cipher */ +extern const OSSL_DISPATCH ossl_rsa_asym_cipher_functions[]; +#ifndef OPENSSL_NO_SM2 +extern const OSSL_DISPATCH ossl_sm2_asym_cipher_functions[]; +#endif + +/* Asym Key encapsulation */ +extern const OSSL_DISPATCH ossl_rsa_asym_kem_functions[]; +#ifndef OPENSSL_NO_EC +extern const OSSL_DISPATCH ossl_ec_asym_kem_functions[]; +# ifndef OPENSSL_NO_ECX +extern const OSSL_DISPATCH ossl_ecx_asym_kem_functions[]; +# endif +#endif +#ifndef OPENSSL_NO_ML_KEM +extern const OSSL_DISPATCH ossl_ml_kem_asym_kem_functions[]; +# ifndef OPENSSL_NO_EC +extern const OSSL_DISPATCH ossl_mlx_kem_asym_kem_functions[]; +# endif +#endif + +/* Encoders */ +extern const OSSL_DISPATCH ossl_rsa_to_PKCS1_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_rsa_to_PKCS1_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_rsa_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_rsa_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_rsa_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_rsa_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_rsa_to_RSA_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_rsa_to_RSA_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_rsa_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_rsa_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_rsa_to_msblob_encoder_functions[]; +extern const OSSL_DISPATCH ossl_rsa_to_pvk_encoder_functions[]; +extern const OSSL_DISPATCH ossl_rsa_to_text_encoder_functions[]; +extern const OSSL_DISPATCH ossl_rsa_to_type_specific_keypair_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_rsa_to_type_specific_keypair_pem_encoder_functions[]; + +extern const OSSL_DISPATCH ossl_rsapss_to_PKCS1_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_rsapss_to_PKCS1_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_rsapss_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_rsapss_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_rsapss_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_rsapss_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_rsapss_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_rsapss_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_rsapss_to_text_encoder_functions[]; + +extern const OSSL_DISPATCH ossl_dh_to_DH_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dh_to_DH_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dh_to_PKCS3_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dh_to_PKCS3_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dh_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dh_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dh_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dh_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dh_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dh_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dh_to_type_specific_params_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dh_to_type_specific_params_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dh_to_text_encoder_functions[]; + +extern const OSSL_DISPATCH ossl_dhx_to_DHX_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dhx_to_DHX_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dhx_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dhx_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dhx_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dhx_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dhx_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dhx_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dhx_to_X9_42_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dhx_to_X9_42_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dhx_to_type_specific_params_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dhx_to_type_specific_params_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dhx_to_text_encoder_functions[]; + +extern const OSSL_DISPATCH ossl_dsa_to_DSA_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dsa_to_DSA_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dsa_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dsa_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dsa_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dsa_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dsa_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dsa_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dsa_to_type_specific_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dsa_to_type_specific_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dsa_to_msblob_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dsa_to_pvk_encoder_functions[]; +extern const OSSL_DISPATCH ossl_dsa_to_text_encoder_functions[]; + +extern const OSSL_DISPATCH ossl_ec_to_EC_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ec_to_EC_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ec_to_blob_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ec_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ec_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ec_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ec_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ec_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ec_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ec_to_X9_62_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ec_to_X9_62_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ec_to_type_specific_no_pub_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ec_to_type_specific_no_pub_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ec_to_text_encoder_functions[]; + +#ifndef OPENSSL_NO_SM2 +extern const OSSL_DISPATCH ossl_sm2_to_SM2_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_sm2_to_SM2_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_sm2_to_blob_encoder_functions[]; +extern const OSSL_DISPATCH ossl_sm2_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_sm2_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_sm2_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_sm2_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_sm2_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_sm2_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_sm2_to_type_specific_no_pub_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_sm2_to_type_specific_no_pub_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_sm2_to_text_encoder_functions[]; +#endif + +extern const OSSL_DISPATCH ossl_ed25519_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ed25519_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ed25519_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ed25519_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ed25519_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ed25519_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ed25519_to_OSSL_current_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ed25519_to_text_encoder_functions[]; + +extern const OSSL_DISPATCH ossl_ed448_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ed448_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ed448_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ed448_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ed448_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ed448_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ed448_to_OSSL_current_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ed448_to_text_encoder_functions[]; + +extern const OSSL_DISPATCH ossl_x25519_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_x25519_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_x25519_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_x25519_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_x25519_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_x25519_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_x25519_to_OSSL_current_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_x25519_to_text_encoder_functions[]; + +extern const OSSL_DISPATCH ossl_x448_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_x448_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_x448_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_x448_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_x448_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_x448_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_x448_to_OSSL_current_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_x448_to_text_encoder_functions[]; + +extern const OSSL_DISPATCH ossl_ml_dsa_44_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_44_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_44_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_44_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_44_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_44_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_44_to_OSSL_current_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_44_to_text_encoder_functions[]; + +extern const OSSL_DISPATCH ossl_ml_dsa_65_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_65_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_65_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_65_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_65_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_65_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_65_to_OSSL_current_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_65_to_text_encoder_functions[]; + +extern const OSSL_DISPATCH ossl_ml_dsa_87_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_87_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_87_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_87_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_87_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_87_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_87_to_OSSL_current_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_dsa_87_to_text_encoder_functions[]; + +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128s_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128f_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192s_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192f_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256s_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256f_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_128s_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_128f_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_192s_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_192f_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_256s_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_256f_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128s_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128f_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192s_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192f_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256s_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256f_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_128s_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_128f_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_192s_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_192f_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_256s_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_256f_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; + +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128s_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128f_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192s_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192f_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256s_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256f_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_128s_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_128f_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_192s_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_192f_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_256s_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_256f_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128s_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128f_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192s_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192f_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256s_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256f_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_128s_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_128f_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_192s_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_192f_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_256s_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_256f_to_PrivateKeyInfo_pem_encoder_functions[]; + +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128s_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128f_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192s_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192f_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256s_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256f_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_128s_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_128f_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_192s_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_192f_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_256s_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_256f_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128s_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128f_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192s_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192f_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256s_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256f_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_128s_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_128f_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_192s_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_192f_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_256s_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_256f_to_SubjectPublicKeyInfo_pem_encoder_functions[]; + +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128s_to_OSSL_current_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128f_to_OSSL_current_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192s_to_OSSL_current_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192f_to_OSSL_current_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256s_to_OSSL_current_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256f_to_OSSL_current_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_128s_to_OSSL_current_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_128f_to_OSSL_current_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_192s_to_OSSL_current_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_192f_to_OSSL_current_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_256s_to_OSSL_current_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_256f_to_OSSL_current_der_encoder_functions[]; + +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128s_to_text_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128f_to_text_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192s_to_text_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192f_to_text_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256s_to_text_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256f_to_text_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_128s_to_text_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_128f_to_text_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_192s_to_text_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_192f_to_text_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_256s_to_text_encoder_functions[]; +extern const OSSL_DISPATCH ossl_slh_dsa_shake_256f_to_text_encoder_functions[]; + +/* Decoders */ +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_dh_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_dh_decoder_functions[]; +extern const OSSL_DISPATCH ossl_type_specific_params_der_to_dh_decoder_functions[]; +extern const OSSL_DISPATCH ossl_DH_der_to_dh_decoder_functions[]; + +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_dhx_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_dhx_decoder_functions[]; +extern const OSSL_DISPATCH ossl_type_specific_params_der_to_dhx_decoder_functions[]; +extern const OSSL_DISPATCH ossl_DHX_der_to_dhx_decoder_functions[]; + +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_dsa_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_dsa_decoder_functions[]; +extern const OSSL_DISPATCH ossl_type_specific_der_to_dsa_decoder_functions[]; +extern const OSSL_DISPATCH ossl_DSA_der_to_dsa_decoder_functions[]; +extern const OSSL_DISPATCH ossl_msblob_to_dsa_decoder_functions[]; +extern const OSSL_DISPATCH ossl_pvk_to_dsa_decoder_functions[]; + +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_ec_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_ec_decoder_functions[]; +extern const OSSL_DISPATCH ossl_type_specific_no_pub_der_to_ec_decoder_functions[]; +extern const OSSL_DISPATCH ossl_EC_der_to_ec_decoder_functions[]; + +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_x25519_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_x25519_decoder_functions[]; + +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_x448_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_x448_decoder_functions[]; + +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_ed25519_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_ed25519_decoder_functions[]; + +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_ed448_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_ed448_decoder_functions[]; + +#ifndef OPENSSL_NO_ML_KEM +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_ml_kem_512_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_ml_kem_512_decoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_kem_512_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_kem_512_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_kem_512_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_kem_512_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_kem_512_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_kem_512_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_kem_512_to_text_encoder_functions[]; + +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_ml_kem_768_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_ml_kem_768_decoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_kem_768_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_kem_768_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_kem_768_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_kem_768_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_kem_768_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_kem_768_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_kem_768_to_text_encoder_functions[]; + +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_ml_kem_1024_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_ml_kem_1024_decoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_kem_1024_to_EncryptedPrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_kem_1024_to_EncryptedPrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_kem_1024_to_PrivateKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_kem_1024_to_PrivateKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_kem_1024_to_SubjectPublicKeyInfo_der_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_kem_1024_to_SubjectPublicKeyInfo_pem_encoder_functions[]; +extern const OSSL_DISPATCH ossl_ml_kem_1024_to_text_encoder_functions[]; +#endif + +#ifndef OPENSSL_NO_SM2 +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_sm2_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_sm2_decoder_functions[]; +extern const OSSL_DISPATCH ossl_type_specific_no_pub_der_to_sm2_decoder_functions[]; +#endif + +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_slh_dsa_sha2_128s_decoder_functions[]; +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_slh_dsa_sha2_128f_decoder_functions[]; +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_slh_dsa_sha2_192s_decoder_functions[]; +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_slh_dsa_sha2_192f_decoder_functions[]; +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_slh_dsa_sha2_256s_decoder_functions[]; +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_slh_dsa_sha2_256f_decoder_functions[]; +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_slh_dsa_shake_128s_decoder_functions[]; +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_slh_dsa_shake_128f_decoder_functions[]; +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_slh_dsa_shake_192s_decoder_functions[]; +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_slh_dsa_shake_192f_decoder_functions[]; +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_slh_dsa_shake_256s_decoder_functions[]; +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_slh_dsa_shake_256f_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_slh_dsa_sha2_128s_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_slh_dsa_sha2_128f_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_slh_dsa_sha2_192s_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_slh_dsa_sha2_192f_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_slh_dsa_sha2_256s_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_slh_dsa_sha2_256f_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_slh_dsa_shake_128s_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_slh_dsa_shake_128f_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_slh_dsa_shake_192s_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_slh_dsa_shake_192f_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_slh_dsa_shake_256s_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_slh_dsa_shake_256f_decoder_functions[]; + +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_rsa_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_rsa_decoder_functions[]; +extern const OSSL_DISPATCH ossl_type_specific_keypair_der_to_rsa_decoder_functions[]; +extern const OSSL_DISPATCH ossl_RSA_der_to_rsa_decoder_functions[]; +extern const OSSL_DISPATCH ossl_msblob_to_rsa_decoder_functions[]; +extern const OSSL_DISPATCH ossl_pvk_to_rsa_decoder_functions[]; + +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_rsapss_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_rsapss_decoder_functions[]; + +extern const OSSL_DISPATCH ossl_EncryptedPrivateKeyInfo_der_to_der_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_der_decoder_functions[]; +extern const OSSL_DISPATCH ossl_pem_to_der_decoder_functions[]; + +extern const OSSL_DISPATCH ossl_file_store_functions[]; +extern const OSSL_DISPATCH ossl_winstore_store_functions[]; + +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_ml_dsa_44_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_ml_dsa_44_decoder_functions[]; + +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_ml_dsa_65_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_ml_dsa_65_decoder_functions[]; + +extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_ml_dsa_87_decoder_functions[]; +extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_ml_dsa_87_decoder_functions[]; + +extern const OSSL_DISPATCH ossl_generic_skeymgmt_functions[]; +extern const OSSL_DISPATCH ossl_aes_skeymgmt_functions[]; diff --git a/crypto/openssl/providers/implementations/include/prov/kdfexchange.h b/crypto/openssl/providers/implementations/include/prov/kdfexchange.h new file mode 100644 index 000000000000..cf08f785ee0c --- /dev/null +++ b/crypto/openssl/providers/implementations/include/prov/kdfexchange.h @@ -0,0 +1,23 @@ +/* + * Copyright 2020-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 + */ + +#include <stdlib.h> +#include <openssl/crypto.h> +#include "internal/refcount.h" + +struct kdf_data_st { + OSSL_LIB_CTX *libctx; + CRYPTO_REF_COUNT refcnt; +}; + +typedef struct kdf_data_st KDF_DATA; + +KDF_DATA *ossl_kdf_data_new(void *provctx); +void ossl_kdf_data_free(KDF_DATA *kdfdata); +int ossl_kdf_data_up_ref(KDF_DATA *kdfdata); diff --git a/crypto/openssl/providers/implementations/include/prov/macsignature.h b/crypto/openssl/providers/implementations/include/prov/macsignature.h new file mode 100644 index 000000000000..e13ff362ce00 --- /dev/null +++ b/crypto/openssl/providers/implementations/include/prov/macsignature.h @@ -0,0 +1,29 @@ +/* + * Copyright 2020-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 + */ + +#include <stdlib.h> +#include <openssl/crypto.h> +#include "internal/refcount.h" +#include "prov/provider_util.h" + +struct mac_key_st { + OSSL_LIB_CTX *libctx; + CRYPTO_REF_COUNT refcnt; + unsigned char *priv_key; + size_t priv_key_len; + PROV_CIPHER cipher; + char *properties; + int cmac; +}; + +typedef struct mac_key_st MAC_KEY; + +MAC_KEY *ossl_mac_key_new(OSSL_LIB_CTX *libctx, int cmac); +void ossl_mac_key_free(MAC_KEY *mackey); +int ossl_mac_key_up_ref(MAC_KEY *mackey); diff --git a/crypto/openssl/providers/implementations/include/prov/md5_sha1.h b/crypto/openssl/providers/implementations/include/prov/md5_sha1.h new file mode 100644 index 000000000000..181267d6b19f --- /dev/null +++ b/crypto/openssl/providers/implementations/include/prov/md5_sha1.h @@ -0,0 +1,36 @@ +/* + * Copyright 1995-2021 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 + */ + +#ifndef OSSL_PROV_MD5_SHA1_H +# define OSSL_PROV_MD5_SHA1_H + +# include <openssl/opensslconf.h> + +# ifndef OPENSSL_NO_MD5 +# include <openssl/e_os2.h> +# include <stddef.h> +# include <openssl/md5.h> +# include <openssl/sha.h> + +# define MD5_SHA1_DIGEST_LENGTH (MD5_DIGEST_LENGTH + SHA_DIGEST_LENGTH) +# define MD5_SHA1_CBLOCK MD5_CBLOCK + +typedef struct md5_sha1_st { + MD5_CTX md5; + SHA_CTX sha1; +} MD5_SHA1_CTX; + +int ossl_md5_sha1_init(MD5_SHA1_CTX *mctx); +int ossl_md5_sha1_update(MD5_SHA1_CTX *mctx, const void *data, size_t count); +int ossl_md5_sha1_final(unsigned char *md, MD5_SHA1_CTX *mctx); +int ossl_md5_sha1_ctrl(MD5_SHA1_CTX *mctx, int cmd, int mslen, void *ms); + +# endif /* OPENSSL_NO_MD5 */ + +#endif /* OSSL_PROV_MD5_SHA1_H */ diff --git a/crypto/openssl/providers/implementations/include/prov/ml_dsa.h b/crypto/openssl/providers/implementations/include/prov/ml_dsa.h new file mode 100644 index 000000000000..fca8a306253f --- /dev/null +++ b/crypto/openssl/providers/implementations/include/prov/ml_dsa.h @@ -0,0 +1,14 @@ +/* + * Copyright 2024-2025 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 + */ + +#include "crypto/ml_dsa.h" +#include "prov/provider_ctx.h" + +ML_DSA_KEY * +ossl_prov_ml_dsa_new(PROV_CTX *provctx, const char *propq, int evp_type); diff --git a/crypto/openssl/providers/implementations/include/prov/ml_kem.h b/crypto/openssl/providers/implementations/include/prov/ml_kem.h new file mode 100644 index 000000000000..b82ef1baa03a --- /dev/null +++ b/crypto/openssl/providers/implementations/include/prov/ml_kem.h @@ -0,0 +1,14 @@ +/* + * Copyright 2024-2025 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 + */ + +#include "crypto/ml_kem.h" +#include "prov/provider_ctx.h" + +ML_KEM_KEY * +ossl_prov_ml_kem_new(PROV_CTX *provctx, const char *propq, int evp_type); diff --git a/crypto/openssl/providers/implementations/include/prov/mlx_kem.h b/crypto/openssl/providers/implementations/include/prov/mlx_kem.h new file mode 100644 index 000000000000..df8317cda08d --- /dev/null +++ b/crypto/openssl/providers/implementations/include/prov/mlx_kem.h @@ -0,0 +1,47 @@ +/* + * Copyright 2024-2025 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 + */ + +#ifndef OSSL_MLX_KEM_H +# define OSSL_MLX_KEM_H +# pragma once + +#include <openssl/evp.h> +#include <openssl/ml_kem.h> +#include <crypto/ml_kem.h> +#include <crypto/ecx.h> + +typedef struct ecdh_vinfo_st { + const char *algorithm_name; + const char *group_name; + size_t pubkey_bytes; + size_t prvkey_bytes; + size_t shsec_bytes; + int ml_kem_slot; + int ml_kem_variant; +} ECDH_VINFO; + +typedef struct mlx_key_st { + OSSL_LIB_CTX *libctx; + char *propq; + const ML_KEM_VINFO *minfo; + const ECDH_VINFO *xinfo; + EVP_PKEY *mkey; + EVP_PKEY *xkey; + unsigned int state; +} MLX_KEY; + +#define MLX_HAVE_NOKEYS 0 +#define MLX_HAVE_PUBKEY 1 +#define MLX_HAVE_PRVKEY 2 + +/* Both key parts have whatever the ML-KEM component has */ +#define mlx_kem_have_pubkey(key) ((key)->state > 0) +#define mlx_kem_have_prvkey(key) ((key)->state > 1) + +#endif diff --git a/crypto/openssl/providers/implementations/include/prov/names.h b/crypto/openssl/providers/implementations/include/prov/names.h new file mode 100644 index 000000000000..3b747ec92c04 --- /dev/null +++ b/crypto/openssl/providers/implementations/include/prov/names.h @@ -0,0 +1,435 @@ +/* + * Copyright 2021-2025 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 + */ + +/* + * Macros for use as names and descriptions in our providers' OSSL_ALGORITHM. + * + * All the strings are formatted the same way: + * + * Our primary name[:other names][:numeric OID] + * + * 'other names' include historical OpenSSL names, NIST names, ASN.1 OBJECT + * IDENTIFIER names, and commonly known aliases. + * + * Where it matters, our primary names follow this format: + * + * ALGNAME[VERSION?][-SUBNAME[VERSION?]?][-SIZE?][-MODE?] + * + * VERSION is only present if there are multiple versions of + * an alg (MD2, MD4, MD5). It may be omitted if there is only + * one version (if a subsequent version is released in the future, + * we can always change the canonical name, and add the old name + * as an alias). + * + * SUBNAME may be present where we are combining multiple + * algorithms together, e.g. MD5-SHA1. + * + * SIZE is only present if multiple versions of an algorithm exist + * with different sizes (e.g. AES-128-CBC, AES-256-CBC) + * + * MODE is only present where applicable. + */ + +/*- + * Symmetric ciphers + * ----------------- + */ +#define PROV_NAMES_AES "AES:2.16.840.1.101.3.4.1" +#define PROV_DESCS_AES "OpenSSL AES opaque secret key" +#define PROV_NAMES_GENERIC "GENERIC-SECRET" +#define PROV_DESCS_GENERIC "OpenSSL generic opaque secret key" + +#define PROV_NAMES_AES_256_ECB "AES-256-ECB:2.16.840.1.101.3.4.1.41" +#define PROV_NAMES_AES_192_ECB "AES-192-ECB:2.16.840.1.101.3.4.1.21" +#define PROV_NAMES_AES_128_ECB "AES-128-ECB:2.16.840.1.101.3.4.1.1" +#define PROV_NAMES_AES_256_CBC "AES-256-CBC:AES256:2.16.840.1.101.3.4.1.42" +#define PROV_NAMES_AES_192_CBC "AES-192-CBC:AES192:2.16.840.1.101.3.4.1.22" +#define PROV_NAMES_AES_128_CBC "AES-128-CBC:AES128:2.16.840.1.101.3.4.1.2" +#define PROV_NAMES_AES_256_CBC_CTS "AES-256-CBC-CTS" +#define PROV_NAMES_AES_192_CBC_CTS "AES-192-CBC-CTS" +#define PROV_NAMES_AES_128_CBC_CTS "AES-128-CBC-CTS" +#define PROV_NAMES_AES_256_OFB "AES-256-OFB:2.16.840.1.101.3.4.1.43" +#define PROV_NAMES_AES_192_OFB "AES-192-OFB:2.16.840.1.101.3.4.1.23" +#define PROV_NAMES_AES_128_OFB "AES-128-OFB:2.16.840.1.101.3.4.1.3" +#define PROV_NAMES_AES_256_CFB "AES-256-CFB:2.16.840.1.101.3.4.1.44" +#define PROV_NAMES_AES_192_CFB "AES-192-CFB:2.16.840.1.101.3.4.1.24" +#define PROV_NAMES_AES_128_CFB "AES-128-CFB:2.16.840.1.101.3.4.1.4" +#define PROV_NAMES_AES_256_CFB1 "AES-256-CFB1" +#define PROV_NAMES_AES_192_CFB1 "AES-192-CFB1" +#define PROV_NAMES_AES_128_CFB1 "AES-128-CFB1" +#define PROV_NAMES_AES_256_CFB8 "AES-256-CFB8" +#define PROV_NAMES_AES_192_CFB8 "AES-192-CFB8" +#define PROV_NAMES_AES_128_CFB8 "AES-128-CFB8" +#define PROV_NAMES_AES_256_CTR "AES-256-CTR" +#define PROV_NAMES_AES_192_CTR "AES-192-CTR" +#define PROV_NAMES_AES_128_CTR "AES-128-CTR" +#define PROV_NAMES_AES_256_XTS "AES-256-XTS:1.3.111.2.1619.0.1.2" +#define PROV_NAMES_AES_128_XTS "AES-128-XTS:1.3.111.2.1619.0.1.1" +#define PROV_NAMES_AES_256_GCM "AES-256-GCM:id-aes256-GCM:2.16.840.1.101.3.4.1.46" +#define PROV_NAMES_AES_192_GCM "AES-192-GCM:id-aes192-GCM:2.16.840.1.101.3.4.1.26" +#define PROV_NAMES_AES_128_GCM "AES-128-GCM:id-aes128-GCM:2.16.840.1.101.3.4.1.6" +#define PROV_NAMES_AES_256_CCM "AES-256-CCM:id-aes256-CCM:2.16.840.1.101.3.4.1.47" +#define PROV_NAMES_AES_192_CCM "AES-192-CCM:id-aes192-CCM:2.16.840.1.101.3.4.1.27" +#define PROV_NAMES_AES_128_CCM "AES-128-CCM:id-aes128-CCM:2.16.840.1.101.3.4.1.7" +#define PROV_NAMES_AES_256_WRAP "AES-256-WRAP:id-aes256-wrap:AES256-WRAP:2.16.840.1.101.3.4.1.45" +#define PROV_NAMES_AES_192_WRAP "AES-192-WRAP:id-aes192-wrap:AES192-WRAP:2.16.840.1.101.3.4.1.25" +#define PROV_NAMES_AES_128_WRAP "AES-128-WRAP:id-aes128-wrap:AES128-WRAP:2.16.840.1.101.3.4.1.5" +#define PROV_NAMES_AES_256_WRAP_PAD "AES-256-WRAP-PAD:id-aes256-wrap-pad:AES256-WRAP-PAD:2.16.840.1.101.3.4.1.48" +#define PROV_NAMES_AES_192_WRAP_PAD "AES-192-WRAP-PAD:id-aes192-wrap-pad:AES192-WRAP-PAD:2.16.840.1.101.3.4.1.28" +#define PROV_NAMES_AES_128_WRAP_PAD "AES-128-WRAP-PAD:id-aes128-wrap-pad:AES128-WRAP-PAD:2.16.840.1.101.3.4.1.8" +#define PROV_NAMES_AES_256_WRAP_INV "AES-256-WRAP-INV:AES256-WRAP-INV" +#define PROV_NAMES_AES_192_WRAP_INV "AES-192-WRAP-INV:AES192-WRAP-INV" +#define PROV_NAMES_AES_128_WRAP_INV "AES-128-WRAP-INV:AES128-WRAP-INV" +#define PROV_NAMES_AES_256_WRAP_PAD_INV "AES-256-WRAP-PAD-INV:AES256-WRAP-PAD-INV" +#define PROV_NAMES_AES_192_WRAP_PAD_INV "AES-192-WRAP-PAD-INV:AES192-WRAP-PAD-INV" +#define PROV_NAMES_AES_128_WRAP_PAD_INV "AES-128-WRAP-PAD-INV:AES128-WRAP-PAD-INV" +#define PROV_NAMES_AES_128_CBC_HMAC_SHA1 "AES-128-CBC-HMAC-SHA1" +#define PROV_NAMES_AES_256_CBC_HMAC_SHA1 "AES-256-CBC-HMAC-SHA1" +#define PROV_NAMES_AES_128_CBC_HMAC_SHA256 "AES-128-CBC-HMAC-SHA256" +#define PROV_NAMES_AES_256_CBC_HMAC_SHA256 "AES-256-CBC-HMAC-SHA256" +#define PROV_NAMES_DES_EDE3_ECB "DES-EDE3-ECB:DES-EDE3" +#define PROV_NAMES_DES_EDE3_CBC "DES-EDE3-CBC:DES3:1.2.840.113549.3.7" +#define PROV_NAMES_NULL "NULL" +#define PROV_NAMES_AES_256_OCB "AES-256-OCB" +#define PROV_NAMES_AES_192_OCB "AES-192-OCB" +#define PROV_NAMES_AES_128_OCB "AES-128-OCB" +#define PROV_NAMES_AES_128_SIV "AES-128-SIV" +#define PROV_NAMES_AES_192_SIV "AES-192-SIV" +#define PROV_NAMES_AES_256_SIV "AES-256-SIV" +#define PROV_NAMES_AES_128_GCM_SIV "AES-128-GCM-SIV" +#define PROV_NAMES_AES_192_GCM_SIV "AES-192-GCM-SIV" +#define PROV_NAMES_AES_256_GCM_SIV "AES-256-GCM-SIV" +#define PROV_NAMES_ARIA_256_GCM "ARIA-256-GCM:1.2.410.200046.1.1.36" +#define PROV_NAMES_ARIA_192_GCM "ARIA-192-GCM:1.2.410.200046.1.1.35" +#define PROV_NAMES_ARIA_128_GCM "ARIA-128-GCM:1.2.410.200046.1.1.34" +#define PROV_NAMES_ARIA_256_CCM "ARIA-256-CCM:1.2.410.200046.1.1.39" +#define PROV_NAMES_ARIA_192_CCM "ARIA-192-CCM:1.2.410.200046.1.1.38" +#define PROV_NAMES_ARIA_128_CCM "ARIA-128-CCM:1.2.410.200046.1.1.37" +#define PROV_NAMES_ARIA_256_ECB "ARIA-256-ECB:1.2.410.200046.1.1.11" +#define PROV_NAMES_ARIA_192_ECB "ARIA-192-ECB:1.2.410.200046.1.1.6" +#define PROV_NAMES_ARIA_128_ECB "ARIA-128-ECB:1.2.410.200046.1.1.1" +#define PROV_NAMES_ARIA_256_CBC "ARIA-256-CBC:ARIA256:1.2.410.200046.1.1.12" +#define PROV_NAMES_ARIA_192_CBC "ARIA-192-CBC:ARIA192:1.2.410.200046.1.1.7" +#define PROV_NAMES_ARIA_128_CBC "ARIA-128-CBC:ARIA128:1.2.410.200046.1.1.2" +#define PROV_NAMES_ARIA_256_OFB "ARIA-256-OFB:1.2.410.200046.1.1.14" +#define PROV_NAMES_ARIA_192_OFB "ARIA-192-OFB:1.2.410.200046.1.1.9" +#define PROV_NAMES_ARIA_128_OFB "ARIA-128-OFB:1.2.410.200046.1.1.4" +#define PROV_NAMES_ARIA_256_CFB "ARIA-256-CFB:1.2.410.200046.1.1.13" +#define PROV_NAMES_ARIA_192_CFB "ARIA-192-CFB:1.2.410.200046.1.1.8" +#define PROV_NAMES_ARIA_128_CFB "ARIA-128-CFB:1.2.410.200046.1.1.3" +#define PROV_NAMES_ARIA_256_CFB1 "ARIA-256-CFB1" +#define PROV_NAMES_ARIA_192_CFB1 "ARIA-192-CFB1" +#define PROV_NAMES_ARIA_128_CFB1 "ARIA-128-CFB1" +#define PROV_NAMES_ARIA_256_CFB8 "ARIA-256-CFB8" +#define PROV_NAMES_ARIA_192_CFB8 "ARIA-192-CFB8" +#define PROV_NAMES_ARIA_128_CFB8 "ARIA-128-CFB8" +#define PROV_NAMES_ARIA_256_CTR "ARIA-256-CTR:1.2.410.200046.1.1.15" +#define PROV_NAMES_ARIA_192_CTR "ARIA-192-CTR:1.2.410.200046.1.1.10" +#define PROV_NAMES_ARIA_128_CTR "ARIA-128-CTR:1.2.410.200046.1.1.5" +#define PROV_NAMES_CAMELLIA_256_ECB "CAMELLIA-256-ECB:0.3.4401.5.3.1.9.41" +#define PROV_NAMES_CAMELLIA_192_ECB "CAMELLIA-192-ECB:0.3.4401.5.3.1.9.21" +#define PROV_NAMES_CAMELLIA_128_ECB "CAMELLIA-128-ECB:0.3.4401.5.3.1.9.1" +#define PROV_NAMES_CAMELLIA_256_CBC "CAMELLIA-256-CBC:CAMELLIA256:1.2.392.200011.61.1.1.1.4" +#define PROV_NAMES_CAMELLIA_192_CBC "CAMELLIA-192-CBC:CAMELLIA192:1.2.392.200011.61.1.1.1.3" +#define PROV_NAMES_CAMELLIA_128_CBC "CAMELLIA-128-CBC:CAMELLIA128:1.2.392.200011.61.1.1.1.2" +#define PROV_NAMES_CAMELLIA_256_CBC_CTS "CAMELLIA-256-CBC-CTS" +#define PROV_NAMES_CAMELLIA_192_CBC_CTS "CAMELLIA-192-CBC-CTS" +#define PROV_NAMES_CAMELLIA_128_CBC_CTS "CAMELLIA-128-CBC-CTS" +#define PROV_NAMES_CAMELLIA_256_OFB "CAMELLIA-256-OFB:0.3.4401.5.3.1.9.43" +#define PROV_NAMES_CAMELLIA_192_OFB "CAMELLIA-192-OFB:0.3.4401.5.3.1.9.23" +#define PROV_NAMES_CAMELLIA_128_OFB "CAMELLIA-128-OFB:0.3.4401.5.3.1.9.3" +#define PROV_NAMES_CAMELLIA_256_CFB "CAMELLIA-256-CFB:0.3.4401.5.3.1.9.44" +#define PROV_NAMES_CAMELLIA_192_CFB "CAMELLIA-192-CFB:0.3.4401.5.3.1.9.24" +#define PROV_NAMES_CAMELLIA_128_CFB "CAMELLIA-128-CFB:0.3.4401.5.3.1.9.4" +#define PROV_NAMES_CAMELLIA_256_CFB1 "CAMELLIA-256-CFB1" +#define PROV_NAMES_CAMELLIA_192_CFB1 "CAMELLIA-192-CFB1" +#define PROV_NAMES_CAMELLIA_128_CFB1 "CAMELLIA-128-CFB1" +#define PROV_NAMES_CAMELLIA_256_CFB8 "CAMELLIA-256-CFB8" +#define PROV_NAMES_CAMELLIA_192_CFB8 "CAMELLIA-192-CFB8" +#define PROV_NAMES_CAMELLIA_128_CFB8 "CAMELLIA-128-CFB8" +#define PROV_NAMES_CAMELLIA_256_CTR "CAMELLIA-256-CTR:0.3.4401.5.3.1.9.49" +#define PROV_NAMES_CAMELLIA_192_CTR "CAMELLIA-192-CTR:0.3.4401.5.3.1.9.29" +#define PROV_NAMES_CAMELLIA_128_CTR "CAMELLIA-128-CTR:0.3.4401.5.3.1.9.9" +#define PROV_NAMES_DES_EDE3_OFB "DES-EDE3-OFB" +#define PROV_NAMES_DES_EDE3_CFB "DES-EDE3-CFB" +#define PROV_NAMES_DES_EDE3_CFB8 "DES-EDE3-CFB8" +#define PROV_NAMES_DES_EDE3_CFB1 "DES-EDE3-CFB1" +#define PROV_NAMES_DES3_WRAP "DES3-WRAP:id-smime-alg-CMS3DESwrap:1.2.840.113549.1.9.16.3.6" +#define PROV_NAMES_DES_EDE_ECB "DES-EDE-ECB:DES-EDE:1.3.14.3.2.17" +#define PROV_NAMES_DES_EDE_CBC "DES-EDE-CBC" +#define PROV_NAMES_DES_EDE_OFB "DES-EDE-OFB" +#define PROV_NAMES_DES_EDE_CFB "DES-EDE-CFB" +#define PROV_NAMES_SM4_ECB "SM4-ECB:1.2.156.10197.1.104.1" +#define PROV_NAMES_SM4_CBC "SM4-CBC:SM4:1.2.156.10197.1.104.2" +#define PROV_NAMES_SM4_CTR "SM4-CTR:1.2.156.10197.1.104.7" +#define PROV_NAMES_SM4_OFB "SM4-OFB:SM4-OFB128:1.2.156.10197.1.104.3" +#define PROV_NAMES_SM4_CFB "SM4-CFB:SM4-CFB128:1.2.156.10197.1.104.4" +#define PROV_NAMES_SM4_GCM "SM4-GCM:1.2.156.10197.1.104.8" +#define PROV_NAMES_SM4_CCM "SM4-CCM:1.2.156.10197.1.104.9" +#define PROV_NAMES_SM4_XTS "SM4-XTS:1.2.156.10197.1.104.10" +#define PROV_NAMES_ChaCha20 "ChaCha20" +#define PROV_NAMES_ChaCha20_Poly1305 "ChaCha20-Poly1305" +#define PROV_NAMES_CAST5_ECB "CAST5-ECB" +#define PROV_NAMES_CAST5_CBC "CAST5-CBC:CAST-CBC:CAST:1.2.840.113533.7.66.10" +#define PROV_NAMES_CAST5_OFB "CAST5-OFB" +#define PROV_NAMES_CAST5_CFB "CAST5-CFB" +#define PROV_NAMES_BF_ECB "BF-ECB" +#define PROV_NAMES_BF_CBC "BF-CBC:BF:BLOWFISH:1.3.6.1.4.1.3029.1.2" +#define PROV_NAMES_BF_OFB "BF-OFB" +#define PROV_NAMES_BF_CFB "BF-CFB" +#define PROV_NAMES_IDEA_ECB "IDEA-ECB" +#define PROV_NAMES_IDEA_CBC "IDEA-CBC:IDEA:1.3.6.1.4.1.188.7.1.1.2" +#define PROV_NAMES_IDEA_OFB "IDEA-OFB:IDEA-OFB64" +#define PROV_NAMES_IDEA_CFB "IDEA-CFB:IDEA-CFB64" +#define PROV_NAMES_SEED_ECB "SEED-ECB:1.2.410.200004.1.3" +#define PROV_NAMES_SEED_CBC "SEED-CBC:SEED:1.2.410.200004.1.4" +#define PROV_NAMES_SEED_OFB "SEED-OFB:SEED-OFB128:1.2.410.200004.1.6" +#define PROV_NAMES_SEED_CFB "SEED-CFB:SEED-CFB128:1.2.410.200004.1.5" +#define PROV_NAMES_RC2_ECB "RC2-ECB" +#define PROV_NAMES_RC2_CBC "RC2-CBC:RC2:RC2-128:1.2.840.113549.3.2" +#define PROV_NAMES_RC2_40_CBC "RC2-40-CBC:RC2-40" +#define PROV_NAMES_RC2_64_CBC "RC2-64-CBC:RC2-64" +#define PROV_NAMES_RC2_CFB "RC2-CFB" +#define PROV_NAMES_RC2_OFB "RC2-OFB" +#define PROV_NAMES_RC4 "RC4:1.2.840.113549.3.4" +#define PROV_NAMES_RC4_40 "RC4-40" +#define PROV_NAMES_RC4_HMAC_MD5 "RC4-HMAC-MD5" +#define PROV_NAMES_RC5_ECB "RC5-ECB" +#define PROV_NAMES_RC5_CBC "RC5-CBC:RC5:1.2.840.113549.3.8" +#define PROV_NAMES_RC5_OFB "RC5-OFB" +#define PROV_NAMES_RC5_CFB "RC5-CFB" +#define PROV_NAMES_DESX_CBC "DESX-CBC:DESX" +#define PROV_NAMES_DES_ECB "DES-ECB:1.3.14.3.2.6" +#define PROV_NAMES_DES_CBC "DES-CBC:DES:1.3.14.3.2.7" +#define PROV_NAMES_DES_OFB "DES-OFB:1.3.14.3.2.8" +#define PROV_NAMES_DES_CFB "DES-CFB:1.3.14.3.2.9" +#define PROV_NAMES_DES_CFB1 "DES-CFB1" +#define PROV_NAMES_DES_CFB8 "DES-CFB8" + +/*- + * Digests + * ------- + */ +#define PROV_NAMES_SHA1 "SHA1:SHA-1:SSL3-SHA1:1.3.14.3.2.26" +#define PROV_NAMES_SHA2_224 "SHA2-224:SHA-224:SHA224:2.16.840.1.101.3.4.2.4" +#define PROV_NAMES_SHA2_256 "SHA2-256:SHA-256:SHA256:2.16.840.1.101.3.4.2.1" +#define PROV_NAMES_SHA2_256_192 "SHA2-256/192:SHA-256/192:SHA256-192" +#define PROV_NAMES_SHA2_384 "SHA2-384:SHA-384:SHA384:2.16.840.1.101.3.4.2.2" +#define PROV_NAMES_SHA2_512 "SHA2-512:SHA-512:SHA512:2.16.840.1.101.3.4.2.3" +#define PROV_NAMES_SHA2_512_224 "SHA2-512/224:SHA-512/224:SHA512-224:2.16.840.1.101.3.4.2.5" +#define PROV_NAMES_SHA2_512_256 "SHA2-512/256:SHA-512/256:SHA512-256:2.16.840.1.101.3.4.2.6" + +/* We agree with NIST here, so one name only */ +#define PROV_NAMES_SHA3_224 "SHA3-224:2.16.840.1.101.3.4.2.7" +#define PROV_NAMES_SHA3_256 "SHA3-256:2.16.840.1.101.3.4.2.8" +#define PROV_NAMES_SHA3_384 "SHA3-384:2.16.840.1.101.3.4.2.9" +#define PROV_NAMES_SHA3_512 "SHA3-512:2.16.840.1.101.3.4.2.10" + +#define PROV_NAMES_KECCAK_224 "KECCAK-224" +#define PROV_NAMES_KECCAK_256 "KECCAK-256" +#define PROV_NAMES_KECCAK_384 "KECCAK-384" +#define PROV_NAMES_KECCAK_512 "KECCAK-512" + +#define PROV_NAMES_SHAKE_128 "SHAKE-128:SHAKE128:2.16.840.1.101.3.4.2.11" +#define PROV_NAMES_SHAKE_256 "SHAKE-256:SHAKE256:2.16.840.1.101.3.4.2.12" + +/* + * KECCAK-KMAC-128 and KECCAK-KMAC-256 as hashes are mostly useful for + * KMAC128 and KMAC256. + */ +#define PROV_NAMES_KECCAK_KMAC_128 "KECCAK-KMAC-128:KECCAK-KMAC128" +#define PROV_NAMES_KECCAK_KMAC_256 "KECCAK-KMAC-256:KECCAK-KMAC256" +/* + * https://blake2.net/ doesn't specify size variants, but mentions that + * Bouncy Castle uses the names BLAKE2b-160, BLAKE2b-256, BLAKE2b-384, and + * BLAKE2b-512 + * If we assume that "2b" and "2s" are versions, that pattern fits with ours. + * We also add our historical names. + */ +#define PROV_NAMES_BLAKE2S_256 "BLAKE2S-256:BLAKE2s256:1.3.6.1.4.1.1722.12.2.2.8" +#define PROV_NAMES_BLAKE2B_512 "BLAKE2B-512:BLAKE2b512:1.3.6.1.4.1.1722.12.2.1.16" +#define PROV_NAMES_SM3 "SM3:1.2.156.10197.1.401" +#define PROV_NAMES_MD5 "MD5:SSL3-MD5:1.2.840.113549.2.5" +#define PROV_NAMES_MD5_SHA1 "MD5-SHA1" +#define PROV_NAMES_MD2 "MD2:1.2.840.113549.2.2" +#define PROV_NAMES_MD4 "MD4:1.2.840.113549.2.4" +#define PROV_NAMES_MDC2 "MDC2:2.5.8.3.101" +#define PROV_NAMES_WHIRLPOOL "WHIRLPOOL:1.0.10118.3.0.55" +#define PROV_NAMES_RIPEMD_160 "RIPEMD-160:RIPEMD160:RIPEMD:RMD160:1.3.36.3.2.1" + +/*- + * KDFs / PRFs + * ----------- + */ +#define PROV_NAMES_HKDF "HKDF" +#define PROV_DESCS_HKDF_SIGN "OpenSSL HKDF via EVP_PKEY implementation" +#define PROV_NAMES_TLS1_3_KDF "TLS13-KDF" +#define PROV_NAMES_SSKDF "SSKDF" +#define PROV_NAMES_PBKDF1 "PBKDF1" +#define PROV_NAMES_PBKDF2 "PBKDF2:1.2.840.113549.1.5.12" +#define PROV_NAMES_PVKKDF "PVKKDF" +#define PROV_NAMES_SSHKDF "SSHKDF" +#define PROV_NAMES_X963KDF "X963KDF:X942KDF-CONCAT" +#define PROV_NAMES_X942KDF_ASN1 "X942KDF-ASN1:X942KDF" +#define PROV_NAMES_TLS1_PRF "TLS1-PRF" +#define PROV_DESCS_TLS1_PRF_SIGN "OpenSSL TLS1_PRF via EVP_PKEY implementation" +#define PROV_NAMES_KBKDF "KBKDF" +#define PROV_NAMES_PKCS12KDF "PKCS12KDF" +#define PROV_NAMES_SCRYPT "SCRYPT:id-scrypt:1.3.6.1.4.1.11591.4.11" +#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 + * ---- + */ +#define PROV_NAMES_HMAC "HMAC" +#define PROV_DESCS_HMAC_SIGN "OpenSSL HMAC via EVP_PKEY implementation" +#define PROV_NAMES_CMAC "CMAC" +#define PROV_DESCS_CMAC_SIGN "OpenSSL CMAC via EVP_PKEY implementation" +#define PROV_NAMES_SIPHASH "SIPHASH" +#define PROV_DESCS_SIPHASH_SIGN "OpenSSL SIPHASH via EVP_PKEY implementation" +#define PROV_NAMES_POLY1305 "POLY1305" +#define PROV_DESCS_POLY1305_SIGN "OpenSSL POLY1305 via EVP_PKEY implementation" +#define PROV_NAMES_GMAC "GMAC:1.0.9797.3.4" +#define PROV_NAMES_KMAC_128 "KMAC-128:KMAC128:2.16.840.1.101.3.4.2.19" +#define PROV_NAMES_KMAC_256 "KMAC-256:KMAC256:2.16.840.1.101.3.4.2.20" +#define PROV_NAMES_BLAKE2BMAC "BLAKE2BMAC:1.3.6.1.4.1.1722.12.2.1" +#define PROV_NAMES_BLAKE2SMAC "BLAKE2SMAC:1.3.6.1.4.1.1722.12.2.2" + +/*- + * RANDs + * ----- + */ +#define PROV_NAMES_CRNG_TEST "CRNG-TEST" +#define PROV_NAMES_CTR_DRBG "CTR-DRBG" +#define PROV_NAMES_HASH_DRBG "HASH-DRBG" +#define PROV_NAMES_HMAC_DRBG "HMAC-DRBG" +#define PROV_NAMES_TEST_RAND "TEST-RAND" +#define PROV_NAMES_SEED_SRC "SEED-SRC" +#define PROV_NAMES_JITTER "JITTER" + +/*- + * Asymmetric algos + * ---------------- + */ +#define PROV_NAMES_EC "EC:id-ecPublicKey:1.2.840.10045.2.1" +#define PROV_DESCS_EC "OpenSSL EC implementation" +#define PROV_NAMES_ECDH "ECDH" +#define PROV_DESCS_ECDH "OpenSSL ECDH implementation" +#define PROV_NAMES_ECDSA "ECDSA" +#define PROV_NAMES_ECDSA_SHA1 "ECDSA-SHA1:ECDSA-SHA-1:ecdsa-with-SHA1:1.2.840.10045.4.1" +#define PROV_NAMES_ECDSA_SHA224 "ECDSA-SHA2-224:ECDSA-SHA224:ecdsa-with-SHA224:1.2.840.10045.4.3.1" +#define PROV_NAMES_ECDSA_SHA256 "ECDSA-SHA2-256:ECDSA-SHA256:ecdsa-with-SHA256:1.2.840.10045.4.3.2" +#define PROV_NAMES_ECDSA_SHA384 "ECDSA-SHA2-384:ECDSA-SHA384:ecdsa-with-SHA384:1.2.840.10045.4.3.3" +#define PROV_NAMES_ECDSA_SHA512 "ECDSA-SHA2-512:ECDSA-SHA512:ecdsa-with-SHA512:1.2.840.10045.4.3.4" +#define PROV_NAMES_ECDSA_SHA3_224 "ECDSA-SHA3-224:ecdsa_with_SHA3-224:id-ecdsa-with-sha3-224:2.16.840.1.101.3.4.3.9" +#define PROV_NAMES_ECDSA_SHA3_256 "ECDSA-SHA3-256:ecdsa_with_SHA3-256:id-ecdsa-with-sha3-256:2.16.840.1.101.3.4.3.10" +#define PROV_NAMES_ECDSA_SHA3_384 "ECDSA-SHA3-384:ecdsa_with_SHA3-384:id-ecdsa-with-sha3-384:2.16.840.1.101.3.4.3.11" +#define PROV_NAMES_ECDSA_SHA3_512 "ECDSA-SHA3-512:ecdsa_with_SHA3-512:id-ecdsa-with-sha3-512:2.16.840.1.101.3.4.3.12" +#define PROV_DESCS_ECDSA "OpenSSL ECDSA implementation" +#define PROV_NAMES_X25519 "X25519:1.3.101.110" +#define PROV_DESCS_X25519 "OpenSSL X25519 implementation" +#define PROV_NAMES_X448 "X448:1.3.101.111" +#define PROV_DESCS_X448 "OpenSSL X448 implementation" +#define PROV_NAMES_ED25519 "ED25519:1.3.101.112" +#define PROV_DESCS_ED25519 "OpenSSL ED25519 implementation" +#define PROV_NAMES_ED25519ph "ED25519ph" +#define PROV_DESCS_ED25519ph "OpenSSL ED25519ph implementation" +#define PROV_NAMES_ED25519ctx "ED25519ctx" +#define PROV_DESCS_ED25519ctx "OpenSSL ED25519ctx implementation" +#define PROV_NAMES_ED448 "ED448:1.3.101.113" +#define PROV_DESCS_ED448 "OpenSSL ED448 implementation" +#define PROV_NAMES_ED448ph "ED448ph" +#define PROV_DESCS_ED448ph "OpenSSL ED448ph implementation" +#define PROV_NAMES_DH "DH:dhKeyAgreement:1.2.840.113549.1.3.1" +#define PROV_DESCS_DH "OpenSSL PKCS#3 DH implementation" +#define PROV_NAMES_DHX "DHX:X9.42 DH:dhpublicnumber:1.2.840.10046.2.1" +#define PROV_DESCS_DHX "OpenSSL X9.42 DH implementation" +#define PROV_NAMES_DSA "DSA:dsaEncryption:1.2.840.10040.4.1" +#define PROV_NAMES_DSA_SHA1 "DSA-SHA1:DSA-SHA-1:dsaWithSHA1:1.2.840.10040.4.3" +#define PROV_NAMES_DSA_SHA224 "DSA-SHA2-224:DSA-SHA224:dsa_with_SHA224:2.16.840.1.101.3.4.3.1" +#define PROV_NAMES_DSA_SHA256 "DSA-SHA2-256:DSA-SHA256:dsa_with_SHA256:2.16.840.1.101.3.4.3.2" +#define PROV_NAMES_DSA_SHA384 "DSA-SHA2-384:DSA-SHA384:dsa_with_SHA384:id-dsa-with-sha384:1.2.840.1.101.3.4.3.3" +#define PROV_NAMES_DSA_SHA512 "DSA-SHA2-512:DSA-SHA512:dsa_with_SHA512:id-dsa-with-sha512:1.2.840.1.101.3.4.3.4" +#define PROV_NAMES_DSA_SHA3_224 "DSA-SHA3-224:dsa_with_SHA3-224:id-dsa-with-sha3-224:2.16.840.1.101.3.4.3.5" +#define PROV_NAMES_DSA_SHA3_256 "DSA-SHA3-256:dsa_with_SHA3-256:id-dsa-with-sha3-256:2.16.840.1.101.3.4.3.6" +#define PROV_NAMES_DSA_SHA3_384 "DSA-SHA3-384:dsa_with_SHA3-384:id-dsa-with-sha3-384:2.16.840.1.101.3.4.3.7" +#define PROV_NAMES_DSA_SHA3_512 "DSA-SHA3-512:dsa_with_SHA3-512:id-dsa-with-sha3-512:2.16.840.1.101.3.4.3.8" +#define PROV_DESCS_DSA "OpenSSL DSA implementation" +#define PROV_NAMES_RSA "RSA:rsaEncryption:1.2.840.113549.1.1.1" +#define PROV_NAMES_RSA_MD2 "RSA-MD2:md2WithRSAEncryption:1.2.840.113549.1.1.2" +#define PROV_NAMES_RSA_MD4 "RSA-MD4:md4WithEncryption:1.2.840.113549.1.1.3" +#define PROV_NAMES_RSA_MD5 "RSA-MD5:md5WithRSAEncryption:1.2.840.113549.1.1.4" +#define PROV_NAMES_RSA_RIPEMD160 "RSA-RIPEMD160:ripemd160WithRSA:1.3.36.3.3.1.2" +#define PROV_NAMES_RSA_SHA1 "RSA-SHA1:RSA-SHA-1:sha1WithRSAEncryption:1.2.840.113549.1.1.5" +#define PROV_NAMES_RSA_SHA256 "RSA-SHA2-256:RSA-SHA256:sha256WithRSAEncryption:1.2.840.113549.1.1.11" +#define PROV_NAMES_RSA_SHA384 "RSA-SHA2-384:RSA-SHA384:sha384WithRSAEncryption:1.2.840.113549.1.1.12" +#define PROV_NAMES_RSA_SHA512 "RSA-SHA2-512:RSA-SHA512:sha512WithRSAEncryption:1.2.840.113549.1.1.13" +#define PROV_NAMES_RSA_SHA224 "RSA-SHA2-224:RSA-SHA224:sha224WithRSAEncryption:1.2.840.113549.1.1.14" +#define PROV_NAMES_RSA_SHA512_224 "RSA-SHA2-512/224:RSA-SHA512-224:sha512-224WithRSAEncryption:1.2.840.113549.1.1.15" +#define PROV_NAMES_RSA_SHA512_256 "RSA-SHA2-512/256:RSA-SHA512-256:sha512-256WithRSAEncryption:1.2.840.113549.1.1.16" +#define PROV_NAMES_RSA_SM3 "RSA-SM3:sm3WithRSAEncryption:1.2.156.10197.1.504" +#define PROV_NAMES_RSA_SHA3_224 "RSA-SHA3-224:id-rsassa-pkcs1-v1_5-with-sha3-224:2.16.840.1.101.3.4.3.13" +#define PROV_NAMES_RSA_SHA3_256 "RSA-SHA3-256:id-rsassa-pkcs1-v1_5-with-sha3-256:2.16.840.1.101.3.4.3.14" +#define PROV_NAMES_RSA_SHA3_384 "RSA-SHA3-384:id-rsassa-pkcs1-v1_5-with-sha3-384:2.16.840.1.101.3.4.3.15" +#define PROV_NAMES_RSA_SHA3_512 "RSA-SHA3-512:id-rsassa-pkcs1-v1_5-with-sha3-512:2.16.840.1.101.3.4.3.16" +#define PROV_DESCS_RSA "OpenSSL RSA implementation" +#define PROV_NAMES_RSA_PSS "RSA-PSS:RSASSA-PSS:1.2.840.113549.1.1.10" +#define PROV_DESCS_RSA_PSS "OpenSSL RSA-PSS implementation" +#define PROV_NAMES_SM2 "SM2:1.2.156.10197.1.301" +#define PROV_DESCS_SM2 "OpenSSL SM2 implementation" +#define PROV_NAMES_ML_DSA_44 "ML-DSA-44:MLDSA44:2.16.840.1.101.3.4.3.17:id-ml-dsa-44" +#define PROV_DESCS_ML_DSA_44 "OpenSSL ML-DSA-44 implementation" +#define PROV_NAMES_ML_DSA_65 "ML-DSA-65:MLDSA65:2.16.840.1.101.3.4.3.18:id-ml-dsa-65" +#define PROV_DESCS_ML_DSA_65 "OpenSSL ML-DSA-65 implementation" +#define PROV_NAMES_ML_DSA_87 "ML-DSA-87:MLDSA87:2.16.840.1.101.3.4.3.19:id-ml-dsa-87" +#define PROV_DESCS_ML_DSA_87 "OpenSSL ML-DSA-87 implementation" +#define PROV_NAMES_ML_KEM_512 "ML-KEM-512:MLKEM512:id-alg-ml-kem-512:2.16.840.1.101.3.4.4.1" +#define PROV_DESCS_ML_KEM_512 "OpenSSL ML-KEM-512 implementation" +#define PROV_NAMES_ML_KEM_768 "ML-KEM-768:MLKEM768:id-alg-ml-kem-768:2.16.840.1.101.3.4.4.2" +#define PROV_DESCS_ML_KEM_768 "OpenSSL ML-KEM-768 implementation" +#define PROV_NAMES_ML_KEM_1024 "ML-KEM-1024:MLKEM1024:id-alg-ml-kem-1024:2.16.840.1.101.3.4.4.3" +#define PROV_DESCS_ML_KEM_1024 "OpenSSL ML-KEM-1024 implementation" +#define PROV_NAMES_X25519MLKEM768 "X25519MLKEM768" +#define PROV_DESCS_X25519MLKEM768 "X25519+ML-KEM-768 TLS hybrid implementation" +#define PROV_NAMES_X448MLKEM1024 "X448MLKEM1024" +#define PROV_DESCS_X448MLKEM1024 "X448+ML-KEM-1024 TLS hybrid implementation" +#define PROV_NAMES_SecP256r1MLKEM768 "SecP256r1MLKEM768" +#define PROV_DESCS_SecP256r1MLKEM768 "P-256+ML-KEM-768 TLS hybrid implementation" +#define PROV_NAMES_SecP384r1MLKEM1024 "SecP384r1MLKEM1024" +#define PROV_DESCS_SecP384r1MLKEM1024 "P-384+ML-KEM-1024 TLS hybrid implementation" +#define PROV_NAMES_SLH_DSA_SHA2_128S "SLH-DSA-SHA2-128s:id-slh-dsa-sha2-128s:2.16.840.1.101.3.4.3.20" +#define PROV_NAMES_SLH_DSA_SHA2_128F "SLH-DSA-SHA2-128f:id-slh-dsa-sha2-128f:2.16.840.1.101.3.4.3.21" +#define PROV_NAMES_SLH_DSA_SHA2_192S "SLH-DSA-SHA2-192s:id-slh-dsa-sha2-192s:2.16.840.1.101.3.4.3.22" +#define PROV_NAMES_SLH_DSA_SHA2_192F "SLH-DSA-SHA2-192f:id-slh-dsa-sha2-192f:2.16.840.1.101.3.4.3.23" +#define PROV_NAMES_SLH_DSA_SHA2_256S "SLH-DSA-SHA2-256s:id-slh-dsa-sha2-256s:2.16.840.1.101.3.4.3.24" +#define PROV_NAMES_SLH_DSA_SHA2_256F "SLH-DSA-SHA2-256f:id-slh-dsa-sha2-256f:2.16.840.1.101.3.4.3.25" +#define PROV_NAMES_SLH_DSA_SHAKE_128S "SLH-DSA-SHAKE-128s:id-slh-dsa-shake-128s:2.16.840.1.101.3.4.3.26" +#define PROV_NAMES_SLH_DSA_SHAKE_128F "SLH-DSA-SHAKE-128f:id-slh-dsa-shake-128f:2.16.840.1.101.3.4.3.27" +#define PROV_NAMES_SLH_DSA_SHAKE_192S "SLH-DSA-SHAKE-192s:id-slh-dsa-shake-192s:2.16.840.1.101.3.4.3.28" +#define PROV_NAMES_SLH_DSA_SHAKE_192F "SLH-DSA-SHAKE-192f:id-slh-dsa-shake-192f:2.16.840.1.101.3.4.3.29" +#define PROV_NAMES_SLH_DSA_SHAKE_256S "SLH-DSA-SHAKE-256s:id-slh-dsa-shake-256s:2.16.840.1.101.3.4.3.30" +#define PROV_NAMES_SLH_DSA_SHAKE_256F "SLH-DSA-SHAKE-256f:id-slh-dsa-shake-256f:2.16.840.1.101.3.4.3.31" +#define PROV_DESCS_SLH_DSA_SHA2_128S "OpenSSL SLH-DSA-SHA2-128s implementation" +#define PROV_DESCS_SLH_DSA_SHA2_128F "OpenSSL SLH-DSA-SHA2-128f implementation" +#define PROV_DESCS_SLH_DSA_SHA2_192S "OpenSSL SLH-DSA-SHA2-192s implementation" +#define PROV_DESCS_SLH_DSA_SHA2_192F "OpenSSL SLH-DSA-SHA2-192f implementation" +#define PROV_DESCS_SLH_DSA_SHA2_256S "OpenSSL SLH-DSA-SHA2-256s implementation" +#define PROV_DESCS_SLH_DSA_SHA2_256F "OpenSSL SLH-DSA-SHA2-256f implementation" +#define PROV_DESCS_SLH_DSA_SHAKE_128S "OpenSSL SLH-DSA-SHAKE-128s implementation" +#define PROV_DESCS_SLH_DSA_SHAKE_128F "OpenSSL SLH-DSA-SHAKE-128f implementation" +#define PROV_DESCS_SLH_DSA_SHAKE_192S "OpenSSL SLH-DSA-SHAKE-192s implementation" +#define PROV_DESCS_SLH_DSA_SHAKE_192F "OpenSSL SLH-DSA-SHAKE-192f implementation" +#define PROV_DESCS_SLH_DSA_SHAKE_256S "OpenSSL SLH-DSA-SHAKE-256s implementation" +#define PROV_DESCS_SLH_DSA_SHAKE_256F "OpenSSL SLH-DSA-SHAKE-256f implementation" diff --git a/crypto/openssl/providers/implementations/include/prov/seeding.h b/crypto/openssl/providers/implementations/include/prov/seeding.h new file mode 100644 index 000000000000..af6cb79fb28f --- /dev/null +++ b/crypto/openssl/providers/implementations/include/prov/seeding.h @@ -0,0 +1,30 @@ +/* + * Copyright 2020-2021 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 + */ + +#include "prov/provider_ctx.h" +#include "crypto/rand_pool.h" + +/* Hardware-based seeding functions. */ +size_t ossl_prov_acquire_entropy_from_tsc(RAND_POOL *pool); +size_t ossl_prov_acquire_entropy_from_cpu(RAND_POOL *pool); + +/* + * External seeding functions from the core dispatch table. + */ +int ossl_prov_seeding_from_dispatch(const OSSL_DISPATCH *fns); + +size_t ossl_prov_get_entropy(PROV_CTX *prov_ctx, unsigned char **pout, + int entropy, size_t min_len, size_t max_len); +void ossl_prov_cleanup_entropy(PROV_CTX *prov_ctx, unsigned char *buf, + size_t len); +size_t ossl_prov_get_nonce(PROV_CTX *prov_ctx, unsigned char **pout, + size_t min_len, size_t max_len, + const void *salt, size_t salt_len); +void ossl_prov_cleanup_nonce(PROV_CTX *prov_ctx, unsigned char *buf, + size_t len); diff --git a/crypto/openssl/providers/implementations/kdfs/argon2.c b/crypto/openssl/providers/implementations/kdfs/argon2.c new file mode 100644 index 000000000000..e3f9aa4f0f5c --- /dev/null +++ b/crypto/openssl/providers/implementations/kdfs/argon2.c @@ -0,0 +1,1560 @@ +/* + * 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 diff --git a/crypto/openssl/providers/implementations/kdfs/build.info b/crypto/openssl/providers/implementations/kdfs/build.info new file mode 100644 index 000000000000..3b7687b8f3a5 --- /dev/null +++ b/crypto/openssl/providers/implementations/kdfs/build.info @@ -0,0 +1,46 @@ +# We make separate GOAL variables for each algorithm, to make it easy to +# switch each to the Legacy provider when needed. + +$TLS1_PRF_GOAL=../../libdefault.a ../../libfips.a +$HKDF_GOAL=../../libdefault.a ../../libfips.a +$KBKDF_GOAL=../../libdefault.a ../../libfips.a +$KRB5KDF_GOAL=../../libdefault.a +$PBKDF1_GOAL=../../liblegacy.a +$PBKDF2_GOAL=../../libdefault.a ../../libfips.a +$PVKKDF_GOAL=../../liblegacy.a +$PKCS12KDF_GOAL=../../libdefault.a +$SSKDF_GOAL=../../libdefault.a ../../libfips.a +$SCRYPT_GOAL=../../libdefault.a +$SSHKDF_GOAL=../../libdefault.a ../../libfips.a +$X942KDF_GOAL=../../libdefault.a ../../libfips.a +$HMAC_DRBG_KDF_GOAL=../../libdefault.a +$ARGON2_GOAL=../../libdefault.a + +SOURCE[$TLS1_PRF_GOAL]=tls1_prf.c + +SOURCE[$HKDF_GOAL]=hkdf.c + +SOURCE[$KBKDF_GOAL]=kbkdf.c + +SOURCE[$KRB5KDF_GOAL]=krb5kdf.c + +SOURCE[$PBKDF1_GOAL]=pbkdf1.c + +SOURCE[$PBKDF2_GOAL]=pbkdf2.c +# Extra code to satisfy the FIPS and non-FIPS separation. +# When the PBKDF2 moves to legacy, this can be removed. +SOURCE[$PBKDF2_GOAL]=pbkdf2_fips.c + +SOURCE[$PBKDF1_GOAL]=pvkkdf.c + +SOURCE[$PKCS12KDF_GOAL]=pkcs12kdf.c + +SOURCE[$SSKDF_GOAL]=sskdf.c + +SOURCE[$SCRYPT_GOAL]=scrypt.c +SOURCE[$SSHKDF_GOAL]=sshkdf.c +SOURCE[$X942KDF_GOAL]=x942kdf.c +DEPEND[x942kdf.o]=../../common/include/prov/der_wrap.h + +SOURCE[$HMAC_DRBG_KDF_GOAL]=hmacdrbg_kdf.c +SOURCE[$ARGON2_GOAL]=argon2.c diff --git a/crypto/openssl/providers/implementations/kdfs/hkdf.c b/crypto/openssl/providers/implementations/kdfs/hkdf.c new file mode 100644 index 000000000000..6e3e8b5c0f66 --- /dev/null +++ b/crypto/openssl/providers/implementations/kdfs/hkdf.c @@ -0,0 +1,954 @@ +/* + * Copyright 2016-2025 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 + */ + +/* + * HMAC low level APIs are deprecated for public use, but still ok for internal + * use. + */ +#include "internal/deprecated.h" + +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <openssl/hmac.h> +#include <openssl/evp.h> +#include <openssl/kdf.h> +#include <openssl/core_names.h> +#include <openssl/proverr.h> +#include "internal/cryptlib.h" +#include "internal/numbers.h" +#include "internal/packet.h" +#include "crypto/evp.h" +#include "prov/provider_ctx.h" +#include "prov/providercommon.h" +#include "prov/implementations.h" +#include "prov/provider_util.h" +#include "prov/securitycheck.h" +#include "internal/e_os.h" +#include "internal/params.h" + +#define HKDF_MAXBUF 2048 +#define HKDF_MAXINFO (32*1024) + +static OSSL_FUNC_kdf_newctx_fn kdf_hkdf_new; +static OSSL_FUNC_kdf_dupctx_fn kdf_hkdf_dup; +static OSSL_FUNC_kdf_freectx_fn kdf_hkdf_free; +static OSSL_FUNC_kdf_reset_fn kdf_hkdf_reset; +static OSSL_FUNC_kdf_derive_fn kdf_hkdf_derive; +static OSSL_FUNC_kdf_settable_ctx_params_fn kdf_hkdf_settable_ctx_params; +static OSSL_FUNC_kdf_set_ctx_params_fn kdf_hkdf_set_ctx_params; +static OSSL_FUNC_kdf_gettable_ctx_params_fn kdf_hkdf_gettable_ctx_params; +static OSSL_FUNC_kdf_get_ctx_params_fn kdf_hkdf_get_ctx_params; +static OSSL_FUNC_kdf_derive_fn kdf_tls1_3_derive; +static OSSL_FUNC_kdf_settable_ctx_params_fn kdf_tls1_3_settable_ctx_params; +static OSSL_FUNC_kdf_set_ctx_params_fn kdf_tls1_3_set_ctx_params; +static OSSL_FUNC_kdf_gettable_ctx_params_fn kdf_tls1_3_gettable_ctx_params; +static OSSL_FUNC_kdf_get_ctx_params_fn kdf_tls1_3_get_ctx_params; + +static int HKDF(OSSL_LIB_CTX *libctx, const EVP_MD *evp_md, + const unsigned char *salt, size_t salt_len, + const unsigned char *key, size_t key_len, + const unsigned char *info, size_t info_len, + unsigned char *okm, size_t okm_len); +static int HKDF_Extract(OSSL_LIB_CTX *libctx, const EVP_MD *evp_md, + const unsigned char *salt, size_t salt_len, + const unsigned char *ikm, size_t ikm_len, + unsigned char *prk, size_t prk_len); +static int HKDF_Expand(const EVP_MD *evp_md, + const unsigned char *prk, size_t prk_len, + const unsigned char *info, size_t info_len, + unsigned char *okm, size_t okm_len); + +/* Settable context parameters that are common across HKDF and the TLS KDF */ +#define HKDF_COMMON_SETTABLES \ + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_MODE, NULL, 0), \ + OSSL_PARAM_int(OSSL_KDF_PARAM_MODE, NULL), \ + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0), \ + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0), \ + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_KEY, NULL, 0), \ + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, NULL, 0) + +/* Gettable context parameters that are common across HKDF and the TLS KDF */ +#define HKDF_COMMON_GETTABLES \ + OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL), \ + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_INFO, NULL, 0) + +typedef struct { + void *provctx; + int mode; + PROV_DIGEST digest; + unsigned char *salt; + size_t salt_len; + unsigned char *key; + size_t key_len; + unsigned char *prefix; + size_t prefix_len; + unsigned char *label; + size_t label_len; + unsigned char *data; + size_t data_len; + unsigned char *info; + size_t info_len; + OSSL_FIPS_IND_DECLARE +} KDF_HKDF; + +static void *kdf_hkdf_new(void *provctx) +{ + KDF_HKDF *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) != NULL) { + ctx->provctx = provctx; + OSSL_FIPS_IND_INIT(ctx) + } + return ctx; +} + +static void kdf_hkdf_free(void *vctx) +{ + KDF_HKDF *ctx = (KDF_HKDF *)vctx; + + if (ctx != NULL) { + kdf_hkdf_reset(ctx); + OPENSSL_free(ctx); + } +} + +static void kdf_hkdf_reset(void *vctx) +{ + KDF_HKDF *ctx = (KDF_HKDF *)vctx; + void *provctx = ctx->provctx; + + ossl_prov_digest_reset(&ctx->digest); +#ifdef OPENSSL_PEDANTIC_ZEROIZATION + OPENSSL_clear_free(ctx->salt, ctx->salt_len); +#else + OPENSSL_free(ctx->salt); +#endif + OPENSSL_free(ctx->prefix); + OPENSSL_free(ctx->label); + OPENSSL_clear_free(ctx->data, ctx->data_len); + OPENSSL_clear_free(ctx->key, ctx->key_len); + OPENSSL_clear_free(ctx->info, ctx->info_len); + memset(ctx, 0, sizeof(*ctx)); + ctx->provctx = provctx; +} + +static void *kdf_hkdf_dup(void *vctx) +{ + const KDF_HKDF *src = (const KDF_HKDF *)vctx; + KDF_HKDF *dest; + + dest = kdf_hkdf_new(src->provctx); + if (dest != NULL) { + if (!ossl_prov_memdup(src->salt, src->salt_len, &dest->salt, + &dest->salt_len) + || !ossl_prov_memdup(src->key, src->key_len, + &dest->key , &dest->key_len) + || !ossl_prov_memdup(src->prefix, src->prefix_len, + &dest->prefix, &dest->prefix_len) + || !ossl_prov_memdup(src->label, src->label_len, + &dest->label, &dest->label_len) + || !ossl_prov_memdup(src->data, src->data_len, + &dest->data, &dest->data_len) + || !ossl_prov_memdup(src->info, src->info_len, + &dest->info, &dest->info_len) + || !ossl_prov_digest_copy(&dest->digest, &src->digest)) + goto err; + dest->mode = src->mode; + OSSL_FIPS_IND_COPY(dest, src) + } + return dest; + + err: + kdf_hkdf_free(dest); + return NULL; +} + +static size_t kdf_hkdf_size(KDF_HKDF *ctx) +{ + int sz; + const EVP_MD *md = ossl_prov_digest_md(&ctx->digest); + + if (ctx->mode != EVP_KDF_HKDF_MODE_EXTRACT_ONLY) + return SIZE_MAX; + + if (md == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST); + return 0; + } + sz = EVP_MD_get_size(md); + if (sz <= 0) + return 0; + + return sz; +} + +#ifdef FIPS_MODULE +static int fips_hkdf_key_check_passed(KDF_HKDF *ctx) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + int key_approved = ossl_kdf_check_key_size(ctx->key_len); + + if (!key_approved) { + if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE0, + libctx, "HKDF", "Key size", + ossl_fips_config_hkdf_key_check)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + } + return 1; +} +#endif + +static int kdf_hkdf_derive(void *vctx, unsigned char *key, size_t keylen, + const OSSL_PARAM params[]) +{ + KDF_HKDF *ctx = (KDF_HKDF *)vctx; + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + const EVP_MD *md; + + if (!ossl_prov_is_running() || !kdf_hkdf_set_ctx_params(ctx, params)) + return 0; + + md = ossl_prov_digest_md(&ctx->digest); + if (md == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST); + return 0; + } + if (ctx->key == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + return 0; + } + if (keylen == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + + switch (ctx->mode) { + case EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND: + default: + return HKDF(libctx, md, ctx->salt, ctx->salt_len, + ctx->key, ctx->key_len, ctx->info, ctx->info_len, key, keylen); + + case EVP_KDF_HKDF_MODE_EXTRACT_ONLY: + return HKDF_Extract(libctx, md, ctx->salt, ctx->salt_len, + ctx->key, ctx->key_len, key, keylen); + + case EVP_KDF_HKDF_MODE_EXPAND_ONLY: + return HKDF_Expand(md, ctx->key, ctx->key_len, ctx->info, + ctx->info_len, key, keylen); + } +} + +static int hkdf_common_set_ctx_params(KDF_HKDF *ctx, const OSSL_PARAM params[]) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + const OSSL_PARAM *p; + int n; + + if (ossl_param_is_empty(params)) + return 1; + + if (OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_DIGEST) != NULL) { + const EVP_MD *md = NULL; + + if (!ossl_prov_digest_load_from_params(&ctx->digest, params, libctx)) + return 0; + + md = ossl_prov_digest_md(&ctx->digest); + if (EVP_MD_xof(md)) { + ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED); + return 0; + } + } + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_MODE)) != NULL) { + if (p->data_type == OSSL_PARAM_UTF8_STRING) { + if (OPENSSL_strcasecmp(p->data, "EXTRACT_AND_EXPAND") == 0) { + ctx->mode = EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND; + } else if (OPENSSL_strcasecmp(p->data, "EXTRACT_ONLY") == 0) { + ctx->mode = EVP_KDF_HKDF_MODE_EXTRACT_ONLY; + } else if (OPENSSL_strcasecmp(p->data, "EXPAND_ONLY") == 0) { + ctx->mode = EVP_KDF_HKDF_MODE_EXPAND_ONLY; + } else { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE); + return 0; + } + } else if (OSSL_PARAM_get_int(p, &n)) { + if (n != EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND + && n != EVP_KDF_HKDF_MODE_EXTRACT_ONLY + && n != EVP_KDF_HKDF_MODE_EXPAND_ONLY) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE); + return 0; + } + ctx->mode = n; + } else { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE); + return 0; + } + } + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KEY)) != NULL) { + OPENSSL_clear_free(ctx->key, ctx->key_len); + ctx->key = NULL; + if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->key, 0, + &ctx->key_len)) + return 0; + } + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SALT)) != NULL) { + OPENSSL_free(ctx->salt); + ctx->salt = NULL; + if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->salt, 0, + &ctx->salt_len)) + return 0; + } + + return 1; +} + +static int kdf_hkdf_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + KDF_HKDF *ctx = vctx; + + if (ossl_param_is_empty(params)) + return 1; + + if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE0, params, + OSSL_KDF_PARAM_FIPS_KEY_CHECK)) + return 0; + + if (!hkdf_common_set_ctx_params(ctx, params)) + return 0; + + if (ossl_param_get1_concat_octet_string(params, OSSL_KDF_PARAM_INFO, + &ctx->info, &ctx->info_len, + HKDF_MAXINFO) == 0) + return 0; + +#ifdef FIPS_MODULE + if (OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KEY) != NULL) + if (!fips_hkdf_key_check_passed(ctx)) + return 0; +#endif + + return 1; +} + +static const OSSL_PARAM *kdf_hkdf_settable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + static const OSSL_PARAM known_settable_ctx_params[] = { + HKDF_COMMON_SETTABLES, + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_INFO, NULL, 0), + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_KEY_CHECK) + OSSL_PARAM_END + }; + return known_settable_ctx_params; +} + +static int hkdf_common_get_ctx_params(KDF_HKDF *ctx, OSSL_PARAM params[]) +{ + OSSL_PARAM *p; + + if (ossl_param_is_empty(params)) + return 1; + + if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL) { + size_t sz = kdf_hkdf_size(ctx); + + if (sz == 0) + return 0; + if (!OSSL_PARAM_set_size_t(p, sz)) + return 0; + } + + if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_INFO)) != NULL) { + if (ctx->info == NULL || ctx->info_len == 0) + p->return_size = 0; + else if (!OSSL_PARAM_set_octet_string(p, ctx->info, ctx->info_len)) + return 0; + } + + return 1; +} + +static int kdf_hkdf_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + KDF_HKDF *ctx = (KDF_HKDF *)vctx; + + if (ossl_param_is_empty(params)) + return 1; + + if (!hkdf_common_get_ctx_params(ctx, params)) + return 0; + + if (!OSSL_FIPS_IND_GET_CTX_PARAM(ctx, params)) + return 0; + + return 1; +} + +static const OSSL_PARAM *kdf_hkdf_gettable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + static const OSSL_PARAM known_gettable_ctx_params[] = { + HKDF_COMMON_GETTABLES, + OSSL_FIPS_IND_GETTABLE_CTX_PARAM() + OSSL_PARAM_END + }; + return known_gettable_ctx_params; +} + +const OSSL_DISPATCH ossl_kdf_hkdf_functions[] = { + { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_hkdf_new }, + { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))kdf_hkdf_dup }, + { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_hkdf_free }, + { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_hkdf_reset }, + { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_hkdf_derive }, + { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS, + (void(*)(void))kdf_hkdf_settable_ctx_params }, + { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_hkdf_set_ctx_params }, + { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS, + (void(*)(void))kdf_hkdf_gettable_ctx_params }, + { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_hkdf_get_ctx_params }, + OSSL_DISPATCH_END +}; + +/* + * Refer to "HMAC-based Extract-and-Expand Key Derivation Function (HKDF)" + * Section 2 (https://tools.ietf.org/html/rfc5869#section-2) and + * "Cryptographic Extraction and Key Derivation: The HKDF Scheme" + * Section 4.2 (https://eprint.iacr.org/2010/264.pdf). + * + * From the paper: + * The scheme HKDF is specified as: + * HKDF(XTS, SKM, CTXinfo, L) = K(1) | K(2) | ... | K(t) + * + * where: + * SKM is source key material + * XTS is extractor salt (which may be null or constant) + * CTXinfo is context information (may be null) + * L is the number of key bits to be produced by KDF + * k is the output length in bits of the hash function used with HMAC + * t = ceil(L/k) + * the value K(t) is truncated to its first d = L mod k bits. + * + * From RFC 5869: + * 2.2. Step 1: Extract + * HKDF-Extract(salt, IKM) -> PRK + * 2.3. Step 2: Expand + * HKDF-Expand(PRK, info, L) -> OKM + */ +static int HKDF(OSSL_LIB_CTX *libctx, const EVP_MD *evp_md, + const unsigned char *salt, size_t salt_len, + const unsigned char *ikm, size_t ikm_len, + const unsigned char *info, size_t info_len, + unsigned char *okm, size_t okm_len) +{ + unsigned char prk[EVP_MAX_MD_SIZE]; + int ret, sz; + size_t prk_len; + + sz = EVP_MD_get_size(evp_md); + if (sz <= 0) + return 0; + prk_len = (size_t)sz; + + /* Step 1: HKDF-Extract(salt, IKM) -> PRK */ + if (!HKDF_Extract(libctx, evp_md, + salt, salt_len, ikm, ikm_len, prk, prk_len)) + return 0; + + /* Step 2: HKDF-Expand(PRK, info, L) -> OKM */ + ret = HKDF_Expand(evp_md, prk, prk_len, info, info_len, okm, okm_len); + OPENSSL_cleanse(prk, sizeof(prk)); + + return ret; +} + +/* + * Refer to "HMAC-based Extract-and-Expand Key Derivation Function (HKDF)" + * Section 2.2 (https://tools.ietf.org/html/rfc5869#section-2.2). + * + * 2.2. Step 1: Extract + * + * HKDF-Extract(salt, IKM) -> PRK + * + * Options: + * Hash a hash function; HashLen denotes the length of the + * hash function output in octets + * + * Inputs: + * salt optional salt value (a non-secret random value); + * if not provided, it is set to a string of HashLen zeros. + * IKM input keying material + * + * Output: + * PRK a pseudorandom key (of HashLen octets) + * + * The output PRK is calculated as follows: + * + * PRK = HMAC-Hash(salt, IKM) + */ +static int HKDF_Extract(OSSL_LIB_CTX *libctx, const EVP_MD *evp_md, + const unsigned char *salt, size_t salt_len, + const unsigned char *ikm, size_t ikm_len, + unsigned char *prk, size_t prk_len) +{ + int sz = EVP_MD_get_size(evp_md); + + if (sz <= 0) + return 0; + if (prk_len != (size_t)sz) { + ERR_raise(ERR_LIB_PROV, PROV_R_WRONG_OUTPUT_BUFFER_SIZE); + return 0; + } + /* calc: PRK = HMAC-Hash(salt, IKM) */ + return + EVP_Q_mac(libctx, "HMAC", NULL, EVP_MD_get0_name(evp_md), NULL, salt, + salt_len, ikm, ikm_len, prk, EVP_MD_get_size(evp_md), NULL) + != NULL; +} + +/* + * Refer to "HMAC-based Extract-and-Expand Key Derivation Function (HKDF)" + * Section 2.3 (https://tools.ietf.org/html/rfc5869#section-2.3). + * + * 2.3. Step 2: Expand + * + * HKDF-Expand(PRK, info, L) -> OKM + * + * Options: + * Hash a hash function; HashLen denotes the length of the + * hash function output in octets + * + * Inputs: + * PRK a pseudorandom key of at least HashLen octets + * (usually, the output from the extract step) + * info optional context and application specific information + * (can be a zero-length string) + * L length of output keying material in octets + * (<= 255*HashLen) + * + * Output: + * OKM output keying material (of L octets) + * + * The output OKM is calculated as follows: + * + * N = ceil(L/HashLen) + * T = T(1) | T(2) | T(3) | ... | T(N) + * OKM = first L octets of T + * + * where: + * T(0) = empty string (zero length) + * T(1) = HMAC-Hash(PRK, T(0) | info | 0x01) + * T(2) = HMAC-Hash(PRK, T(1) | info | 0x02) + * T(3) = HMAC-Hash(PRK, T(2) | info | 0x03) + * ... + * + * (where the constant concatenated to the end of each T(n) is a + * single octet.) + */ +static int HKDF_Expand(const EVP_MD *evp_md, + const unsigned char *prk, size_t prk_len, + const unsigned char *info, size_t info_len, + unsigned char *okm, size_t okm_len) +{ + HMAC_CTX *hmac; + int ret = 0, sz; + unsigned int i; + unsigned char prev[EVP_MAX_MD_SIZE]; + size_t done_len = 0, dig_len, n; + + sz = EVP_MD_get_size(evp_md); + if (sz <= 0) + return 0; + dig_len = (size_t)sz; + + /* calc: N = ceil(L/HashLen) */ + n = okm_len / dig_len; + if (okm_len % dig_len) + n++; + + if (n > 255 || okm == NULL) + return 0; + + if ((hmac = HMAC_CTX_new()) == NULL) + return 0; + + if (!HMAC_Init_ex(hmac, prk, prk_len, evp_md, NULL)) + goto err; + + for (i = 1; i <= n; i++) { + size_t copy_len; + const unsigned char ctr = i; + + /* calc: T(i) = HMAC-Hash(PRK, T(i - 1) | info | i) */ + if (i > 1) { + if (!HMAC_Init_ex(hmac, NULL, 0, NULL, NULL)) + goto err; + + if (!HMAC_Update(hmac, prev, dig_len)) + goto err; + } + + if (!HMAC_Update(hmac, info, info_len)) + goto err; + + if (!HMAC_Update(hmac, &ctr, 1)) + goto err; + + if (!HMAC_Final(hmac, prev, NULL)) + goto err; + + copy_len = (dig_len > okm_len - done_len) ? + okm_len - done_len : + dig_len; + + memcpy(okm + done_len, prev, copy_len); + + done_len += copy_len; + } + ret = 1; + + err: + OPENSSL_cleanse(prev, sizeof(prev)); + HMAC_CTX_free(hmac); + return ret; +} + +/* + * TLS uses slight variations of the above and for FIPS validation purposes, + * they need to be present here. + * Refer to RFC 8446 section 7 for specific details. + */ + +/* + * Given a |secret|; a |label| of length |labellen|; and |data| of length + * |datalen| (e.g. typically a hash of the handshake messages), derive a new + * secret |outlen| bytes long and store it in the location pointed to be |out|. + * The |data| value may be zero length. Returns 1 on success and 0 on failure. + */ +static int prov_tls13_hkdf_expand(const EVP_MD *md, + const unsigned char *key, size_t keylen, + const unsigned char *prefix, size_t prefixlen, + const unsigned char *label, size_t labellen, + const unsigned char *data, size_t datalen, + unsigned char *out, size_t outlen) +{ + size_t hkdflabellen; + unsigned char hkdflabel[HKDF_MAXBUF]; + WPACKET pkt; + + /* + * 2 bytes for length of derived secret + 1 byte for length of combined + * prefix and label + bytes for the label itself + 1 byte length of hash + * + bytes for the hash itself. We've got the maximum the KDF can handle + * which should always be sufficient. + */ + if (!WPACKET_init_static_len(&pkt, hkdflabel, sizeof(hkdflabel), 0) + || !WPACKET_put_bytes_u16(&pkt, outlen) + || !WPACKET_start_sub_packet_u8(&pkt) + || !WPACKET_memcpy(&pkt, prefix, prefixlen) + || !WPACKET_memcpy(&pkt, label, labellen) + || !WPACKET_close(&pkt) + || !WPACKET_sub_memcpy_u8(&pkt, data, (data == NULL) ? 0 : datalen) + || !WPACKET_get_total_written(&pkt, &hkdflabellen) + || !WPACKET_finish(&pkt)) { + WPACKET_cleanup(&pkt); + return 0; + } + + return HKDF_Expand(md, key, keylen, hkdflabel, hkdflabellen, + out, outlen); +} + +static int prov_tls13_hkdf_generate_secret(OSSL_LIB_CTX *libctx, + const EVP_MD *md, + const unsigned char *prevsecret, + size_t prevsecretlen, + const unsigned char *insecret, + size_t insecretlen, + const unsigned char *prefix, + size_t prefixlen, + const unsigned char *label, + size_t labellen, + unsigned char *out, size_t outlen) +{ + size_t mdlen; + int ret; + unsigned char preextractsec[EVP_MAX_MD_SIZE]; + /* Always filled with zeros */ + static const unsigned char default_zeros[EVP_MAX_MD_SIZE]; + + ret = EVP_MD_get_size(md); + /* Ensure cast to size_t is safe */ + if (ret <= 0) + return 0; + mdlen = (size_t)ret; + + if (insecret == NULL) { + insecret = default_zeros; + insecretlen = mdlen; + } + if (prevsecret == NULL) { + prevsecret = default_zeros; + prevsecretlen = mdlen; + } else { + EVP_MD_CTX *mctx = EVP_MD_CTX_new(); + unsigned char hash[EVP_MAX_MD_SIZE]; + + /* The pre-extract derive step uses a hash of no messages */ + if (mctx == NULL + || EVP_DigestInit_ex(mctx, md, NULL) <= 0 + || EVP_DigestFinal_ex(mctx, hash, NULL) <= 0) { + EVP_MD_CTX_free(mctx); + return 0; + } + EVP_MD_CTX_free(mctx); + + /* Generate the pre-extract secret */ + if (!prov_tls13_hkdf_expand(md, prevsecret, prevsecretlen, + prefix, prefixlen, label, labellen, + hash, mdlen, preextractsec, mdlen)) + return 0; + prevsecret = preextractsec; + prevsecretlen = mdlen; + } + + ret = HKDF_Extract(libctx, md, prevsecret, prevsecretlen, + insecret, insecretlen, out, outlen); + + if (prevsecret == preextractsec) + OPENSSL_cleanse(preextractsec, mdlen); + return ret; +} + +#ifdef FIPS_MODULE +static int fips_tls1_3_digest_check_passed(KDF_HKDF *ctx, const EVP_MD *md) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + /* + * Perform digest check + * + * According to RFC 8446 appendix B.4, the valid hash functions are + * specified in FIPS 180-4. However, it only lists SHA2-256 and SHA2-384 in + * the table. ACVP also only lists the same set of hash functions. + */ + int digest_unapproved = !EVP_MD_is_a(md, SN_sha256) + && !EVP_MD_is_a(md, SN_sha384); + + if (digest_unapproved) { + if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE0, + libctx, "TLS13 KDF", "Digest", + ossl_fips_config_tls13_kdf_digest_check)) { + ERR_raise(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED); + return 0; + } + } + return 1; +} + +/* + * Calculate the correct length of the secret key. + * + * RFC 8446: + * If a given secret is not available, then the 0-value consisting of a + * string of Hash.length bytes set to zeros is used. + */ +static size_t fips_tls1_3_key_size(KDF_HKDF *ctx) +{ + const EVP_MD *md = ossl_prov_digest_md(&ctx->digest); + size_t key_size = 0; + + if (ctx->key != NULL) + key_size = ctx->key_len; + else if (md != NULL) + key_size = EVP_MD_size(md); + + return key_size; +} + +static int fips_tls1_3_key_check_passed(KDF_HKDF *ctx) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + int key_approved = ossl_kdf_check_key_size(fips_tls1_3_key_size(ctx)); + + if (!key_approved) { + if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE1, + libctx, "TLS13 KDF", "Key size", + ossl_fips_config_tls13_kdf_key_check)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + } + return 1; +} +#endif + +static int kdf_tls1_3_derive(void *vctx, unsigned char *key, size_t keylen, + const OSSL_PARAM params[]) +{ + KDF_HKDF *ctx = (KDF_HKDF *)vctx; + const EVP_MD *md; + + if (!ossl_prov_is_running() || !kdf_tls1_3_set_ctx_params(ctx, params)) + return 0; + + md = ossl_prov_digest_md(&ctx->digest); + if (md == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST); + return 0; + } + + switch (ctx->mode) { + default: + return 0; + + case EVP_KDF_HKDF_MODE_EXTRACT_ONLY: + return prov_tls13_hkdf_generate_secret(PROV_LIBCTX_OF(ctx->provctx), + md, + ctx->salt, ctx->salt_len, + ctx->key, ctx->key_len, + ctx->prefix, ctx->prefix_len, + ctx->label, ctx->label_len, + key, keylen); + + case EVP_KDF_HKDF_MODE_EXPAND_ONLY: + return prov_tls13_hkdf_expand(md, ctx->key, ctx->key_len, + ctx->prefix, ctx->prefix_len, + ctx->label, ctx->label_len, + ctx->data, ctx->data_len, + key, keylen); + } +} + +static int kdf_tls1_3_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p; + KDF_HKDF *ctx = vctx; + + if (ossl_param_is_empty(params)) + return 1; + + if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE0, params, + OSSL_KDF_PARAM_FIPS_DIGEST_CHECK)) + return 0; + if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE1, params, + OSSL_KDF_PARAM_FIPS_KEY_CHECK)) + return 0; + + if (!hkdf_common_set_ctx_params(ctx, params)) + return 0; + + if (ctx->mode == EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE); + return 0; + } + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PREFIX)) != NULL) { + OPENSSL_free(ctx->prefix); + ctx->prefix = NULL; + if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->prefix, 0, + &ctx->prefix_len)) + return 0; + } + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_LABEL)) != NULL) { + OPENSSL_free(ctx->label); + ctx->label = NULL; + if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->label, 0, + &ctx->label_len)) + return 0; + } + + OPENSSL_clear_free(ctx->data, ctx->data_len); + ctx->data = NULL; + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_DATA)) != NULL + && !OSSL_PARAM_get_octet_string(p, (void **)&ctx->data, 0, + &ctx->data_len)) + return 0; + +#ifdef FIPS_MODULE + if (OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_DIGEST) != NULL) { + const EVP_MD *md = ossl_prov_digest_md(&ctx->digest); + + if (!fips_tls1_3_digest_check_passed(ctx, md)) + return 0; + } + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KEY)) != NULL) + if (!fips_tls1_3_key_check_passed(ctx)) + return 0; +#endif + + return 1; +} + +static const OSSL_PARAM *kdf_tls1_3_settable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + static const OSSL_PARAM known_settable_ctx_params[] = { + HKDF_COMMON_SETTABLES, + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_PREFIX, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_LABEL, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_DATA, NULL, 0), + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_DIGEST_CHECK) + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_KEY_CHECK) + OSSL_PARAM_END + }; + return known_settable_ctx_params; +} + +static int kdf_tls1_3_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + KDF_HKDF *ctx = (KDF_HKDF *)vctx; + + if (ossl_param_is_empty(params)) + return 1; + + if (!hkdf_common_get_ctx_params(ctx, params)) + return 0; + + if (!OSSL_FIPS_IND_GET_CTX_PARAM(ctx, params)) + return 0; + + return 1; +} + +static const OSSL_PARAM *kdf_tls1_3_gettable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + static const OSSL_PARAM known_gettable_ctx_params[] = { + HKDF_COMMON_GETTABLES, + OSSL_FIPS_IND_GETTABLE_CTX_PARAM() + OSSL_PARAM_END + }; + return known_gettable_ctx_params; +} + +const OSSL_DISPATCH ossl_kdf_tls1_3_kdf_functions[] = { + { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_hkdf_new }, + { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))kdf_hkdf_dup }, + { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_hkdf_free }, + { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_hkdf_reset }, + { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_tls1_3_derive }, + { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS, + (void(*)(void))kdf_tls1_3_settable_ctx_params }, + { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_tls1_3_set_ctx_params }, + { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS, + (void(*)(void))kdf_tls1_3_gettable_ctx_params }, + { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_tls1_3_get_ctx_params }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/kdfs/hmacdrbg_kdf.c b/crypto/openssl/providers/implementations/kdfs/hmacdrbg_kdf.c new file mode 100644 index 000000000000..9ed214c3da46 --- /dev/null +++ b/crypto/openssl/providers/implementations/kdfs/hmacdrbg_kdf.c @@ -0,0 +1,263 @@ +/* + * Copyright 2022-2024 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 + */ + +#include <stdlib.h> +#include <string.h> +#include <openssl/crypto.h> +#include <openssl/err.h> +#include <openssl/kdf.h> +#include <openssl/proverr.h> +#include <openssl/core_names.h> +#include "prov/providercommon.h" +#include "prov/implementations.h" +#include "prov/hmac_drbg.h" +#include "prov/provider_ctx.h" + +static OSSL_FUNC_kdf_newctx_fn hmac_drbg_kdf_new; +static OSSL_FUNC_kdf_dupctx_fn hmac_drbg_kdf_dup; +static OSSL_FUNC_kdf_freectx_fn hmac_drbg_kdf_free; +static OSSL_FUNC_kdf_reset_fn hmac_drbg_kdf_reset; +static OSSL_FUNC_kdf_derive_fn hmac_drbg_kdf_derive; +static OSSL_FUNC_kdf_settable_ctx_params_fn hmac_drbg_kdf_settable_ctx_params; +static OSSL_FUNC_kdf_set_ctx_params_fn hmac_drbg_kdf_set_ctx_params; +static OSSL_FUNC_kdf_gettable_ctx_params_fn hmac_drbg_kdf_gettable_ctx_params; +static OSSL_FUNC_kdf_get_ctx_params_fn hmac_drbg_kdf_get_ctx_params; + +typedef struct { + PROV_DRBG_HMAC base; + void *provctx; + unsigned char *entropy, *nonce; + size_t entropylen, noncelen; + int init; +} KDF_HMAC_DRBG; + +static void *hmac_drbg_kdf_new(void *provctx) +{ + KDF_HMAC_DRBG *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->provctx = provctx; + return ctx; +} + +static void hmac_drbg_kdf_reset(void *vctx) +{ + KDF_HMAC_DRBG *ctx = (KDF_HMAC_DRBG *)vctx; + PROV_DRBG_HMAC *drbg = &ctx->base; + void *provctx = ctx->provctx; + + EVP_MAC_CTX_free(drbg->ctx); + ossl_prov_digest_reset(&drbg->digest); + OPENSSL_clear_free(ctx->entropy, ctx->entropylen); + OPENSSL_clear_free(ctx->nonce, ctx->noncelen); + OPENSSL_cleanse(ctx, sizeof(*ctx)); + ctx->provctx = provctx; +} + +static void hmac_drbg_kdf_free(void *vctx) +{ + KDF_HMAC_DRBG *ctx = (KDF_HMAC_DRBG *)vctx; + + if (ctx != NULL) { + hmac_drbg_kdf_reset(ctx); + OPENSSL_free(ctx); + } +} + +static int ossl_drbg_hmac_dup(PROV_DRBG_HMAC *dst, const PROV_DRBG_HMAC *src) { + if (src->ctx != NULL) { + dst->ctx = EVP_MAC_CTX_dup(src->ctx); + if (dst->ctx == NULL) + return 0; + } + if (!ossl_prov_digest_copy(&dst->digest, &src->digest)) + return 0; + memcpy(dst->K, src->K, sizeof(dst->K)); + memcpy(dst->V, src->V, sizeof(dst->V)); + dst->blocklen = src->blocklen; + return 1; +} + +static void *hmac_drbg_kdf_dup(void *vctx) +{ + const KDF_HMAC_DRBG *src = (const KDF_HMAC_DRBG *)vctx; + KDF_HMAC_DRBG *dst; + + dst = hmac_drbg_kdf_new(src->provctx); + if (dst != NULL) { + if (!ossl_drbg_hmac_dup(&dst->base, &src->base) + || !ossl_prov_memdup(src->entropy, src->entropylen, + &dst->entropy , &dst->entropylen) + || !ossl_prov_memdup(src->nonce, src->noncelen, + &dst->nonce, &dst->noncelen)) + goto err; + dst->init = src->init; + } + return dst; + + err: + hmac_drbg_kdf_free(dst); + return NULL; +} + +static int hmac_drbg_kdf_derive(void *vctx, unsigned char *out, size_t outlen, + const OSSL_PARAM params[]) +{ + KDF_HMAC_DRBG *ctx = (KDF_HMAC_DRBG *)vctx; + PROV_DRBG_HMAC *drbg = &ctx->base; + + if (!ossl_prov_is_running() + || !hmac_drbg_kdf_set_ctx_params(vctx, params)) + return 0; + if (!ctx->init) { + if (ctx->entropy == NULL + || ctx->entropylen == 0 + || ctx->nonce == NULL + || ctx->noncelen == 0 + || !ossl_drbg_hmac_init(drbg, ctx->entropy, ctx->entropylen, + ctx->nonce, ctx->noncelen, NULL, 0)) + return 0; + ctx->init = 1; + } + + return ossl_drbg_hmac_generate(drbg, out, outlen, NULL, 0); +} + +static int hmac_drbg_kdf_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + KDF_HMAC_DRBG *hmac = (KDF_HMAC_DRBG *)vctx; + PROV_DRBG_HMAC *drbg = &hmac->base; + const char *name; + const EVP_MD *md; + OSSL_PARAM *p; + + p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_MAC); + if (p != NULL) { + if (drbg->ctx == NULL) + return 0; + name = EVP_MAC_get0_name(EVP_MAC_CTX_get0_mac(drbg->ctx)); + if (!OSSL_PARAM_set_utf8_string(p, name)) + return 0; + } + + p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_DIGEST); + if (p != NULL) { + md = ossl_prov_digest_md(&drbg->digest); + if (md == NULL || !OSSL_PARAM_set_utf8_string(p, EVP_MD_get0_name(md))) + return 0; + } + return 1; +} + +static const OSSL_PARAM *hmac_drbg_kdf_gettable_ctx_params( + ossl_unused void *vctx, ossl_unused void *p_ctx) +{ + static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_MAC, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_END + }; + return known_gettable_ctx_params; +} + +static int hmac_drbg_kdf_set_ctx_params(void *vctx, + const OSSL_PARAM params[]) +{ + KDF_HMAC_DRBG *hmac = (KDF_HMAC_DRBG *)vctx; + PROV_DRBG_HMAC *drbg = &hmac->base; + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(hmac->provctx); + const EVP_MD *md; + const OSSL_PARAM *p; + void *ptr = NULL; + size_t size = 0; + int md_size; + + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_HMACDRBG_ENTROPY); + if (p != NULL) { + if (!OSSL_PARAM_get_octet_string(p, &ptr, 0, &size)) + return 0; + OPENSSL_free(hmac->entropy); + hmac->entropy = ptr; + hmac->entropylen = size; + hmac->init = 0; + ptr = NULL; + } + + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_HMACDRBG_NONCE); + if (p != NULL) { + if (!OSSL_PARAM_get_octet_string(p, &ptr, 0, &size)) + return 0; + OPENSSL_free(hmac->nonce); + hmac->nonce = ptr; + hmac->noncelen = size; + hmac->init = 0; + } + + p = OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_DIGEST); + if (p != NULL) { + if (!ossl_prov_digest_load_from_params(&drbg->digest, params, libctx)) + return 0; + + /* Confirm digest is allowed. Allow all digests that are not XOF */ + md = ossl_prov_digest_md(&drbg->digest); + if (md != NULL) { + if (EVP_MD_xof(md)) { + ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED); + return 0; + } + md_size = EVP_MD_get_size(md); + if (md_size <= 0) + return 0; + drbg->blocklen = (size_t)md_size; + } + return ossl_prov_macctx_load_from_params(&drbg->ctx, params, + "HMAC", NULL, NULL, libctx); + } + return 1; +} + +static const OSSL_PARAM *hmac_drbg_kdf_settable_ctx_params( + ossl_unused void *vctx, ossl_unused void *p_ctx) +{ + static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_HMACDRBG_ENTROPY, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_HMACDRBG_NONCE, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_END + }; + return known_settable_ctx_params; +} + +const OSSL_DISPATCH ossl_kdf_hmac_drbg_functions[] = { + { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))hmac_drbg_kdf_new }, + { OSSL_FUNC_KDF_FREECTX, (void(*)(void))hmac_drbg_kdf_free }, + { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))hmac_drbg_kdf_dup }, + { OSSL_FUNC_KDF_RESET, (void(*)(void))hmac_drbg_kdf_reset }, + { OSSL_FUNC_KDF_DERIVE, (void(*)(void))hmac_drbg_kdf_derive }, + { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS, + (void(*)(void))hmac_drbg_kdf_settable_ctx_params }, + { OSSL_FUNC_KDF_SET_CTX_PARAMS, + (void(*)(void))hmac_drbg_kdf_set_ctx_params }, + { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS, + (void(*)(void))hmac_drbg_kdf_gettable_ctx_params }, + { OSSL_FUNC_KDF_GET_CTX_PARAMS, + (void(*)(void))hmac_drbg_kdf_get_ctx_params }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/kdfs/kbkdf.c b/crypto/openssl/providers/implementations/kdfs/kbkdf.c new file mode 100644 index 000000000000..9ba834ac36d6 --- /dev/null +++ b/crypto/openssl/providers/implementations/kdfs/kbkdf.c @@ -0,0 +1,525 @@ +/* + * Copyright 2019-2024 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2019 Red Hat, Inc. + * + * 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 + */ + +/* + * This implements https://csrc.nist.gov/publications/detail/sp/800-108/final + * section 5.1 ("counter mode") and section 5.2 ("feedback mode") in both HMAC + * and CMAC. That document does not name the KDFs it defines; the name is + * derived from + * https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/Key-Derivation + * + * Note that section 5.3 ("double-pipeline mode") is not implemented, though + * it would be possible to do so in the future. + * + * These versions all assume the counter is used. It would be relatively + * straightforward to expose a configuration handle should the need arise. + * + * Variable names attempt to match those of SP800-108. + */ + +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> + +#include <openssl/core_names.h> +#include <openssl/evp.h> +#include <openssl/hmac.h> +#include <openssl/kdf.h> +#include <openssl/params.h> +#include <openssl/proverr.h> + +#include "internal/cryptlib.h" +#include "crypto/evp.h" +#include "internal/numbers.h" +#include "internal/endian.h" +#include "prov/implementations.h" +#include "prov/provider_ctx.h" +#include "prov/provider_util.h" +#include "prov/providercommon.h" +#include "prov/securitycheck.h" +#include "internal/e_os.h" +#include "internal/params.h" + +#define ossl_min(a, b) ((a) < (b)) ? (a) : (b) + +typedef enum { + COUNTER = 0, + FEEDBACK +} kbkdf_mode; + +/* Our context structure. */ +typedef struct { + void *provctx; + kbkdf_mode mode; + EVP_MAC_CTX *ctx_init; + + /* Names are lowercased versions of those found in SP800-108. */ + int r; + unsigned char *ki; + size_t ki_len; + unsigned char *label; + size_t label_len; + unsigned char *context; + size_t context_len; + unsigned char *iv; + size_t iv_len; + int use_l; + int is_kmac; + int use_separator; + OSSL_FIPS_IND_DECLARE +} KBKDF; + +/* Definitions needed for typechecking. */ +static OSSL_FUNC_kdf_newctx_fn kbkdf_new; +static OSSL_FUNC_kdf_dupctx_fn kbkdf_dup; +static OSSL_FUNC_kdf_freectx_fn kbkdf_free; +static OSSL_FUNC_kdf_reset_fn kbkdf_reset; +static OSSL_FUNC_kdf_derive_fn kbkdf_derive; +static OSSL_FUNC_kdf_settable_ctx_params_fn kbkdf_settable_ctx_params; +static OSSL_FUNC_kdf_set_ctx_params_fn kbkdf_set_ctx_params; +static OSSL_FUNC_kdf_gettable_ctx_params_fn kbkdf_gettable_ctx_params; +static OSSL_FUNC_kdf_get_ctx_params_fn kbkdf_get_ctx_params; + +/* Not all platforms have htobe32(). */ +static uint32_t be32(uint32_t host) +{ + uint32_t big = 0; + DECLARE_IS_ENDIAN; + + if (!IS_LITTLE_ENDIAN) + return host; + + big |= (host & 0xff000000) >> 24; + big |= (host & 0x00ff0000) >> 8; + big |= (host & 0x0000ff00) << 8; + big |= (host & 0x000000ff) << 24; + return big; +} + +static void init(KBKDF *ctx) +{ + ctx->r = 32; + ctx->use_l = 1; + ctx->use_separator = 1; + ctx->is_kmac = 0; +} + +static void *kbkdf_new(void *provctx) +{ + KBKDF *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + ctx = OPENSSL_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + + ctx->provctx = provctx; + OSSL_FIPS_IND_INIT(ctx) + init(ctx); + return ctx; +} + +static void kbkdf_free(void *vctx) +{ + KBKDF *ctx = (KBKDF *)vctx; + + if (ctx != NULL) { + kbkdf_reset(ctx); + OPENSSL_free(ctx); + } +} + +static void kbkdf_reset(void *vctx) +{ + KBKDF *ctx = (KBKDF *)vctx; + void *provctx = ctx->provctx; + + EVP_MAC_CTX_free(ctx->ctx_init); + OPENSSL_clear_free(ctx->context, ctx->context_len); + OPENSSL_clear_free(ctx->label, ctx->label_len); + OPENSSL_clear_free(ctx->ki, ctx->ki_len); + OPENSSL_clear_free(ctx->iv, ctx->iv_len); + memset(ctx, 0, sizeof(*ctx)); + ctx->provctx = provctx; + init(ctx); +} + +static void *kbkdf_dup(void *vctx) +{ + const KBKDF *src = (const KBKDF *)vctx; + KBKDF *dest; + + dest = kbkdf_new(src->provctx); + if (dest != NULL) { + dest->ctx_init = EVP_MAC_CTX_dup(src->ctx_init); + if (dest->ctx_init == NULL + || !ossl_prov_memdup(src->ki, src->ki_len, + &dest->ki, &dest->ki_len) + || !ossl_prov_memdup(src->label, src->label_len, + &dest->label, &dest->label_len) + || !ossl_prov_memdup(src->context, src->context_len, + &dest->context, &dest->context_len) + || !ossl_prov_memdup(src->iv, src->iv_len, + &dest->iv, &dest->iv_len)) + goto err; + dest->mode = src->mode; + dest->r = src->r; + dest->use_l = src->use_l; + dest->use_separator = src->use_separator; + dest->is_kmac = src->is_kmac; + OSSL_FIPS_IND_COPY(dest, src) + } + return dest; + + err: + kbkdf_free(dest); + return NULL; +} + +#ifdef FIPS_MODULE +static int fips_kbkdf_key_check_passed(KBKDF *ctx) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + int key_approved = ossl_kdf_check_key_size(ctx->ki_len); + + if (!key_approved) { + if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE0, + libctx, "KBKDF", "Key size", + ossl_fips_config_kbkdf_key_check)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + } + return 1; +} +#endif + +/* SP800-108 section 5.1 or section 5.2 depending on mode. */ +static int derive(EVP_MAC_CTX *ctx_init, kbkdf_mode mode, unsigned char *iv, + size_t iv_len, unsigned char *label, size_t label_len, + unsigned char *context, size_t context_len, + unsigned char *k_i, size_t h, uint32_t l, int has_separator, + unsigned char *ko, size_t ko_len, int r) +{ + int ret = 0; + EVP_MAC_CTX *ctx = NULL; + size_t written = 0, to_write, k_i_len = iv_len; + const unsigned char zero = 0; + uint32_t counter, i; + /* + * From SP800-108: + * The fixed input data is a concatenation of a Label, + * a separation indicator 0x00, the Context, and L. + * One or more of these fixed input data fields may be omitted. + * + * has_separator == 0 means that the separator is omitted. + * Passing a value of l == 0 means that L is omitted. + * The Context and L are omitted automatically if a NULL buffer is passed. + */ + int has_l = (l != 0); + + /* Setup K(0) for feedback mode. */ + if (iv_len > 0) + memcpy(k_i, iv, iv_len); + + for (counter = 1; written < ko_len; counter++) { + i = be32(counter); + + ctx = EVP_MAC_CTX_dup(ctx_init); + if (ctx == NULL) + goto done; + + /* Perform feedback, if appropriate. */ + if (mode == FEEDBACK && !EVP_MAC_update(ctx, k_i, k_i_len)) + goto done; + + if (!EVP_MAC_update(ctx, 4 - (r / 8) + (unsigned char *)&i, r / 8) + || !EVP_MAC_update(ctx, label, label_len) + || (has_separator && !EVP_MAC_update(ctx, &zero, 1)) + || !EVP_MAC_update(ctx, context, context_len) + || (has_l && !EVP_MAC_update(ctx, (unsigned char *)&l, 4)) + || !EVP_MAC_final(ctx, k_i, NULL, h)) + goto done; + + to_write = ko_len - written; + memcpy(ko + written, k_i, ossl_min(to_write, h)); + written += h; + + k_i_len = h; + EVP_MAC_CTX_free(ctx); + ctx = NULL; + } + + ret = 1; +done: + EVP_MAC_CTX_free(ctx); + return ret; +} + +/* This must be run before the key is set */ +static int kmac_init(EVP_MAC_CTX *ctx, const unsigned char *custom, size_t customlen) +{ + OSSL_PARAM params[2]; + + if (custom == NULL || customlen == 0) + return 1; + params[0] = OSSL_PARAM_construct_octet_string(OSSL_MAC_PARAM_CUSTOM, + (void *)custom, customlen); + params[1] = OSSL_PARAM_construct_end(); + return EVP_MAC_CTX_set_params(ctx, params) > 0; +} + +static int kmac_derive(EVP_MAC_CTX *ctx, unsigned char *out, size_t outlen, + const unsigned char *context, size_t contextlen) +{ + OSSL_PARAM params[2]; + + params[0] = OSSL_PARAM_construct_size_t(OSSL_MAC_PARAM_SIZE, &outlen); + params[1] = OSSL_PARAM_construct_end(); + return EVP_MAC_CTX_set_params(ctx, params) > 0 + && EVP_MAC_update(ctx, context, contextlen) + && EVP_MAC_final(ctx, out, NULL, outlen); +} + +static int kbkdf_derive(void *vctx, unsigned char *key, size_t keylen, + const OSSL_PARAM params[]) +{ + KBKDF *ctx = (KBKDF *)vctx; + int ret = 0; + unsigned char *k_i = NULL; + uint32_t l = 0; + size_t h = 0; + uint64_t counter_max; + + if (!ossl_prov_is_running() || !kbkdf_set_ctx_params(ctx, params)) + return 0; + + /* label, context, and iv are permitted to be empty. Check everything + * else. */ + if (ctx->ctx_init == NULL) { + if (ctx->ki_len == 0 || ctx->ki == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET); + return 0; + } + /* Could either be missing MAC or missing message digest or missing + * cipher - arbitrarily, I pick this one. */ + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MAC); + return 0; + } + + /* Fail if the output length is zero */ + if (keylen == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + + if (ctx->is_kmac) { + ret = kmac_derive(ctx->ctx_init, key, keylen, + ctx->context, ctx->context_len); + goto done; + } + + h = EVP_MAC_CTX_get_mac_size(ctx->ctx_init); + if (h == 0) + goto done; + + if (ctx->iv_len != 0 && ctx->iv_len != h) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SEED_LENGTH); + goto done; + } + + if (ctx->mode == COUNTER) { + /* Fail if keylen is too large for r */ + counter_max = (uint64_t)1 << (uint64_t)ctx->r; + if ((uint64_t)(keylen / h) >= counter_max) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + goto done; + } + } + + if (ctx->use_l != 0) + l = be32(keylen * 8); + + k_i = OPENSSL_zalloc(h); + if (k_i == NULL) + goto done; + + ret = derive(ctx->ctx_init, ctx->mode, ctx->iv, ctx->iv_len, ctx->label, + ctx->label_len, ctx->context, ctx->context_len, k_i, h, l, + ctx->use_separator, key, keylen, ctx->r); +done: + if (ret != 1) + OPENSSL_cleanse(key, keylen); + OPENSSL_clear_free(k_i, h); + return ret; +} + +static int kbkdf_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + KBKDF *ctx = (KBKDF *)vctx; + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + const OSSL_PARAM *p; + + if (ossl_param_is_empty(params)) + return 1; + + if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE0, params, + OSSL_KDF_PARAM_FIPS_KEY_CHECK)) + return 0; + + if (!ossl_prov_macctx_load_from_params(&ctx->ctx_init, params, NULL, + NULL, NULL, libctx)) + return 0; + if (ctx->ctx_init != NULL) { + ctx->is_kmac = 0; + if (EVP_MAC_is_a(EVP_MAC_CTX_get0_mac(ctx->ctx_init), + OSSL_MAC_NAME_KMAC128) + || EVP_MAC_is_a(EVP_MAC_CTX_get0_mac(ctx->ctx_init), + OSSL_MAC_NAME_KMAC256)) { + ctx->is_kmac = 1; + } else if (!EVP_MAC_is_a(EVP_MAC_CTX_get0_mac(ctx->ctx_init), + OSSL_MAC_NAME_HMAC) + && !EVP_MAC_is_a(EVP_MAC_CTX_get0_mac(ctx->ctx_init), + OSSL_MAC_NAME_CMAC)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MAC); + return 0; + } + } + + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_MODE); + if (p != NULL + && OPENSSL_strncasecmp("counter", p->data, p->data_size) == 0) { + ctx->mode = COUNTER; + } else if (p != NULL + && OPENSSL_strncasecmp("feedback", p->data, p->data_size) == 0) { + ctx->mode = FEEDBACK; + } else if (p != NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE); + return 0; + } + + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KEY); + if (p != NULL) { + if (ossl_param_get1_octet_string(p, OSSL_KDF_PARAM_KEY, + &ctx->ki, &ctx->ki_len) == 0) + return 0; +#ifdef FIPS_MODULE + if (!fips_kbkdf_key_check_passed(ctx)) + return 0; +#endif + } + + if (ossl_param_get1_octet_string(params, OSSL_KDF_PARAM_SALT, + &ctx->label, &ctx->label_len) == 0) + return 0; + + if (ossl_param_get1_concat_octet_string(params, OSSL_KDF_PARAM_INFO, + &ctx->context, &ctx->context_len, + 0) == 0) + return 0; + + if (ossl_param_get1_octet_string(params, OSSL_KDF_PARAM_SEED, + &ctx->iv, &ctx->iv_len) == 0) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KBKDF_USE_L); + if (p != NULL && !OSSL_PARAM_get_int(p, &ctx->use_l)) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KBKDF_R); + if (p != NULL) { + int new_r = 0; + + if (!OSSL_PARAM_get_int(p, &new_r)) + return 0; + if (new_r != 8 && new_r != 16 && new_r != 24 && new_r != 32) + return 0; + ctx->r = new_r; + } + + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KBKDF_USE_SEPARATOR); + if (p != NULL && !OSSL_PARAM_get_int(p, &ctx->use_separator)) + return 0; + + /* Set up digest context, if we can. */ + if (ctx->ctx_init != NULL && ctx->ki_len != 0) { + if ((ctx->is_kmac && !kmac_init(ctx->ctx_init, ctx->label, ctx->label_len)) + || !EVP_MAC_init(ctx->ctx_init, ctx->ki, ctx->ki_len, NULL)) + return 0; + } + return 1; +} + +static const OSSL_PARAM *kbkdf_settable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_INFO, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_KEY, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SEED, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_CIPHER, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_MAC, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_MODE, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_int(OSSL_KDF_PARAM_KBKDF_USE_L, NULL), + OSSL_PARAM_int(OSSL_KDF_PARAM_KBKDF_USE_SEPARATOR, NULL), + OSSL_PARAM_int(OSSL_KDF_PARAM_KBKDF_R, NULL), + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_KEY_CHECK) + OSSL_PARAM_END, + }; + return known_settable_ctx_params; +} + +static int kbkdf_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ +#ifdef FIPS_MODULE + KBKDF *ctx = (KBKDF *)vctx; +#endif + OSSL_PARAM *p; + + /* KBKDF can produce results as large as you like. */ + p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE); + if (p != NULL && !OSSL_PARAM_set_size_t(p, SIZE_MAX)) + return 0; + + if (!OSSL_FIPS_IND_GET_CTX_PARAM(ctx, params)) + return 0; + return 1; +} + +static const OSSL_PARAM *kbkdf_gettable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL), + OSSL_FIPS_IND_GETTABLE_CTX_PARAM() + OSSL_PARAM_END + }; + return known_gettable_ctx_params; +} + +const OSSL_DISPATCH ossl_kdf_kbkdf_functions[] = { + { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kbkdf_new }, + { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))kbkdf_dup }, + { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kbkdf_free }, + { OSSL_FUNC_KDF_RESET, (void(*)(void))kbkdf_reset }, + { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kbkdf_derive }, + { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS, + (void(*)(void))kbkdf_settable_ctx_params }, + { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kbkdf_set_ctx_params }, + { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS, + (void(*)(void))kbkdf_gettable_ctx_params }, + { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kbkdf_get_ctx_params }, + OSSL_DISPATCH_END, +}; diff --git a/crypto/openssl/providers/implementations/kdfs/krb5kdf.c b/crypto/openssl/providers/implementations/kdfs/krb5kdf.c new file mode 100644 index 000000000000..13623ec7302e --- /dev/null +++ b/crypto/openssl/providers/implementations/kdfs/krb5kdf.c @@ -0,0 +1,497 @@ +/* + * Copyright 2018-2025 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 + */ + +/* + * DES low level APIs are deprecated for public use, but still ok for internal + * use. We access the DES_set_odd_parity(3) function here. + */ +#include "internal/deprecated.h" + +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> + +#include <openssl/core_names.h> +#include <openssl/des.h> +#include <openssl/evp.h> +#include <openssl/kdf.h> +#include <openssl/proverr.h> + +#include "internal/cryptlib.h" +#include "crypto/evp.h" +#include "internal/numbers.h" +#include "prov/implementations.h" +#include "prov/provider_ctx.h" +#include "prov/provider_util.h" +#include "prov/providercommon.h" + +/* KRB5 KDF defined in RFC 3961, Section 5.1 */ + +static OSSL_FUNC_kdf_newctx_fn krb5kdf_new; +static OSSL_FUNC_kdf_dupctx_fn krb5kdf_dup; +static OSSL_FUNC_kdf_freectx_fn krb5kdf_free; +static OSSL_FUNC_kdf_reset_fn krb5kdf_reset; +static OSSL_FUNC_kdf_derive_fn krb5kdf_derive; +static OSSL_FUNC_kdf_settable_ctx_params_fn krb5kdf_settable_ctx_params; +static OSSL_FUNC_kdf_set_ctx_params_fn krb5kdf_set_ctx_params; +static OSSL_FUNC_kdf_gettable_ctx_params_fn krb5kdf_gettable_ctx_params; +static OSSL_FUNC_kdf_get_ctx_params_fn krb5kdf_get_ctx_params; + +static int KRB5KDF(const EVP_CIPHER *cipher, ENGINE *engine, + const unsigned char *key, size_t key_len, + const unsigned char *constant, size_t constant_len, + unsigned char *okey, size_t okey_len); + +typedef struct { + void *provctx; + PROV_CIPHER cipher; + unsigned char *key; + size_t key_len; + unsigned char *constant; + size_t constant_len; +} KRB5KDF_CTX; + +static void *krb5kdf_new(void *provctx) +{ + KRB5KDF_CTX *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) == NULL) + return NULL; + ctx->provctx = provctx; + return ctx; +} + +static void krb5kdf_free(void *vctx) +{ + KRB5KDF_CTX *ctx = (KRB5KDF_CTX *)vctx; + + if (ctx != NULL) { + krb5kdf_reset(ctx); + OPENSSL_free(ctx); + } +} + +static void krb5kdf_reset(void *vctx) +{ + KRB5KDF_CTX *ctx = (KRB5KDF_CTX *)vctx; + void *provctx = ctx->provctx; + + ossl_prov_cipher_reset(&ctx->cipher); + OPENSSL_clear_free(ctx->key, ctx->key_len); + OPENSSL_clear_free(ctx->constant, ctx->constant_len); + memset(ctx, 0, sizeof(*ctx)); + ctx->provctx = provctx; +} + +static int krb5kdf_set_membuf(unsigned char **dst, size_t *dst_len, + const OSSL_PARAM *p) +{ + OPENSSL_clear_free(*dst, *dst_len); + *dst = NULL; + *dst_len = 0; + return OSSL_PARAM_get_octet_string(p, (void **)dst, 0, dst_len); +} + +static void *krb5kdf_dup(void *vctx) +{ + const KRB5KDF_CTX *src = (const KRB5KDF_CTX *)vctx; + KRB5KDF_CTX *dest; + + dest = krb5kdf_new(src->provctx); + if (dest != NULL) { + if (!ossl_prov_memdup(src->key, src->key_len, + &dest->key, &dest->key_len) + || !ossl_prov_memdup(src->constant, src->constant_len, + &dest->constant , &dest->constant_len) + || !ossl_prov_cipher_copy(&dest->cipher, &src->cipher)) + goto err; + } + return dest; + + err: + krb5kdf_free(dest); + return NULL; +} + +static int krb5kdf_derive(void *vctx, unsigned char *key, size_t keylen, + const OSSL_PARAM params[]) +{ + KRB5KDF_CTX *ctx = (KRB5KDF_CTX *)vctx; + const EVP_CIPHER *cipher; + ENGINE *engine; + + if (!ossl_prov_is_running() || !krb5kdf_set_ctx_params(ctx, params)) + return 0; + + cipher = ossl_prov_cipher_cipher(&ctx->cipher); + if (cipher == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_CIPHER); + return 0; + } + if (ctx->key == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + return 0; + } + if (ctx->constant == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_CONSTANT); + return 0; + } + engine = ossl_prov_cipher_engine(&ctx->cipher); + return KRB5KDF(cipher, engine, ctx->key, ctx->key_len, + ctx->constant, ctx->constant_len, + key, keylen); +} + +static int krb5kdf_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p; + KRB5KDF_CTX *ctx = vctx; + OSSL_LIB_CTX *provctx = PROV_LIBCTX_OF(ctx->provctx); + + if (ossl_param_is_empty(params)) + return 1; + + if (!ossl_prov_cipher_load_from_params(&ctx->cipher, params, provctx)) + return 0; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KEY)) != NULL) + if (!krb5kdf_set_membuf(&ctx->key, &ctx->key_len, p)) + return 0; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_CONSTANT)) + != NULL) + if (!krb5kdf_set_membuf(&ctx->constant, &ctx->constant_len, p)) + return 0; + + return 1; +} + +static const OSSL_PARAM *krb5kdf_settable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_CIPHER, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_KEY, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_CONSTANT, NULL, 0), + OSSL_PARAM_END + }; + return known_settable_ctx_params; +} + +static int krb5kdf_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + KRB5KDF_CTX *ctx = (KRB5KDF_CTX *)vctx; + const EVP_CIPHER *cipher; + size_t len; + OSSL_PARAM *p; + + cipher = ossl_prov_cipher_cipher(&ctx->cipher); + if (cipher) + len = EVP_CIPHER_get_key_length(cipher); + else + len = EVP_MAX_KEY_LENGTH; + + if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL) + return OSSL_PARAM_set_size_t(p, len); + return -2; +} + +static const OSSL_PARAM *krb5kdf_gettable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + 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_krb5kdf_functions[] = { + { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))krb5kdf_new }, + { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))krb5kdf_dup }, + { OSSL_FUNC_KDF_FREECTX, (void(*)(void))krb5kdf_free }, + { OSSL_FUNC_KDF_RESET, (void(*)(void))krb5kdf_reset }, + { OSSL_FUNC_KDF_DERIVE, (void(*)(void))krb5kdf_derive }, + { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS, + (void(*)(void))krb5kdf_settable_ctx_params }, + { OSSL_FUNC_KDF_SET_CTX_PARAMS, + (void(*)(void))krb5kdf_set_ctx_params }, + { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS, + (void(*)(void))krb5kdf_gettable_ctx_params }, + { OSSL_FUNC_KDF_GET_CTX_PARAMS, + (void(*)(void))krb5kdf_get_ctx_params }, + OSSL_DISPATCH_END +}; + +#ifndef OPENSSL_NO_DES +/* + * DES3 is a special case, it requires a random-to-key function and its + * input truncated to 21 bytes of the 24 produced by the cipher. + * See RFC3961 6.3.1 + */ +static int fixup_des3_key(unsigned char *key) +{ + unsigned char *cblock; + int i, j; + + for (i = 2; i >= 0; i--) { + cblock = &key[i * 8]; + memmove(cblock, &key[i * 7], 7); + cblock[7] = 0; + for (j = 0; j < 7; j++) + cblock[7] |= (cblock[j] & 1) << (j + 1); + DES_set_odd_parity((DES_cblock *)cblock); + } + + /* fail if keys are such that triple des degrades to single des */ + if (CRYPTO_memcmp(&key[0], &key[8], 8) == 0 || + CRYPTO_memcmp(&key[8], &key[16], 8) == 0) { + return 0; + } + + return 1; +} +#endif + +/* + * N-fold(K) where blocksize is N, and constant_len is K + * Note: Here |= denotes concatenation + * + * L = lcm(N,K) + * R = L/K + * + * for r: 1 -> R + * s |= constant rot 13*(r-1)) + * + * block = 0 + * for k: 1 -> K + * block += s[N(k-1)..(N-1)k] (ones'-complement addition) + * + * Optimizing for space we compute: + * for each l in L-1 -> 0: + * s[l] = (constant rot 13*(l/K))[l%k] + * block[l % N] += s[l] (with carry) + * finally add carry if any + */ +static void n_fold(unsigned char *block, unsigned int blocksize, + const unsigned char *constant, size_t constant_len) +{ + unsigned int tmp, gcd, remainder, lcm, carry; + int b, l; + + if (constant_len == blocksize) { + memcpy(block, constant, constant_len); + return; + } + + /* Least Common Multiple of lengths: LCM(a,b)*/ + gcd = blocksize; + remainder = constant_len; + /* Calculate Great Common Divisor first GCD(a,b) */ + while (remainder != 0) { + tmp = gcd % remainder; + gcd = remainder; + remainder = tmp; + } + /* resulting a is the GCD, LCM(a,b) = |a*b|/GCD(a,b) */ + lcm = blocksize * constant_len / gcd; + + /* now spread out the bits */ + memset(block, 0, blocksize); + + /* last to first to be able to bring carry forward */ + carry = 0; + for (l = lcm - 1; l >= 0; l--) { + unsigned int rotbits, rshift, rbyte; + + /* destination byte in block is l % N */ + b = l % blocksize; + /* Our virtual s buffer is R = L/K long (K = constant_len) */ + /* So we rotate backwards from R-1 to 0 (none) rotations */ + rotbits = 13 * (l / constant_len); + /* find the byte on s where rotbits falls onto */ + rbyte = l - (rotbits / 8); + /* calculate how much shift on that byte */ + rshift = rotbits & 0x07; + /* rbyte % constant_len gives us the unrotated byte in the + * constant buffer, get also the previous byte then + * appropriately shift them to get the rotated byte we need */ + tmp = (constant[(rbyte-1) % constant_len] << (8 - rshift) + | constant[rbyte % constant_len] >> rshift) + & 0xff; + /* add with carry to any value placed by previous passes */ + tmp += carry + block[b]; + block[b] = tmp & 0xff; + /* save any carry that may be left */ + carry = tmp >> 8; + } + + /* if any carry is left at the end, add it through the number */ + for (b = blocksize - 1; b >= 0 && carry != 0; b--) { + carry += block[b]; + block[b] = carry & 0xff; + carry >>= 8; + } +} + +static int cipher_init(EVP_CIPHER_CTX *ctx, + const EVP_CIPHER *cipher, ENGINE *engine, + const unsigned char *key, size_t key_len) +{ + int klen, ret; + + ret = EVP_EncryptInit_ex(ctx, cipher, engine, NULL, NULL); + if (!ret) + goto out; + /* set the key len for the odd variable key len cipher */ + klen = EVP_CIPHER_CTX_get_key_length(ctx); + if (key_len != (size_t)klen) { + ret = EVP_CIPHER_CTX_set_key_length(ctx, key_len); + if (ret <= 0) { + ret = 0; + goto out; + } + } + ret = EVP_EncryptInit_ex(ctx, NULL, NULL, key, NULL); + if (!ret) + goto out; + /* we never want padding, either the length requested is a multiple of + * the cipher block size or we are passed a cipher that can cope with + * partial blocks via techniques like cipher text stealing */ + ret = EVP_CIPHER_CTX_set_padding(ctx, 0); + if (!ret) + goto out; + +out: + return ret; +} + +static int KRB5KDF(const EVP_CIPHER *cipher, ENGINE *engine, + const unsigned char *key, size_t key_len, + const unsigned char *constant, size_t constant_len, + unsigned char *okey, size_t okey_len) +{ + EVP_CIPHER_CTX *ctx = NULL; + unsigned char block[EVP_MAX_BLOCK_LENGTH * 2]; + unsigned char *plainblock, *cipherblock; + size_t blocksize; + size_t cipherlen; + size_t osize; +#ifndef OPENSSL_NO_DES + int des3_no_fixup = 0; +#endif + int ret; + + if (key_len != okey_len) { +#ifndef OPENSSL_NO_DES + /* special case for 3des, where the caller may be requesting + * the random raw key, instead of the fixed up key */ + if (EVP_CIPHER_get_nid(cipher) == NID_des_ede3_cbc && + key_len == 24 && okey_len == 21) { + des3_no_fixup = 1; + } else { +#endif + ERR_raise(ERR_LIB_PROV, PROV_R_WRONG_OUTPUT_BUFFER_SIZE); + return 0; +#ifndef OPENSSL_NO_DES + } +#endif + } + + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) + return 0; + + ret = cipher_init(ctx, cipher, engine, key, key_len); + if (!ret) + goto out; + + /* Initialize input block */ + blocksize = EVP_CIPHER_CTX_get_block_size(ctx); + + if (blocksize == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_CIPHER); + ret = 0; + goto out; + } + + if (constant_len > blocksize) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_CONSTANT_LENGTH); + ret = 0; + goto out; + } + + n_fold(block, blocksize, constant, constant_len); + plainblock = block; + cipherblock = block + EVP_MAX_BLOCK_LENGTH; + + for (osize = 0; osize < okey_len; osize += cipherlen) { + int olen; + + ret = EVP_EncryptUpdate(ctx, cipherblock, &olen, + plainblock, blocksize); + if (!ret) + goto out; + cipherlen = olen; + ret = EVP_EncryptFinal_ex(ctx, cipherblock, &olen); + if (!ret) + goto out; + if (olen != 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_WRONG_FINAL_BLOCK_LENGTH); + ret = 0; + goto out; + } + + /* write cipherblock out */ + if (cipherlen > okey_len - osize) + cipherlen = okey_len - osize; + memcpy(okey + osize, cipherblock, cipherlen); + + if (okey_len > osize + cipherlen) { + /* we need to reinitialize cipher context per spec */ + ret = EVP_CIPHER_CTX_reset(ctx); + if (!ret) + goto out; + ret = cipher_init(ctx, cipher, engine, key, key_len); + if (!ret) + goto out; + + /* also swap block offsets so last ciphertext becomes new + * plaintext */ + plainblock = cipherblock; + if (cipherblock == block) { + cipherblock += EVP_MAX_BLOCK_LENGTH; + } else { + cipherblock = block; + } + } + } + +#ifndef OPENSSL_NO_DES + if (EVP_CIPHER_get_nid(cipher) == NID_des_ede3_cbc && !des3_no_fixup) { + ret = fixup_des3_key(okey); + if (!ret) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GENERATE_KEY); + goto out; + } + } +#endif + + ret = 1; + +out: + EVP_CIPHER_CTX_free(ctx); + OPENSSL_cleanse(block, EVP_MAX_BLOCK_LENGTH * 2); + return ret; +} + diff --git a/crypto/openssl/providers/implementations/kdfs/pbkdf1.c b/crypto/openssl/providers/implementations/kdfs/pbkdf1.c new file mode 100644 index 000000000000..1b7e4d8a2eab --- /dev/null +++ b/crypto/openssl/providers/implementations/kdfs/pbkdf1.c @@ -0,0 +1,270 @@ +/* + * Copyright 1999-2024 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 + */ + +#include <openssl/trace.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <openssl/evp.h> +#include <openssl/kdf.h> +#include <openssl/core_names.h> +#include <openssl/proverr.h> +#include "internal/cryptlib.h" +#include "internal/numbers.h" +#include "crypto/evp.h" +#include "prov/provider_ctx.h" +#include "prov/providercommon.h" +#include "prov/implementations.h" +#include "prov/provider_util.h" + +static OSSL_FUNC_kdf_newctx_fn kdf_pbkdf1_new; +static OSSL_FUNC_kdf_dupctx_fn kdf_pbkdf1_dup; +static OSSL_FUNC_kdf_freectx_fn kdf_pbkdf1_free; +static OSSL_FUNC_kdf_reset_fn kdf_pbkdf1_reset; +static OSSL_FUNC_kdf_derive_fn kdf_pbkdf1_derive; +static OSSL_FUNC_kdf_settable_ctx_params_fn kdf_pbkdf1_settable_ctx_params; +static OSSL_FUNC_kdf_set_ctx_params_fn kdf_pbkdf1_set_ctx_params; +static OSSL_FUNC_kdf_gettable_ctx_params_fn kdf_pbkdf1_gettable_ctx_params; +static OSSL_FUNC_kdf_get_ctx_params_fn kdf_pbkdf1_get_ctx_params; + +typedef struct { + void *provctx; + PROV_DIGEST digest; + unsigned char *pass; + size_t pass_len; + unsigned char *salt; + size_t salt_len; + uint64_t iter; +} KDF_PBKDF1; + +/* + * PKCS5 PBKDF1 compatible key/IV generation as specified in: + * https://tools.ietf.org/html/rfc8018#page-10 + */ + +static int kdf_pbkdf1_do_derive(const unsigned char *pass, size_t passlen, + const unsigned char *salt, size_t saltlen, + uint64_t iter, const EVP_MD *md_type, + unsigned char *out, size_t n) +{ + uint64_t i; + int mdsize, ret = 0; + unsigned char md_tmp[EVP_MAX_MD_SIZE]; + EVP_MD_CTX *ctx = NULL; + + ctx = EVP_MD_CTX_new(); + if (ctx == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_EVP_LIB); + goto err; + } + + if (!EVP_DigestInit_ex(ctx, md_type, NULL) + || !EVP_DigestUpdate(ctx, pass, passlen) + || !EVP_DigestUpdate(ctx, salt, saltlen) + || !EVP_DigestFinal_ex(ctx, md_tmp, NULL)) + goto err; + mdsize = EVP_MD_size(md_type); + if (mdsize <= 0) + goto err; + if (n > (size_t)mdsize) { + ERR_raise(ERR_LIB_PROV, PROV_R_LENGTH_TOO_LARGE); + goto err; + } + + for (i = 1; i < iter; i++) { + if (!EVP_DigestInit_ex(ctx, md_type, NULL)) + goto err; + if (!EVP_DigestUpdate(ctx, md_tmp, mdsize)) + goto err; + if (!EVP_DigestFinal_ex(ctx, md_tmp, NULL)) + goto err; + } + + memcpy(out, md_tmp, n); + ret = 1; +err: + OPENSSL_cleanse(md_tmp, EVP_MAX_MD_SIZE); + EVP_MD_CTX_free(ctx); + return ret; +} + +static void *kdf_pbkdf1_new(void *provctx) +{ + KDF_PBKDF1 *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + ctx = OPENSSL_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + ctx->provctx = provctx; + return ctx; +} + +static void kdf_pbkdf1_cleanup(KDF_PBKDF1 *ctx) +{ + ossl_prov_digest_reset(&ctx->digest); + OPENSSL_free(ctx->salt); + OPENSSL_clear_free(ctx->pass, ctx->pass_len); + memset(ctx, 0, sizeof(*ctx)); +} + +static void kdf_pbkdf1_free(void *vctx) +{ + KDF_PBKDF1 *ctx = (KDF_PBKDF1 *)vctx; + + if (ctx != NULL) { + kdf_pbkdf1_cleanup(ctx); + OPENSSL_free(ctx); + } +} + +static void kdf_pbkdf1_reset(void *vctx) +{ + KDF_PBKDF1 *ctx = (KDF_PBKDF1 *)vctx; + void *provctx = ctx->provctx; + + kdf_pbkdf1_cleanup(ctx); + ctx->provctx = provctx; +} + +static void *kdf_pbkdf1_dup(void *vctx) +{ + const KDF_PBKDF1 *src = (const KDF_PBKDF1 *)vctx; + KDF_PBKDF1 *dest; + + dest = kdf_pbkdf1_new(src->provctx); + if (dest != NULL) { + if (!ossl_prov_memdup(src->salt, src->salt_len, + &dest->salt, &dest->salt_len) + || !ossl_prov_memdup(src->pass, src->pass_len, + &dest->pass , &dest->pass_len) + || !ossl_prov_digest_copy(&dest->digest, &src->digest)) + goto err; + dest->iter = src->iter; + } + return dest; + + err: + kdf_pbkdf1_free(dest); + return NULL; +} + +static int kdf_pbkdf1_set_membuf(unsigned char **buffer, size_t *buflen, + const OSSL_PARAM *p) +{ + OPENSSL_clear_free(*buffer, *buflen); + *buffer = NULL; + *buflen = 0; + + if (p->data_size == 0) { + if ((*buffer = OPENSSL_malloc(1)) == NULL) + return 0; + } else if (p->data != NULL) { + if (!OSSL_PARAM_get_octet_string(p, (void **)buffer, 0, buflen)) + return 0; + } + return 1; +} + +static int kdf_pbkdf1_derive(void *vctx, unsigned char *key, size_t keylen, + const OSSL_PARAM params[]) +{ + KDF_PBKDF1 *ctx = (KDF_PBKDF1 *)vctx; + const EVP_MD *md; + + if (!ossl_prov_is_running() || !kdf_pbkdf1_set_ctx_params(ctx, params)) + return 0; + + if (ctx->pass == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_PASS); + return 0; + } + + if (ctx->salt == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_SALT); + return 0; + } + + md = ossl_prov_digest_md(&ctx->digest); + return kdf_pbkdf1_do_derive(ctx->pass, ctx->pass_len, ctx->salt, ctx->salt_len, + ctx->iter, md, key, keylen); +} + +static int kdf_pbkdf1_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p; + KDF_PBKDF1 *ctx = vctx; + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + + if (!ossl_prov_digest_load_from_params(&ctx->digest, params, libctx)) + return 0; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PASSWORD)) != NULL) + if (!kdf_pbkdf1_set_membuf(&ctx->pass, &ctx->pass_len, p)) + return 0; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SALT)) != NULL) + if (!kdf_pbkdf1_set_membuf(&ctx->salt, &ctx->salt_len, p)) + return 0; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_ITER)) != NULL) + if (!OSSL_PARAM_get_uint64(p, &ctx->iter)) + return 0; + return 1; +} + +static const OSSL_PARAM *kdf_pbkdf1_settable_ctx_params(ossl_unused void *ctx, + ossl_unused void *p_ctx) +{ + static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_PASSWORD, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, NULL, 0), + OSSL_PARAM_uint64(OSSL_KDF_PARAM_ITER, NULL), + OSSL_PARAM_END + }; + return known_settable_ctx_params; +} + +static int kdf_pbkdf1_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + OSSL_PARAM *p; + + 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_pbkdf1_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_pbkdf1_functions[] = { + { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_pbkdf1_new }, + { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))kdf_pbkdf1_dup }, + { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_pbkdf1_free }, + { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_pbkdf1_reset }, + { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_pbkdf1_derive }, + { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS, + (void(*)(void))kdf_pbkdf1_settable_ctx_params }, + { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_pbkdf1_set_ctx_params }, + { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS, + (void(*)(void))kdf_pbkdf1_gettable_ctx_params }, + { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_pbkdf1_get_ctx_params }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/kdfs/pbkdf2.c b/crypto/openssl/providers/implementations/kdfs/pbkdf2.c new file mode 100644 index 000000000000..b38331406412 --- /dev/null +++ b/crypto/openssl/providers/implementations/kdfs/pbkdf2.c @@ -0,0 +1,467 @@ +/* + * Copyright 2018-2024 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 + */ + +/* + * HMAC low level APIs are deprecated for public use, but still ok for internal + * use. + */ +#include "internal/deprecated.h" + +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <openssl/hmac.h> +#include <openssl/evp.h> +#include <openssl/kdf.h> +#include <openssl/core_names.h> +#include <openssl/proverr.h> +#include "internal/cryptlib.h" +#include "internal/numbers.h" +#include "crypto/evp.h" +#include "prov/provider_ctx.h" +#include "prov/providercommon.h" +#include "prov/implementations.h" +#include "prov/provider_util.h" +#include "prov/securitycheck.h" +#include "pbkdf2.h" + +/* Constants specified in SP800-132 */ +#define KDF_PBKDF2_MIN_KEY_LEN_BITS 112 +#define KDF_PBKDF2_MAX_KEY_LEN_DIGEST_RATIO 0xFFFFFFFF +#define KDF_PBKDF2_MIN_ITERATIONS 1000 +#define KDF_PBKDF2_MIN_SALT_LEN (128 / 8) + +static OSSL_FUNC_kdf_newctx_fn kdf_pbkdf2_new; +static OSSL_FUNC_kdf_dupctx_fn kdf_pbkdf2_dup; +static OSSL_FUNC_kdf_freectx_fn kdf_pbkdf2_free; +static OSSL_FUNC_kdf_reset_fn kdf_pbkdf2_reset; +static OSSL_FUNC_kdf_derive_fn kdf_pbkdf2_derive; +static OSSL_FUNC_kdf_settable_ctx_params_fn kdf_pbkdf2_settable_ctx_params; +static OSSL_FUNC_kdf_set_ctx_params_fn kdf_pbkdf2_set_ctx_params; +static OSSL_FUNC_kdf_gettable_ctx_params_fn kdf_pbkdf2_gettable_ctx_params; +static OSSL_FUNC_kdf_get_ctx_params_fn kdf_pbkdf2_get_ctx_params; + +typedef struct { + void *provctx; + unsigned char *pass; + size_t pass_len; + unsigned char *salt; + size_t salt_len; + uint64_t iter; + PROV_DIGEST digest; + int lower_bound_checks; + OSSL_FIPS_IND_DECLARE +} KDF_PBKDF2; + +static int pbkdf2_derive(KDF_PBKDF2 *ctx, const char *pass, size_t passlen, + const unsigned char *salt, int saltlen, uint64_t iter, + const EVP_MD *digest, unsigned char *key, + size_t keylen, int lower_bound_checks); + +static void kdf_pbkdf2_init(KDF_PBKDF2 *ctx); + +static void *kdf_pbkdf2_new_no_init(void *provctx) +{ + KDF_PBKDF2 *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + ctx = OPENSSL_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + ctx->provctx = provctx; + OSSL_FIPS_IND_INIT(ctx); + return ctx; +} + +static void *kdf_pbkdf2_new(void *provctx) +{ + KDF_PBKDF2 *ctx = kdf_pbkdf2_new_no_init(provctx); + + if (ctx != NULL) + kdf_pbkdf2_init(ctx); + return ctx; +} + +static void kdf_pbkdf2_cleanup(KDF_PBKDF2 *ctx) +{ + ossl_prov_digest_reset(&ctx->digest); +#ifdef OPENSSL_PEDANTIC_ZEROIZATION + OPENSSL_clear_free(ctx->salt, ctx->salt_len); +#else + OPENSSL_free(ctx->salt); +#endif + OPENSSL_clear_free(ctx->pass, ctx->pass_len); + memset(ctx, 0, sizeof(*ctx)); +} + +static void kdf_pbkdf2_free(void *vctx) +{ + KDF_PBKDF2 *ctx = (KDF_PBKDF2 *)vctx; + + if (ctx != NULL) { + kdf_pbkdf2_cleanup(ctx); + OPENSSL_free(ctx); + } +} + +static void kdf_pbkdf2_reset(void *vctx) +{ + KDF_PBKDF2 *ctx = (KDF_PBKDF2 *)vctx; + void *provctx = ctx->provctx; + + kdf_pbkdf2_cleanup(ctx); + ctx->provctx = provctx; + kdf_pbkdf2_init(ctx); +} + +static void *kdf_pbkdf2_dup(void *vctx) +{ + const KDF_PBKDF2 *src = (const KDF_PBKDF2 *)vctx; + KDF_PBKDF2 *dest; + + /* We need a new PBKDF2 object but uninitialised since we're filling it */ + dest = kdf_pbkdf2_new_no_init(src->provctx); + if (dest != NULL) { + if (!ossl_prov_memdup(src->salt, src->salt_len, + &dest->salt, &dest->salt_len) + || !ossl_prov_memdup(src->pass, src->pass_len, + &dest->pass, &dest->pass_len) + || !ossl_prov_digest_copy(&dest->digest, &src->digest)) + goto err; + dest->iter = src->iter; + dest->lower_bound_checks = src->lower_bound_checks; + OSSL_FIPS_IND_COPY(dest, src) + } + return dest; + + err: + kdf_pbkdf2_free(dest); + return NULL; +} + +static void kdf_pbkdf2_init(KDF_PBKDF2 *ctx) +{ + OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END }; + OSSL_LIB_CTX *provctx = PROV_LIBCTX_OF(ctx->provctx); + + params[0] = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, + SN_sha1, 0); + if (!ossl_prov_digest_load_from_params(&ctx->digest, params, provctx)) + /* This is an error, but there is no way to indicate such directly */ + ossl_prov_digest_reset(&ctx->digest); + ctx->iter = PKCS5_DEFAULT_ITER; + ctx->lower_bound_checks = ossl_kdf_pbkdf2_default_checks; +} + +static int pbkdf2_set_membuf(unsigned char **buffer, size_t *buflen, + const OSSL_PARAM *p) +{ + OPENSSL_clear_free(*buffer, *buflen); + *buffer = NULL; + *buflen = 0; + + if (p->data_size == 0) { + if ((*buffer = OPENSSL_malloc(1)) == NULL) + return 0; + } else if (p->data != NULL) { + if (!OSSL_PARAM_get_octet_string(p, (void **)buffer, 0, buflen)) + return 0; + } + return 1; +} + +static int pbkdf2_lower_bound_check_passed(int saltlen, uint64_t iter, + size_t keylen, int *error, + const char **desc) +{ + if ((keylen * 8) < KDF_PBKDF2_MIN_KEY_LEN_BITS) { + *error = PROV_R_KEY_SIZE_TOO_SMALL; + if (desc != NULL) + *desc = "Key size"; + return 0; + } + if (saltlen < KDF_PBKDF2_MIN_SALT_LEN) { + *error = PROV_R_INVALID_SALT_LENGTH; + if (desc != NULL) + *desc = "Salt size"; + return 0; + } + if (iter < KDF_PBKDF2_MIN_ITERATIONS) { + *error = PROV_R_INVALID_ITERATION_COUNT; + if (desc != NULL) + *desc = "Iteration count"; + return 0; + } + + return 1; +} + +#ifdef FIPS_MODULE +static int fips_lower_bound_check_passed(KDF_PBKDF2 *ctx, size_t keylen) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + int error = 0; + const char *desc = NULL; + int approved = pbkdf2_lower_bound_check_passed(ctx->salt_len, ctx->iter, + keylen, &error, &desc); + + if (!approved) { + if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE0, libctx, + "PBKDF2", desc, + ossl_fips_config_pbkdf2_lower_bound_check)) { + ERR_raise(ERR_LIB_PROV, error); + return 0; + } + } + return 1; +} +#endif + +static int kdf_pbkdf2_derive(void *vctx, unsigned char *key, size_t keylen, + const OSSL_PARAM params[]) +{ + KDF_PBKDF2 *ctx = (KDF_PBKDF2 *)vctx; + const EVP_MD *md; + + if (!ossl_prov_is_running() || !kdf_pbkdf2_set_ctx_params(ctx, params)) + return 0; + + if (ctx->pass == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_PASS); + return 0; + } + + if (ctx->salt == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_SALT); + return 0; + } + + md = ossl_prov_digest_md(&ctx->digest); + return pbkdf2_derive(ctx, (char *)ctx->pass, ctx->pass_len, + ctx->salt, ctx->salt_len, ctx->iter, + md, key, keylen, ctx->lower_bound_checks); +} + +static int kdf_pbkdf2_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p; + KDF_PBKDF2 *ctx = vctx; + OSSL_LIB_CTX *provctx = PROV_LIBCTX_OF(ctx->provctx); + int pkcs5; + uint64_t iter, min_iter; + const EVP_MD *md; + + if (ossl_param_is_empty(params)) + return 1; + + if (OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_DIGEST) != NULL) { + if (!ossl_prov_digest_load_from_params(&ctx->digest, params, provctx)) + return 0; + md = ossl_prov_digest_md(&ctx->digest); + if (EVP_MD_xof(md)) { + ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED); + return 0; + } + } + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PKCS5)) != NULL) { + if (!OSSL_PARAM_get_int(p, &pkcs5)) + return 0; + ctx->lower_bound_checks = pkcs5 == 0; +#ifdef FIPS_MODULE + ossl_FIPS_IND_set_settable(OSSL_FIPS_IND_GET(ctx), + OSSL_FIPS_IND_SETTABLE0, + ctx->lower_bound_checks); +#endif + } + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PASSWORD)) != NULL) + if (!pbkdf2_set_membuf(&ctx->pass, &ctx->pass_len, p)) + return 0; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SALT)) != NULL) { + if (ctx->lower_bound_checks != 0 + && p->data_size < KDF_PBKDF2_MIN_SALT_LEN) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SALT_LENGTH); + return 0; + } + if (!pbkdf2_set_membuf(&ctx->salt, &ctx->salt_len, p)) + return 0; + } + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_ITER)) != NULL) { + if (!OSSL_PARAM_get_uint64(p, &iter)) + return 0; + min_iter = ctx->lower_bound_checks != 0 ? KDF_PBKDF2_MIN_ITERATIONS : 1; + if (iter < min_iter) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_ITERATION_COUNT); + return 0; + } + ctx->iter = iter; + } + return 1; +} + +static const OSSL_PARAM *kdf_pbkdf2_settable_ctx_params(ossl_unused void *ctx, + ossl_unused void *p_ctx) +{ + static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_PASSWORD, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, NULL, 0), + OSSL_PARAM_uint64(OSSL_KDF_PARAM_ITER, NULL), + OSSL_PARAM_int(OSSL_KDF_PARAM_PKCS5, NULL), + OSSL_PARAM_END + }; + return known_settable_ctx_params; +} + +static int kdf_pbkdf2_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + OSSL_PARAM *p; + + if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL) + if (!OSSL_PARAM_set_size_t(p, SIZE_MAX)) + return 0; + + if (!OSSL_FIPS_IND_GET_CTX_PARAM((KDF_PBKDF2 *) vctx, params)) + return 0; + return 1; +} + +static const OSSL_PARAM *kdf_pbkdf2_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_FIPS_IND_GETTABLE_CTX_PARAM() + OSSL_PARAM_END + }; + return known_gettable_ctx_params; +} + +const OSSL_DISPATCH ossl_kdf_pbkdf2_functions[] = { + { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_pbkdf2_new }, + { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))kdf_pbkdf2_dup }, + { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_pbkdf2_free }, + { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_pbkdf2_reset }, + { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_pbkdf2_derive }, + { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS, + (void(*)(void))kdf_pbkdf2_settable_ctx_params }, + { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_pbkdf2_set_ctx_params }, + { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS, + (void(*)(void))kdf_pbkdf2_gettable_ctx_params }, + { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_pbkdf2_get_ctx_params }, + OSSL_DISPATCH_END +}; + +/* + * This is an implementation of PKCS#5 v2.0 password based encryption key + * derivation function PBKDF2. SHA1 version verified against test vectors + * posted by Peter Gutmann to the PKCS-TNG mailing list. + * + * The constraints specified by SP800-132 have been added i.e. + * - Check the range of the key length. + * - Minimum iteration count of 1000. + * - Randomly-generated portion of the salt shall be at least 128 bits. + */ +static int pbkdf2_derive(KDF_PBKDF2 *ctx, const char *pass, size_t passlen, + const unsigned char *salt, int saltlen, uint64_t iter, + const EVP_MD *digest, unsigned char *key, + size_t keylen, int lower_bound_checks) +{ + int ret = 0; + unsigned char digtmp[EVP_MAX_MD_SIZE], *p, itmp[4]; + int cplen, k, tkeylen, mdlen; + uint64_t j; + unsigned long i = 1; + HMAC_CTX *hctx_tpl = NULL, *hctx = NULL; + + mdlen = EVP_MD_get_size(digest); + if (mdlen <= 0) + return 0; + + /* + * This check should always be done because keylen / mdlen >= (2^32 - 1) + * results in an overflow of the loop counter 'i'. + */ + if ((keylen / mdlen) >= KDF_PBKDF2_MAX_KEY_LEN_DIGEST_RATIO) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + +#ifdef FIPS_MODULE + if (!fips_lower_bound_check_passed(ctx, keylen)) + return 0; +#else + if (lower_bound_checks) { + int error = 0; + int passed = pbkdf2_lower_bound_check_passed(saltlen, iter, keylen, + &error, NULL); + + if (!passed) { + ERR_raise(ERR_LIB_PROV, error); + return 0; + } + } +#endif + + hctx_tpl = HMAC_CTX_new(); + if (hctx_tpl == NULL) + return 0; + p = key; + tkeylen = keylen; + if (!HMAC_Init_ex(hctx_tpl, pass, passlen, digest, NULL)) + goto err; + hctx = HMAC_CTX_new(); + if (hctx == NULL) + goto err; + while (tkeylen) { + if (tkeylen > mdlen) + cplen = mdlen; + else + cplen = tkeylen; + /* + * We are unlikely to ever use more than 256 blocks (5120 bits!) but + * just in case... + */ + itmp[0] = (unsigned char)((i >> 24) & 0xff); + itmp[1] = (unsigned char)((i >> 16) & 0xff); + itmp[2] = (unsigned char)((i >> 8) & 0xff); + itmp[3] = (unsigned char)(i & 0xff); + if (!HMAC_CTX_copy(hctx, hctx_tpl)) + goto err; + if (!HMAC_Update(hctx, salt, saltlen) + || !HMAC_Update(hctx, itmp, 4) + || !HMAC_Final(hctx, digtmp, NULL)) + goto err; + memcpy(p, digtmp, cplen); + for (j = 1; j < iter; j++) { + if (!HMAC_CTX_copy(hctx, hctx_tpl)) + goto err; + if (!HMAC_Update(hctx, digtmp, mdlen) + || !HMAC_Final(hctx, digtmp, NULL)) + goto err; + for (k = 0; k < cplen; k++) + p[k] ^= digtmp[k]; + } + tkeylen -= cplen; + i++; + p += cplen; + } + ret = 1; + +err: + HMAC_CTX_free(hctx); + HMAC_CTX_free(hctx_tpl); + return ret; +} diff --git a/crypto/openssl/providers/implementations/kdfs/pbkdf2.h b/crypto/openssl/providers/implementations/kdfs/pbkdf2.h new file mode 100644 index 000000000000..7759c03136d5 --- /dev/null +++ b/crypto/openssl/providers/implementations/kdfs/pbkdf2.h @@ -0,0 +1,14 @@ +/* + * Copyright 2019-2021 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 + */ + +/* + * Available in pbkdfe_fips.c, and compiled with different values depending + * on we're in the FIPS module or not. + */ +extern const int ossl_kdf_pbkdf2_default_checks; diff --git a/crypto/openssl/providers/implementations/kdfs/pbkdf2_fips.c b/crypto/openssl/providers/implementations/kdfs/pbkdf2_fips.c new file mode 100644 index 000000000000..e43ef16455f1 --- /dev/null +++ b/crypto/openssl/providers/implementations/kdfs/pbkdf2_fips.c @@ -0,0 +1,20 @@ +/* + * Copyright 2019-2021 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 + */ + +#include "pbkdf2.h" + +/* + * For backwards compatibility reasons, + * Extra checks are done by default in fips mode only. + */ +#ifdef FIPS_MODULE +const int ossl_kdf_pbkdf2_default_checks = 1; +#else +const int ossl_kdf_pbkdf2_default_checks = 0; +#endif /* FIPS_MODULE */ diff --git a/crypto/openssl/providers/implementations/kdfs/pkcs12kdf.c b/crypto/openssl/providers/implementations/kdfs/pkcs12kdf.c new file mode 100644 index 000000000000..b4ca4fff475d --- /dev/null +++ b/crypto/openssl/providers/implementations/kdfs/pkcs12kdf.c @@ -0,0 +1,319 @@ +/* + * Copyright 1999-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 + */ + +#include <openssl/trace.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <openssl/evp.h> +#include <openssl/kdf.h> +#include <openssl/core_names.h> +#include <openssl/proverr.h> +#include "internal/cryptlib.h" +#include "internal/numbers.h" +#include "crypto/evp.h" +#include "prov/provider_ctx.h" +#include "prov/providercommon.h" +#include "prov/implementations.h" +#include "prov/provider_util.h" + +static OSSL_FUNC_kdf_newctx_fn kdf_pkcs12_new; +static OSSL_FUNC_kdf_dupctx_fn kdf_pkcs12_dup; +static OSSL_FUNC_kdf_freectx_fn kdf_pkcs12_free; +static OSSL_FUNC_kdf_reset_fn kdf_pkcs12_reset; +static OSSL_FUNC_kdf_derive_fn kdf_pkcs12_derive; +static OSSL_FUNC_kdf_settable_ctx_params_fn kdf_pkcs12_settable_ctx_params; +static OSSL_FUNC_kdf_set_ctx_params_fn kdf_pkcs12_set_ctx_params; +static OSSL_FUNC_kdf_gettable_ctx_params_fn kdf_pkcs12_gettable_ctx_params; +static OSSL_FUNC_kdf_get_ctx_params_fn kdf_pkcs12_get_ctx_params; + +typedef struct { + void *provctx; + PROV_DIGEST digest; + unsigned char *pass; + size_t pass_len; + unsigned char *salt; + size_t salt_len; + uint64_t iter; + int id; +} KDF_PKCS12; + +/* PKCS12 compatible key/IV generation */ + +static int pkcs12kdf_derive(const unsigned char *pass, size_t passlen, + const unsigned char *salt, size_t saltlen, + int id, uint64_t iter, const EVP_MD *md_type, + unsigned char *out, size_t n) +{ + unsigned char *B = NULL, *D = NULL, *I = NULL, *p = NULL, *Ai = NULL; + size_t Slen, Plen, Ilen; + size_t i, j, k, u, v; + uint64_t iter_cnt; + int ret = 0, ui, vi; + EVP_MD_CTX *ctx = NULL; + + ctx = EVP_MD_CTX_new(); + if (ctx == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_EVP_LIB); + goto end; + } + vi = EVP_MD_get_block_size(md_type); + ui = EVP_MD_get_size(md_type); + if (ui <= 0 || vi <= 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_SIZE); + goto end; + } + u = (size_t)ui; + v = (size_t)vi; + D = OPENSSL_malloc(v); + Ai = OPENSSL_malloc(u); + B = OPENSSL_malloc(v + 1); + Slen = v * ((saltlen + v - 1) / v); + if (passlen != 0) + Plen = v * ((passlen + v - 1) / v); + else + Plen = 0; + Ilen = Slen + Plen; + I = OPENSSL_malloc(Ilen); + if (D == NULL || Ai == NULL || B == NULL || I == NULL) + goto end; + for (i = 0; i < v; i++) + D[i] = id; + p = I; + for (i = 0; i < Slen; i++) + *p++ = salt[i % saltlen]; + for (i = 0; i < Plen; i++) + *p++ = pass[i % passlen]; + for (;;) { + if (!EVP_DigestInit_ex(ctx, md_type, NULL) + || !EVP_DigestUpdate(ctx, D, v) + || !EVP_DigestUpdate(ctx, I, Ilen) + || !EVP_DigestFinal_ex(ctx, Ai, NULL)) + goto end; + for (iter_cnt = 1; iter_cnt < iter; iter_cnt++) { + if (!EVP_DigestInit_ex(ctx, md_type, NULL) + || !EVP_DigestUpdate(ctx, Ai, u) + || !EVP_DigestFinal_ex(ctx, Ai, NULL)) + goto end; + } + memcpy(out, Ai, n < u ? n : u); + if (u >= n) { + ret = 1; + break; + } + n -= u; + out += u; + for (j = 0; j < v; j++) + B[j] = Ai[j % u]; + for (j = 0; j < Ilen; j += v) { + unsigned char *Ij = I + j; + uint16_t c = 1; + + /* Work out Ij = Ij + B + 1 */ + for (k = v; k > 0;) { + k--; + c += Ij[k] + B[k]; + Ij[k] = (unsigned char)c; + c >>= 8; + } + } + } + + end: + OPENSSL_free(Ai); + OPENSSL_free(B); + OPENSSL_free(D); + OPENSSL_free(I); + EVP_MD_CTX_free(ctx); + return ret; +} + +static void *kdf_pkcs12_new(void *provctx) +{ + KDF_PKCS12 *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + ctx = OPENSSL_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + ctx->provctx = provctx; + return ctx; +} + +static void kdf_pkcs12_cleanup(KDF_PKCS12 *ctx) +{ + ossl_prov_digest_reset(&ctx->digest); + OPENSSL_free(ctx->salt); + OPENSSL_clear_free(ctx->pass, ctx->pass_len); + memset(ctx, 0, sizeof(*ctx)); +} + +static void kdf_pkcs12_free(void *vctx) +{ + KDF_PKCS12 *ctx = (KDF_PKCS12 *)vctx; + + if (ctx != NULL) { + kdf_pkcs12_cleanup(ctx); + OPENSSL_free(ctx); + } +} + +static void kdf_pkcs12_reset(void *vctx) +{ + KDF_PKCS12 *ctx = (KDF_PKCS12 *)vctx; + void *provctx = ctx->provctx; + + kdf_pkcs12_cleanup(ctx); + ctx->provctx = provctx; +} + +static void *kdf_pkcs12_dup(void *vctx) +{ + const KDF_PKCS12 *src = (const KDF_PKCS12 *)vctx; + KDF_PKCS12 *dest; + + dest = kdf_pkcs12_new(src->provctx); + if (dest != NULL) { + if (!ossl_prov_memdup(src->salt, src->salt_len, + &dest->salt, &dest->salt_len) + || !ossl_prov_memdup(src->pass, src->pass_len, + &dest->pass , &dest->pass_len) + || !ossl_prov_digest_copy(&dest->digest, &src->digest)) + goto err; + dest->iter = src->iter; + dest->id = src->id; + } + return dest; + + err: + kdf_pkcs12_free(dest); + return NULL; +} + +static int pkcs12kdf_set_membuf(unsigned char **buffer, size_t *buflen, + const OSSL_PARAM *p) +{ + OPENSSL_clear_free(*buffer, *buflen); + *buffer = NULL; + *buflen = 0; + + if (p->data_size == 0) { + if ((*buffer = OPENSSL_malloc(1)) == NULL) + return 0; + } else if (p->data != NULL) { + if (!OSSL_PARAM_get_octet_string(p, (void **)buffer, 0, buflen)) + return 0; + } + return 1; +} + +static int kdf_pkcs12_derive(void *vctx, unsigned char *key, size_t keylen, + const OSSL_PARAM params[]) +{ + KDF_PKCS12 *ctx = (KDF_PKCS12 *)vctx; + const EVP_MD *md; + + if (!ossl_prov_is_running() || !kdf_pkcs12_set_ctx_params(ctx, params)) + return 0; + + if (ctx->pass == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_PASS); + return 0; + } + + if (ctx->salt == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_SALT); + return 0; + } + + md = ossl_prov_digest_md(&ctx->digest); + return pkcs12kdf_derive(ctx->pass, ctx->pass_len, ctx->salt, ctx->salt_len, + ctx->id, ctx->iter, md, key, keylen); +} + +static int kdf_pkcs12_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p; + KDF_PKCS12 *ctx = vctx; + OSSL_LIB_CTX *provctx = PROV_LIBCTX_OF(ctx->provctx); + + if (ossl_param_is_empty(params)) + return 1; + + if (!ossl_prov_digest_load_from_params(&ctx->digest, params, provctx)) + return 0; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PASSWORD)) != NULL) + if (!pkcs12kdf_set_membuf(&ctx->pass, &ctx->pass_len, p)) + return 0; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SALT)) != NULL) + if (!pkcs12kdf_set_membuf(&ctx->salt, &ctx->salt_len, p)) + return 0; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PKCS12_ID)) != NULL) + if (!OSSL_PARAM_get_int(p, &ctx->id)) + return 0; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_ITER)) != NULL) + if (!OSSL_PARAM_get_uint64(p, &ctx->iter)) + return 0; + return 1; +} + +static const OSSL_PARAM *kdf_pkcs12_settable_ctx_params( + ossl_unused void *ctx, ossl_unused void *provctx) +{ + static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_PASSWORD, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, NULL, 0), + OSSL_PARAM_uint64(OSSL_KDF_PARAM_ITER, NULL), + OSSL_PARAM_int(OSSL_KDF_PARAM_PKCS12_ID, NULL), + OSSL_PARAM_END + }; + return known_settable_ctx_params; +} + +static int kdf_pkcs12_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + OSSL_PARAM *p; + + 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_pkcs12_gettable_ctx_params( + ossl_unused void *ctx, ossl_unused void *provctx) +{ + 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_pkcs12_functions[] = { + { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_pkcs12_new }, + { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))kdf_pkcs12_dup }, + { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_pkcs12_free }, + { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_pkcs12_reset }, + { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_pkcs12_derive }, + { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS, + (void(*)(void))kdf_pkcs12_settable_ctx_params }, + { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_pkcs12_set_ctx_params }, + { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS, + (void(*)(void))kdf_pkcs12_gettable_ctx_params }, + { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_pkcs12_get_ctx_params }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/kdfs/pvkkdf.c b/crypto/openssl/providers/implementations/kdfs/pvkkdf.c new file mode 100644 index 000000000000..f60092d77173 --- /dev/null +++ b/crypto/openssl/providers/implementations/kdfs/pvkkdf.c @@ -0,0 +1,248 @@ +/* + * Copyright 2018-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 + */ + +#include <openssl/evp.h> +#include <openssl/core_names.h> +#include <openssl/proverr.h> +#include <openssl/err.h> +#include "internal/numbers.h" /* SIZE_MAX */ +#include "prov/provider_ctx.h" +#include "prov/providercommon.h" +#include "prov/implementations.h" +#include "prov/provider_util.h" + +static OSSL_FUNC_kdf_newctx_fn kdf_pvk_new; +static OSSL_FUNC_kdf_dupctx_fn kdf_pvk_dup; +static OSSL_FUNC_kdf_freectx_fn kdf_pvk_free; +static OSSL_FUNC_kdf_reset_fn kdf_pvk_reset; +static OSSL_FUNC_kdf_derive_fn kdf_pvk_derive; +static OSSL_FUNC_kdf_settable_ctx_params_fn kdf_pvk_settable_ctx_params; +static OSSL_FUNC_kdf_set_ctx_params_fn kdf_pvk_set_ctx_params; +static OSSL_FUNC_kdf_gettable_ctx_params_fn kdf_pvk_gettable_ctx_params; +static OSSL_FUNC_kdf_get_ctx_params_fn kdf_pvk_get_ctx_params; + +typedef struct { + void *provctx; + unsigned char *pass; + size_t pass_len; + unsigned char *salt; + size_t salt_len; + PROV_DIGEST digest; +} KDF_PVK; + +static void kdf_pvk_init(KDF_PVK *ctx); + +static void *kdf_pvk_new(void *provctx) +{ + KDF_PVK *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + ctx = OPENSSL_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + ctx->provctx = provctx; + kdf_pvk_init(ctx); + return ctx; +} + +static void kdf_pvk_cleanup(KDF_PVK *ctx) +{ + ossl_prov_digest_reset(&ctx->digest); + OPENSSL_free(ctx->salt); + OPENSSL_clear_free(ctx->pass, ctx->pass_len); + OPENSSL_cleanse(ctx, sizeof(*ctx)); +} + +static void kdf_pvk_free(void *vctx) +{ + KDF_PVK *ctx = (KDF_PVK *)vctx; + + if (ctx != NULL) { + kdf_pvk_cleanup(ctx); + OPENSSL_free(ctx); + } +} + +static void *kdf_pvk_dup(void *vctx) +{ + const KDF_PVK *src = (const KDF_PVK *)vctx; + KDF_PVK *dest; + + dest = kdf_pvk_new(src->provctx); + if (dest != NULL) + if (!ossl_prov_memdup(src->salt, src->salt_len, + &dest->salt, &dest->salt_len) + || !ossl_prov_memdup(src->pass, src->pass_len, + &dest->pass , &dest->pass_len) + || !ossl_prov_digest_copy(&dest->digest, &src->digest)) + goto err; + return dest; + + err: + kdf_pvk_free(dest); + return NULL; +} + +static void kdf_pvk_reset(void *vctx) +{ + KDF_PVK *ctx = (KDF_PVK *)vctx; + void *provctx = ctx->provctx; + + kdf_pvk_cleanup(ctx); + ctx->provctx = provctx; + kdf_pvk_init(ctx); +} + +static void kdf_pvk_init(KDF_PVK *ctx) +{ + OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END }; + OSSL_LIB_CTX *provctx = PROV_LIBCTX_OF(ctx->provctx); + + params[0] = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, + SN_sha1, 0); + if (!ossl_prov_digest_load_from_params(&ctx->digest, params, provctx)) + /* This is an error, but there is no way to indicate such directly */ + ossl_prov_digest_reset(&ctx->digest); +} + +static int pvk_set_membuf(unsigned char **buffer, size_t *buflen, + const OSSL_PARAM *p) +{ + OPENSSL_clear_free(*buffer, *buflen); + *buffer = NULL; + *buflen = 0; + + if (p->data_size == 0) { + if ((*buffer = OPENSSL_malloc(1)) == NULL) + return 0; + } else if (p->data != NULL) { + if (!OSSL_PARAM_get_octet_string(p, (void **)buffer, 0, buflen)) + return 0; + } + return 1; +} + +static int kdf_pvk_derive(void *vctx, unsigned char *key, size_t keylen, + const OSSL_PARAM params[]) +{ + KDF_PVK *ctx = (KDF_PVK *)vctx; + const EVP_MD *md; + EVP_MD_CTX *mctx; + int res; + + if (!ossl_prov_is_running() || !kdf_pvk_set_ctx_params(ctx, params)) + return 0; + + if (ctx->pass == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_PASS); + return 0; + } + + if (ctx->salt == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_SALT); + return 0; + } + + md = ossl_prov_digest_md(&ctx->digest); + if (md == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST); + return 0; + } + res = EVP_MD_get_size(md); + if (res <= 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_BAD_LENGTH); + return 0; + } + if ((size_t)res > keylen) { + ERR_raise(ERR_LIB_PROV, PROV_R_LENGTH_TOO_LARGE); + return 0; + } + + mctx = EVP_MD_CTX_new(); + res = mctx != NULL + && EVP_DigestInit_ex(mctx, md, NULL) + && EVP_DigestUpdate(mctx, ctx->salt, ctx->salt_len) + && EVP_DigestUpdate(mctx, ctx->pass, ctx->pass_len) + && EVP_DigestFinal_ex(mctx, key, NULL); + EVP_MD_CTX_free(mctx); + return res; +} + +static int kdf_pvk_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p; + KDF_PVK *ctx = vctx; + OSSL_LIB_CTX *provctx = PROV_LIBCTX_OF(ctx->provctx); + + if (ossl_param_is_empty(params)) + return 1; + + if (!ossl_prov_digest_load_from_params(&ctx->digest, params, provctx)) + return 0; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PASSWORD)) != NULL) + if (!pvk_set_membuf(&ctx->pass, &ctx->pass_len, p)) + return 0; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SALT)) != NULL) { + if (!pvk_set_membuf(&ctx->salt, &ctx->salt_len, p)) + return 0; + } + + return 1; +} + +static const OSSL_PARAM *kdf_pvk_settable_ctx_params(ossl_unused void *ctx, + ossl_unused void *p_ctx) +{ + static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_PASSWORD, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, NULL, 0), + OSSL_PARAM_END + }; + return known_settable_ctx_params; +} + +static int kdf_pvk_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + OSSL_PARAM *p; + + 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_pvk_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_pvk_functions[] = { + { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_pvk_new }, + { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))kdf_pvk_dup }, + { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_pvk_free }, + { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_pvk_reset }, + { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_pvk_derive }, + { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS, + (void(*)(void))kdf_pvk_settable_ctx_params }, + { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_pvk_set_ctx_params }, + { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS, + (void(*)(void))kdf_pvk_gettable_ctx_params }, + { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_pvk_get_ctx_params }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/kdfs/scrypt.c b/crypto/openssl/providers/implementations/kdfs/scrypt.c new file mode 100644 index 000000000000..e27b09eb99a4 --- /dev/null +++ b/crypto/openssl/providers/implementations/kdfs/scrypt.c @@ -0,0 +1,549 @@ +/* + * Copyright 2017-2025 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 + */ + +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <openssl/evp.h> +#include <openssl/kdf.h> +#include <openssl/err.h> +#include <openssl/core_names.h> +#include <openssl/proverr.h> +#include "crypto/evp.h" +#include "internal/numbers.h" +#include "prov/implementations.h" +#include "prov/provider_ctx.h" +#include "prov/providercommon.h" +#include "prov/provider_util.h" + +#ifndef OPENSSL_NO_SCRYPT + +static OSSL_FUNC_kdf_newctx_fn kdf_scrypt_new; +static OSSL_FUNC_kdf_dupctx_fn kdf_scrypt_dup; +static OSSL_FUNC_kdf_freectx_fn kdf_scrypt_free; +static OSSL_FUNC_kdf_reset_fn kdf_scrypt_reset; +static OSSL_FUNC_kdf_derive_fn kdf_scrypt_derive; +static OSSL_FUNC_kdf_settable_ctx_params_fn kdf_scrypt_settable_ctx_params; +static OSSL_FUNC_kdf_set_ctx_params_fn kdf_scrypt_set_ctx_params; +static OSSL_FUNC_kdf_gettable_ctx_params_fn kdf_scrypt_gettable_ctx_params; +static OSSL_FUNC_kdf_get_ctx_params_fn kdf_scrypt_get_ctx_params; + +static int scrypt_alg(const char *pass, size_t passlen, + const unsigned char *salt, size_t saltlen, + uint64_t N, uint64_t r, uint64_t p, uint64_t maxmem, + unsigned char *key, size_t keylen, EVP_MD *sha256, + OSSL_LIB_CTX *libctx, const char *propq); + +typedef struct { + OSSL_LIB_CTX *libctx; + char *propq; + unsigned char *pass; + size_t pass_len; + unsigned char *salt; + size_t salt_len; + uint64_t N; + uint64_t r, p; + uint64_t maxmem_bytes; + EVP_MD *sha256; +} KDF_SCRYPT; + +static void kdf_scrypt_init(KDF_SCRYPT *ctx); + +static void *kdf_scrypt_new_inner(OSSL_LIB_CTX *libctx) +{ + KDF_SCRYPT *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + ctx = OPENSSL_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + ctx->libctx = libctx; + kdf_scrypt_init(ctx); + return ctx; +} + +static void *kdf_scrypt_new(void *provctx) +{ + return kdf_scrypt_new_inner(PROV_LIBCTX_OF(provctx)); +} + +static void kdf_scrypt_free(void *vctx) +{ + KDF_SCRYPT *ctx = (KDF_SCRYPT *)vctx; + + if (ctx != NULL) { + OPENSSL_free(ctx->propq); + EVP_MD_free(ctx->sha256); + kdf_scrypt_reset(ctx); + OPENSSL_free(ctx); + } +} + +static void kdf_scrypt_reset(void *vctx) +{ + KDF_SCRYPT *ctx = (KDF_SCRYPT *)vctx; + + OPENSSL_free(ctx->salt); + ctx->salt = NULL; + OPENSSL_clear_free(ctx->pass, ctx->pass_len); + ctx->pass = NULL; + kdf_scrypt_init(ctx); +} + +static void *kdf_scrypt_dup(void *vctx) +{ + const KDF_SCRYPT *src = (const KDF_SCRYPT *)vctx; + KDF_SCRYPT *dest; + + dest = kdf_scrypt_new_inner(src->libctx); + if (dest != NULL) { + if (src->sha256 != NULL && !EVP_MD_up_ref(src->sha256)) + goto err; + if (src->propq != NULL) { + dest->propq = OPENSSL_strdup(src->propq); + if (dest->propq == NULL) + goto err; + } + if (!ossl_prov_memdup(src->salt, src->salt_len, + &dest->salt, &dest->salt_len) + || !ossl_prov_memdup(src->pass, src->pass_len, + &dest->pass , &dest->pass_len)) + goto err; + dest->N = src->N; + dest->r = src->r; + dest->p = src->p; + dest->maxmem_bytes = src->maxmem_bytes; + dest->sha256 = src->sha256; + } + return dest; + + err: + kdf_scrypt_free(dest); + return NULL; +} + +static void kdf_scrypt_init(KDF_SCRYPT *ctx) +{ + /* Default values are the most conservative recommendation given in the + * original paper of C. Percival. Derivation uses roughly 1 GiB of memory + * for this parameter choice (approx. 128 * r * N * p bytes). + */ + ctx->N = 1 << 20; + ctx->r = 8; + ctx->p = 1; + ctx->maxmem_bytes = 1025 * 1024 * 1024; +} + +static int scrypt_set_membuf(unsigned char **buffer, size_t *buflen, + const OSSL_PARAM *p) +{ + OPENSSL_clear_free(*buffer, *buflen); + *buffer = NULL; + *buflen = 0; + + if (p->data_size == 0) { + if ((*buffer = OPENSSL_malloc(1)) == NULL) + return 0; + } else if (p->data != NULL) { + if (!OSSL_PARAM_get_octet_string(p, (void **)buffer, 0, buflen)) + return 0; + } + return 1; +} + +static int set_digest(KDF_SCRYPT *ctx) +{ + EVP_MD_free(ctx->sha256); + ctx->sha256 = EVP_MD_fetch(ctx->libctx, "sha256", ctx->propq); + if (ctx->sha256 == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_UNABLE_TO_LOAD_SHA256); + return 0; + } + return 1; +} + +static int set_property_query(KDF_SCRYPT *ctx, const char *propq) +{ + OPENSSL_free(ctx->propq); + ctx->propq = NULL; + if (propq != NULL) { + ctx->propq = OPENSSL_strdup(propq); + if (ctx->propq == NULL) + return 0; + } + return 1; +} + +static int kdf_scrypt_derive(void *vctx, unsigned char *key, size_t keylen, + const OSSL_PARAM params[]) +{ + KDF_SCRYPT *ctx = (KDF_SCRYPT *)vctx; + + if (!ossl_prov_is_running() || !kdf_scrypt_set_ctx_params(ctx, params)) + return 0; + + if (ctx->pass == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_PASS); + return 0; + } + + if (ctx->salt == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_SALT); + return 0; + } + + if (ctx->sha256 == NULL && !set_digest(ctx)) + return 0; + + return scrypt_alg((char *)ctx->pass, ctx->pass_len, ctx->salt, + ctx->salt_len, ctx->N, ctx->r, ctx->p, + ctx->maxmem_bytes, key, keylen, ctx->sha256, + ctx->libctx, ctx->propq); +} + +static int is_power_of_two(uint64_t value) +{ + return (value != 0) && ((value & (value - 1)) == 0); +} + +static int kdf_scrypt_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p; + KDF_SCRYPT *ctx = vctx; + uint64_t u64_value; + + if (ossl_param_is_empty(params)) + return 1; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PASSWORD)) != NULL) + if (!scrypt_set_membuf(&ctx->pass, &ctx->pass_len, p)) + return 0; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SALT)) != NULL) + if (!scrypt_set_membuf(&ctx->salt, &ctx->salt_len, p)) + return 0; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SCRYPT_N)) + != NULL) { + if (!OSSL_PARAM_get_uint64(p, &u64_value) + || u64_value <= 1 + || !is_power_of_two(u64_value)) + return 0; + ctx->N = u64_value; + } + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SCRYPT_R)) + != NULL) { + if (!OSSL_PARAM_get_uint64(p, &u64_value) || u64_value < 1) + return 0; + ctx->r = u64_value; + } + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SCRYPT_P)) + != NULL) { + if (!OSSL_PARAM_get_uint64(p, &u64_value) || u64_value < 1) + return 0; + ctx->p = u64_value; + } + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SCRYPT_MAXMEM)) + != NULL) { + if (!OSSL_PARAM_get_uint64(p, &u64_value) || u64_value < 1) + return 0; + ctx->maxmem_bytes = u64_value; + } + + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PROPERTIES); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_UTF8_STRING + || !set_property_query(ctx, p->data) + || !set_digest(ctx)) + return 0; + } + return 1; +} + +static const OSSL_PARAM *kdf_scrypt_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_uint64(OSSL_KDF_PARAM_SCRYPT_N, NULL), + OSSL_PARAM_uint32(OSSL_KDF_PARAM_SCRYPT_R, NULL), + OSSL_PARAM_uint32(OSSL_KDF_PARAM_SCRYPT_P, NULL), + OSSL_PARAM_uint64(OSSL_KDF_PARAM_SCRYPT_MAXMEM, NULL), + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_END + }; + return known_settable_ctx_params; +} + +static int kdf_scrypt_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + OSSL_PARAM *p; + + 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_scrypt_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_scrypt_functions[] = { + { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_scrypt_new }, + { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))kdf_scrypt_dup }, + { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_scrypt_free }, + { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_scrypt_reset }, + { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_scrypt_derive }, + { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS, + (void(*)(void))kdf_scrypt_settable_ctx_params }, + { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_scrypt_set_ctx_params }, + { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS, + (void(*)(void))kdf_scrypt_gettable_ctx_params }, + { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_scrypt_get_ctx_params }, + OSSL_DISPATCH_END +}; + +#define R(a,b) (((a) << (b)) | ((a) >> (32 - (b)))) +static void salsa208_word_specification(uint32_t inout[16]) +{ + int i; + uint32_t x[16]; + + memcpy(x, inout, sizeof(x)); + for (i = 8; i > 0; i -= 2) { + x[4] ^= R(x[0] + x[12], 7); + x[8] ^= R(x[4] + x[0], 9); + x[12] ^= R(x[8] + x[4], 13); + x[0] ^= R(x[12] + x[8], 18); + x[9] ^= R(x[5] + x[1], 7); + x[13] ^= R(x[9] + x[5], 9); + x[1] ^= R(x[13] + x[9], 13); + x[5] ^= R(x[1] + x[13], 18); + x[14] ^= R(x[10] + x[6], 7); + x[2] ^= R(x[14] + x[10], 9); + x[6] ^= R(x[2] + x[14], 13); + x[10] ^= R(x[6] + x[2], 18); + x[3] ^= R(x[15] + x[11], 7); + x[7] ^= R(x[3] + x[15], 9); + x[11] ^= R(x[7] + x[3], 13); + x[15] ^= R(x[11] + x[7], 18); + x[1] ^= R(x[0] + x[3], 7); + x[2] ^= R(x[1] + x[0], 9); + x[3] ^= R(x[2] + x[1], 13); + x[0] ^= R(x[3] + x[2], 18); + x[6] ^= R(x[5] + x[4], 7); + x[7] ^= R(x[6] + x[5], 9); + x[4] ^= R(x[7] + x[6], 13); + x[5] ^= R(x[4] + x[7], 18); + x[11] ^= R(x[10] + x[9], 7); + x[8] ^= R(x[11] + x[10], 9); + x[9] ^= R(x[8] + x[11], 13); + x[10] ^= R(x[9] + x[8], 18); + x[12] ^= R(x[15] + x[14], 7); + x[13] ^= R(x[12] + x[15], 9); + x[14] ^= R(x[13] + x[12], 13); + x[15] ^= R(x[14] + x[13], 18); + } + for (i = 0; i < 16; ++i) + inout[i] += x[i]; + OPENSSL_cleanse(x, sizeof(x)); +} + +static void scryptBlockMix(uint32_t *B_, uint32_t *B, uint64_t r) +{ + uint64_t i, j; + uint32_t X[16], *pB; + + memcpy(X, B + (r * 2 - 1) * 16, sizeof(X)); + pB = B; + for (i = 0; i < r * 2; i++) { + for (j = 0; j < 16; j++) + X[j] ^= *pB++; + salsa208_word_specification(X); + memcpy(B_ + (i / 2 + (i & 1) * r) * 16, X, sizeof(X)); + } + OPENSSL_cleanse(X, sizeof(X)); +} + +static void scryptROMix(unsigned char *B, uint64_t r, uint64_t N, + uint32_t *X, uint32_t *T, uint32_t *V) +{ + unsigned char *pB; + uint32_t *pV; + uint64_t i, k; + + /* Convert from little endian input */ + for (pV = V, i = 0, pB = B; i < 32 * r; i++, pV++) { + *pV = *pB++; + *pV |= *pB++ << 8; + *pV |= *pB++ << 16; + *pV |= (uint32_t)*pB++ << 24; + } + + for (i = 1; i < N; i++, pV += 32 * r) + scryptBlockMix(pV, pV - 32 * r, r); + + scryptBlockMix(X, V + (N - 1) * 32 * r, r); + + for (i = 0; i < N; i++) { + uint32_t j; + j = X[16 * (2 * r - 1)] % N; + pV = V + 32 * r * j; + for (k = 0; k < 32 * r; k++) + T[k] = X[k] ^ *pV++; + scryptBlockMix(X, T, r); + } + /* Convert output to little endian */ + for (i = 0, pB = B; i < 32 * r; i++) { + uint32_t xtmp = X[i]; + *pB++ = xtmp & 0xff; + *pB++ = (xtmp >> 8) & 0xff; + *pB++ = (xtmp >> 16) & 0xff; + *pB++ = (xtmp >> 24) & 0xff; + } +} + +#ifndef SIZE_MAX +# define SIZE_MAX ((size_t)-1) +#endif + +/* + * Maximum power of two that will fit in uint64_t: this should work on + * most (all?) platforms. + */ + +#define LOG2_UINT64_MAX (sizeof(uint64_t) * 8 - 1) + +/* + * Maximum value of p * r: + * p <= ((2^32-1) * hLen) / MFLen => + * p <= ((2^32-1) * 32) / (128 * r) => + * p * r <= (2^30-1) + */ + +#define SCRYPT_PR_MAX ((1 << 30) - 1) + +static int scrypt_alg(const char *pass, size_t passlen, + const unsigned char *salt, size_t saltlen, + uint64_t N, uint64_t r, uint64_t p, uint64_t maxmem, + unsigned char *key, size_t keylen, EVP_MD *sha256, + OSSL_LIB_CTX *libctx, const char *propq) +{ + int rv = 0; + unsigned char *B; + uint32_t *X, *V, *T; + uint64_t i, Blen, Vlen; + + /* Sanity check parameters */ + /* initial check, r,p must be non zero, N >= 2 and a power of 2 */ + if (r == 0 || p == 0 || N < 2 || (N & (N - 1))) + return 0; + /* Check p * r < SCRYPT_PR_MAX avoiding overflow */ + if (p > SCRYPT_PR_MAX / r) { + ERR_raise(ERR_LIB_EVP, EVP_R_MEMORY_LIMIT_EXCEEDED); + return 0; + } + + /* + * Need to check N: if 2^(128 * r / 8) overflows limit this is + * automatically satisfied since N <= UINT64_MAX. + */ + + if (16 * r <= LOG2_UINT64_MAX) { + if (N >= (((uint64_t)1) << (16 * r))) { + ERR_raise(ERR_LIB_EVP, EVP_R_MEMORY_LIMIT_EXCEEDED); + return 0; + } + } + + /* Memory checks: check total allocated buffer size fits in uint64_t */ + + /* + * B size in section 5 step 1.S + * Note: we know p * 128 * r < UINT64_MAX because we already checked + * p * r < SCRYPT_PR_MAX + */ + Blen = p * 128 * r; + /* + * Yet we pass it as integer to PKCS5_PBKDF2_HMAC... [This would + * have to be revised when/if PKCS5_PBKDF2_HMAC accepts size_t.] + */ + if (Blen > INT_MAX) { + ERR_raise(ERR_LIB_EVP, EVP_R_MEMORY_LIMIT_EXCEEDED); + return 0; + } + + /* + * Check 32 * r * (N + 2) * sizeof(uint32_t) fits in uint64_t + * This is combined size V, X and T (section 4) + */ + i = UINT64_MAX / (32 * sizeof(uint32_t)); + if (N + 2 > i / r) { + ERR_raise(ERR_LIB_EVP, EVP_R_MEMORY_LIMIT_EXCEEDED); + return 0; + } + Vlen = 32 * r * (N + 2) * sizeof(uint32_t); + + /* check total allocated size fits in uint64_t */ + if (Blen > UINT64_MAX - Vlen) { + ERR_raise(ERR_LIB_EVP, EVP_R_MEMORY_LIMIT_EXCEEDED); + return 0; + } + + /* Check that the maximum memory doesn't exceed a size_t limits */ + if (maxmem > SIZE_MAX) + maxmem = SIZE_MAX; + + if (Blen + Vlen > maxmem) { + ERR_raise(ERR_LIB_EVP, EVP_R_MEMORY_LIMIT_EXCEEDED); + return 0; + } + + /* If no key return to indicate parameters are OK */ + if (key == NULL) + return 1; + + B = OPENSSL_malloc((size_t)(Blen + Vlen)); + if (B == NULL) + return 0; + X = (uint32_t *)(B + Blen); + T = X + 32 * r; + V = T + 32 * r; + if (ossl_pkcs5_pbkdf2_hmac_ex(pass, passlen, salt, saltlen, 1, sha256, + (int)Blen, B, libctx, propq) == 0) + goto err; + + for (i = 0; i < p; i++) + scryptROMix(B + 128 * r * i, r, N, X, T, V); + + if (ossl_pkcs5_pbkdf2_hmac_ex(pass, passlen, B, (int)Blen, 1, sha256, + keylen, key, libctx, propq) == 0) + goto err; + rv = 1; + err: + if (rv == 0) + ERR_raise(ERR_LIB_EVP, EVP_R_PBKDF2_ERROR); + + OPENSSL_clear_free(B, (size_t)(Blen + Vlen)); + return rv; +} + +#endif diff --git a/crypto/openssl/providers/implementations/kdfs/sshkdf.c b/crypto/openssl/providers/implementations/kdfs/sshkdf.c new file mode 100644 index 000000000000..4a9b141320bf --- /dev/null +++ b/crypto/openssl/providers/implementations/kdfs/sshkdf.c @@ -0,0 +1,411 @@ +/* + * Copyright 2018-2024 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 + */ + +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <openssl/evp.h> +#include <openssl/kdf.h> +#include <openssl/core_names.h> +#include <openssl/proverr.h> +#include "internal/cryptlib.h" +#include "internal/numbers.h" +#include "crypto/evp.h" +#include "prov/provider_ctx.h" +#include "prov/providercommon.h" +#include "prov/implementations.h" +#include "prov/provider_util.h" +#include "prov/securitycheck.h" + +/* See RFC 4253, Section 7.2 */ +static OSSL_FUNC_kdf_newctx_fn kdf_sshkdf_new; +static OSSL_FUNC_kdf_dupctx_fn kdf_sshkdf_dup; +static OSSL_FUNC_kdf_freectx_fn kdf_sshkdf_free; +static OSSL_FUNC_kdf_reset_fn kdf_sshkdf_reset; +static OSSL_FUNC_kdf_derive_fn kdf_sshkdf_derive; +static OSSL_FUNC_kdf_settable_ctx_params_fn kdf_sshkdf_settable_ctx_params; +static OSSL_FUNC_kdf_set_ctx_params_fn kdf_sshkdf_set_ctx_params; +static OSSL_FUNC_kdf_gettable_ctx_params_fn kdf_sshkdf_gettable_ctx_params; +static OSSL_FUNC_kdf_get_ctx_params_fn kdf_sshkdf_get_ctx_params; + +static int SSHKDF(const EVP_MD *evp_md, + const unsigned char *key, size_t key_len, + const unsigned char *xcghash, size_t xcghash_len, + const unsigned char *session_id, size_t session_id_len, + char type, unsigned char *okey, size_t okey_len); + +typedef struct { + void *provctx; + PROV_DIGEST digest; + unsigned char *key; /* K */ + size_t key_len; + unsigned char *xcghash; /* H */ + size_t xcghash_len; + char type; /* X */ + unsigned char *session_id; + size_t session_id_len; + OSSL_FIPS_IND_DECLARE +} KDF_SSHKDF; + +static void *kdf_sshkdf_new(void *provctx) +{ + KDF_SSHKDF *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) != NULL) { + ctx->provctx = provctx; + OSSL_FIPS_IND_INIT(ctx) + } + return ctx; +} + +static void kdf_sshkdf_free(void *vctx) +{ + KDF_SSHKDF *ctx = (KDF_SSHKDF *)vctx; + + if (ctx != NULL) { + kdf_sshkdf_reset(ctx); + OPENSSL_free(ctx); + } +} + +static void kdf_sshkdf_reset(void *vctx) +{ + KDF_SSHKDF *ctx = (KDF_SSHKDF *)vctx; + void *provctx = ctx->provctx; + + ossl_prov_digest_reset(&ctx->digest); + OPENSSL_clear_free(ctx->key, ctx->key_len); + OPENSSL_clear_free(ctx->xcghash, ctx->xcghash_len); + OPENSSL_clear_free(ctx->session_id, ctx->session_id_len); + memset(ctx, 0, sizeof(*ctx)); + ctx->provctx = provctx; +} + +static void *kdf_sshkdf_dup(void *vctx) +{ + const KDF_SSHKDF *src = (const KDF_SSHKDF *)vctx; + KDF_SSHKDF *dest; + + dest = kdf_sshkdf_new(src->provctx); + if (dest != NULL) { + if (!ossl_prov_memdup(src->key, src->key_len, + &dest->key, &dest->key_len) + || !ossl_prov_memdup(src->xcghash, src->xcghash_len, + &dest->xcghash , &dest->xcghash_len) + || !ossl_prov_memdup(src->session_id, src->session_id_len, + &dest->session_id , &dest->session_id_len) + || !ossl_prov_digest_copy(&dest->digest, &src->digest)) + goto err; + dest->type = src->type; + OSSL_FIPS_IND_COPY(dest, src) + } + return dest; + + err: + kdf_sshkdf_free(dest); + return NULL; +} + +static int sshkdf_set_membuf(unsigned char **dst, size_t *dst_len, + const OSSL_PARAM *p) +{ + OPENSSL_clear_free(*dst, *dst_len); + *dst = NULL; + *dst_len = 0; + return OSSL_PARAM_get_octet_string(p, (void **)dst, 0, dst_len); +} + +#ifdef FIPS_MODULE +static int fips_digest_check_passed(KDF_SSHKDF *ctx, const EVP_MD *md) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + /* + * Perform digest check + * + * According to NIST SP 800-135r1 section 5.2, the valid hash functions are + * specified in FIPS 180-3. ACVP also only lists the same set of hash + * functions. + */ + int digest_unapproved = !EVP_MD_is_a(md, SN_sha1) + && !EVP_MD_is_a(md, SN_sha224) + && !EVP_MD_is_a(md, SN_sha256) + && !EVP_MD_is_a(md, SN_sha384) + && !EVP_MD_is_a(md, SN_sha512); + + if (digest_unapproved) { + if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE0, + libctx, "SSHKDF", "Digest", + ossl_fips_config_sshkdf_digest_check)) { + ERR_raise(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED); + return 0; + } + } + return 1; +} + +static int fips_key_check_passed(KDF_SSHKDF *ctx) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + int key_approved = ossl_kdf_check_key_size(ctx->key_len); + + if (!key_approved) { + if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE1, + libctx, "SSHKDF", "Key size", + ossl_fips_config_sshkdf_key_check)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + } + return 1; +} +#endif + +static int kdf_sshkdf_derive(void *vctx, unsigned char *key, size_t keylen, + const OSSL_PARAM params[]) +{ + KDF_SSHKDF *ctx = (KDF_SSHKDF *)vctx; + const EVP_MD *md; + + if (!ossl_prov_is_running() || !kdf_sshkdf_set_ctx_params(ctx, params)) + return 0; + + md = ossl_prov_digest_md(&ctx->digest); + if (md == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST); + return 0; + } + if (ctx->key == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + return 0; + } + if (ctx->xcghash == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_XCGHASH); + return 0; + } + if (ctx->session_id == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_SESSION_ID); + return 0; + } + if (ctx->type == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_TYPE); + return 0; + } + + return SSHKDF(md, ctx->key, ctx->key_len, + ctx->xcghash, ctx->xcghash_len, + ctx->session_id, ctx->session_id_len, + ctx->type, key, keylen); +} + +static int kdf_sshkdf_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p; + KDF_SSHKDF *ctx = vctx; + OSSL_LIB_CTX *provctx = PROV_LIBCTX_OF(ctx->provctx); + + if (ossl_param_is_empty(params)) + return 1; + + if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE0, params, + OSSL_KDF_PARAM_FIPS_DIGEST_CHECK)) + return 0; + if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE1, params, + OSSL_KDF_PARAM_FIPS_KEY_CHECK)) + return 0; + + if (OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_DIGEST) != NULL) { + const EVP_MD *md = NULL; + + if (!ossl_prov_digest_load_from_params(&ctx->digest, params, provctx)) + return 0; + + md = ossl_prov_digest_md(&ctx->digest); + if (EVP_MD_xof(md)) { + ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED); + return 0; + } + +#ifdef FIPS_MODULE + if (!fips_digest_check_passed(ctx, md)) + return 0; +#endif + } + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KEY)) != NULL) { + if (!sshkdf_set_membuf(&ctx->key, &ctx->key_len, p)) + return 0; + +#ifdef FIPS_MODULE + if (!fips_key_check_passed(ctx)) + return 0; +#endif + } + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SSHKDF_XCGHASH)) + != NULL) + if (!sshkdf_set_membuf(&ctx->xcghash, &ctx->xcghash_len, p)) + return 0; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SSHKDF_SESSION_ID)) + != NULL) + if (!sshkdf_set_membuf(&ctx->session_id, &ctx->session_id_len, p)) + return 0; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SSHKDF_TYPE)) + != NULL) { + const char *kdftype; + + if (!OSSL_PARAM_get_utf8_string_ptr(p, &kdftype)) + return 0; + /* Expect one character (byte in this case) */ + if (kdftype == NULL || p->data_size != 1) + return 0; + if (kdftype[0] < 65 || kdftype[0] > 70) { + ERR_raise(ERR_LIB_PROV, PROV_R_VALUE_ERROR); + return 0; + } + ctx->type = kdftype[0]; + } + return 1; +} + +static const OSSL_PARAM *kdf_sshkdf_settable_ctx_params(ossl_unused void *ctx, + ossl_unused void *p_ctx) +{ + static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_KEY, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SSHKDF_XCGHASH, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SSHKDF_SESSION_ID, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_SSHKDF_TYPE, NULL, 0), + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_DIGEST_CHECK) + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_KEY_CHECK) + OSSL_PARAM_END + }; + return known_settable_ctx_params; +} + +static int kdf_sshkdf_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + OSSL_PARAM *p; + + if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL) { + if (!OSSL_PARAM_set_size_t(p, SIZE_MAX)) + return 0; + } + if (!OSSL_FIPS_IND_GET_CTX_PARAM(((KDF_SSHKDF *)vctx), params)) + return 0; + return 1; +} + +static const OSSL_PARAM *kdf_sshkdf_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_FIPS_IND_GETTABLE_CTX_PARAM() + OSSL_PARAM_END + }; + return known_gettable_ctx_params; +} + +const OSSL_DISPATCH ossl_kdf_sshkdf_functions[] = { + { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_sshkdf_new }, + { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))kdf_sshkdf_dup }, + { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_sshkdf_free }, + { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_sshkdf_reset }, + { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_sshkdf_derive }, + { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS, + (void(*)(void))kdf_sshkdf_settable_ctx_params }, + { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_sshkdf_set_ctx_params }, + { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS, + (void(*)(void))kdf_sshkdf_gettable_ctx_params }, + { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_sshkdf_get_ctx_params }, + OSSL_DISPATCH_END +}; + +static int SSHKDF(const EVP_MD *evp_md, + const unsigned char *key, size_t key_len, + const unsigned char *xcghash, size_t xcghash_len, + const unsigned char *session_id, size_t session_id_len, + char type, unsigned char *okey, size_t okey_len) +{ + EVP_MD_CTX *md = NULL; + unsigned char digest[EVP_MAX_MD_SIZE]; + unsigned int dsize = 0; + size_t cursize = 0; + int ret = 0; + + md = EVP_MD_CTX_new(); + if (md == NULL) + return 0; + + if (!EVP_DigestInit_ex(md, evp_md, NULL)) + goto out; + + if (!EVP_DigestUpdate(md, key, key_len)) + goto out; + + if (!EVP_DigestUpdate(md, xcghash, xcghash_len)) + goto out; + + if (!EVP_DigestUpdate(md, &type, 1)) + goto out; + + if (!EVP_DigestUpdate(md, session_id, session_id_len)) + goto out; + + if (!EVP_DigestFinal_ex(md, digest, &dsize)) + goto out; + + if (okey_len < dsize) { + memcpy(okey, digest, okey_len); + ret = 1; + goto out; + } + + memcpy(okey, digest, dsize); + + for (cursize = dsize; cursize < okey_len; cursize += dsize) { + + if (!EVP_DigestInit_ex(md, evp_md, NULL)) + goto out; + + if (!EVP_DigestUpdate(md, key, key_len)) + goto out; + + if (!EVP_DigestUpdate(md, xcghash, xcghash_len)) + goto out; + + if (!EVP_DigestUpdate(md, okey, cursize)) + goto out; + + if (!EVP_DigestFinal_ex(md, digest, &dsize)) + goto out; + + if (okey_len < cursize + dsize) { + memcpy(okey + cursize, digest, okey_len - cursize); + ret = 1; + goto out; + } + + memcpy(okey + cursize, digest, dsize); + } + + ret = 1; + +out: + EVP_MD_CTX_free(md); + OPENSSL_cleanse(digest, EVP_MAX_MD_SIZE); + return ret; +} diff --git a/crypto/openssl/providers/implementations/kdfs/sskdf.c b/crypto/openssl/providers/implementations/kdfs/sskdf.c new file mode 100644 index 000000000000..3c377db0058e --- /dev/null +++ b/crypto/openssl/providers/implementations/kdfs/sskdf.c @@ -0,0 +1,786 @@ +/* + * Copyright 2019-2024 The OpenSSL Project Authors. All Rights Reserved. + * Copyright (c) 2019, Oracle and/or its affiliates. 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 + */ + +/* + * Refer to https://csrc.nist.gov/publications/detail/sp/800-56c/rev-1/final + * Section 4.1. + * + * The Single Step KDF algorithm is given by: + * + * Result(0) = empty bit string (i.e., the null string). + * For i = 1 to reps, do the following: + * Increment counter by 1. + * Result(i) = Result(i - 1) || H(counter || Z || FixedInfo). + * DKM = LeftmostBits(Result(reps), L)) + * + * NOTES: + * Z is a shared secret required to produce the derived key material. + * counter is a 4 byte buffer. + * FixedInfo is a bit string containing context specific data. + * DKM is the output derived key material. + * L is the required size of the DKM. + * reps = [L / H_outputBits] + * H(x) is the auxiliary function that can be either a hash, HMAC or KMAC. + * H_outputBits is the length of the output of the auxiliary function H(x). + * + * Currently there is not a comprehensive list of test vectors for this + * algorithm, especially for H(x) = HMAC and H(x) = KMAC. + * Test vectors for H(x) = Hash are indirectly used by CAVS KAS tests. + */ +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <openssl/hmac.h> +#include <openssl/evp.h> +#include <openssl/kdf.h> +#include <openssl/core_names.h> +#include <openssl/params.h> +#include <openssl/proverr.h> +#include "internal/cryptlib.h" +#include "internal/numbers.h" +#include "crypto/evp.h" +#include "prov/provider_ctx.h" +#include "prov/providercommon.h" +#include "prov/implementations.h" +#include "prov/provider_util.h" +#include "prov/securitycheck.h" +#include "internal/params.h" + +typedef struct { + void *provctx; + EVP_MAC_CTX *macctx; /* H(x) = HMAC_hash OR H(x) = KMAC */ + PROV_DIGEST digest; /* H(x) = hash(x) */ + unsigned char *secret; + size_t secret_len; + unsigned char *info; + size_t info_len; + unsigned char *salt; + size_t salt_len; + size_t out_len; /* optional KMAC parameter */ + int is_kmac; + OSSL_FIPS_IND_DECLARE +} KDF_SSKDF; + +#define SSKDF_MAX_INLEN (1<<30) +#define SSKDF_KMAC128_DEFAULT_SALT_SIZE (168 - 4) +#define SSKDF_KMAC256_DEFAULT_SALT_SIZE (136 - 4) + +/* KMAC uses a Customisation string of 'KDF' */ +static const unsigned char kmac_custom_str[] = { 0x4B, 0x44, 0x46 }; + +static OSSL_FUNC_kdf_newctx_fn sskdf_new; +static OSSL_FUNC_kdf_dupctx_fn sskdf_dup; +static OSSL_FUNC_kdf_freectx_fn sskdf_free; +static OSSL_FUNC_kdf_reset_fn sskdf_reset; +static OSSL_FUNC_kdf_derive_fn sskdf_derive; +static OSSL_FUNC_kdf_settable_ctx_params_fn sskdf_settable_ctx_params; +static OSSL_FUNC_kdf_set_ctx_params_fn sskdf_set_ctx_params; +static OSSL_FUNC_kdf_gettable_ctx_params_fn sskdf_gettable_ctx_params; +static OSSL_FUNC_kdf_get_ctx_params_fn sskdf_get_ctx_params; +static OSSL_FUNC_kdf_derive_fn x963kdf_derive; +static OSSL_FUNC_kdf_settable_ctx_params_fn x963kdf_settable_ctx_params; +static OSSL_FUNC_kdf_set_ctx_params_fn x963kdf_set_ctx_params; +static OSSL_FUNC_kdf_gettable_ctx_params_fn x963kdf_gettable_ctx_params; +static OSSL_FUNC_kdf_get_ctx_params_fn x963kdf_get_ctx_params; + +/* Settable context parameters that are common across SSKDF and X963 KDF */ +#define SSKDF_COMMON_SETTABLES \ + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SECRET, NULL, 0), \ + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_KEY, NULL, 0), \ + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_INFO, NULL, 0), \ + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0), \ + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0), \ + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_MAC, NULL, 0), \ + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, NULL, 0), \ + OSSL_PARAM_size_t(OSSL_KDF_PARAM_MAC_SIZE, NULL) + +/* Gettable context parameters that are common across SSKDF and X963 KDF */ +#define SSKDF_COMMON_GETTABLES \ + OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL) + +/* + * Refer to https://csrc.nist.gov/publications/detail/sp/800-56c/rev-1/final + * Section 4. One-Step Key Derivation using H(x) = hash(x) + * Note: X9.63 also uses this code with the only difference being that the + * counter is appended to the secret 'z'. + * i.e. + * result[i] = Hash(counter || z || info) for One Step OR + * result[i] = Hash(z || counter || info) for X9.63. + */ +static int SSKDF_hash_kdm(const EVP_MD *kdf_md, + const unsigned char *z, size_t z_len, + const unsigned char *info, size_t info_len, + unsigned int append_ctr, + unsigned char *derived_key, size_t derived_key_len) +{ + int ret = 0, hlen; + size_t counter, out_len, len = derived_key_len; + unsigned char c[4]; + unsigned char mac[EVP_MAX_MD_SIZE]; + unsigned char *out = derived_key; + EVP_MD_CTX *ctx = NULL, *ctx_init = NULL; + + if (z_len > SSKDF_MAX_INLEN || info_len > SSKDF_MAX_INLEN + || derived_key_len > SSKDF_MAX_INLEN + || derived_key_len == 0) + return 0; + + hlen = EVP_MD_get_size(kdf_md); + if (hlen <= 0) + return 0; + out_len = (size_t)hlen; + + ctx = EVP_MD_CTX_create(); + ctx_init = EVP_MD_CTX_create(); + if (ctx == NULL || ctx_init == NULL) + goto end; + + if (!EVP_DigestInit(ctx_init, kdf_md)) + goto end; + + for (counter = 1;; counter++) { + c[0] = (unsigned char)((counter >> 24) & 0xff); + c[1] = (unsigned char)((counter >> 16) & 0xff); + c[2] = (unsigned char)((counter >> 8) & 0xff); + c[3] = (unsigned char)(counter & 0xff); + + if (!(EVP_MD_CTX_copy_ex(ctx, ctx_init) + && (append_ctr || EVP_DigestUpdate(ctx, c, sizeof(c))) + && EVP_DigestUpdate(ctx, z, z_len) + && (!append_ctr || EVP_DigestUpdate(ctx, c, sizeof(c))) + && EVP_DigestUpdate(ctx, info, info_len))) + goto end; + if (len >= out_len) { + if (!EVP_DigestFinal_ex(ctx, out, NULL)) + goto end; + out += out_len; + len -= out_len; + if (len == 0) + break; + } else { + if (!EVP_DigestFinal_ex(ctx, mac, NULL)) + goto end; + memcpy(out, mac, len); + break; + } + } + ret = 1; +end: + EVP_MD_CTX_destroy(ctx); + EVP_MD_CTX_destroy(ctx_init); + OPENSSL_cleanse(mac, sizeof(mac)); + return ret; +} + +static int kmac_init(EVP_MAC_CTX *ctx, const unsigned char *custom, + size_t custom_len, size_t kmac_out_len, + size_t derived_key_len, unsigned char **out) +{ + OSSL_PARAM params[2]; + + /* Only KMAC has custom data - so return if not KMAC */ + if (custom == NULL) + return 1; + + params[0] = OSSL_PARAM_construct_octet_string(OSSL_MAC_PARAM_CUSTOM, + (void *)custom, custom_len); + params[1] = OSSL_PARAM_construct_end(); + + if (!EVP_MAC_CTX_set_params(ctx, params)) + return 0; + + /* By default only do one iteration if kmac_out_len is not specified */ + if (kmac_out_len == 0) + kmac_out_len = derived_key_len; + /* otherwise check the size is valid */ + else if (!(kmac_out_len == derived_key_len + || kmac_out_len == 20 + || kmac_out_len == 28 + || kmac_out_len == 32 + || kmac_out_len == 48 + || kmac_out_len == 64)) + return 0; + + params[0] = OSSL_PARAM_construct_size_t(OSSL_MAC_PARAM_SIZE, + &kmac_out_len); + + if (EVP_MAC_CTX_set_params(ctx, params) <= 0) + return 0; + + /* + * For kmac the output buffer can be larger than EVP_MAX_MD_SIZE: so + * alloc a buffer for this case. + */ + if (kmac_out_len > EVP_MAX_MD_SIZE) { + *out = OPENSSL_zalloc(kmac_out_len); + if (*out == NULL) + return 0; + } + return 1; +} + +/* + * Refer to https://csrc.nist.gov/publications/detail/sp/800-56c/rev-1/final + * Section 4. One-Step Key Derivation using MAC: i.e either + * H(x) = HMAC-hash(salt, x) OR + * H(x) = KMAC#(salt, x, outbits, CustomString='KDF') + */ +static int SSKDF_mac_kdm(EVP_MAC_CTX *ctx_init, + const unsigned char *kmac_custom, + size_t kmac_custom_len, size_t kmac_out_len, + const unsigned char *salt, size_t salt_len, + const unsigned char *z, size_t z_len, + const unsigned char *info, size_t info_len, + unsigned char *derived_key, size_t derived_key_len) +{ + int ret = 0; + size_t counter, out_len, len; + unsigned char c[4]; + unsigned char mac_buf[EVP_MAX_MD_SIZE]; + unsigned char *out = derived_key; + EVP_MAC_CTX *ctx = NULL; + unsigned char *mac = mac_buf, *kmac_buffer = NULL; + + if (z_len > SSKDF_MAX_INLEN || info_len > SSKDF_MAX_INLEN + || derived_key_len > SSKDF_MAX_INLEN + || derived_key_len == 0) + return 0; + + if (!kmac_init(ctx_init, kmac_custom, kmac_custom_len, kmac_out_len, + derived_key_len, &kmac_buffer)) + goto end; + if (kmac_buffer != NULL) + mac = kmac_buffer; + + if (!EVP_MAC_init(ctx_init, salt, salt_len, NULL)) + goto end; + + out_len = EVP_MAC_CTX_get_mac_size(ctx_init); /* output size */ + if (out_len <= 0 || (mac == mac_buf && out_len > sizeof(mac_buf))) + goto end; + len = derived_key_len; + + for (counter = 1;; counter++) { + c[0] = (unsigned char)((counter >> 24) & 0xff); + c[1] = (unsigned char)((counter >> 16) & 0xff); + c[2] = (unsigned char)((counter >> 8) & 0xff); + c[3] = (unsigned char)(counter & 0xff); + + ctx = EVP_MAC_CTX_dup(ctx_init); + if (!(ctx != NULL + && EVP_MAC_update(ctx, c, sizeof(c)) + && EVP_MAC_update(ctx, z, z_len) + && EVP_MAC_update(ctx, info, info_len))) + goto end; + if (len >= out_len) { + if (!EVP_MAC_final(ctx, out, NULL, len)) + goto end; + out += out_len; + len -= out_len; + if (len == 0) + break; + } else { + if (!EVP_MAC_final(ctx, mac, NULL, out_len)) + goto end; + memcpy(out, mac, len); + break; + } + EVP_MAC_CTX_free(ctx); + ctx = NULL; + } + ret = 1; +end: + if (kmac_buffer != NULL) + OPENSSL_clear_free(kmac_buffer, kmac_out_len); + else + OPENSSL_cleanse(mac_buf, sizeof(mac_buf)); + + EVP_MAC_CTX_free(ctx); + return ret; +} + +static void *sskdf_new(void *provctx) +{ + KDF_SSKDF *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) != NULL) { + ctx->provctx = provctx; + OSSL_FIPS_IND_INIT(ctx) + } + return ctx; +} + +static void sskdf_reset(void *vctx) +{ + KDF_SSKDF *ctx = (KDF_SSKDF *)vctx; + void *provctx = ctx->provctx; + + EVP_MAC_CTX_free(ctx->macctx); + ossl_prov_digest_reset(&ctx->digest); + OPENSSL_clear_free(ctx->secret, ctx->secret_len); + OPENSSL_clear_free(ctx->info, ctx->info_len); + OPENSSL_clear_free(ctx->salt, ctx->salt_len); + memset(ctx, 0, sizeof(*ctx)); + ctx->provctx = provctx; +} + +static void sskdf_free(void *vctx) +{ + KDF_SSKDF *ctx = (KDF_SSKDF *)vctx; + + if (ctx != NULL) { + sskdf_reset(ctx); + OPENSSL_free(ctx); + } +} + +static void *sskdf_dup(void *vctx) +{ + const KDF_SSKDF *src = (const KDF_SSKDF *)vctx; + KDF_SSKDF *dest; + + dest = sskdf_new(src->provctx); + if (dest != NULL) { + if (src->macctx != NULL) { + dest->macctx = EVP_MAC_CTX_dup(src->macctx); + if (dest->macctx == NULL) + goto err; + } + if (!ossl_prov_memdup(src->info, src->info_len, + &dest->info, &dest->info_len) + || !ossl_prov_memdup(src->salt, src->salt_len, + &dest->salt , &dest->salt_len) + || !ossl_prov_memdup(src->secret, src->secret_len, + &dest->secret, &dest->secret_len) + || !ossl_prov_digest_copy(&dest->digest, &src->digest)) + goto err; + dest->out_len = src->out_len; + dest->is_kmac = src->is_kmac; + OSSL_FIPS_IND_COPY(dest, src) + } + return dest; + + err: + sskdf_free(dest); + return NULL; +} + +static size_t sskdf_size(KDF_SSKDF *ctx) +{ + int len; + const EVP_MD *md = NULL; + + if (ctx->is_kmac) + return SIZE_MAX; + + md = ossl_prov_digest_md(&ctx->digest); + if (md == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST); + return 0; + } + len = EVP_MD_get_size(md); + return (len <= 0) ? 0 : (size_t)len; +} + +#ifdef FIPS_MODULE +static int fips_sskdf_key_check_passed(KDF_SSKDF *ctx) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + int key_approved = ossl_kdf_check_key_size(ctx->secret_len); + + if (!key_approved) { + if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE0, + libctx, "SSKDF", "Key size", + ossl_fips_config_sskdf_key_check)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + } + return 1; +} +#endif + +static int sskdf_derive(void *vctx, unsigned char *key, size_t keylen, + const OSSL_PARAM params[]) +{ + KDF_SSKDF *ctx = (KDF_SSKDF *)vctx; + const EVP_MD *md; + + if (!ossl_prov_is_running() || !sskdf_set_ctx_params(ctx, params)) + return 0; + if (ctx->secret == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_SECRET); + return 0; + } + + md = ossl_prov_digest_md(&ctx->digest); + + if (ctx->macctx != NULL) { + /* H(x) = KMAC or H(x) = HMAC */ + int ret; + const unsigned char *custom = NULL; + size_t custom_len = 0; + int default_salt_len; + EVP_MAC *mac = EVP_MAC_CTX_get0_mac(ctx->macctx); + + if (EVP_MAC_is_a(mac, OSSL_MAC_NAME_HMAC)) { + /* H(x) = HMAC(x, salt, hash) */ + if (md == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST); + return 0; + } + default_salt_len = EVP_MD_get_size(md); + if (default_salt_len <= 0) + return 0; + } else if (ctx->is_kmac) { + /* H(x) = KMACzzz(x, salt, custom) */ + custom = kmac_custom_str; + custom_len = sizeof(kmac_custom_str); + if (EVP_MAC_is_a(mac, OSSL_MAC_NAME_KMAC128)) + default_salt_len = SSKDF_KMAC128_DEFAULT_SALT_SIZE; + else + default_salt_len = SSKDF_KMAC256_DEFAULT_SALT_SIZE; + } else { + ERR_raise(ERR_LIB_PROV, PROV_R_UNSUPPORTED_MAC_TYPE); + return 0; + } + /* If no salt is set then use a default_salt of zeros */ + if (ctx->salt == NULL || ctx->salt_len <= 0) { + ctx->salt = OPENSSL_zalloc(default_salt_len); + if (ctx->salt == NULL) + return 0; + ctx->salt_len = default_salt_len; + } + ret = SSKDF_mac_kdm(ctx->macctx, + custom, custom_len, ctx->out_len, + ctx->salt, ctx->salt_len, + ctx->secret, ctx->secret_len, + ctx->info, ctx->info_len, key, keylen); + return ret; + } else { + /* H(x) = hash */ + if (md == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST); + return 0; + } + return SSKDF_hash_kdm(md, ctx->secret, ctx->secret_len, + ctx->info, ctx->info_len, 0, key, keylen); + } +} + +#ifdef FIPS_MODULE +static int fips_x963kdf_digest_check_passed(KDF_SSKDF *ctx, const EVP_MD *md) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + /* + * Perform digest check + * + * X963KDF is a KDF defined in ANSI-X9.63. According to ACVP specification + * section 7.3.1, only SHA-2 and SHA-3 can be regarded as valid hash + * functions. + */ + int digest_unapproved = (ctx->is_kmac != 1) && EVP_MD_is_a(md, SN_sha1); + + if (digest_unapproved) { + if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE0, + libctx, "X963KDF", "Digest", + ossl_fips_config_x963kdf_digest_check)) { + ERR_raise(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED); + return 0; + } + } + return 1; +} + +static int fips_x963kdf_key_check_passed(KDF_SSKDF *ctx) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + int key_approved = ossl_kdf_check_key_size(ctx->secret_len); + + if (!key_approved) { + if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE1, + libctx, "X963KDF", "Key size", + ossl_fips_config_x963kdf_key_check)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + } + return 1; +} +#endif + +static int x963kdf_derive(void *vctx, unsigned char *key, size_t keylen, + const OSSL_PARAM params[]) +{ + KDF_SSKDF *ctx = (KDF_SSKDF *)vctx; + const EVP_MD *md; + + if (!ossl_prov_is_running() || !x963kdf_set_ctx_params(ctx, params)) + return 0; + + if (ctx->secret == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_SECRET); + return 0; + } + + if (ctx->macctx != NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_SUPPORTED); + return 0; + } + + /* H(x) = hash */ + md = ossl_prov_digest_md(&ctx->digest); + if (md == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST); + return 0; + } + + return SSKDF_hash_kdm(md, ctx->secret, ctx->secret_len, + ctx->info, ctx->info_len, 1, key, keylen); +} + +static int sskdf_common_set_ctx_params(KDF_SSKDF *ctx, const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p; + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + const EVP_MD *md = NULL; + size_t sz; + int r; + + if (ossl_param_is_empty(params)) + return 1; + + if (!ossl_prov_macctx_load_from_params(&ctx->macctx, params, + NULL, NULL, NULL, libctx)) + return 0; + if (ctx->macctx != NULL) { + if (EVP_MAC_is_a(EVP_MAC_CTX_get0_mac(ctx->macctx), + OSSL_MAC_NAME_KMAC128) + || EVP_MAC_is_a(EVP_MAC_CTX_get0_mac(ctx->macctx), + OSSL_MAC_NAME_KMAC256)) { + ctx->is_kmac = 1; + } + } + + if (OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_DIGEST) != NULL) { + if (!ossl_prov_digest_load_from_params(&ctx->digest, params, libctx)) + return 0; + + md = ossl_prov_digest_md(&ctx->digest); + if (EVP_MD_xof(md)) { + ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED); + return 0; + } + } + + r = ossl_param_get1_octet_string(params, OSSL_KDF_PARAM_SECRET, + &ctx->secret, &ctx->secret_len); + if (r == -1) + r = ossl_param_get1_octet_string(params, OSSL_KDF_PARAM_KEY, + &ctx->secret, &ctx->secret_len); + if (r == 0) + return 0; + + if (ossl_param_get1_concat_octet_string(params, OSSL_KDF_PARAM_INFO, + &ctx->info, &ctx->info_len, 0) == 0) + return 0; + + if (ossl_param_get1_octet_string(params, OSSL_KDF_PARAM_SALT, + &ctx->salt, &ctx->salt_len) == 0) + return 0; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_MAC_SIZE)) + != NULL) { + if (!OSSL_PARAM_get_size_t(p, &sz) || sz == 0) + return 0; + ctx->out_len = sz; + } + return 1; +} + +static int sskdf_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + KDF_SSKDF *ctx = (KDF_SSKDF *)vctx; + + if (ossl_param_is_empty(params)) + return 1; + + if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE0, params, + OSSL_KDF_PARAM_FIPS_KEY_CHECK)) + return 0; + + if (!sskdf_common_set_ctx_params(ctx, params)) + return 0; + +#ifdef FIPS_MODULE + if ((OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KEY) != NULL) || + (OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SECRET) != NULL)) + if (!fips_sskdf_key_check_passed(ctx)) + return 0; +#endif + + return 1; +} + +static const OSSL_PARAM *sskdf_settable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + static const OSSL_PARAM known_settable_ctx_params[] = { + SSKDF_COMMON_SETTABLES, + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_KEY_CHECK) + OSSL_PARAM_END + }; + return known_settable_ctx_params; +} + +static int sskdf_common_get_ctx_params(KDF_SSKDF *ctx, OSSL_PARAM params[]) +{ + OSSL_PARAM *p; + + if (ossl_param_is_empty(params)) + return 1; + + if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL) { + if (!OSSL_PARAM_set_size_t(p, sskdf_size(ctx))) + return 0; + } + + return 1; +} + +static int sskdf_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + KDF_SSKDF *ctx = (KDF_SSKDF *)vctx; + + if (ossl_param_is_empty(params)) + return 1; + + if (!sskdf_common_get_ctx_params(ctx, params)) + return 0; + + if (!OSSL_FIPS_IND_GET_CTX_PARAM(ctx, params)) + return 0; + + return 1; +} + +static const OSSL_PARAM *sskdf_gettable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + static const OSSL_PARAM known_gettable_ctx_params[] = { + SSKDF_COMMON_GETTABLES, + OSSL_FIPS_IND_GETTABLE_CTX_PARAM() + OSSL_PARAM_END + }; + return known_gettable_ctx_params; +} + +static int x963kdf_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + KDF_SSKDF *ctx = (KDF_SSKDF *)vctx; + + if (ossl_param_is_empty(params)) + return 1; + + if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE0, params, + OSSL_KDF_PARAM_FIPS_DIGEST_CHECK)) + return 0; + if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE1, params, + OSSL_KDF_PARAM_FIPS_KEY_CHECK)) + return 0; + + if (!sskdf_common_set_ctx_params(ctx, params)) + return 0; + +#ifdef FIPS_MODULE + if (OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_DIGEST) != NULL) { + const EVP_MD *md = ossl_prov_digest_md(&ctx->digest); + + if (!fips_x963kdf_digest_check_passed(ctx, md)) + return 0; + } + + if ((OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KEY) != NULL) || + (OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SECRET) != NULL)) + if (!fips_x963kdf_key_check_passed(ctx)) + return 0; +#endif + + return 1; +} + +static const OSSL_PARAM *x963kdf_settable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + static const OSSL_PARAM known_settable_ctx_params[] = { + SSKDF_COMMON_SETTABLES, + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_DIGEST_CHECK) + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_KEY_CHECK) + OSSL_PARAM_END + }; + return known_settable_ctx_params; +} + +static int x963kdf_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + KDF_SSKDF *ctx = (KDF_SSKDF *)vctx; + + if (!sskdf_common_get_ctx_params(ctx, params)) + return 0; + + if (!OSSL_FIPS_IND_GET_CTX_PARAM(ctx, params)) + return 0; + + return 1; +} + +static const OSSL_PARAM *x963kdf_gettable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + static const OSSL_PARAM known_gettable_ctx_params[] = { + SSKDF_COMMON_GETTABLES, + OSSL_FIPS_IND_GETTABLE_CTX_PARAM() + OSSL_PARAM_END + }; + return known_gettable_ctx_params; +} + +const OSSL_DISPATCH ossl_kdf_sskdf_functions[] = { + { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))sskdf_new }, + { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))sskdf_dup }, + { OSSL_FUNC_KDF_FREECTX, (void(*)(void))sskdf_free }, + { OSSL_FUNC_KDF_RESET, (void(*)(void))sskdf_reset }, + { OSSL_FUNC_KDF_DERIVE, (void(*)(void))sskdf_derive }, + { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS, + (void(*)(void))sskdf_settable_ctx_params }, + { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))sskdf_set_ctx_params }, + { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS, + (void(*)(void))sskdf_gettable_ctx_params }, + { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))sskdf_get_ctx_params }, + OSSL_DISPATCH_END +}; + +const OSSL_DISPATCH ossl_kdf_x963_kdf_functions[] = { + { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))sskdf_new }, + { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))sskdf_dup }, + { OSSL_FUNC_KDF_FREECTX, (void(*)(void))sskdf_free }, + { OSSL_FUNC_KDF_RESET, (void(*)(void))sskdf_reset }, + { OSSL_FUNC_KDF_DERIVE, (void(*)(void))x963kdf_derive }, + { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS, + (void(*)(void))x963kdf_settable_ctx_params }, + { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))x963kdf_set_ctx_params }, + { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS, + (void(*)(void))x963kdf_gettable_ctx_params }, + { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))x963kdf_get_ctx_params }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/kdfs/tls1_prf.c b/crypto/openssl/providers/implementations/kdfs/tls1_prf.c new file mode 100644 index 000000000000..4b6469c00d23 --- /dev/null +++ b/crypto/openssl/providers/implementations/kdfs/tls1_prf.c @@ -0,0 +1,585 @@ +/* + * Copyright 2016-2024 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 + */ + +/* + * Refer to "The TLS Protocol Version 1.0" Section 5 + * (https://tools.ietf.org/html/rfc2246#section-5) and + * "The Transport Layer Security (TLS) Protocol Version 1.2" Section 5 + * (https://tools.ietf.org/html/rfc5246#section-5). + * + * For TLS v1.0 and TLS v1.1 the TLS PRF algorithm is given by: + * + * PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR + * P_SHA-1(S2, label + seed) + * + * where P_MD5 and P_SHA-1 are defined by P_<hash>, below, and S1 and S2 are + * two halves of the secret (with the possibility of one shared byte, in the + * case where the length of the original secret is odd). S1 is taken from the + * first half of the secret, S2 from the second half. + * + * For TLS v1.2 the TLS PRF algorithm is given by: + * + * PRF(secret, label, seed) = P_<hash>(secret, label + seed) + * + * where hash is SHA-256 for all cipher suites defined in RFC 5246 as well as + * those published prior to TLS v1.2 while the TLS v1.2 protocol is in effect, + * unless defined otherwise by the cipher suite. + * + * P_<hash> is an expansion function that uses a single hash function to expand + * a secret and seed into an arbitrary quantity of output: + * + * P_<hash>(secret, seed) = HMAC_<hash>(secret, A(1) + seed) + + * HMAC_<hash>(secret, A(2) + seed) + + * HMAC_<hash>(secret, A(3) + seed) + ... + * + * where + indicates concatenation. P_<hash> can be iterated as many times as + * is necessary to produce the required quantity of data. + * + * A(i) is defined as: + * A(0) = seed + * A(i) = HMAC_<hash>(secret, A(i-1)) + */ + +/* + * Low level APIs (such as DH) are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <openssl/evp.h> +#include <openssl/kdf.h> +#include <openssl/core_names.h> +#include <openssl/params.h> +#include <openssl/proverr.h> +#include "internal/cryptlib.h" +#include "internal/numbers.h" +#include "crypto/evp.h" +#include "prov/provider_ctx.h" +#include "prov/providercommon.h" +#include "prov/implementations.h" +#include "prov/provider_util.h" +#include "prov/securitycheck.h" +#include "internal/e_os.h" +#include "internal/safe_math.h" + +OSSL_SAFE_MATH_UNSIGNED(size_t, size_t) + +static OSSL_FUNC_kdf_newctx_fn kdf_tls1_prf_new; +static OSSL_FUNC_kdf_dupctx_fn kdf_tls1_prf_dup; +static OSSL_FUNC_kdf_freectx_fn kdf_tls1_prf_free; +static OSSL_FUNC_kdf_reset_fn kdf_tls1_prf_reset; +static OSSL_FUNC_kdf_derive_fn kdf_tls1_prf_derive; +static OSSL_FUNC_kdf_settable_ctx_params_fn kdf_tls1_prf_settable_ctx_params; +static OSSL_FUNC_kdf_set_ctx_params_fn kdf_tls1_prf_set_ctx_params; +static OSSL_FUNC_kdf_gettable_ctx_params_fn kdf_tls1_prf_gettable_ctx_params; +static OSSL_FUNC_kdf_get_ctx_params_fn kdf_tls1_prf_get_ctx_params; + +static int tls1_prf_alg(EVP_MAC_CTX *mdctx, EVP_MAC_CTX *sha1ctx, + const unsigned char *sec, size_t slen, + const unsigned char *seed, size_t seed_len, + unsigned char *out, size_t olen); + +#define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74" +#define TLS_MD_MASTER_SECRET_CONST_SIZE 13 + +/* TLS KDF kdf context structure */ +typedef struct { + void *provctx; + + /* MAC context for the main digest */ + EVP_MAC_CTX *P_hash; + /* MAC context for SHA1 for the MD5/SHA-1 combined PRF */ + EVP_MAC_CTX *P_sha1; + + /* Secret value to use for PRF */ + unsigned char *sec; + size_t seclen; + /* Concatenated seed data */ + unsigned char *seed; + size_t seedlen; + + OSSL_FIPS_IND_DECLARE +} TLS1_PRF; + +static void *kdf_tls1_prf_new(void *provctx) +{ + TLS1_PRF *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) != NULL) { + ctx->provctx = provctx; + OSSL_FIPS_IND_INIT(ctx) + } + return ctx; +} + +static void kdf_tls1_prf_free(void *vctx) +{ + TLS1_PRF *ctx = (TLS1_PRF *)vctx; + + if (ctx != NULL) { + kdf_tls1_prf_reset(ctx); + OPENSSL_free(ctx); + } +} + +static void kdf_tls1_prf_reset(void *vctx) +{ + TLS1_PRF *ctx = (TLS1_PRF *)vctx; + void *provctx = ctx->provctx; + + EVP_MAC_CTX_free(ctx->P_hash); + EVP_MAC_CTX_free(ctx->P_sha1); + OPENSSL_clear_free(ctx->sec, ctx->seclen); + OPENSSL_clear_free(ctx->seed, ctx->seedlen); + memset(ctx, 0, sizeof(*ctx)); + ctx->provctx = provctx; +} + +static void *kdf_tls1_prf_dup(void *vctx) +{ + const TLS1_PRF *src = (const TLS1_PRF *)vctx; + TLS1_PRF *dest; + + dest = kdf_tls1_prf_new(src->provctx); + if (dest != NULL) { + if (src->P_hash != NULL + && (dest->P_hash = EVP_MAC_CTX_dup(src->P_hash)) == NULL) + goto err; + if (src->P_sha1 != NULL + && (dest->P_sha1 = EVP_MAC_CTX_dup(src->P_sha1)) == NULL) + goto err; + if (!ossl_prov_memdup(src->sec, src->seclen, &dest->sec, &dest->seclen)) + goto err; + if (!ossl_prov_memdup(src->seed, src->seedlen, &dest->seed, + &dest->seedlen)) + goto err; + OSSL_FIPS_IND_COPY(dest, src) + } + return dest; + + err: + kdf_tls1_prf_free(dest); + return NULL; +} + +#ifdef FIPS_MODULE + +static int fips_ems_check_passed(TLS1_PRF *ctx) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + /* + * Check that TLS is using EMS. + * + * The seed buffer is prepended with a label. + * If EMS mode is enforced then the label "master secret" is not allowed, + * We do the check this way since the PRF is used for other purposes, as well + * as "extended master secret". + */ + int ems_approved = (ctx->seedlen < TLS_MD_MASTER_SECRET_CONST_SIZE + || memcmp(ctx->seed, TLS_MD_MASTER_SECRET_CONST, + TLS_MD_MASTER_SECRET_CONST_SIZE) != 0); + + if (!ems_approved) { + if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE0, + libctx, "TLS_PRF", "EMS", + ossl_fips_config_tls1_prf_ems_check)) { + ERR_raise(ERR_LIB_PROV, PROV_R_EMS_NOT_ENABLED); + return 0; + } + } + return 1; +} + +static int fips_digest_check_passed(TLS1_PRF *ctx, const EVP_MD *md) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + /* + * Perform digest check + * + * According to NIST SP 800-135r1 section 5.2, the valid hash functions are + * specified in FIPS 180-3. ACVP also only lists the same set of hash + * functions. + */ + int digest_unapproved = !EVP_MD_is_a(md, SN_sha256) + && !EVP_MD_is_a(md, SN_sha384) + && !EVP_MD_is_a(md, SN_sha512); + + if (digest_unapproved) { + if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE1, + libctx, "TLS_PRF", "Digest", + ossl_fips_config_tls1_prf_digest_check)) { + ERR_raise(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED); + return 0; + } + } + return 1; +} + +static int fips_key_check_passed(TLS1_PRF *ctx) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + int key_approved = ossl_kdf_check_key_size(ctx->seclen); + + if (!key_approved) { + if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE2, + libctx, "TLS_PRF", "Key size", + ossl_fips_config_tls1_prf_key_check)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + } + return 1; +} +#endif + +static int kdf_tls1_prf_derive(void *vctx, unsigned char *key, size_t keylen, + const OSSL_PARAM params[]) +{ + TLS1_PRF *ctx = (TLS1_PRF *)vctx; + + if (!ossl_prov_is_running() || !kdf_tls1_prf_set_ctx_params(ctx, params)) + return 0; + + if (ctx->P_hash == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST); + return 0; + } + if (ctx->sec == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_SECRET); + return 0; + } + if (ctx->seedlen == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_SEED); + return 0; + } + if (keylen == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + +#ifdef FIPS_MODULE + if (!fips_ems_check_passed(ctx)) + return 0; +#endif + + return tls1_prf_alg(ctx->P_hash, ctx->P_sha1, + ctx->sec, ctx->seclen, + ctx->seed, ctx->seedlen, + key, keylen); +} + +static int kdf_tls1_prf_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p; + TLS1_PRF *ctx = vctx; + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + + if (ossl_param_is_empty(params)) + return 1; + + if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE0, params, + OSSL_KDF_PARAM_FIPS_EMS_CHECK)) + return 0; + if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE1, params, + OSSL_KDF_PARAM_FIPS_DIGEST_CHECK)) + return 0; + if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE2, params, + OSSL_KDF_PARAM_FIPS_KEY_CHECK)) + return 0; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_DIGEST)) != NULL) { + PROV_DIGEST digest; + const EVP_MD *md = NULL; + + if (OPENSSL_strcasecmp(p->data, SN_md5_sha1) == 0) { + if (!ossl_prov_macctx_load_from_params(&ctx->P_hash, params, + OSSL_MAC_NAME_HMAC, + NULL, SN_md5, libctx) + || !ossl_prov_macctx_load_from_params(&ctx->P_sha1, params, + OSSL_MAC_NAME_HMAC, + NULL, SN_sha1, libctx)) + return 0; + } else { + EVP_MAC_CTX_free(ctx->P_sha1); + if (!ossl_prov_macctx_load_from_params(&ctx->P_hash, params, + OSSL_MAC_NAME_HMAC, + NULL, NULL, libctx)) + return 0; + } + + memset(&digest, 0, sizeof(digest)); + if (!ossl_prov_digest_load_from_params(&digest, params, libctx)) + return 0; + + md = ossl_prov_digest_md(&digest); + if (EVP_MD_xof(md)) { + ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED); + ossl_prov_digest_reset(&digest); + return 0; + } + +#ifdef FIPS_MODULE + if (!fips_digest_check_passed(ctx, md)) { + ossl_prov_digest_reset(&digest); + return 0; + } +#endif + + ossl_prov_digest_reset(&digest); + } + + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SECRET)) != NULL) { + OPENSSL_clear_free(ctx->sec, ctx->seclen); + ctx->sec = NULL; + if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->sec, 0, &ctx->seclen)) + return 0; + +#ifdef FIPS_MODULE + if (!fips_key_check_passed(ctx)) + return 0; +#endif + } + /* The seed fields concatenate, so process them all */ + if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SEED)) != NULL) { + for (; p != NULL; p = OSSL_PARAM_locate_const(p + 1, + OSSL_KDF_PARAM_SEED)) { + if (p->data_size != 0 && p->data != NULL) { + const void *val = NULL; + size_t sz = 0; + unsigned char *seed; + size_t seedlen; + int err = 0; + + if (!OSSL_PARAM_get_octet_string_ptr(p, &val, &sz)) + return 0; + + seedlen = safe_add_size_t(ctx->seedlen, sz, &err); + if (err) + return 0; + + seed = OPENSSL_clear_realloc(ctx->seed, ctx->seedlen, seedlen); + if (!seed) + return 0; + + ctx->seed = seed; + if (ossl_assert(sz != 0)) + memcpy(ctx->seed + ctx->seedlen, val, sz); + ctx->seedlen = seedlen; + } + } + } + return 1; +} + +static const OSSL_PARAM *kdf_tls1_prf_settable_ctx_params( + ossl_unused void *ctx, ossl_unused void *provctx) +{ + static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SECRET, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SEED, NULL, 0), + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_EMS_CHECK) + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_DIGEST_CHECK) + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_KEY_CHECK) + OSSL_PARAM_END + }; + return known_settable_ctx_params; +} + +static int kdf_tls1_prf_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + OSSL_PARAM *p; + + if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL) { + if (!OSSL_PARAM_set_size_t(p, SIZE_MAX)) + return 0; + } + if (!OSSL_FIPS_IND_GET_CTX_PARAM(((TLS1_PRF *)vctx), params)) + return 0; + return 1; +} + +static const OSSL_PARAM *kdf_tls1_prf_gettable_ctx_params( + ossl_unused void *ctx, ossl_unused void *provctx) +{ + static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL), + OSSL_FIPS_IND_GETTABLE_CTX_PARAM() + OSSL_PARAM_END + }; + return known_gettable_ctx_params; +} + +const OSSL_DISPATCH ossl_kdf_tls1_prf_functions[] = { + { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_tls1_prf_new }, + { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))kdf_tls1_prf_dup }, + { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_tls1_prf_free }, + { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_tls1_prf_reset }, + { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_tls1_prf_derive }, + { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS, + (void(*)(void))kdf_tls1_prf_settable_ctx_params }, + { OSSL_FUNC_KDF_SET_CTX_PARAMS, + (void(*)(void))kdf_tls1_prf_set_ctx_params }, + { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS, + (void(*)(void))kdf_tls1_prf_gettable_ctx_params }, + { OSSL_FUNC_KDF_GET_CTX_PARAMS, + (void(*)(void))kdf_tls1_prf_get_ctx_params }, + OSSL_DISPATCH_END +}; + +/* + * Refer to "The TLS Protocol Version 1.0" Section 5 + * (https://tools.ietf.org/html/rfc2246#section-5) and + * "The Transport Layer Security (TLS) Protocol Version 1.2" Section 5 + * (https://tools.ietf.org/html/rfc5246#section-5). + * + * P_<hash> is an expansion function that uses a single hash function to expand + * a secret and seed into an arbitrary quantity of output: + * + * P_<hash>(secret, seed) = HMAC_<hash>(secret, A(1) + seed) + + * HMAC_<hash>(secret, A(2) + seed) + + * HMAC_<hash>(secret, A(3) + seed) + ... + * + * where + indicates concatenation. P_<hash> can be iterated as many times as + * is necessary to produce the required quantity of data. + * + * A(i) is defined as: + * A(0) = seed + * A(i) = HMAC_<hash>(secret, A(i-1)) + */ +static int tls1_prf_P_hash(EVP_MAC_CTX *ctx_init, + const unsigned char *sec, size_t sec_len, + const unsigned char *seed, size_t seed_len, + unsigned char *out, size_t olen) +{ + size_t chunk; + EVP_MAC_CTX *ctx = NULL, *ctx_Ai = NULL; + unsigned char Ai[EVP_MAX_MD_SIZE]; + size_t Ai_len; + int ret = 0; + + if (!EVP_MAC_init(ctx_init, sec, sec_len, NULL)) + goto err; + chunk = EVP_MAC_CTX_get_mac_size(ctx_init); + if (chunk == 0) + goto err; + /* A(0) = seed */ + ctx_Ai = EVP_MAC_CTX_dup(ctx_init); + if (ctx_Ai == NULL) + goto err; + if (seed != NULL && !EVP_MAC_update(ctx_Ai, seed, seed_len)) + goto err; + + for (;;) { + /* calc: A(i) = HMAC_<hash>(secret, A(i-1)) */ + if (!EVP_MAC_final(ctx_Ai, Ai, &Ai_len, sizeof(Ai))) + goto err; + EVP_MAC_CTX_free(ctx_Ai); + ctx_Ai = NULL; + + /* calc next chunk: HMAC_<hash>(secret, A(i) + seed) */ + ctx = EVP_MAC_CTX_dup(ctx_init); + if (ctx == NULL) + goto err; + if (!EVP_MAC_update(ctx, Ai, Ai_len)) + goto err; + /* save state for calculating next A(i) value */ + if (olen > chunk) { + ctx_Ai = EVP_MAC_CTX_dup(ctx); + if (ctx_Ai == NULL) + goto err; + } + if (seed != NULL && !EVP_MAC_update(ctx, seed, seed_len)) + goto err; + if (olen <= chunk) { + /* last chunk - use Ai as temp bounce buffer */ + if (!EVP_MAC_final(ctx, Ai, &Ai_len, sizeof(Ai))) + goto err; + memcpy(out, Ai, olen); + break; + } + if (!EVP_MAC_final(ctx, out, NULL, olen)) + goto err; + EVP_MAC_CTX_free(ctx); + ctx = NULL; + out += chunk; + olen -= chunk; + } + ret = 1; + err: + EVP_MAC_CTX_free(ctx); + EVP_MAC_CTX_free(ctx_Ai); + OPENSSL_cleanse(Ai, sizeof(Ai)); + return ret; +} + +/* + * Refer to "The TLS Protocol Version 1.0" Section 5 + * (https://tools.ietf.org/html/rfc2246#section-5) and + * "The Transport Layer Security (TLS) Protocol Version 1.2" Section 5 + * (https://tools.ietf.org/html/rfc5246#section-5). + * + * For TLS v1.0 and TLS v1.1: + * + * PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR + * P_SHA-1(S2, label + seed) + * + * S1 is taken from the first half of the secret, S2 from the second half. + * + * L_S = length in bytes of secret; + * L_S1 = L_S2 = ceil(L_S / 2); + * + * For TLS v1.2: + * + * PRF(secret, label, seed) = P_<hash>(secret, label + seed) + */ +static int tls1_prf_alg(EVP_MAC_CTX *mdctx, EVP_MAC_CTX *sha1ctx, + const unsigned char *sec, size_t slen, + const unsigned char *seed, size_t seed_len, + unsigned char *out, size_t olen) +{ + if (sha1ctx != NULL) { + /* TLS v1.0 and TLS v1.1 */ + size_t i; + unsigned char *tmp; + /* calc: L_S1 = L_S2 = ceil(L_S / 2) */ + size_t L_S1 = (slen + 1) / 2; + size_t L_S2 = L_S1; + + if (!tls1_prf_P_hash(mdctx, sec, L_S1, + seed, seed_len, out, olen)) + return 0; + + if ((tmp = OPENSSL_malloc(olen)) == NULL) + return 0; + + if (!tls1_prf_P_hash(sha1ctx, sec + slen - L_S2, L_S2, + seed, seed_len, tmp, olen)) { + OPENSSL_clear_free(tmp, olen); + return 0; + } + for (i = 0; i < olen; i++) + out[i] ^= tmp[i]; + OPENSSL_clear_free(tmp, olen); + return 1; + } + + /* TLS v1.2 */ + if (!tls1_prf_P_hash(mdctx, sec, slen, seed, seed_len, out, olen)) + return 0; + + return 1; +} diff --git a/crypto/openssl/providers/implementations/kdfs/x942kdf.c b/crypto/openssl/providers/implementations/kdfs/x942kdf.c new file mode 100644 index 000000000000..63164d8b8fbe --- /dev/null +++ b/crypto/openssl/providers/implementations/kdfs/x942kdf.c @@ -0,0 +1,679 @@ +/* + * Copyright 2019-2024 The OpenSSL Project Authors. All Rights Reserved. + * Copyright (c) 2019, Oracle and/or its affiliates. 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 + */ + +#include "internal/e_os.h" +#include <openssl/core_names.h> +#include <openssl/core_dispatch.h> +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/params.h> +#include <openssl/proverr.h> +#include "internal/packet.h" +#include "internal/der.h" +#include "internal/nelem.h" +#include "prov/provider_ctx.h" +#include "prov/providercommon.h" +#include "prov/implementations.h" +#include "prov/provider_util.h" +#include "prov/securitycheck.h" +#include "prov/der_wrap.h" + +#define X942KDF_MAX_INLEN (1 << 30) + +static OSSL_FUNC_kdf_newctx_fn x942kdf_new; +static OSSL_FUNC_kdf_dupctx_fn x942kdf_dup; +static OSSL_FUNC_kdf_freectx_fn x942kdf_free; +static OSSL_FUNC_kdf_reset_fn x942kdf_reset; +static OSSL_FUNC_kdf_derive_fn x942kdf_derive; +static OSSL_FUNC_kdf_settable_ctx_params_fn x942kdf_settable_ctx_params; +static OSSL_FUNC_kdf_set_ctx_params_fn x942kdf_set_ctx_params; +static OSSL_FUNC_kdf_gettable_ctx_params_fn x942kdf_gettable_ctx_params; +static OSSL_FUNC_kdf_get_ctx_params_fn x942kdf_get_ctx_params; + +typedef struct { + void *provctx; + PROV_DIGEST digest; + unsigned char *secret; + size_t secret_len; + unsigned char *acvpinfo; + size_t acvpinfo_len; + unsigned char *partyuinfo, *partyvinfo, *supp_pubinfo, *supp_privinfo; + size_t partyuinfo_len, partyvinfo_len, supp_pubinfo_len, supp_privinfo_len; + size_t dkm_len; + const unsigned char *cek_oid; + size_t cek_oid_len; + int use_keybits; + OSSL_FIPS_IND_DECLARE +} KDF_X942; + +/* + * A table of allowed wrapping algorithms, oids and the associated output + * lengths. + * NOTE: RC2wrap and camellia128_wrap have been removed as there are no + * corresponding ciphers for these operations. + */ +static const struct { + const char *name; + const unsigned char *oid; + size_t oid_len; + size_t keklen; /* size in bytes */ +} kek_algs[] = { + { "AES-128-WRAP", ossl_der_oid_id_aes128_wrap, DER_OID_SZ_id_aes128_wrap, + 16 }, + { "AES-192-WRAP", ossl_der_oid_id_aes192_wrap, DER_OID_SZ_id_aes192_wrap, + 24 }, + { "AES-256-WRAP", ossl_der_oid_id_aes256_wrap, DER_OID_SZ_id_aes256_wrap, + 32 }, +#ifndef FIPS_MODULE + { "DES3-WRAP", ossl_der_oid_id_alg_CMS3DESwrap, + DER_OID_SZ_id_alg_CMS3DESwrap, 24 }, +#endif +}; + +static int find_alg_id(OSSL_LIB_CTX *libctx, const char *algname, + const char *propq, size_t *id) +{ + int ret = 1; + size_t i; + EVP_CIPHER *cipher; + + cipher = EVP_CIPHER_fetch(libctx, algname, propq); + if (cipher != NULL) { + for (i = 0; i < OSSL_NELEM(kek_algs); i++) { + if (EVP_CIPHER_is_a(cipher, kek_algs[i].name)) { + *id = i; + goto end; + } + } + } + ret = 0; + ERR_raise(ERR_LIB_PROV, PROV_R_UNSUPPORTED_CEK_ALG); +end: + EVP_CIPHER_free(cipher); + return ret; +} + +static int DER_w_keyinfo(WPACKET *pkt, + const unsigned char *der_oid, size_t der_oidlen, + unsigned char **pcounter) +{ + return ossl_DER_w_begin_sequence(pkt, -1) + /* Store the initial value of 1 into the counter */ + && ossl_DER_w_octet_string_uint32(pkt, -1, 1) + /* Remember where we stored the counter in the buffer */ + && (pcounter == NULL + || (*pcounter = WPACKET_get_curr(pkt)) != NULL) + && ossl_DER_w_precompiled(pkt, -1, der_oid, der_oidlen) + && ossl_DER_w_end_sequence(pkt, -1); +} + +static int der_encode_sharedinfo(WPACKET *pkt, unsigned char *buf, size_t buflen, + const unsigned char *der_oid, size_t der_oidlen, + const unsigned char *acvp, size_t acvplen, + const unsigned char *partyu, size_t partyulen, + const unsigned char *partyv, size_t partyvlen, + const unsigned char *supp_pub, size_t supp_publen, + const unsigned char *supp_priv, size_t supp_privlen, + uint32_t keylen_bits, unsigned char **pcounter) +{ + return (buf != NULL ? WPACKET_init_der(pkt, buf, buflen) : + WPACKET_init_null_der(pkt)) + && ossl_DER_w_begin_sequence(pkt, -1) + && (supp_priv == NULL + || ossl_DER_w_octet_string(pkt, 3, supp_priv, supp_privlen)) + && (supp_pub == NULL + || ossl_DER_w_octet_string(pkt, 2, supp_pub, supp_publen)) + && (keylen_bits == 0 + || ossl_DER_w_octet_string_uint32(pkt, 2, keylen_bits)) + && (partyv == NULL || ossl_DER_w_octet_string(pkt, 1, partyv, partyvlen)) + && (partyu == NULL || ossl_DER_w_octet_string(pkt, 0, partyu, partyulen)) + && (acvp == NULL || ossl_DER_w_precompiled(pkt, -1, acvp, acvplen)) + && DER_w_keyinfo(pkt, der_oid, der_oidlen, pcounter) + && ossl_DER_w_end_sequence(pkt, -1) + && WPACKET_finish(pkt); +} + +/* + * Encode the other info structure. + * + * The ANS X9.42-2003 standard uses OtherInfo: + * + * OtherInfo ::= SEQUENCE { + * keyInfo KeySpecificInfo, + * partyUInfo [0] OCTET STRING OPTIONAL, + * partyVInfo [1] OCTET STRING OPTIONAL, + * suppPubInfo [2] OCTET STRING OPTIONAL, + * suppPrivInfo [3] OCTET STRING OPTIONAL + * } + * + * KeySpecificInfo ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * counter OCTET STRING SIZE (4..4) + * } + * + * RFC2631 Section 2.1.2 Contains the following definition for OtherInfo + * + * OtherInfo ::= SEQUENCE { + * keyInfo KeySpecificInfo, + * partyAInfo [0] OCTET STRING OPTIONAL, + * suppPubInfo [2] OCTET STRING + * } + * Where suppPubInfo is the key length (in bits) (stored into 4 bytes) + * + * |keylen| is the length (in bytes) of the generated KEK. It is stored into + * suppPubInfo (in bits). It is ignored if the value is 0. + * |cek_oid| The oid of the key wrapping algorithm. + * |cek_oidlen| The length (in bytes) of the key wrapping algorithm oid, + * |acvp| is the optional blob of DER data representing one or more of the + * OtherInfo fields related to |partyu|, |partyv|, |supp_pub| and |supp_priv|. + * This field should normally be NULL. If |acvp| is non NULL then |partyu|, + * |partyv|, |supp_pub| and |supp_priv| should all be NULL. + * |acvp_len| is the |acvp| length (in bytes). + * |partyu| is the optional public info contributed by the initiator. + * It can be NULL. (It is also used as the ukm by CMS). + * |partyu_len| is the |partyu| length (in bytes). + * |partyv| is the optional public info contributed by the responder. + * It can be NULL. + * |partyv_len| is the |partyv| length (in bytes). + * |supp_pub| is the optional additional, mutually-known public information. + * It can be NULL. |keylen| should be 0 if this is not NULL. + * |supp_pub_len| is the |supp_pub| length (in bytes). + * |supp_priv| is the optional additional, mutually-known private information. + * It can be NULL. + * |supp_priv_len| is the |supp_priv| length (in bytes). + * |der| is the returned encoded data. It must be freed by the caller. + * |der_len| is the returned size of the encoded data. + * |out_ctr| returns a pointer to the counter data which is embedded inside the + * encoded data. This allows the counter bytes to be updated without + * re-encoding. + * + * Returns: 1 if successfully encoded, or 0 otherwise. + * Assumptions: |der|, |der_len| & |out_ctr| are not NULL. + */ +static int +x942_encode_otherinfo(size_t keylen, + const unsigned char *cek_oid, size_t cek_oid_len, + const unsigned char *acvp, size_t acvp_len, + const unsigned char *partyu, size_t partyu_len, + const unsigned char *partyv, size_t partyv_len, + const unsigned char *supp_pub, size_t supp_pub_len, + const unsigned char *supp_priv, size_t supp_priv_len, + unsigned char **der, size_t *der_len, + unsigned char **out_ctr) +{ + int ret = 0; + unsigned char *pcounter = NULL, *der_buf = NULL; + size_t der_buflen = 0; + WPACKET pkt; + uint32_t keylen_bits; + + /* keylenbits must fit into 4 bytes */ + if (keylen > 0xFFFFFF) + return 0; + keylen_bits = 8 * keylen; + + /* Calculate the size of the buffer */ + if (!der_encode_sharedinfo(&pkt, NULL, 0, cek_oid, cek_oid_len, + acvp, acvp_len, + partyu, partyu_len, partyv, partyv_len, + supp_pub, supp_pub_len, supp_priv, supp_priv_len, + keylen_bits, NULL) + || !WPACKET_get_total_written(&pkt, &der_buflen)) + goto err; + WPACKET_cleanup(&pkt); + /* Alloc the buffer */ + der_buf = OPENSSL_zalloc(der_buflen); + if (der_buf == NULL) + goto err; + /* Encode into the buffer */ + if (!der_encode_sharedinfo(&pkt, der_buf, der_buflen, cek_oid, cek_oid_len, + acvp, acvp_len, + partyu, partyu_len, partyv, partyv_len, + supp_pub, supp_pub_len, supp_priv, supp_priv_len, + keylen_bits, &pcounter)) + goto err; + /* + * Since we allocated the exact size required, the buffer should point to the + * start of the allocated buffer at this point. + */ + if (WPACKET_get_curr(&pkt) != der_buf) + goto err; + + /* + * The data for the DER encoded octet string of a 32 bit counter = 1 + * should be 04 04 00 00 00 01 + * So just check the header is correct and skip over it. + * This counter will be incremented in the kdf update loop. + */ + if (pcounter == NULL + || pcounter[0] != 0x04 + || pcounter[1] != 0x04) + goto err; + *out_ctr = (pcounter + 2); + *der = der_buf; + *der_len = der_buflen; + ret = 1; +err: + WPACKET_cleanup(&pkt); + return ret; +} + +static int x942kdf_hash_kdm(const EVP_MD *kdf_md, + const unsigned char *z, size_t z_len, + const unsigned char *other, size_t other_len, + unsigned char *ctr, + unsigned char *derived_key, size_t derived_key_len) +{ + int ret = 0, hlen; + size_t counter, out_len, len = derived_key_len; + unsigned char mac[EVP_MAX_MD_SIZE]; + unsigned char *out = derived_key; + EVP_MD_CTX *ctx = NULL, *ctx_init = NULL; + + if (z_len > X942KDF_MAX_INLEN + || other_len > X942KDF_MAX_INLEN + || derived_key_len > X942KDF_MAX_INLEN + || derived_key_len == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_BAD_LENGTH); + return 0; + } + + hlen = EVP_MD_get_size(kdf_md); + if (hlen <= 0) + return 0; + out_len = (size_t)hlen; + + ctx = EVP_MD_CTX_create(); + ctx_init = EVP_MD_CTX_create(); + if (ctx == NULL || ctx_init == NULL) + goto end; + + if (!EVP_DigestInit(ctx_init, kdf_md)) + goto end; + + for (counter = 1;; counter++) { + /* updating the ctr modifies 4 bytes in the 'other' buffer */ + ctr[0] = (unsigned char)((counter >> 24) & 0xff); + ctr[1] = (unsigned char)((counter >> 16) & 0xff); + ctr[2] = (unsigned char)((counter >> 8) & 0xff); + ctr[3] = (unsigned char)(counter & 0xff); + + if (!EVP_MD_CTX_copy_ex(ctx, ctx_init) + || !EVP_DigestUpdate(ctx, z, z_len) + || !EVP_DigestUpdate(ctx, other, other_len)) + goto end; + if (len >= out_len) { + if (!EVP_DigestFinal_ex(ctx, out, NULL)) + goto end; + out += out_len; + len -= out_len; + if (len == 0) + break; + } else { + if (!EVP_DigestFinal_ex(ctx, mac, NULL)) + goto end; + memcpy(out, mac, len); + break; + } + } + ret = 1; +end: + EVP_MD_CTX_free(ctx); + EVP_MD_CTX_free(ctx_init); + OPENSSL_cleanse(mac, sizeof(mac)); + return ret; +} + +static void *x942kdf_new(void *provctx) +{ + KDF_X942 *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + ctx = OPENSSL_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + + ctx->provctx = provctx; + OSSL_FIPS_IND_INIT(ctx) + ctx->use_keybits = 1; + return ctx; +} + +static void x942kdf_reset(void *vctx) +{ + KDF_X942 *ctx = (KDF_X942 *)vctx; + void *provctx = ctx->provctx; + + ossl_prov_digest_reset(&ctx->digest); + OPENSSL_clear_free(ctx->secret, ctx->secret_len); + OPENSSL_clear_free(ctx->acvpinfo, ctx->acvpinfo_len); + OPENSSL_clear_free(ctx->partyuinfo, ctx->partyuinfo_len); + OPENSSL_clear_free(ctx->partyvinfo, ctx->partyvinfo_len); + OPENSSL_clear_free(ctx->supp_pubinfo, ctx->supp_pubinfo_len); + OPENSSL_clear_free(ctx->supp_privinfo, ctx->supp_privinfo_len); + memset(ctx, 0, sizeof(*ctx)); + ctx->provctx = provctx; + ctx->use_keybits = 1; +} + +static void x942kdf_free(void *vctx) +{ + KDF_X942 *ctx = (KDF_X942 *)vctx; + + if (ctx != NULL) { + x942kdf_reset(ctx); + OPENSSL_free(ctx); + } +} + +static void *x942kdf_dup(void *vctx) +{ + const KDF_X942 *src = (const KDF_X942 *)vctx; + KDF_X942 *dest; + + dest = x942kdf_new(src->provctx); + if (dest != NULL) { + if (!ossl_prov_memdup(src->secret, src->secret_len, + &dest->secret , &dest->secret_len) + || !ossl_prov_memdup(src->acvpinfo, src->acvpinfo_len, + &dest->acvpinfo , &dest->acvpinfo_len) + || !ossl_prov_memdup(src->partyuinfo, src->partyuinfo_len, + &dest->partyuinfo , &dest->partyuinfo_len) + || !ossl_prov_memdup(src->partyvinfo, src->partyvinfo_len, + &dest->partyvinfo , &dest->partyvinfo_len) + || !ossl_prov_memdup(src->supp_pubinfo, src->supp_pubinfo_len, + &dest->supp_pubinfo, + &dest->supp_pubinfo_len) + || !ossl_prov_memdup(src->supp_privinfo, src->supp_privinfo_len, + &dest->supp_privinfo, + &dest->supp_privinfo_len) + || !ossl_prov_digest_copy(&dest->digest, &src->digest)) + goto err; + dest->cek_oid = src->cek_oid; + dest->cek_oid_len = src->cek_oid_len; + dest->dkm_len = src->dkm_len; + dest->use_keybits = src->use_keybits; + OSSL_FIPS_IND_COPY(dest, src) + } + return dest; + + err: + x942kdf_free(dest); + return NULL; +} + +static int x942kdf_set_buffer(unsigned char **out, size_t *out_len, + const OSSL_PARAM *p) +{ + if (p->data_size == 0 || p->data == NULL) + return 1; + + OPENSSL_free(*out); + *out = NULL; + return OSSL_PARAM_get_octet_string(p, (void **)out, 0, out_len); +} + +static size_t x942kdf_size(KDF_X942 *ctx) +{ + int len; + const EVP_MD *md = ossl_prov_digest_md(&ctx->digest); + + if (md == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST); + return 0; + } + len = EVP_MD_get_size(md); + return (len <= 0) ? 0 : (size_t)len; +} + +#ifdef FIPS_MODULE +static int fips_x942kdf_key_check_passed(KDF_X942 *ctx) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + int key_approved = ossl_kdf_check_key_size(ctx->secret_len); + + if (!key_approved) { + if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE0, + libctx, "X942KDF", "Key size", + ossl_fips_config_x942kdf_key_check)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + } + return 1; +} +#endif + +static int x942kdf_derive(void *vctx, unsigned char *key, size_t keylen, + const OSSL_PARAM params[]) +{ + KDF_X942 *ctx = (KDF_X942 *)vctx; + const EVP_MD *md; + int ret = 0; + unsigned char *ctr; + unsigned char *der = NULL; + size_t der_len = 0; + + if (!ossl_prov_is_running() || !x942kdf_set_ctx_params(ctx, params)) + return 0; + + /* + * These 2 options encode to the same field so only one of them should be + * active at once. + */ + if (ctx->use_keybits && ctx->supp_pubinfo != NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_PUBINFO); + return 0; + } + /* + * If the blob of acvp data is used then the individual info fields that it + * replaces should not also be defined. + */ + if (ctx->acvpinfo != NULL + && (ctx->partyuinfo != NULL + || ctx->partyvinfo != NULL + || ctx->supp_pubinfo != NULL + || ctx->supp_privinfo != NULL)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DATA); + return 0; + } + if (ctx->secret == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_SECRET); + return 0; + } + md = ossl_prov_digest_md(&ctx->digest); + if (md == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST); + return 0; + } + if (ctx->cek_oid == NULL || ctx->cek_oid_len == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_CEK_ALG); + return 0; + } + if (ctx->partyuinfo != NULL && ctx->partyuinfo_len >= X942KDF_MAX_INLEN) { + /* + * Note the ukm length MUST be 512 bits if it is used. + * For backwards compatibility the old check is being done. + */ + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_UKM_LENGTH); + return 0; + } + /* generate the otherinfo der */ + if (!x942_encode_otherinfo(ctx->use_keybits ? ctx->dkm_len : 0, + ctx->cek_oid, ctx->cek_oid_len, + ctx->acvpinfo, ctx->acvpinfo_len, + ctx->partyuinfo, ctx->partyuinfo_len, + ctx->partyvinfo, ctx->partyvinfo_len, + ctx->supp_pubinfo, ctx->supp_pubinfo_len, + ctx->supp_privinfo, ctx->supp_privinfo_len, + &der, &der_len, &ctr)) { + ERR_raise(ERR_LIB_PROV, PROV_R_BAD_ENCODING); + return 0; + } + ret = x942kdf_hash_kdm(md, ctx->secret, ctx->secret_len, + der, der_len, ctr, key, keylen); + OPENSSL_free(der); + return ret; +} + +static int x942kdf_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p, *pq; + KDF_X942 *ctx = vctx; + OSSL_LIB_CTX *provctx = PROV_LIBCTX_OF(ctx->provctx); + const char *propq = NULL; + const EVP_MD *md; + size_t id; + + if (ossl_param_is_empty(params)) + return 1; + + if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE0, params, + OSSL_KDF_PARAM_FIPS_KEY_CHECK)) + return 0; + + if (OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_DIGEST) != NULL) { + if (!ossl_prov_digest_load_from_params(&ctx->digest, params, provctx)) + return 0; + md = ossl_prov_digest_md(&ctx->digest); + if (EVP_MD_xof(md)) { + ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED); + return 0; + } + } + + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SECRET); + if (p == NULL) + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KEY); + if (p != NULL) { + if (!x942kdf_set_buffer(&ctx->secret, &ctx->secret_len, p)) + return 0; +#ifdef FIPS_MODULE + if (!fips_x942kdf_key_check_passed(ctx)) + return 0; +#endif + } + + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_X942_ACVPINFO); + if (p != NULL + && !x942kdf_set_buffer(&ctx->acvpinfo, &ctx->acvpinfo_len, p)) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_X942_PARTYUINFO); + if (p == NULL) + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_UKM); + if (p != NULL + && !x942kdf_set_buffer(&ctx->partyuinfo, &ctx->partyuinfo_len, p)) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_X942_PARTYVINFO); + if (p != NULL + && !x942kdf_set_buffer(&ctx->partyvinfo, &ctx->partyvinfo_len, p)) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_X942_USE_KEYBITS); + if (p != NULL && !OSSL_PARAM_get_int(p, &ctx->use_keybits)) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_X942_SUPP_PUBINFO); + if (p != NULL) { + if (!x942kdf_set_buffer(&ctx->supp_pubinfo, &ctx->supp_pubinfo_len, p)) + return 0; + ctx->use_keybits = 0; + } + + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_X942_SUPP_PRIVINFO); + if (p != NULL + && !x942kdf_set_buffer(&ctx->supp_privinfo, &ctx->supp_privinfo_len, p)) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_CEK_ALG); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_UTF8_STRING) + return 0; + pq = OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_PROPERTIES); + /* + * We already grab the properties during ossl_prov_digest_load_from_params() + * so there is no need to check the validity again.. + */ + if (pq != NULL) + propq = p->data; + if (find_alg_id(provctx, p->data, propq, &id) == 0) + return 0; + ctx->cek_oid = kek_algs[id].oid; + ctx->cek_oid_len = kek_algs[id].oid_len; + ctx->dkm_len = kek_algs[id].keklen; + } + return 1; +} + +static const OSSL_PARAM *x942kdf_settable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SECRET, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_KEY, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_UKM, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_X942_ACVPINFO, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_X942_PARTYUINFO, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_X942_PARTYVINFO, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_X942_SUPP_PUBINFO, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KDF_PARAM_X942_SUPP_PRIVINFO, NULL, 0), + OSSL_PARAM_int(OSSL_KDF_PARAM_X942_USE_KEYBITS, NULL), + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_CEK_ALG, NULL, 0), + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_KEY_CHECK) + OSSL_PARAM_END + }; + return known_settable_ctx_params; +} + +static int x942kdf_get_ctx_params(void *vctx, OSSL_PARAM params[]) +{ + KDF_X942 *ctx = (KDF_X942 *)vctx; + OSSL_PARAM *p; + + p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE); + if (p != NULL && !OSSL_PARAM_set_size_t(p, x942kdf_size(ctx))) + return 0; + + if (!OSSL_FIPS_IND_GET_CTX_PARAM(ctx, params)) + return 0; + return 1; +} + +static const OSSL_PARAM *x942kdf_gettable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL), + OSSL_FIPS_IND_GETTABLE_CTX_PARAM() + OSSL_PARAM_END + }; + return known_gettable_ctx_params; +} + +const OSSL_DISPATCH ossl_kdf_x942_kdf_functions[] = { + { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))x942kdf_new }, + { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))x942kdf_dup }, + { OSSL_FUNC_KDF_FREECTX, (void(*)(void))x942kdf_free }, + { OSSL_FUNC_KDF_RESET, (void(*)(void))x942kdf_reset }, + { OSSL_FUNC_KDF_DERIVE, (void(*)(void))x942kdf_derive }, + { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS, + (void(*)(void))x942kdf_settable_ctx_params }, + { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))x942kdf_set_ctx_params }, + { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS, + (void(*)(void))x942kdf_gettable_ctx_params }, + { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))x942kdf_get_ctx_params }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/kem/build.info b/crypto/openssl/providers/implementations/kem/build.info new file mode 100644 index 000000000000..05b8b185127d --- /dev/null +++ b/crypto/openssl/providers/implementations/kem/build.info @@ -0,0 +1,26 @@ +# We make separate GOAL variables for each algorithm, to make it easy to +# switch each to the Legacy provider when needed. + +$RSA_KEM_GOAL=../../libdefault.a ../../libfips.a +$EC_KEM_GOAL=../../libdefault.a +$TEMPLATE_KEM_GOAL=../../libtemplate.a +$ML_KEM_GOAL=../../libdefault.a ../../libfips.a +$TLS_ML_KEM_HYBRID_GOAL=../../libdefault.a ../../libfips.a + +SOURCE[$RSA_KEM_GOAL]=rsa_kem.c + +IF[{- !$disabled{ec} -}] + SOURCE[$EC_KEM_GOAL]=kem_util.c ec_kem.c + IF[{- !$disabled{ecx} -}] + SOURCE[$EC_KEM_GOAL]=ecx_kem.c + ENDIF +ENDIF + +IF[{- !$disabled{'ml-kem'} -}] + IF[{- !$disabled{ec} -}] + SOURCE[$TLS_ML_KEM_HYBRID_GOAL]=mlx_kem.c + ENDIF + SOURCE[$ML_KEM_GOAL] = ml_kem_kem.c +ENDIF + +SOURCE[$TEMPLATE_KEM_GOAL]=template_kem.c diff --git a/crypto/openssl/providers/implementations/kem/ec_kem.c b/crypto/openssl/providers/implementations/kem/ec_kem.c new file mode 100644 index 000000000000..975f41144ad0 --- /dev/null +++ b/crypto/openssl/providers/implementations/kem/ec_kem.c @@ -0,0 +1,815 @@ +/* + * Copyright 2022-2025 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 + */ + +/* + * The following implementation is part of RFC 9180 related to DHKEM using + * EC keys (i.e. P-256, P-384 and P-521) + * References to Sections in the comments below refer to RFC 9180. + */ + +#include "internal/deprecated.h" + +#include <openssl/crypto.h> +#include <openssl/evp.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/ec.h> +#include <openssl/params.h> +#include <openssl/err.h> +#include <openssl/proverr.h> +#include <openssl/kdf.h> +#include <openssl/rand.h> +#include "prov/provider_ctx.h" +#include "prov/implementations.h" +#include "prov/securitycheck.h" +#include "prov/providercommon.h" + +#include <openssl/hpke.h> +#include "internal/hpke_util.h" +#include "crypto/ec.h" +#include "prov/ecx.h" +#include "eckem.h" + +typedef struct { + EC_KEY *recipient_key; + EC_KEY *sender_authkey; + OSSL_LIB_CTX *libctx; + char *propq; + unsigned int mode; + unsigned int op; + unsigned char *ikm; + size_t ikmlen; + const char *kdfname; + const OSSL_HPKE_KEM_INFO *info; +} PROV_EC_CTX; + +static OSSL_FUNC_kem_newctx_fn eckem_newctx; +static OSSL_FUNC_kem_encapsulate_init_fn eckem_encapsulate_init; +static OSSL_FUNC_kem_auth_encapsulate_init_fn eckem_auth_encapsulate_init; +static OSSL_FUNC_kem_encapsulate_fn eckem_encapsulate; +static OSSL_FUNC_kem_decapsulate_init_fn eckem_decapsulate_init; +static OSSL_FUNC_kem_auth_decapsulate_init_fn eckem_auth_decapsulate_init; +static OSSL_FUNC_kem_decapsulate_fn eckem_decapsulate; +static OSSL_FUNC_kem_freectx_fn eckem_freectx; +static OSSL_FUNC_kem_set_ctx_params_fn eckem_set_ctx_params; +static OSSL_FUNC_kem_settable_ctx_params_fn eckem_settable_ctx_params; + +/* ASCII: "KEM", in hex for EBCDIC compatibility */ +static const char LABEL_KEM[] = "\x4b\x45\x4d"; + +static int eckey_check(const EC_KEY *ec, int requires_privatekey) +{ + int rv = 0; + BN_CTX *bnctx = NULL; + BIGNUM *rem = NULL; + const BIGNUM *priv = EC_KEY_get0_private_key(ec); + const EC_POINT *pub = EC_KEY_get0_public_key(ec); + + /* Keys always require a public component */ + if (pub == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY); + return 0; + } + if (priv == NULL) { + return (requires_privatekey == 0); + } else { + /* If there is a private key, check that is non zero (mod order) */ + const EC_GROUP *group = EC_KEY_get0_group(ec); + const BIGNUM *order = EC_GROUP_get0_order(group); + + bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(ec)); + rem = BN_new(); + + if (order != NULL && rem != NULL && bnctx != NULL) { + rv = BN_mod(rem, priv, order, bnctx) + && !BN_is_zero(rem); + } + } + BN_free(rem); + BN_CTX_free(bnctx); + return rv; +} + +/* Returns NULL if the curve is not supported */ +static const char *ec_curvename_get0(const EC_KEY *ec) +{ + const EC_GROUP *group = EC_KEY_get0_group(ec); + + return EC_curve_nid2nist(EC_GROUP_get_curve_name(group)); +} + +/* + * Set the recipient key, and free any existing key. + * ec can be NULL. + * The ec key may have only a private or public component + * (but it must have a group). + */ +static int recipient_key_set(PROV_EC_CTX *ctx, EC_KEY *ec) +{ + EC_KEY_free(ctx->recipient_key); + ctx->recipient_key = NULL; + + if (ec != NULL) { + const char *curve = ec_curvename_get0(ec); + + if (curve == NULL) + return -2; + ctx->info = ossl_HPKE_KEM_INFO_find_curve(curve); + if (ctx->info == NULL) + return -2; + if (!EC_KEY_up_ref(ec)) + return 0; + ctx->recipient_key = ec; + ctx->kdfname = "HKDF"; + } + return 1; +} + +/* + * Set the senders auth key, and free any existing auth key. + * ec can be NULL. + */ +static int sender_authkey_set(PROV_EC_CTX *ctx, EC_KEY *ec) +{ + EC_KEY_free(ctx->sender_authkey); + ctx->sender_authkey = NULL; + + if (ec != NULL) { + if (!EC_KEY_up_ref(ec)) + return 0; + ctx->sender_authkey = ec; + } + return 1; +} + +/* + * Serializes a encoded public key buffer into a EC public key. + * Params: + * in Contains the group. + * pubbuf The encoded public key buffer + * Returns: The created public EC key, or NULL if there is an error. + */ +static EC_KEY *eckey_frompub(EC_KEY *in, + const unsigned char *pubbuf, size_t pubbuflen) +{ + EC_KEY *key; + + key = EC_KEY_new_ex(ossl_ec_key_get_libctx(in), ossl_ec_key_get0_propq(in)); + if (key == NULL) + goto err; + if (!EC_KEY_set_group(key, EC_KEY_get0_group(in))) + goto err; + if (!EC_KEY_oct2key(key, pubbuf, pubbuflen, NULL)) + goto err; + return key; +err: + EC_KEY_free(key); + return NULL; +} + +/* + * Deserialises a EC public key into a encoded byte array. + * Returns: 1 if successful or 0 otherwise. + */ +static int ecpubkey_todata(const EC_KEY *ec, unsigned char *out, size_t *outlen, + size_t maxoutlen) +{ + const EC_POINT *pub; + const EC_GROUP *group; + + group = EC_KEY_get0_group(ec); + pub = EC_KEY_get0_public_key(ec); + *outlen = EC_POINT_point2oct(group, pub, POINT_CONVERSION_UNCOMPRESSED, + out, maxoutlen, NULL); + return *outlen != 0; +} + +static void *eckem_newctx(void *provctx) +{ + PROV_EC_CTX *ctx = OPENSSL_zalloc(sizeof(PROV_EC_CTX)); + + if (ctx == NULL) + return NULL; + ctx->libctx = PROV_LIBCTX_OF(provctx); + ctx->mode = KEM_MODE_DHKEM; + + return ctx; +} + +static void eckem_freectx(void *vectx) +{ + PROV_EC_CTX *ctx = (PROV_EC_CTX *)vectx; + + OPENSSL_clear_free(ctx->ikm, ctx->ikmlen); + recipient_key_set(ctx, NULL); + sender_authkey_set(ctx, NULL); + OPENSSL_free(ctx); +} + +static int ossl_ec_match_params(const EC_KEY *key1, const EC_KEY *key2) +{ + int ret; + BN_CTX *ctx = NULL; + const EC_GROUP *group1 = EC_KEY_get0_group(key1); + const EC_GROUP *group2 = EC_KEY_get0_group(key2); + + ctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(key1)); + if (ctx == NULL) + return 0; + + ret = group1 != NULL + && group2 != NULL + && EC_GROUP_cmp(group1, group2, ctx) == 0; + if (!ret) + ERR_raise(ERR_LIB_PROV, PROV_R_MISMATCHING_DOMAIN_PARAMETERS); + BN_CTX_free(ctx); + return ret; +} + +static int eckem_init(void *vctx, int operation, void *vec, void *vauth, + const OSSL_PARAM params[]) +{ + int rv; + PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx; + EC_KEY *ec = vec; + EC_KEY *auth = vauth; + + if (!ossl_prov_is_running()) + return 0; + + if (!eckey_check(ec, operation == EVP_PKEY_OP_DECAPSULATE)) + return 0; + rv = recipient_key_set(ctx, ec); + if (rv <= 0) + return rv; + + if (auth != NULL) { + if (!ossl_ec_match_params(ec, auth) + || !eckey_check(auth, operation == EVP_PKEY_OP_ENCAPSULATE) + || !sender_authkey_set(ctx, auth)) + return 0; + } + + ctx->op = operation; + return eckem_set_ctx_params(vctx, params); +} + +static int eckem_encapsulate_init(void *vctx, void *vec, + const OSSL_PARAM params[]) +{ + return eckem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, vec, NULL, params); +} + +static int eckem_decapsulate_init(void *vctx, void *vec, + const OSSL_PARAM params[]) +{ + return eckem_init(vctx, EVP_PKEY_OP_DECAPSULATE, vec, NULL, params); +} + +static int eckem_auth_encapsulate_init(void *vctx, void *vecx, void *vauthpriv, + const OSSL_PARAM params[]) +{ + return eckem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, vecx, vauthpriv, params); +} + +static int eckem_auth_decapsulate_init(void *vctx, void *vecx, void *vauthpub, + const OSSL_PARAM params[]) +{ + return eckem_init(vctx, EVP_PKEY_OP_DECAPSULATE, vecx, vauthpub, params); +} + +static int eckem_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx; + const OSSL_PARAM *p; + int mode; + + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_IKME); + if (p != NULL) { + void *tmp = NULL; + size_t tmplen = 0; + + if (p->data != NULL && p->data_size != 0) { + if (!OSSL_PARAM_get_octet_string(p, &tmp, 0, &tmplen)) + return 0; + } + OPENSSL_clear_free(ctx->ikm, ctx->ikmlen); + /* Set the ephemeral seed */ + ctx->ikm = tmp; + ctx->ikmlen = tmplen; + } + + p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_OPERATION); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_UTF8_STRING) + return 0; + mode = ossl_eckem_modename2id(p->data); + if (mode == KEM_MODE_UNDEFINED) + return 0; + ctx->mode = mode; + } + return 1; +} + +static const OSSL_PARAM known_settable_eckem_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_KEM_PARAM_OPERATION, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KEM_PARAM_IKME, NULL, 0), + OSSL_PARAM_END +}; + +static const OSSL_PARAM *eckem_settable_ctx_params(ossl_unused void *vctx, + ossl_unused void *provctx) +{ + return known_settable_eckem_ctx_params; +} + +/* + * See Section 4.1 DH-Based KEM (DHKEM) ExtractAndExpand + */ +static int dhkem_extract_and_expand(EVP_KDF_CTX *kctx, + unsigned char *okm, size_t okmlen, + uint16_t kemid, + const unsigned char *dhkm, size_t dhkmlen, + const unsigned char *kemctx, + size_t kemctxlen) +{ + uint8_t suiteid[2]; + uint8_t prk[EVP_MAX_MD_SIZE]; + size_t prklen = okmlen; + int ret; + + if (prklen > sizeof(prk)) + return 0; + + suiteid[0] = (kemid >> 8) & 0xff; + suiteid[1] = kemid & 0xff; + + ret = ossl_hpke_labeled_extract(kctx, prk, prklen, + NULL, 0, LABEL_KEM, suiteid, sizeof(suiteid), + OSSL_DHKEM_LABEL_EAE_PRK, dhkm, dhkmlen) + && ossl_hpke_labeled_expand(kctx, okm, okmlen, prk, prklen, + LABEL_KEM, suiteid, sizeof(suiteid), + OSSL_DHKEM_LABEL_SHARED_SECRET, + kemctx, kemctxlen); + OPENSSL_cleanse(prk, prklen); + return ret; +} + +/* + * See Section 7.1.3 DeriveKeyPair. + * + * This function is used by ec keygen. + * (For this reason it does not use any of the state stored in PROV_EC_CTX). + * + * Params: + * ec An initialized ec key. + * priv The buffer to store the generated private key into (it is assumed + * this is of length alg->encodedprivlen). + * ikm buffer containing the input key material (seed). This must be set. + * ikmlen size of the ikm buffer in bytes + * Returns: + * 1 if successful or 0 otherwise. + */ +int ossl_ec_dhkem_derive_private(EC_KEY *ec, BIGNUM *priv, + const unsigned char *ikm, size_t ikmlen) +{ + int ret = 0; + EVP_KDF_CTX *kdfctx = NULL; + uint8_t suiteid[2]; + unsigned char prk[OSSL_HPKE_MAX_SECRET]; + unsigned char privbuf[OSSL_HPKE_MAX_PRIVATE]; + const BIGNUM *order; + unsigned char counter = 0; + const char *curve = ec_curvename_get0(ec); + const OSSL_HPKE_KEM_INFO *info; + + if (curve == NULL) + return -2; + + info = ossl_HPKE_KEM_INFO_find_curve(curve); + if (info == NULL) + return -2; + + kdfctx = ossl_kdf_ctx_create("HKDF", info->mdname, + ossl_ec_key_get_libctx(ec), + ossl_ec_key_get0_propq(ec)); + if (kdfctx == NULL) + return 0; + + /* ikmlen should have a length of at least Nsk */ + if (ikmlen < info->Nsk) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_INPUT_LENGTH, + "ikm length is :%zu, should be at least %zu", + ikmlen, info->Nsk); + goto err; + } + + suiteid[0] = info->kem_id / 256; + suiteid[1] = info->kem_id % 256; + + if (!ossl_hpke_labeled_extract(kdfctx, prk, info->Nsecret, + NULL, 0, LABEL_KEM, suiteid, sizeof(suiteid), + OSSL_DHKEM_LABEL_DKP_PRK, ikm, ikmlen)) + goto err; + + order = EC_GROUP_get0_order(EC_KEY_get0_group(ec)); + do { + if (!ossl_hpke_labeled_expand(kdfctx, privbuf, info->Nsk, + prk, info->Nsecret, + LABEL_KEM, suiteid, sizeof(suiteid), + OSSL_DHKEM_LABEL_CANDIDATE, + &counter, 1)) + goto err; + privbuf[0] &= info->bitmask; + if (BN_bin2bn(privbuf, info->Nsk, priv) == NULL) + goto err; + if (counter == 0xFF) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GENERATE_KEY); + goto err; + } + counter++; + } while (BN_is_zero(priv) || BN_cmp(priv, order) >= 0); + ret = 1; +err: + OPENSSL_cleanse(prk, sizeof(prk)); + OPENSSL_cleanse(privbuf, sizeof(privbuf)); + EVP_KDF_CTX_free(kdfctx); + return ret; +} + +/* + * Do a keygen operation without having to use EVP_PKEY. + * Params: + * ctx Context object + * ikm The seed material - if this is NULL, then a random seed is used. + * Returns: + * The generated EC key, or NULL on failure. + */ +static EC_KEY *derivekey(PROV_EC_CTX *ctx, + const unsigned char *ikm, size_t ikmlen) +{ + int ret = 0; + EC_KEY *key; + unsigned char *seed = (unsigned char *)ikm; + size_t seedlen = ikmlen; + unsigned char tmpbuf[OSSL_HPKE_MAX_PRIVATE]; + + key = EC_KEY_new_ex(ctx->libctx, ctx->propq); + if (key == NULL) + goto err; + if (!EC_KEY_set_group(key, EC_KEY_get0_group(ctx->recipient_key))) + goto err; + + /* Generate a random seed if there is no input ikm */ + if (seed == NULL || seedlen == 0) { + seedlen = ctx->info->Nsk; + if (seedlen > sizeof(tmpbuf)) + goto err; + if (RAND_priv_bytes_ex(ctx->libctx, tmpbuf, seedlen, 0) <= 0) + goto err; + seed = tmpbuf; + } + ret = ossl_ec_generate_key_dhkem(key, seed, seedlen); +err: + if (seed != ikm) + OPENSSL_cleanse(seed, seedlen); + if (ret <= 0) { + EC_KEY_free(key); + key = NULL; + } + return key; +} + +/* + * Before doing a key exchange the public key of the peer needs to be checked + * Note that the group check is not done here as we have already checked + * that it only uses one of the approved curve names when the key was set. + * + * Returns 1 if the public key is valid, or 0 if it fails. + */ +static int check_publickey(const EC_KEY *pub) +{ + int ret = 0; + BN_CTX *bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(pub)); + + if (bnctx == NULL) + return 0; + ret = ossl_ec_key_public_check(pub, bnctx); + BN_CTX_free(bnctx); + + return ret; +} + +/* + * Do an ecdh key exchange. + * dhkm = DH(sender, peer) + * + * NOTE: Instead of using EVP_PKEY_derive() API's, we use EC_KEY operations + * to avoid messy conversions back to EVP_PKEY. + * + * Returns the size of the secret if successful, or 0 otherwise, + */ +static int generate_ecdhkm(const EC_KEY *sender, const EC_KEY *peer, + unsigned char *out, size_t maxout, + unsigned int secretsz) +{ + const EC_GROUP *group = EC_KEY_get0_group(sender); + size_t secretlen = (EC_GROUP_get_degree(group) + 7) / 8; + + if (secretlen != secretsz || secretlen > maxout) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "secretsz invalid"); + return 0; + } + + if (!check_publickey(peer)) + return 0; + return ECDH_compute_key(out, secretlen, EC_KEY_get0_public_key(peer), + sender, NULL) > 0; +} + +/* + * Derive a secret using ECDH (code is shared by the encap and decap) + * + * dhkm = Concat(ecdh(privkey1, peerkey1), ecdh(privkey2, peerkey2) + * kemctx = Concat(sender_pub, recipient_pub, ctx->sender_authkey) + * secret = dhkem_extract_and_expand(kemid, dhkm, kemctx); + * + * Params: + * ctx Object that contains algorithm state and constants. + * secret The returned secret (with a length ctx->alg->secretlen bytes). + * privkey1 A private key used for ECDH key derivation. + * peerkey1 A public key used for ECDH key derivation with privkey1 + * privkey2 A optional private key used for a second ECDH key derivation. + * It can be NULL. + * peerkey2 A optional public key used for a second ECDH key derivation + * with privkey2,. It can be NULL. + * sender_pub The senders public key in encoded form. + * recipient_pub The recipients public key in encoded form. + * Notes: + * The second ecdh() is only used for the HPKE auth modes when both privkey2 + * and peerkey2 are non NULL (i.e. ctx->sender_authkey is not NULL). + */ +static int derive_secret(PROV_EC_CTX *ctx, unsigned char *secret, + const EC_KEY *privkey1, const EC_KEY *peerkey1, + const EC_KEY *privkey2, const EC_KEY *peerkey2, + const unsigned char *sender_pub, + const unsigned char *recipient_pub) +{ + int ret = 0; + EVP_KDF_CTX *kdfctx = NULL; + unsigned char sender_authpub[OSSL_HPKE_MAX_PUBLIC]; + unsigned char dhkm[OSSL_HPKE_MAX_PRIVATE * 2]; + unsigned char kemctx[OSSL_HPKE_MAX_PUBLIC * 3]; + size_t sender_authpublen; + size_t kemctxlen = 0, dhkmlen = 0; + const OSSL_HPKE_KEM_INFO *info = ctx->info; + size_t encodedpublen = info->Npk; + size_t encodedprivlen = info->Nsk; + int auth = ctx->sender_authkey != NULL; + + if (!generate_ecdhkm(privkey1, peerkey1, dhkm, sizeof(dhkm), encodedprivlen)) + goto err; + dhkmlen = encodedprivlen; + kemctxlen = 2 * encodedpublen; + + /* Concat the optional second ECDH (used for Auth) */ + if (auth) { + /* Get the public key of the auth sender in encoded form */ + if (!ecpubkey_todata(ctx->sender_authkey, sender_authpub, + &sender_authpublen, sizeof(sender_authpub))) + goto err; + if (sender_authpublen != encodedpublen) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, + "Invalid sender auth public key"); + goto err; + } + if (!generate_ecdhkm(privkey2, peerkey2, + dhkm + dhkmlen, sizeof(dhkm) - dhkmlen, + encodedprivlen)) + goto err; + dhkmlen += encodedprivlen; + kemctxlen += encodedpublen; + } + if (kemctxlen > sizeof(kemctx)) + goto err; + + /* kemctx is the concat of both sides encoded public key */ + memcpy(kemctx, sender_pub, info->Npk); + memcpy(kemctx + info->Npk, recipient_pub, info->Npk); + if (auth) + memcpy(kemctx + 2 * encodedpublen, sender_authpub, encodedpublen); + kdfctx = ossl_kdf_ctx_create(ctx->kdfname, info->mdname, + ctx->libctx, ctx->propq); + if (kdfctx == NULL) + goto err; + if (!dhkem_extract_and_expand(kdfctx, secret, info->Nsecret, + info->kem_id, dhkm, dhkmlen, + kemctx, kemctxlen)) + goto err; + ret = 1; +err: + OPENSSL_cleanse(dhkm, dhkmlen); + EVP_KDF_CTX_free(kdfctx); + return ret; +} + +/* + * Do a DHKEM encapsulate operation. + * + * See Section 4.1 Encap() and AuthEncap() + * + * Params: + * ctx A context object holding the recipients public key and the + * optional senders auth private key. + * enc A buffer to return the senders ephemeral public key. + * Setting this to NULL allows the enclen and secretlen to return + * values, without calculating the secret. + * enclen Passes in the max size of the enc buffer and returns the + * encoded public key length. + * secret A buffer to return the calculated shared secret. + * secretlen Passes in the max size of the secret buffer and returns the + * secret length. + * Returns: 1 on success or 0 otherwise. + */ +static int dhkem_encap(PROV_EC_CTX *ctx, + unsigned char *enc, size_t *enclen, + unsigned char *secret, size_t *secretlen) +{ + int ret = 0; + EC_KEY *sender_ephemkey = NULL; + unsigned char sender_pub[OSSL_HPKE_MAX_PUBLIC]; + unsigned char recipient_pub[OSSL_HPKE_MAX_PUBLIC]; + size_t sender_publen, recipient_publen; + const OSSL_HPKE_KEM_INFO *info = ctx->info; + + if (enc == NULL) { + if (enclen == NULL && secretlen == NULL) + return 0; + if (enclen != NULL) + *enclen = info->Nenc; + if (secretlen != NULL) + *secretlen = info->Nsecret; + return 1; + } + + if (*secretlen < info->Nsecret) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small"); + return 0; + } + if (*enclen < info->Nenc) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*enclen too small"); + return 0; + } + + /* Create an ephemeral key */ + sender_ephemkey = derivekey(ctx, ctx->ikm, ctx->ikmlen); + if (sender_ephemkey == NULL) + goto err; + if (!ecpubkey_todata(sender_ephemkey, sender_pub, &sender_publen, + sizeof(sender_pub)) + || !ecpubkey_todata(ctx->recipient_key, recipient_pub, + &recipient_publen, sizeof(recipient_pub))) + goto err; + + if (sender_publen != info->Npk + || recipient_publen != sender_publen) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid public key"); + goto err; + } + + if (!derive_secret(ctx, secret, + sender_ephemkey, ctx->recipient_key, + ctx->sender_authkey, ctx->recipient_key, + sender_pub, recipient_pub)) + goto err; + + /* Return the senders ephemeral public key in encoded form */ + memcpy(enc, sender_pub, sender_publen); + *enclen = sender_publen; + *secretlen = info->Nsecret; + ret = 1; +err: + EC_KEY_free(sender_ephemkey); + return ret; +} + +/* + * Do a DHKEM decapsulate operation. + * See Section 4.1 Decap() and Auth Decap() + * + * Params: + * ctx A context object holding the recipients private key and the + * optional senders auth public key. + * secret A buffer to return the calculated shared secret. Setting this to + * NULL can be used to return the secretlen. + * secretlen Passes in the max size of the secret buffer and returns the + * secret length. + * enc A buffer containing the senders ephemeral public key that was returned + * from dhkem_encap(). + * enclen The length in bytes of enc. + * Returns: 1 If the shared secret is returned or 0 on error. + */ +static int dhkem_decap(PROV_EC_CTX *ctx, + unsigned char *secret, size_t *secretlen, + const unsigned char *enc, size_t enclen) +{ + int ret = 0; + EC_KEY *sender_ephempubkey = NULL; + const OSSL_HPKE_KEM_INFO *info = ctx->info; + unsigned char recipient_pub[OSSL_HPKE_MAX_PUBLIC]; + size_t recipient_publen; + size_t encodedpublen = info->Npk; + + if (secret == NULL) { + *secretlen = info->Nsecret; + return 1; + } + + if (*secretlen < info->Nsecret) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small"); + return 0; + } + if (enclen != encodedpublen) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid enc public key"); + return 0; + } + + sender_ephempubkey = eckey_frompub(ctx->recipient_key, enc, enclen); + if (sender_ephempubkey == NULL) + goto err; + if (!ecpubkey_todata(ctx->recipient_key, recipient_pub, &recipient_publen, + sizeof(recipient_pub))) + goto err; + if (recipient_publen != encodedpublen) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid recipient public key"); + goto err; + } + + if (!derive_secret(ctx, secret, + ctx->recipient_key, sender_ephempubkey, + ctx->recipient_key, ctx->sender_authkey, + enc, recipient_pub)) + goto err; + *secretlen = info->Nsecret; + ret = 1; +err: + EC_KEY_free(sender_ephempubkey); + return ret; +} + +static int eckem_encapsulate(void *vctx, unsigned char *out, size_t *outlen, + unsigned char *secret, size_t *secretlen) +{ + PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx; + + switch (ctx->mode) { + case KEM_MODE_DHKEM: + return dhkem_encap(ctx, out, outlen, secret, secretlen); + default: + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE); + return -2; + } +} + +static int eckem_decapsulate(void *vctx, unsigned char *out, size_t *outlen, + const unsigned char *in, size_t inlen) +{ + PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx; + + switch (ctx->mode) { + case KEM_MODE_DHKEM: + return dhkem_decap(ctx, out, outlen, in, inlen); + default: + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE); + return -2; + } +} + +const OSSL_DISPATCH ossl_ec_asym_kem_functions[] = { + { OSSL_FUNC_KEM_NEWCTX, (void (*)(void))eckem_newctx }, + { OSSL_FUNC_KEM_ENCAPSULATE_INIT, + (void (*)(void))eckem_encapsulate_init }, + { OSSL_FUNC_KEM_ENCAPSULATE, (void (*)(void))eckem_encapsulate }, + { OSSL_FUNC_KEM_DECAPSULATE_INIT, + (void (*)(void))eckem_decapsulate_init }, + { OSSL_FUNC_KEM_DECAPSULATE, (void (*)(void))eckem_decapsulate }, + { OSSL_FUNC_KEM_FREECTX, (void (*)(void))eckem_freectx }, + { OSSL_FUNC_KEM_SET_CTX_PARAMS, + (void (*)(void))eckem_set_ctx_params }, + { OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS, + (void (*)(void))eckem_settable_ctx_params }, + { OSSL_FUNC_KEM_AUTH_ENCAPSULATE_INIT, + (void (*)(void))eckem_auth_encapsulate_init }, + { OSSL_FUNC_KEM_AUTH_DECAPSULATE_INIT, + (void (*)(void))eckem_auth_decapsulate_init }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/kem/eckem.h b/crypto/openssl/providers/implementations/kem/eckem.h new file mode 100644 index 000000000000..2e46a0f2ffab --- /dev/null +++ b/crypto/openssl/providers/implementations/kem/eckem.h @@ -0,0 +1,13 @@ +/* + * 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 + */ + +#define KEM_MODE_UNDEFINED 0 +#define KEM_MODE_DHKEM 1 + +int ossl_eckem_modename2id(const char *name); diff --git a/crypto/openssl/providers/implementations/kem/ecx_kem.c b/crypto/openssl/providers/implementations/kem/ecx_kem.c new file mode 100644 index 000000000000..823662dd37c3 --- /dev/null +++ b/crypto/openssl/providers/implementations/kem/ecx_kem.c @@ -0,0 +1,705 @@ +/* + * Copyright 2022-2025 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 + */ + +/* + * The following implementation is part of RFC 9180 related to DHKEM using + * ECX keys (i.e. X25519 and X448) + * References to Sections in the comments below refer to RFC 9180. + */ + +#include "internal/deprecated.h" + +#include <string.h> +#include <openssl/crypto.h> +#include <openssl/evp.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/params.h> +#include <openssl/kdf.h> +#include <openssl/err.h> +#include <openssl/sha.h> +#include <openssl/rand.h> +#include <openssl/proverr.h> +#include "prov/provider_ctx.h" +#include "prov/implementations.h" +#include "prov/securitycheck.h" +#include "prov/providercommon.h" +#include "prov/ecx.h" +#include "crypto/ecx.h" +#include <openssl/hpke.h> +#include "internal/hpke_util.h" +#include "eckem.h" + +#define MAX_ECX_KEYLEN X448_KEYLEN + +/* KEM identifiers from Section 7.1 "Table 2 KEM IDs" */ +#define KEMID_X25519_HKDF_SHA256 0x20 +#define KEMID_X448_HKDF_SHA512 0x21 + +/* ASCII: "KEM", in hex for EBCDIC compatibility */ +static const char LABEL_KEM[] = "\x4b\x45\x4d"; + +typedef struct { + ECX_KEY *recipient_key; + ECX_KEY *sender_authkey; + OSSL_LIB_CTX *libctx; + char *propq; + unsigned int mode; + unsigned int op; + unsigned char *ikm; + size_t ikmlen; + const char *kdfname; + const OSSL_HPKE_KEM_INFO *info; +} PROV_ECX_CTX; + +static OSSL_FUNC_kem_newctx_fn ecxkem_newctx; +static OSSL_FUNC_kem_encapsulate_init_fn ecxkem_encapsulate_init; +static OSSL_FUNC_kem_encapsulate_fn ecxkem_encapsulate; +static OSSL_FUNC_kem_decapsulate_init_fn ecxkem_decapsulate_init; +static OSSL_FUNC_kem_decapsulate_fn ecxkem_decapsulate; +static OSSL_FUNC_kem_freectx_fn ecxkem_freectx; +static OSSL_FUNC_kem_set_ctx_params_fn ecxkem_set_ctx_params; +static OSSL_FUNC_kem_auth_encapsulate_init_fn ecxkem_auth_encapsulate_init; +static OSSL_FUNC_kem_auth_decapsulate_init_fn ecxkem_auth_decapsulate_init; + +/* + * Set KEM values as specified in Section 7.1 "Table 2 KEM IDs" + * There is only one set of values for X25519 and X448. + * Additional values could be set via set_params if required. + */ +static const OSSL_HPKE_KEM_INFO *get_kem_info(ECX_KEY *ecx) +{ + const char *name = NULL; + + if (ecx->type == ECX_KEY_TYPE_X25519) + name = SN_X25519; + else + name = SN_X448; + return ossl_HPKE_KEM_INFO_find_curve(name); +} + +/* + * Set the recipient key, and free any existing key. + * ecx can be NULL. The ecx key may have only a private or public component. + */ +static int recipient_key_set(PROV_ECX_CTX *ctx, ECX_KEY *ecx) +{ + ossl_ecx_key_free(ctx->recipient_key); + ctx->recipient_key = NULL; + if (ecx != NULL) { + ctx->info = get_kem_info(ecx); + if (ctx->info == NULL) + return -2; + ctx->kdfname = "HKDF"; + if (!ossl_ecx_key_up_ref(ecx)) + return 0; + ctx->recipient_key = ecx; + } + return 1; +} + +/* + * Set the senders auth key, and free any existing auth key. + * ecx can be NULL. + */ +static int sender_authkey_set(PROV_ECX_CTX *ctx, ECX_KEY *ecx) +{ + ossl_ecx_key_free(ctx->sender_authkey); + ctx->sender_authkey = NULL; + + if (ecx != NULL) { + if (!ossl_ecx_key_up_ref(ecx)) + return 0; + ctx->sender_authkey = ecx; + } + return 1; +} + +/* + * Serialize a public key from byte array's for the encoded public keys. + * ctx is used to access the key type. + * Returns: The created ECX_KEY or NULL on error. + */ +static ECX_KEY *ecxkey_pubfromdata(PROV_ECX_CTX *ctx, + const unsigned char *pubbuf, size_t pubbuflen) +{ + ECX_KEY *ecx = NULL; + OSSL_PARAM params[2], *p = params; + + *p++ = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PUB_KEY, + (char *)pubbuf, pubbuflen); + *p = OSSL_PARAM_construct_end(); + + ecx = ossl_ecx_key_new(ctx->libctx, ctx->recipient_key->type, 1, ctx->propq); + if (ecx == NULL) + return NULL; + if (ossl_ecx_key_fromdata(ecx, params, 0) <= 0) { + ossl_ecx_key_free(ecx); + ecx = NULL; + } + return ecx; +} + +static unsigned char *ecx_pubkey(ECX_KEY *ecx) +{ + if (ecx == NULL || !ecx->haspubkey) { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY); + return 0; + } + return ecx->pubkey; +} + +static void *ecxkem_newctx(void *provctx) +{ + PROV_ECX_CTX *ctx = OPENSSL_zalloc(sizeof(PROV_ECX_CTX)); + + if (ctx == NULL) + return NULL; + ctx->libctx = PROV_LIBCTX_OF(provctx); + ctx->mode = KEM_MODE_DHKEM; + + return ctx; +} + +static void ecxkem_freectx(void *vectx) +{ + PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vectx; + + OPENSSL_clear_free(ctx->ikm, ctx->ikmlen); + recipient_key_set(ctx, NULL); + sender_authkey_set(ctx, NULL); + OPENSSL_free(ctx); +} + +static int ecx_match_params(const ECX_KEY *key1, const ECX_KEY *key2) +{ + return (key1->type == key2->type && key1->keylen == key2->keylen); +} + +static int ecx_key_check(const ECX_KEY *ecx, int requires_privatekey) +{ + if (ecx->privkey == NULL) + return (requires_privatekey == 0); + return 1; +} + +static int ecxkem_init(void *vecxctx, int operation, void *vecx, void *vauth, + ossl_unused const OSSL_PARAM params[]) +{ + int rv; + PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vecxctx; + ECX_KEY *ecx = vecx; + ECX_KEY *auth = vauth; + + if (!ossl_prov_is_running()) + return 0; + + if (!ecx_key_check(ecx, operation == EVP_PKEY_OP_DECAPSULATE)) + return 0; + rv = recipient_key_set(ctx, ecx); + if (rv <= 0) + return rv; + + if (auth != NULL) { + if (!ecx_match_params(auth, ctx->recipient_key) + || !ecx_key_check(auth, operation == EVP_PKEY_OP_ENCAPSULATE) + || !sender_authkey_set(ctx, auth)) + return 0; + } + + ctx->op = operation; + return ecxkem_set_ctx_params(vecxctx, params); +} + +static int ecxkem_encapsulate_init(void *vecxctx, void *vecx, + const OSSL_PARAM params[]) +{ + return ecxkem_init(vecxctx, EVP_PKEY_OP_ENCAPSULATE, vecx, NULL, params); +} + +static int ecxkem_decapsulate_init(void *vecxctx, void *vecx, + const OSSL_PARAM params[]) +{ + return ecxkem_init(vecxctx, EVP_PKEY_OP_DECAPSULATE, vecx, NULL, params); +} + +static int ecxkem_auth_encapsulate_init(void *vctx, void *vecx, void *vauthpriv, + const OSSL_PARAM params[]) +{ + return ecxkem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, vecx, vauthpriv, params); +} + +static int ecxkem_auth_decapsulate_init(void *vctx, void *vecx, void *vauthpub, + const OSSL_PARAM params[]) +{ + return ecxkem_init(vctx, EVP_PKEY_OP_DECAPSULATE, vecx, vauthpub, params); +} + +static int ecxkem_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vctx; + const OSSL_PARAM *p; + int mode; + + if (ctx == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_IKME); + if (p != NULL) { + void *tmp = NULL; + size_t tmplen = 0; + + if (p->data != NULL && p->data_size != 0) { + if (!OSSL_PARAM_get_octet_string(p, &tmp, 0, &tmplen)) + return 0; + } + OPENSSL_clear_free(ctx->ikm, ctx->ikmlen); + ctx->ikm = tmp; + ctx->ikmlen = tmplen; + } + p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_OPERATION); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_UTF8_STRING) + return 0; + mode = ossl_eckem_modename2id(p->data); + if (mode == KEM_MODE_UNDEFINED) + return 0; + ctx->mode = mode; + } + return 1; +} + +static const OSSL_PARAM known_settable_ecxkem_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_KEM_PARAM_OPERATION, NULL, 0), + OSSL_PARAM_octet_string(OSSL_KEM_PARAM_IKME, NULL, 0), + OSSL_PARAM_END +}; + +static const OSSL_PARAM *ecxkem_settable_ctx_params(ossl_unused void *vctx, + ossl_unused void *provctx) +{ + return known_settable_ecxkem_ctx_params; +} + +/* + * See Section 4.1 DH-Based KEM (DHKEM) ExtractAndExpand + */ +static int dhkem_extract_and_expand(EVP_KDF_CTX *kctx, + unsigned char *okm, size_t okmlen, + uint16_t kemid, + const unsigned char *dhkm, size_t dhkmlen, + const unsigned char *kemctx, + size_t kemctxlen) +{ + uint8_t suiteid[2]; + uint8_t prk[EVP_MAX_MD_SIZE]; + size_t prklen = okmlen; /* Nh */ + int ret; + + if (prklen > sizeof(prk)) + return 0; + + suiteid[0] = (kemid >> 8) &0xff; + suiteid[1] = kemid & 0xff; + + ret = ossl_hpke_labeled_extract(kctx, prk, prklen, + NULL, 0, LABEL_KEM, suiteid, sizeof(suiteid), + OSSL_DHKEM_LABEL_EAE_PRK, dhkm, dhkmlen) + && ossl_hpke_labeled_expand(kctx, okm, okmlen, prk, prklen, + LABEL_KEM, suiteid, sizeof(suiteid), + OSSL_DHKEM_LABEL_SHARED_SECRET, + kemctx, kemctxlen); + OPENSSL_cleanse(prk, prklen); + return ret; +} + +/* + * See Section 7.1.3 DeriveKeyPair. + * + * This function is used by ecx keygen. + * (For this reason it does not use any of the state stored in PROV_ECX_CTX). + * + * Params: + * ecx An initialized ecx key. + * privout The buffer to store the generated private key into (it is assumed + * this is of length ecx->keylen). + * ikm buffer containing the input key material (seed). This must be non NULL. + * ikmlen size of the ikm buffer in bytes + * Returns: + * 1 if successful or 0 otherwise. + */ +int ossl_ecx_dhkem_derive_private(ECX_KEY *ecx, unsigned char *privout, + const unsigned char *ikm, size_t ikmlen) +{ + int ret = 0; + EVP_KDF_CTX *kdfctx = NULL; + unsigned char prk[EVP_MAX_MD_SIZE]; + uint8_t suiteid[2]; + const OSSL_HPKE_KEM_INFO *info = get_kem_info(ecx); + + /* ikmlen should have a length of at least Nsk */ + if (ikmlen < info->Nsk) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_INPUT_LENGTH, + "ikm length is :%zu, should be at least %zu", + ikmlen, info->Nsk); + goto err; + } + + kdfctx = ossl_kdf_ctx_create("HKDF", info->mdname, ecx->libctx, ecx->propq); + if (kdfctx == NULL) + return 0; + + suiteid[0] = info->kem_id / 256; + suiteid[1] = info->kem_id % 256; + + if (!ossl_hpke_labeled_extract(kdfctx, prk, info->Nsecret, + NULL, 0, LABEL_KEM, suiteid, sizeof(suiteid), + OSSL_DHKEM_LABEL_DKP_PRK, ikm, ikmlen)) + goto err; + + if (!ossl_hpke_labeled_expand(kdfctx, privout, info->Nsk, prk, info->Nsecret, + LABEL_KEM, suiteid, sizeof(suiteid), + OSSL_DHKEM_LABEL_SK, NULL, 0)) + goto err; + ret = 1; +err: + OPENSSL_cleanse(prk, sizeof(prk)); + EVP_KDF_CTX_free(kdfctx); + return ret; +} + +/* + * Do a keygen operation without having to use EVP_PKEY. + * Params: + * ctx Context object + * ikm The seed material - if this is NULL, then a random seed is used. + * Returns: + * The generated ECX key, or NULL on failure. + */ +static ECX_KEY *derivekey(PROV_ECX_CTX *ctx, + const unsigned char *ikm, size_t ikmlen) +{ + int ok = 0; + ECX_KEY *key; + unsigned char *privkey; + unsigned char *seed = (unsigned char *)ikm; + size_t seedlen = ikmlen; + unsigned char tmpbuf[OSSL_HPKE_MAX_PRIVATE]; + const OSSL_HPKE_KEM_INFO *info = ctx->info; + + key = ossl_ecx_key_new(ctx->libctx, ctx->recipient_key->type, 0, ctx->propq); + if (key == NULL) + return NULL; + privkey = ossl_ecx_key_allocate_privkey(key); + if (privkey == NULL) + goto err; + + /* Generate a random seed if there is no input ikm */ + if (seed == NULL || seedlen == 0) { + if (info->Nsk > sizeof(tmpbuf)) + goto err; + if (RAND_priv_bytes_ex(ctx->libctx, tmpbuf, info->Nsk, 0) <= 0) + goto err; + seed = tmpbuf; + seedlen = info->Nsk; + } + if (!ossl_ecx_dhkem_derive_private(key, privkey, seed, seedlen)) + goto err; + if (!ossl_ecx_public_from_private(key)) + goto err; + key->haspubkey = 1; + ok = 1; +err: + if (!ok) { + ossl_ecx_key_free(key); + key = NULL; + } + if (seed != ikm) + OPENSSL_cleanse(seed, seedlen); + return key; +} + +/* + * Do an ecxdh key exchange. + * dhkm = DH(sender, peer) + * + * NOTE: Instead of using EVP_PKEY_derive() API's, we use ECX_KEY operations + * to avoid messy conversions back to EVP_PKEY. + * + * Returns the size of the secret if successful, or 0 otherwise, + */ +static int generate_ecxdhkm(const ECX_KEY *sender, const ECX_KEY *peer, + unsigned char *out, size_t maxout, + unsigned int secretsz) +{ + size_t len = 0; + + /* NOTE: ossl_ecx_compute_key checks for shared secret being all zeros */ + return ossl_ecx_compute_key((ECX_KEY *)peer, (ECX_KEY *)sender, + sender->keylen, out, &len, maxout); +} + +/* + * Derive a secret using ECXDH (code is shared by the encap and decap) + * + * dhkm = Concat(ecxdh(privkey1, peerkey1), ecdh(privkey2, peerkey2) + * kemctx = Concat(sender_pub, recipient_pub, ctx->sender_authkey) + * secret = dhkem_extract_and_expand(kemid, dhkm, kemctx); + * + * Params: + * ctx Object that contains algorithm state and constants. + * secret The returned secret (with a length ctx->alg->secretlen bytes). + * privkey1 A private key used for ECXDH key derivation. + * peerkey1 A public key used for ECXDH key derivation with privkey1 + * privkey2 A optional private key used for a second ECXDH key derivation. + * It can be NULL. + * peerkey2 A optional public key used for a second ECXDH key derivation + * with privkey2,. It can be NULL. + * sender_pub The senders public key in encoded form. + * recipient_pub The recipients public key in encoded form. + * Notes: + * The second ecdh() is only used for the HPKE auth modes when both privkey2 + * and peerkey2 are non NULL (i.e. ctx->sender_authkey is not NULL). + */ +static int derive_secret(PROV_ECX_CTX *ctx, unsigned char *secret, + const ECX_KEY *privkey1, const ECX_KEY *peerkey1, + const ECX_KEY *privkey2, const ECX_KEY *peerkey2, + const unsigned char *sender_pub, + const unsigned char *recipient_pub) +{ + int ret = 0; + EVP_KDF_CTX *kdfctx = NULL; + unsigned char *sender_authpub = NULL; + unsigned char dhkm[MAX_ECX_KEYLEN * 2]; + unsigned char kemctx[MAX_ECX_KEYLEN * 3]; + size_t kemctxlen = 0, dhkmlen = 0; + const OSSL_HPKE_KEM_INFO *info = ctx->info; + int auth = ctx->sender_authkey != NULL; + size_t encodedkeylen = info->Npk; + + if (!generate_ecxdhkm(privkey1, peerkey1, dhkm, sizeof(dhkm), encodedkeylen)) + goto err; + dhkmlen = encodedkeylen; + + /* Concat the optional second ECXDH (used for Auth) */ + if (auth) { + if (!generate_ecxdhkm(privkey2, peerkey2, + dhkm + dhkmlen, sizeof(dhkm) - dhkmlen, + encodedkeylen)) + goto err; + /* Get the public key of the auth sender in encoded form */ + sender_authpub = ecx_pubkey(ctx->sender_authkey); + if (sender_authpub == NULL) + goto err; + dhkmlen += encodedkeylen; + } + kemctxlen = encodedkeylen + dhkmlen; + if (kemctxlen > sizeof(kemctx)) + goto err; + + /* kemctx is the concat of both sides encoded public key */ + memcpy(kemctx, sender_pub, encodedkeylen); + memcpy(kemctx + encodedkeylen, recipient_pub, encodedkeylen); + if (auth) + memcpy(kemctx + 2 * encodedkeylen, sender_authpub, encodedkeylen); + kdfctx = ossl_kdf_ctx_create(ctx->kdfname, info->mdname, + ctx->libctx, ctx->propq); + if (kdfctx == NULL) + goto err; + if (!dhkem_extract_and_expand(kdfctx, secret, info->Nsecret, + info->kem_id, dhkm, dhkmlen, + kemctx, kemctxlen)) + goto err; + ret = 1; +err: + OPENSSL_cleanse(dhkm, dhkmlen); + EVP_KDF_CTX_free(kdfctx); + return ret; +} + +/* + * Do a DHKEM encapsulate operation. + * + * See Section 4.1 Encap() and AuthEncap() + * + * Params: + * ctx A context object holding the recipients public key and the + * optional senders auth private key. + * enc A buffer to return the senders ephemeral public key. + * Setting this to NULL allows the enclen and secretlen to return + * values, without calculating the secret. + * enclen Passes in the max size of the enc buffer and returns the + * encoded public key length. + * secret A buffer to return the calculated shared secret. + * secretlen Passes in the max size of the secret buffer and returns the + * secret length. + * Returns: 1 on success or 0 otherwise. + */ +static int dhkem_encap(PROV_ECX_CTX *ctx, + unsigned char *enc, size_t *enclen, + unsigned char *secret, size_t *secretlen) +{ + int ret = 0; + ECX_KEY *sender_ephemkey = NULL; + unsigned char *sender_ephempub, *recipient_pub; + const OSSL_HPKE_KEM_INFO *info = ctx->info; + + if (enc == NULL) { + if (enclen == NULL && secretlen == NULL) + return 0; + if (enclen != NULL) + *enclen = info->Nenc; + if (secretlen != NULL) + *secretlen = info->Nsecret; + return 1; + } + + if (*secretlen < info->Nsecret) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small"); + return 0; + } + if (*enclen < info->Nenc) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*enclen too small"); + return 0; + } + + /* Create an ephemeral key */ + sender_ephemkey = derivekey(ctx, ctx->ikm, ctx->ikmlen); + + sender_ephempub = ecx_pubkey(sender_ephemkey); + recipient_pub = ecx_pubkey(ctx->recipient_key); + if (sender_ephempub == NULL || recipient_pub == NULL) + goto err; + + if (!derive_secret(ctx, secret, + sender_ephemkey, ctx->recipient_key, + ctx->sender_authkey, ctx->recipient_key, + sender_ephempub, recipient_pub)) + goto err; + + /* Return the public part of the ephemeral key */ + memcpy(enc, sender_ephempub, info->Nenc); + *enclen = info->Nenc; + *secretlen = info->Nsecret; + ret = 1; +err: + ossl_ecx_key_free(sender_ephemkey); + return ret; +} + +/* + * Do a DHKEM decapsulate operation. + * See Section 4.1 Decap() and Auth Decap() + * + * Params: + * ctx A context object holding the recipients private key and the + * optional senders auth public key. + * secret A buffer to return the calculated shared secret. Setting this to + * NULL can be used to return the secretlen. + * secretlen Passes in the max size of the secret buffer and returns the + * secret length. + * enc A buffer containing the senders ephemeral public key that was returned + * from dhkem_encap(). + * enclen The length in bytes of enc. + * Returns: 1 If the shared secret is returned or 0 on error. + */ +static int dhkem_decap(PROV_ECX_CTX *ctx, + unsigned char *secret, size_t *secretlen, + const unsigned char *enc, size_t enclen) +{ + int ret = 0; + ECX_KEY *recipient_privkey = ctx->recipient_key; + ECX_KEY *sender_ephempubkey = NULL; + const OSSL_HPKE_KEM_INFO *info = ctx->info; + unsigned char *recipient_pub; + + if (secret == NULL) { + *secretlen = info->Nsecret; + return 1; + } + if (*secretlen < info->Nsecret) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small"); + return 0; + } + if (enclen != info->Nenc) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid enc public key"); + return 0; + } + + /* Get the public part of the ephemeral key created by encap */ + sender_ephempubkey = ecxkey_pubfromdata(ctx, enc, enclen); + if (sender_ephempubkey == NULL) + goto err; + + recipient_pub = ecx_pubkey(recipient_privkey); + if (recipient_pub == NULL) + goto err; + + if (!derive_secret(ctx, secret, + ctx->recipient_key, sender_ephempubkey, + ctx->recipient_key, ctx->sender_authkey, + enc, recipient_pub)) + goto err; + + *secretlen = info->Nsecret; + ret = 1; +err: + ossl_ecx_key_free(sender_ephempubkey); + return ret; +} + +static int ecxkem_encapsulate(void *vctx, unsigned char *out, size_t *outlen, + unsigned char *secret, size_t *secretlen) +{ + PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vctx; + + switch (ctx->mode) { + case KEM_MODE_DHKEM: + return dhkem_encap(ctx, out, outlen, secret, secretlen); + default: + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE); + return -2; + } +} + +static int ecxkem_decapsulate(void *vctx, unsigned char *out, size_t *outlen, + const unsigned char *in, size_t inlen) +{ + PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vctx; + + switch (ctx->mode) { + case KEM_MODE_DHKEM: + return dhkem_decap(vctx, out, outlen, in, inlen); + default: + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE); + return -2; + } +} + +const OSSL_DISPATCH ossl_ecx_asym_kem_functions[] = { + { OSSL_FUNC_KEM_NEWCTX, (void (*)(void))ecxkem_newctx }, + { OSSL_FUNC_KEM_ENCAPSULATE_INIT, + (void (*)(void))ecxkem_encapsulate_init }, + { OSSL_FUNC_KEM_ENCAPSULATE, (void (*)(void))ecxkem_encapsulate }, + { OSSL_FUNC_KEM_DECAPSULATE_INIT, + (void (*)(void))ecxkem_decapsulate_init }, + { OSSL_FUNC_KEM_DECAPSULATE, (void (*)(void))ecxkem_decapsulate }, + { OSSL_FUNC_KEM_FREECTX, (void (*)(void))ecxkem_freectx }, + { OSSL_FUNC_KEM_SET_CTX_PARAMS, + (void (*)(void))ecxkem_set_ctx_params }, + { OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS, + (void (*)(void))ecxkem_settable_ctx_params }, + { OSSL_FUNC_KEM_AUTH_ENCAPSULATE_INIT, + (void (*)(void))ecxkem_auth_encapsulate_init }, + { OSSL_FUNC_KEM_AUTH_DECAPSULATE_INIT, + (void (*)(void))ecxkem_auth_decapsulate_init }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/kem/kem_util.c b/crypto/openssl/providers/implementations/kem/kem_util.c new file mode 100644 index 000000000000..1fd52e1c2d52 --- /dev/null +++ b/crypto/openssl/providers/implementations/kem/kem_util.c @@ -0,0 +1,37 @@ +/* + * 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 + */ + +#include <string.h> /* for memcpy() */ +#include <openssl/core_names.h> +#include <openssl/crypto.h> +#include "eckem.h" + +typedef struct { + unsigned int id; + const char *mode; +} KEM_MODE; + +static const KEM_MODE eckem_modename_id_map[] = { + { KEM_MODE_DHKEM, OSSL_KEM_PARAM_OPERATION_DHKEM }, + { 0, NULL } +}; + +int ossl_eckem_modename2id(const char *name) +{ + size_t i; + + if (name == NULL) + return KEM_MODE_UNDEFINED; + + for (i = 0; eckem_modename_id_map[i].mode != NULL; ++i) { + if (OPENSSL_strcasecmp(name, eckem_modename_id_map[i].mode) == 0) + return eckem_modename_id_map[i].id; + } + return KEM_MODE_UNDEFINED; +} diff --git a/crypto/openssl/providers/implementations/kem/ml_kem_kem.c b/crypto/openssl/providers/implementations/kem/ml_kem_kem.c new file mode 100644 index 000000000000..27aa3b819836 --- /dev/null +++ b/crypto/openssl/providers/implementations/kem/ml_kem_kem.c @@ -0,0 +1,268 @@ +/* + * Copyright 2024-2025 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 + */ + +#include <string.h> +#include <openssl/crypto.h> +#include <openssl/evp.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/params.h> +#include <openssl/err.h> +#include <openssl/proverr.h> +#include "crypto/ml_kem.h" +#include "prov/provider_ctx.h" +#include "prov/implementations.h" +#include "prov/securitycheck.h" +#include "prov/providercommon.h" + +static OSSL_FUNC_kem_newctx_fn ml_kem_newctx; +static OSSL_FUNC_kem_freectx_fn ml_kem_freectx; +static OSSL_FUNC_kem_encapsulate_init_fn ml_kem_encapsulate_init; +static OSSL_FUNC_kem_encapsulate_fn ml_kem_encapsulate; +static OSSL_FUNC_kem_decapsulate_init_fn ml_kem_decapsulate_init; +static OSSL_FUNC_kem_decapsulate_fn ml_kem_decapsulate; +static OSSL_FUNC_kem_set_ctx_params_fn ml_kem_set_ctx_params; +static OSSL_FUNC_kem_settable_ctx_params_fn ml_kem_settable_ctx_params; + +typedef struct { + ML_KEM_KEY *key; + uint8_t entropy_buf[ML_KEM_RANDOM_BYTES]; + uint8_t *entropy; + int op; +} PROV_ML_KEM_CTX; + +static void *ml_kem_newctx(void *provctx) +{ + PROV_ML_KEM_CTX *ctx; + + if ((ctx = OPENSSL_malloc(sizeof(*ctx))) == NULL) + return NULL; + + ctx->key = NULL; + ctx->entropy = NULL; + ctx->op = 0; + return ctx; +} + +static void ml_kem_freectx(void *vctx) +{ + PROV_ML_KEM_CTX *ctx = vctx; + + if (ctx->entropy != NULL) + OPENSSL_cleanse(ctx->entropy, ML_KEM_RANDOM_BYTES); + OPENSSL_free(ctx); +} + +static int ml_kem_init(void *vctx, int op, void *key, + const OSSL_PARAM params[]) +{ + PROV_ML_KEM_CTX *ctx = vctx; + + if (!ossl_prov_is_running()) + return 0; + ctx->key = key; + ctx->op = op; + return ml_kem_set_ctx_params(vctx, params); +} + +static int ml_kem_encapsulate_init(void *vctx, void *vkey, + const OSSL_PARAM params[]) +{ + ML_KEM_KEY *key = vkey; + + if (!ossl_ml_kem_have_pubkey(key)) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + return 0; + } + return ml_kem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, key, params); +} + +static int ml_kem_decapsulate_init(void *vctx, void *vkey, + const OSSL_PARAM params[]) +{ + ML_KEM_KEY *key = vkey; + + if (!ossl_ml_kem_have_prvkey(key)) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + return 0; + } + return ml_kem_init(vctx, EVP_PKEY_OP_DECAPSULATE, key, params); +} + +static int ml_kem_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_ML_KEM_CTX *ctx = vctx; + const OSSL_PARAM *p; + + if (ctx == NULL) + return 0; + + if (ctx->op == EVP_PKEY_OP_DECAPSULATE && ctx->entropy != NULL) { + /* Decapsulation is deterministic */ + OPENSSL_cleanse(ctx->entropy, ML_KEM_RANDOM_BYTES); + ctx->entropy = NULL; + } + + if (ossl_param_is_empty(params)) + return 1; + + /* Encapsulation ephemeral input key material "ikmE" */ + if (ctx->op == EVP_PKEY_OP_ENCAPSULATE + && (p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_IKME)) != NULL) { + size_t len = ML_KEM_RANDOM_BYTES; + + ctx->entropy = ctx->entropy_buf; + if (OSSL_PARAM_get_octet_string(p, (void **)&ctx->entropy, + len, &len) + && len == ML_KEM_RANDOM_BYTES) + return 1; + + /* Possibly, but much less likely wrong type */ + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SEED_LENGTH); + ctx->entropy = NULL; + return 0; + } + + return 1; +} + +static const OSSL_PARAM *ml_kem_settable_ctx_params(ossl_unused void *vctx, + ossl_unused void *provctx) +{ + static const OSSL_PARAM params[] = { + OSSL_PARAM_octet_string(OSSL_KEM_PARAM_IKME, NULL, 0), + OSSL_PARAM_END + }; + + return params; +} + +static int ml_kem_encapsulate(void *vctx, unsigned char *ctext, size_t *clen, + unsigned char *shsec, size_t *slen) +{ + PROV_ML_KEM_CTX *ctx = vctx; + ML_KEM_KEY *key = ctx->key; + const ML_KEM_VINFO *v; + size_t encap_clen; + size_t encap_slen; + int ret = 0; + + if (!ossl_ml_kem_have_pubkey(key)) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + goto end; + } + v = ossl_ml_kem_key_vinfo(key); + encap_clen = v->ctext_bytes; + encap_slen = ML_KEM_SHARED_SECRET_BYTES; + + if (ctext == NULL) { + if (clen == NULL && slen == NULL) + return 0; + if (clen != NULL) + *clen = encap_clen; + if (slen != NULL) + *slen = encap_slen; + return 1; + } + if (shsec == NULL) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_OUTPUT_BUFFER, + "NULL shared-secret buffer"); + goto end; + } + + if (clen == NULL) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER, + "null ciphertext input/output length pointer"); + goto end; + } else if (*clen < encap_clen) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL, + "ciphertext buffer too small"); + goto end; + } else { + *clen = encap_clen; + } + + if (slen == NULL) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER, + "null shared secret input/output length pointer"); + goto end; + } else if (*slen < encap_slen) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL, + "shared-secret buffer too small"); + goto end; + } else { + *slen = encap_slen; + } + + if (ctx->entropy != NULL) + ret = ossl_ml_kem_encap_seed(ctext, encap_clen, shsec, encap_slen, + ctx->entropy, ML_KEM_RANDOM_BYTES, key); + else + ret = ossl_ml_kem_encap_rand(ctext, encap_clen, shsec, encap_slen, key); + + end: + /* + * One shot entropy, each encapsulate call must either provide a new + * "ikmE", or else will use a random value. If a caller sets an explicit + * ikmE once for testing, and later performs multiple encapsulations + * without again calling encapsulate_init(), these should not share the + * original entropy. + */ + if (ctx->entropy != NULL) { + OPENSSL_cleanse(ctx->entropy, ML_KEM_RANDOM_BYTES); + ctx->entropy = NULL; + } + return ret; +} + +static int ml_kem_decapsulate(void *vctx, uint8_t *shsec, size_t *slen, + const uint8_t *ctext, size_t clen) +{ + PROV_ML_KEM_CTX *ctx = vctx; + ML_KEM_KEY *key = ctx->key; + size_t decap_slen = ML_KEM_SHARED_SECRET_BYTES; + + if (!ossl_ml_kem_have_prvkey(key)) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + return 0; + } + + if (shsec == NULL) { + if (slen == NULL) + return 0; + *slen = ML_KEM_SHARED_SECRET_BYTES; + return 1; + } + + /* For now tolerate newly-deprecated NULL length pointers. */ + if (slen == NULL) { + slen = &decap_slen; + } else if (*slen < decap_slen) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL, + "shared-secret buffer too small"); + return 0; + } else { + *slen = decap_slen; + } + + /* ML-KEM decap handles incorrect ciphertext lengths internally */ + return ossl_ml_kem_decap(shsec, decap_slen, ctext, clen, key); +} + +const OSSL_DISPATCH ossl_ml_kem_asym_kem_functions[] = { + { OSSL_FUNC_KEM_NEWCTX, (OSSL_FUNC) ml_kem_newctx }, + { OSSL_FUNC_KEM_ENCAPSULATE_INIT, (OSSL_FUNC) ml_kem_encapsulate_init }, + { OSSL_FUNC_KEM_ENCAPSULATE, (OSSL_FUNC) ml_kem_encapsulate }, + { OSSL_FUNC_KEM_DECAPSULATE_INIT, (OSSL_FUNC) ml_kem_decapsulate_init }, + { OSSL_FUNC_KEM_DECAPSULATE, (OSSL_FUNC) ml_kem_decapsulate }, + { OSSL_FUNC_KEM_FREECTX, (OSSL_FUNC) ml_kem_freectx }, + { OSSL_FUNC_KEM_SET_CTX_PARAMS, (OSSL_FUNC) ml_kem_set_ctx_params }, + { OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS, (OSSL_FUNC) ml_kem_settable_ctx_params }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/kem/mlx_kem.c b/crypto/openssl/providers/implementations/kem/mlx_kem.c new file mode 100644 index 000000000000..197c345d85cb --- /dev/null +++ b/crypto/openssl/providers/implementations/kem/mlx_kem.c @@ -0,0 +1,341 @@ +/* + * Copyright 2024-2025 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 + */ + +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/crypto.h> +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/params.h> +#include <openssl/proverr.h> +#include <openssl/rand.h> +#include "prov/implementations.h" +#include "prov/mlx_kem.h" +#include "prov/provider_ctx.h" +#include "prov/providercommon.h" + +static OSSL_FUNC_kem_newctx_fn mlx_kem_newctx; +static OSSL_FUNC_kem_freectx_fn mlx_kem_freectx; +static OSSL_FUNC_kem_encapsulate_init_fn mlx_kem_encapsulate_init; +static OSSL_FUNC_kem_encapsulate_fn mlx_kem_encapsulate; +static OSSL_FUNC_kem_decapsulate_init_fn mlx_kem_decapsulate_init; +static OSSL_FUNC_kem_decapsulate_fn mlx_kem_decapsulate; +static OSSL_FUNC_kem_set_ctx_params_fn mlx_kem_set_ctx_params; +static OSSL_FUNC_kem_settable_ctx_params_fn mlx_kem_settable_ctx_params; + +typedef struct { + OSSL_LIB_CTX *libctx; + MLX_KEY *key; + int op; +} PROV_MLX_KEM_CTX; + +static void *mlx_kem_newctx(void *provctx) +{ + PROV_MLX_KEM_CTX *ctx; + + if ((ctx = OPENSSL_malloc(sizeof(*ctx))) == NULL) + return NULL; + + ctx->libctx = PROV_LIBCTX_OF(provctx); + ctx->key = NULL; + ctx->op = 0; + return ctx; +} + +static void mlx_kem_freectx(void *vctx) +{ + OPENSSL_free(vctx); +} + +static int mlx_kem_init(void *vctx, int op, void *key, + ossl_unused const OSSL_PARAM params[]) +{ + PROV_MLX_KEM_CTX *ctx = vctx; + + if (!ossl_prov_is_running()) + return 0; + ctx->key = key; + ctx->op = op; + return 1; +} + +static int +mlx_kem_encapsulate_init(void *vctx, void *vkey, const OSSL_PARAM params[]) +{ + MLX_KEY *key = vkey; + + if (!mlx_kem_have_pubkey(key)) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + return 0; + } + return mlx_kem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, key, params); +} + +static int +mlx_kem_decapsulate_init(void *vctx, void *vkey, const OSSL_PARAM params[]) +{ + MLX_KEY *key = vkey; + + if (!mlx_kem_have_prvkey(key)) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + return 0; + } + return mlx_kem_init(vctx, EVP_PKEY_OP_DECAPSULATE, key, params); +} + +static const OSSL_PARAM *mlx_kem_settable_ctx_params(ossl_unused void *vctx, + ossl_unused void *provctx) +{ + static const OSSL_PARAM params[] = { OSSL_PARAM_END }; + + return params; +} + +static int +mlx_kem_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + return 1; +} + +static int mlx_kem_encapsulate(void *vctx, unsigned char *ctext, size_t *clen, + unsigned char *shsec, size_t *slen) +{ + MLX_KEY *key = ((PROV_MLX_KEM_CTX *) vctx)->key; + EVP_PKEY_CTX *ctx = NULL; + EVP_PKEY *xkey = NULL; + size_t encap_clen; + size_t encap_slen; + uint8_t *cbuf; + uint8_t *sbuf; + int ml_kem_slot = key->xinfo->ml_kem_slot; + int ret = 0; + + if (!mlx_kem_have_pubkey(key)) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + goto end; + } + encap_clen = key->minfo->ctext_bytes + key->xinfo->pubkey_bytes; + encap_slen = ML_KEM_SHARED_SECRET_BYTES + key->xinfo->shsec_bytes; + + if (ctext == NULL) { + if (clen == NULL && slen == NULL) + return 0; + if (clen != NULL) + *clen = encap_clen; + if (slen != NULL) + *slen = encap_slen; + return 1; + } + if (shsec == NULL) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_OUTPUT_BUFFER, + "null shared-secret output buffer"); + return 0; + } + + if (clen == NULL) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER, + "null ciphertext input/output length pointer"); + return 0; + } else if (*clen < encap_clen) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL, + "ciphertext buffer too small"); + return 0; + } else { + *clen = encap_clen; + } + + if (slen == NULL) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER, + "null shared secret input/output length pointer"); + return 0; + } else if (*slen < encap_slen) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL, + "shared-secret buffer too small"); + return 0; + } else { + *slen = encap_slen; + } + + /* ML-KEM encapsulation */ + encap_clen = key->minfo->ctext_bytes; + encap_slen = ML_KEM_SHARED_SECRET_BYTES; + cbuf = ctext + ml_kem_slot * key->xinfo->pubkey_bytes; + sbuf = shsec + ml_kem_slot * key->xinfo->shsec_bytes; + ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->mkey, key->propq); + if (ctx == NULL + || EVP_PKEY_encapsulate_init(ctx, NULL) <= 0 + || EVP_PKEY_encapsulate(ctx, cbuf, &encap_clen, sbuf, &encap_slen) <= 0) + goto end; + if (encap_clen != key->minfo->ctext_bytes) { + ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR, + "unexpected %s ciphertext output size: %lu", + key->minfo->algorithm_name, (unsigned long) encap_clen); + goto end; + } + if (encap_slen != ML_KEM_SHARED_SECRET_BYTES) { + ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR, + "unexpected %s shared secret output size: %lu", + key->minfo->algorithm_name, (unsigned long) encap_slen); + goto end; + } + EVP_PKEY_CTX_free(ctx); + + /*- + * ECDHE encapsulation + * + * Generate own ephemeral private key and add its public key to ctext. + * + * Note, we could support a settable parameter that sets an extant ECDH + * keypair as the keys to use in encap, making it possible to reuse the + * same (TLS client) ECDHE keypair for both the classical EC keyshare and a + * corresponding ECDHE + ML-KEM keypair. But the TLS layer would then need + * know that this is a hybrid, and that it can partly reuse the same keys + * as another group for which a keyshare will be sent. Deferred until we + * support generating multiple keyshares, there's a workable keyshare + * prediction specification, and the optimisation is justified. + */ + cbuf = ctext + (1 - ml_kem_slot) * key->minfo->ctext_bytes; + encap_clen = key->xinfo->pubkey_bytes; + ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->xkey, key->propq); + if (ctx == NULL + || EVP_PKEY_keygen_init(ctx) <= 0 + || EVP_PKEY_keygen(ctx, &xkey) <= 0 + || EVP_PKEY_get_octet_string_param(xkey, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, + cbuf, encap_clen, &encap_clen) <= 0) + goto end; + if (encap_clen != key->xinfo->pubkey_bytes) { + ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR, + "unexpected %s public key output size: %lu", + key->xinfo->algorithm_name, (unsigned long) encap_clen); + goto end; + } + EVP_PKEY_CTX_free(ctx); + + /* Derive the ECDH shared secret */ + encap_slen = key->xinfo->shsec_bytes; + sbuf = shsec + (1 - ml_kem_slot) * ML_KEM_SHARED_SECRET_BYTES; + ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, xkey, key->propq); + if (ctx == NULL + || EVP_PKEY_derive_init(ctx) <= 0 + || EVP_PKEY_derive_set_peer(ctx, key->xkey) <= 0 + || EVP_PKEY_derive(ctx, sbuf, &encap_slen) <= 0) + goto end; + if (encap_slen != key->xinfo->shsec_bytes) { + ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR, + "unexpected %s shared secret output size: %lu", + key->xinfo->algorithm_name, (unsigned long) encap_slen); + goto end; + } + + ret = 1; + end: + EVP_PKEY_free(xkey); + EVP_PKEY_CTX_free(ctx); + return ret; +} + +static int mlx_kem_decapsulate(void *vctx, uint8_t *shsec, size_t *slen, + const uint8_t *ctext, size_t clen) +{ + MLX_KEY *key = ((PROV_MLX_KEM_CTX *) vctx)->key; + EVP_PKEY_CTX *ctx = NULL; + EVP_PKEY *xkey = NULL; + const uint8_t *cbuf; + uint8_t *sbuf; + size_t decap_slen = ML_KEM_SHARED_SECRET_BYTES + key->xinfo->shsec_bytes; + size_t decap_clen = key->minfo->ctext_bytes + key->xinfo->pubkey_bytes; + int ml_kem_slot = key->xinfo->ml_kem_slot; + int ret = 0; + + if (!mlx_kem_have_prvkey(key)) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + return 0; + } + + if (shsec == NULL) { + if (slen == NULL) + return 0; + *slen = decap_slen; + return 1; + } + + /* For now tolerate newly-deprecated NULL length pointers. */ + if (slen == NULL) { + slen = &decap_slen; + } else if (*slen < decap_slen) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL, + "shared-secret buffer too small"); + return 0; + } else { + *slen = decap_slen; + } + if (clen != decap_clen) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_WRONG_CIPHERTEXT_SIZE, + "wrong decapsulation input ciphertext size: %lu", + (unsigned long) clen); + return 0; + } + + /* ML-KEM decapsulation */ + decap_clen = key->minfo->ctext_bytes; + decap_slen = ML_KEM_SHARED_SECRET_BYTES; + cbuf = ctext + ml_kem_slot * key->xinfo->pubkey_bytes; + sbuf = shsec + ml_kem_slot * key->xinfo->shsec_bytes; + ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->mkey, key->propq); + if (ctx == NULL + || EVP_PKEY_decapsulate_init(ctx, NULL) <= 0 + || EVP_PKEY_decapsulate(ctx, sbuf, &decap_slen, cbuf, decap_clen) <= 0) + goto end; + if (decap_slen != ML_KEM_SHARED_SECRET_BYTES) { + ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR, + "unexpected %s shared secret output size: %lu", + key->minfo->algorithm_name, (unsigned long) decap_slen); + goto end; + } + EVP_PKEY_CTX_free(ctx); + + /* ECDH decapsulation */ + decap_clen = key->xinfo->pubkey_bytes; + decap_slen = key->xinfo->shsec_bytes; + cbuf = ctext + (1 - ml_kem_slot) * key->minfo->ctext_bytes; + sbuf = shsec + (1 - ml_kem_slot) * ML_KEM_SHARED_SECRET_BYTES; + ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->xkey, key->propq); + if (ctx == NULL + || (xkey = EVP_PKEY_new()) == NULL + || EVP_PKEY_copy_parameters(xkey, key->xkey) <= 0 + || EVP_PKEY_set1_encoded_public_key(xkey, cbuf, decap_clen) <= 0 + || EVP_PKEY_derive_init(ctx) <= 0 + || EVP_PKEY_derive_set_peer(ctx, xkey) <= 0 + || EVP_PKEY_derive(ctx, sbuf, &decap_slen) <= 0) + goto end; + if (decap_slen != key->xinfo->shsec_bytes) { + ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR, + "unexpected %s shared secret output size: %lu", + key->xinfo->algorithm_name, (unsigned long) decap_slen); + goto end; + } + + ret = 1; + end: + EVP_PKEY_CTX_free(ctx); + EVP_PKEY_free(xkey); + return ret; +} + +const OSSL_DISPATCH ossl_mlx_kem_asym_kem_functions[] = { + { OSSL_FUNC_KEM_NEWCTX, (OSSL_FUNC) mlx_kem_newctx }, + { OSSL_FUNC_KEM_ENCAPSULATE_INIT, (OSSL_FUNC) mlx_kem_encapsulate_init }, + { OSSL_FUNC_KEM_ENCAPSULATE, (OSSL_FUNC) mlx_kem_encapsulate }, + { OSSL_FUNC_KEM_DECAPSULATE_INIT, (OSSL_FUNC) mlx_kem_decapsulate_init }, + { OSSL_FUNC_KEM_DECAPSULATE, (OSSL_FUNC) mlx_kem_decapsulate }, + { OSSL_FUNC_KEM_FREECTX, (OSSL_FUNC) mlx_kem_freectx }, + { OSSL_FUNC_KEM_SET_CTX_PARAMS, (OSSL_FUNC) mlx_kem_set_ctx_params }, + { OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS, (OSSL_FUNC) mlx_kem_settable_ctx_params }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/kem/rsa_kem.c b/crypto/openssl/providers/implementations/kem/rsa_kem.c new file mode 100644 index 000000000000..7494dcc0107f --- /dev/null +++ b/crypto/openssl/providers/implementations/kem/rsa_kem.c @@ -0,0 +1,449 @@ +/* + * Copyright 2020-2025 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 + */ + +/* + * RSA low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" +#include "internal/nelem.h" +#include <openssl/crypto.h> +#include <openssl/evp.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/rsa.h> +#include <openssl/params.h> +#include <openssl/err.h> +#include <openssl/proverr.h> +#include "crypto/rsa.h" +#include "prov/provider_ctx.h" +#include "prov/providercommon.h" +#include "prov/implementations.h" +#include "prov/securitycheck.h" + +static OSSL_FUNC_kem_newctx_fn rsakem_newctx; +static OSSL_FUNC_kem_encapsulate_init_fn rsakem_encapsulate_init; +static OSSL_FUNC_kem_encapsulate_fn rsakem_generate; +static OSSL_FUNC_kem_decapsulate_init_fn rsakem_decapsulate_init; +static OSSL_FUNC_kem_decapsulate_fn rsakem_recover; +static OSSL_FUNC_kem_freectx_fn rsakem_freectx; +static OSSL_FUNC_kem_dupctx_fn rsakem_dupctx; +static OSSL_FUNC_kem_get_ctx_params_fn rsakem_get_ctx_params; +static OSSL_FUNC_kem_gettable_ctx_params_fn rsakem_gettable_ctx_params; +static OSSL_FUNC_kem_set_ctx_params_fn rsakem_set_ctx_params; +static OSSL_FUNC_kem_settable_ctx_params_fn rsakem_settable_ctx_params; + +/* + * Only the KEM for RSASVE as defined in SP800-56b r2 is implemented + * currently. + */ +#define KEM_OP_UNDEFINED -1 +#define KEM_OP_RSASVE 0 + +/* + * What's passed as an actual key is defined by the KEYMGMT interface. + * We happen to know that our KEYMGMT simply passes RSA structures, so + * we use that here too. + */ +typedef struct { + OSSL_LIB_CTX *libctx; + RSA *rsa; + int op; + OSSL_FIPS_IND_DECLARE +} PROV_RSA_CTX; + +static const OSSL_ITEM rsakem_opname_id_map[] = { + { KEM_OP_RSASVE, OSSL_KEM_PARAM_OPERATION_RSASVE }, +}; + +static int name2id(const char *name, const OSSL_ITEM *map, size_t sz) +{ + size_t i; + + if (name == NULL) + return -1; + + for (i = 0; i < sz; ++i) { + if (OPENSSL_strcasecmp(map[i].ptr, name) == 0) + return map[i].id; + } + return -1; +} + +static int rsakem_opname2id(const char *name) +{ + return name2id(name, rsakem_opname_id_map, OSSL_NELEM(rsakem_opname_id_map)); +} + +static void *rsakem_newctx(void *provctx) +{ + PROV_RSA_CTX *prsactx; + + if (!ossl_prov_is_running()) + return NULL; + + prsactx = OPENSSL_zalloc(sizeof(PROV_RSA_CTX)); + if (prsactx == NULL) + return NULL; + prsactx->libctx = PROV_LIBCTX_OF(provctx); + prsactx->op = KEM_OP_RSASVE; + OSSL_FIPS_IND_INIT(prsactx) + + return prsactx; +} + +static void rsakem_freectx(void *vprsactx) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + + RSA_free(prsactx->rsa); + OPENSSL_free(prsactx); +} + +static void *rsakem_dupctx(void *vprsactx) +{ + PROV_RSA_CTX *srcctx = (PROV_RSA_CTX *)vprsactx; + PROV_RSA_CTX *dstctx; + + if (!ossl_prov_is_running()) + return NULL; + + dstctx = OPENSSL_zalloc(sizeof(*srcctx)); + if (dstctx == NULL) + return NULL; + + *dstctx = *srcctx; + if (dstctx->rsa != NULL && !RSA_up_ref(dstctx->rsa)) { + OPENSSL_free(dstctx); + return NULL; + } + return dstctx; +} + +static int rsakem_init(void *vprsactx, void *vrsa, + const OSSL_PARAM params[], int operation, + const char *desc) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + int protect = 0; + + if (!ossl_prov_is_running()) + return 0; + + if (prsactx == NULL || vrsa == NULL) + return 0; + + if (!ossl_rsa_key_op_get_protect(vrsa, operation, &protect)) + return 0; + if (!RSA_up_ref(vrsa)) + return 0; + RSA_free(prsactx->rsa); + prsactx->rsa = vrsa; + + OSSL_FIPS_IND_SET_APPROVED(prsactx) + if (!rsakem_set_ctx_params(prsactx, params)) + return 0; +#ifdef FIPS_MODULE + if (!ossl_fips_ind_rsa_key_check(OSSL_FIPS_IND_GET(prsactx), + OSSL_FIPS_IND_SETTABLE0, prsactx->libctx, + prsactx->rsa, desc, protect)) + return 0; +#endif + return 1; +} + +static int rsakem_encapsulate_init(void *vprsactx, void *vrsa, + const OSSL_PARAM params[]) +{ + return rsakem_init(vprsactx, vrsa, params, EVP_PKEY_OP_ENCAPSULATE, + "RSA Encapsulate Init"); +} + +static int rsakem_decapsulate_init(void *vprsactx, void *vrsa, + const OSSL_PARAM params[]) +{ + return rsakem_init(vprsactx, vrsa, params, EVP_PKEY_OP_DECAPSULATE, + "RSA Decapsulate Init"); +} + +static int rsakem_get_ctx_params(void *vprsactx, OSSL_PARAM *params) +{ + PROV_RSA_CTX *ctx = (PROV_RSA_CTX *)vprsactx; + + if (ctx == NULL) + return 0; + + if (!OSSL_FIPS_IND_GET_CTX_PARAM(ctx, params)) + return 0; + return 1; +} + +static const OSSL_PARAM known_gettable_rsakem_ctx_params[] = { + OSSL_FIPS_IND_GETTABLE_CTX_PARAM() + OSSL_PARAM_END +}; + +static const OSSL_PARAM *rsakem_gettable_ctx_params(ossl_unused void *vprsactx, + ossl_unused void *provctx) +{ + return known_gettable_rsakem_ctx_params; +} + +static int rsakem_set_ctx_params(void *vprsactx, const OSSL_PARAM params[]) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + const OSSL_PARAM *p; + int op; + + if (prsactx == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 1; + + if (!OSSL_FIPS_IND_SET_CTX_PARAM(prsactx, OSSL_FIPS_IND_SETTABLE0, params, + OSSL_KEM_PARAM_FIPS_KEY_CHECK)) + return 0; + p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_OPERATION); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_UTF8_STRING) + return 0; + op = rsakem_opname2id(p->data); + if (op < 0) + return 0; + prsactx->op = op; + } + return 1; +} + +static const OSSL_PARAM known_settable_rsakem_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_KEM_PARAM_OPERATION, NULL, 0), + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KEM_PARAM_FIPS_KEY_CHECK) + OSSL_PARAM_END +}; + +static const OSSL_PARAM *rsakem_settable_ctx_params(ossl_unused void *vprsactx, + ossl_unused void *provctx) +{ + return known_settable_rsakem_ctx_params; +} + +/* + * NIST.SP.800-56Br2 + * 7.2.1.2 RSASVE Generate Operation (RSASVE.GENERATE). + * + * Generate a random in the range 1 < z < (n – 1) + */ +static int rsasve_gen_rand_bytes(RSA *rsa_pub, + unsigned char *out, int outlen) +{ + int ret = 0; + BN_CTX *bnctx; + BIGNUM *z, *nminus3; + + bnctx = BN_CTX_secure_new_ex(ossl_rsa_get0_libctx(rsa_pub)); + if (bnctx == NULL) + return 0; + + /* + * Generate a random in the range 1 < z < (n – 1). + * Since BN_priv_rand_range_ex() returns a value in range 0 <= r < max + * We can achieve this by adding 2.. but then we need to subtract 3 from + * the upper bound i.e: 2 + (0 <= r < (n - 3)) + */ + BN_CTX_start(bnctx); + nminus3 = BN_CTX_get(bnctx); + z = BN_CTX_get(bnctx); + ret = (z != NULL + && (BN_copy(nminus3, RSA_get0_n(rsa_pub)) != NULL) + && BN_sub_word(nminus3, 3) + && BN_priv_rand_range_ex(z, nminus3, 0, bnctx) + && BN_add_word(z, 2) + && (BN_bn2binpad(z, out, outlen) == outlen)); + BN_CTX_end(bnctx); + BN_CTX_free(bnctx); + return ret; +} + +/* + * NIST.SP.800-56Br2 + * 7.2.1.2 RSASVE Generate Operation (RSASVE.GENERATE). + */ +static int rsasve_generate(PROV_RSA_CTX *prsactx, + unsigned char *out, size_t *outlen, + unsigned char *secret, size_t *secretlen) +{ + int ret; + size_t nlen; + + /* Step (1): nlen = Ceil(len(n)/8) */ + nlen = RSA_size(prsactx->rsa); + + if (out == NULL) { + if (nlen == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY); + return 0; + } + if (outlen == NULL && secretlen == NULL) + return 0; + if (outlen != NULL) + *outlen = nlen; + if (secretlen != NULL) + *secretlen = nlen; + return 1; + } + + /* + * If outlen is specified, then it must report the length + * of the out buffer on input so that we can confirm + * its size is sufficent for encapsulation + */ + if (outlen != NULL && *outlen < nlen) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_OUTPUT_LENGTH); + return 0; + } + + /* + * Step (2): Generate a random byte string z of nlen bytes where + * 1 < z < n - 1 + */ + if (!rsasve_gen_rand_bytes(prsactx->rsa, secret, nlen)) + return 0; + + /* Step(3): out = RSAEP((n,e), z) */ + ret = RSA_public_encrypt(nlen, secret, out, prsactx->rsa, RSA_NO_PADDING); + if (ret) { + ret = 1; + if (outlen != NULL) + *outlen = nlen; + if (secretlen != NULL) + *secretlen = nlen; + } else { + OPENSSL_cleanse(secret, nlen); + } + return ret; +} + +/** + * rsasve_recover - Recovers a secret value from ciphertext using an RSA + * private key. Once, recovered, the secret value is considered to be a + * shared secret. Algorithm is preformed as per + * NIST SP 800-56B Rev 2 + * 7.2.1.3 RSASVE Recovery Operation (RSASVE.RECOVER). + * + * This function performs RSA decryption using the private key from the + * provided RSA context (`prsactx`). It takes the input ciphertext, decrypts + * it, and writes the decrypted message to the output buffer. + * + * @prsactx: The RSA context containing the private key. + * @out: The output buffer to store the decrypted message. + * @outlen: On input, the size of the output buffer. On successful + * completion, the actual length of the decrypted message. + * @in: The input buffer containing the ciphertext to be decrypted. + * @inlen: The length of the input ciphertext in bytes. + * + * Returns 1 on success, or 0 on error. In case of error, appropriate + * error messages are raised using the ERR_raise function. + */ +static int rsasve_recover(PROV_RSA_CTX *prsactx, + unsigned char *out, size_t *outlen, + const unsigned char *in, size_t inlen) +{ + size_t nlen; + int ret; + + /* Step (1): get the byte length of n */ + nlen = RSA_size(prsactx->rsa); + + if (out == NULL) { + if (nlen == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY); + return 0; + } + *outlen = nlen; + return 1; + } + + /* + * Step (2): check the input ciphertext 'inlen' matches the nlen + * and that outlen is at least nlen bytes + */ + if (inlen != nlen) { + ERR_raise(ERR_LIB_PROV, PROV_R_BAD_LENGTH); + return 0; + } + + /* + * If outlen is specified, then it must report the length + * of the out buffer, so that we can confirm that it is of + * sufficient size to hold the output of decapsulation + */ + if (outlen != NULL && *outlen < nlen) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_OUTPUT_LENGTH); + return 0; + } + + /* Step (3): out = RSADP((n,d), in) */ + ret = RSA_private_decrypt(inlen, in, out, prsactx->rsa, RSA_NO_PADDING); + if (ret > 0 && outlen != NULL) + *outlen = ret; + return ret > 0; +} + +static int rsakem_generate(void *vprsactx, unsigned char *out, size_t *outlen, + unsigned char *secret, size_t *secretlen) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + + if (!ossl_prov_is_running()) + return 0; + + switch (prsactx->op) { + case KEM_OP_RSASVE: + return rsasve_generate(prsactx, out, outlen, secret, secretlen); + default: + return -2; + } +} + +static int rsakem_recover(void *vprsactx, unsigned char *out, size_t *outlen, + const unsigned char *in, size_t inlen) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + + if (!ossl_prov_is_running()) + return 0; + + switch (prsactx->op) { + case KEM_OP_RSASVE: + return rsasve_recover(prsactx, out, outlen, in, inlen); + default: + return -2; + } +} + +const OSSL_DISPATCH ossl_rsa_asym_kem_functions[] = { + { OSSL_FUNC_KEM_NEWCTX, (void (*)(void))rsakem_newctx }, + { OSSL_FUNC_KEM_ENCAPSULATE_INIT, + (void (*)(void))rsakem_encapsulate_init }, + { OSSL_FUNC_KEM_ENCAPSULATE, (void (*)(void))rsakem_generate }, + { OSSL_FUNC_KEM_DECAPSULATE_INIT, + (void (*)(void))rsakem_decapsulate_init }, + { OSSL_FUNC_KEM_DECAPSULATE, (void (*)(void))rsakem_recover }, + { OSSL_FUNC_KEM_FREECTX, (void (*)(void))rsakem_freectx }, + { OSSL_FUNC_KEM_DUPCTX, (void (*)(void))rsakem_dupctx }, + { OSSL_FUNC_KEM_GET_CTX_PARAMS, + (void (*)(void))rsakem_get_ctx_params }, + { OSSL_FUNC_KEM_GETTABLE_CTX_PARAMS, + (void (*)(void))rsakem_gettable_ctx_params }, + { OSSL_FUNC_KEM_SET_CTX_PARAMS, + (void (*)(void))rsakem_set_ctx_params }, + { OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS, + (void (*)(void))rsakem_settable_ctx_params }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/kem/template_kem.c b/crypto/openssl/providers/implementations/kem/template_kem.c new file mode 100644 index 000000000000..c621a31a9c35 --- /dev/null +++ b/crypto/openssl/providers/implementations/kem/template_kem.c @@ -0,0 +1,193 @@ +/* + * Copyright 2024-2025 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 + */ + +#include <string.h> +#include <openssl/crypto.h> +#include <openssl/evp.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/params.h> +#include <openssl/err.h> +#include <openssl/proverr.h> +#include "prov/provider_ctx.h" +#include "prov/implementations.h" +#include "prov/securitycheck.h" +#include "prov/providercommon.h" + +extern const OSSL_DISPATCH ossl_template_asym_kem_functions[]; + +#define BUFSIZE 1000 +#if defined(NDEBUG) || defined(OPENSSL_NO_STDIO) +static void debug_print(char *fmt, ...) +{ +} + +#else +static void debug_print(char *fmt, ...) +{ + char out[BUFSIZE]; + va_list argptr; + + va_start(argptr, fmt); + vsnprintf(out, BUFSIZE, fmt, argptr); + va_end(argptr); + if (getenv("TEMPLATEKEM")) + fprintf(stderr, "TEMPLATE_KEM: %s", out); +} +#endif + +typedef struct { + OSSL_LIB_CTX *libctx; + /* some algorithm-specific key struct */ + int op; +} PROV_TEMPLATE_CTX; + +static OSSL_FUNC_kem_newctx_fn template_newctx; +static OSSL_FUNC_kem_encapsulate_init_fn template_encapsulate_init; +static OSSL_FUNC_kem_encapsulate_fn template_encapsulate; +static OSSL_FUNC_kem_decapsulate_init_fn template_decapsulate_init; +static OSSL_FUNC_kem_decapsulate_fn template_decapsulate; +static OSSL_FUNC_kem_freectx_fn template_freectx; +static OSSL_FUNC_kem_set_ctx_params_fn template_set_ctx_params; +static OSSL_FUNC_kem_settable_ctx_params_fn template_settable_ctx_params; + +static void *template_newctx(void *provctx) +{ + PROV_TEMPLATE_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx)); + + debug_print("newctx called\n"); + if (ctx == NULL) + return NULL; + ctx->libctx = PROV_LIBCTX_OF(provctx); + + debug_print("newctx returns %p\n", ctx); + return ctx; +} + +static void template_freectx(void *vctx) +{ + PROV_TEMPLATE_CTX *ctx = (PROV_TEMPLATE_CTX *)vctx; + + debug_print("freectx %p\n", ctx); + OPENSSL_free(ctx); +} + +static int template_init(void *vctx, int operation, void *vkey, void *vauth, + ossl_unused const OSSL_PARAM params[]) +{ + PROV_TEMPLATE_CTX *ctx = (PROV_TEMPLATE_CTX *)vctx; + + debug_print("init %p / %p\n", ctx, vkey); + if (!ossl_prov_is_running()) + return 0; + + /* check and fill in reference to key */ + ctx->op = operation; + debug_print("init OK\n"); + return 1; +} + +static int template_encapsulate_init(void *vctx, void *vkey, + const OSSL_PARAM params[]) +{ + return template_init(vctx, EVP_PKEY_OP_ENCAPSULATE, vkey, NULL, params); +} + +static int template_decapsulate_init(void *vctx, void *vkey, + const OSSL_PARAM params[]) +{ + return template_init(vctx, EVP_PKEY_OP_DECAPSULATE, vkey, NULL, params); +} + +static int template_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_TEMPLATE_CTX *ctx = (PROV_TEMPLATE_CTX *)vctx; + + debug_print("set ctx params %p\n", ctx); + if (ctx == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 1; + + debug_print("set ctx params OK\n"); + return 1; +} + +static const OSSL_PARAM known_settable_template_ctx_params[] = { + /* possibly more params */ + OSSL_PARAM_END +}; + +static const OSSL_PARAM *template_settable_ctx_params(ossl_unused void *vctx, + ossl_unused void *provctx) +{ + return known_settable_template_ctx_params; +} + +static int template_encapsulate(void *vctx, unsigned char *out, size_t *outlen, + unsigned char *secret, size_t *secretlen) +{ + debug_print("encaps %p to %p\n", vctx, out); + + /* add algorithm-specific length checks */ + + if (outlen != NULL) + *outlen = 0; /* replace with real encapsulated data length */ + if (secretlen != NULL) + *secretlen = 0; /* replace with real shared secret length */ + + if (out == NULL) { + if (outlen != NULL && secretlen != NULL) + debug_print("encaps outlens set to %zu and %zu\n", *outlen, *secretlen); + return 1; + } + + /* check key and perform real KEM operation */ + + debug_print("encaps OK\n"); + return 1; +} + +static int template_decapsulate(void *vctx, unsigned char *out, size_t *outlen, + const unsigned char *in, size_t inlen) +{ + debug_print("decaps %p to %p inlen at %zu\n", vctx, out, inlen); + + /* add algorithm-specific length checks */ + + if (outlen != NULL) + *outlen = 0; /* replace with shared secret length */ + + if (out == NULL) { + if (outlen != NULL) + debug_print("decaps outlen set to %zu \n", *outlen); + return 1; + } + + /* check key and perform real decaps operation */ + + debug_print("decaps OK\n"); + return 1; +} + +const OSSL_DISPATCH ossl_template_asym_kem_functions[] = { + { OSSL_FUNC_KEM_NEWCTX, (void (*)(void))template_newctx }, + { OSSL_FUNC_KEM_ENCAPSULATE_INIT, + (void (*)(void))template_encapsulate_init }, + { OSSL_FUNC_KEM_ENCAPSULATE, (void (*)(void))template_encapsulate }, + { OSSL_FUNC_KEM_DECAPSULATE_INIT, + (void (*)(void))template_decapsulate_init }, + { OSSL_FUNC_KEM_DECAPSULATE, (void (*)(void))template_decapsulate }, + { OSSL_FUNC_KEM_FREECTX, (void (*)(void))template_freectx }, + { OSSL_FUNC_KEM_SET_CTX_PARAMS, + (void (*)(void))template_set_ctx_params }, + { OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS, + (void (*)(void))template_settable_ctx_params }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/keymgmt/build.info b/crypto/openssl/providers/implementations/keymgmt/build.info new file mode 100644 index 000000000000..908a32f3555e --- /dev/null +++ b/crypto/openssl/providers/implementations/keymgmt/build.info @@ -0,0 +1,65 @@ +# We make separate GOAL variables for each algorithm, to make it easy to +# switch each to the Legacy provider when needed. + +$DH_GOAL=../../libdefault.a ../../libfips.a +$DSA_GOAL=../../libdefault.a ../../libfips.a +$EC_GOAL=../../libdefault.a ../../libfips.a +$ECX_GOAL=../../libdefault.a ../../libfips.a +$KDF_GOAL=../../libdefault.a ../../libfips.a +$MAC_GOAL=../../libdefault.a ../../libfips.a +$RSA_GOAL=../../libdefault.a ../../libfips.a +$TEMPLATE_GOAL=../../libtemplate.a +$ML_DSA_GOAL=../../libdefault.a ../../libfips.a +$ML_KEM_GOAL=../../libdefault.a ../../libfips.a +$TLS_ML_KEM_HYBRID_GOAL=../../libdefault.a ../../libfips.a +$SLH_DSA_GOAL=../../libdefault.a ../../libfips.a + +IF[{- !$disabled{dh} -}] + SOURCE[$DH_GOAL]=dh_kmgmt.c +ENDIF +IF[{- !$disabled{dsa} -}] + SOURCE[$DSA_GOAL]=dsa_kmgmt.c +ENDIF +IF[{- !$disabled{ec} -}] + SOURCE[$EC_GOAL]=ec_kmgmt.c +ENDIF + +IF[{- !$disabled{asm} -}] + $ECDEF_s390x=S390X_EC_ASM + + # Now that we have defined all the arch specific variables, use the + # appropriate one, and define the appropriate macros + IF[$ECASM_{- $target{asm_arch} -}] + $ECDEF=$ECDEF_{- $target{asm_arch} -} + ENDIF +ENDIF + +IF[{- !$disabled{ec} -}] + IF[{- !$disabled{ecx} -}] + SOURCE[$ECX_GOAL]=ecx_kmgmt.c + DEFINE[$ECX_GOAL]=$ECDEF + ENDIF +ENDIF + +IF[{- !$disabled{'ml-kem'} -}] + IF[{- !$disabled{ec} -}] + SOURCE[$TLS_ML_KEM_HYBRID_GOAL]=mlx_kmgmt.c + ENDIF + SOURCE[$ML_KEM_GOAL]=ml_kem_kmgmt.c +ENDIF + +SOURCE[$RSA_GOAL]=rsa_kmgmt.c + +SOURCE[$KDF_GOAL]=kdf_legacy_kmgmt.c + +SOURCE[$MAC_GOAL]=mac_legacy_kmgmt.c + +SOURCE[$TEMPLATE_GOAL]=template_kmgmt.c + +IF[{- !$disabled{'ml-dsa'} -}] + SOURCE[$ML_DSA_GOAL]=ml_dsa_kmgmt.c +ENDIF + +IF[{- !$disabled{'slh-dsa'} -}] + SOURCE[$SLH_DSA_GOAL]=slh_dsa_kmgmt.c +ENDIF diff --git a/crypto/openssl/providers/implementations/keymgmt/dh_kmgmt.c b/crypto/openssl/providers/implementations/keymgmt/dh_kmgmt.c new file mode 100644 index 000000000000..0e9e837383f2 --- /dev/null +++ b/crypto/openssl/providers/implementations/keymgmt/dh_kmgmt.c @@ -0,0 +1,912 @@ +/* + * Copyright 2019-2025 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 + */ + +/* + * DH low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" +#include "internal/common.h" + +#include <string.h> /* strcmp */ +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/bn.h> +#include <openssl/err.h> +#include <openssl/self_test.h> +#include "prov/implementations.h" +#include "prov/providercommon.h" +#include "prov/provider_ctx.h" +#include "crypto/dh.h" +#include "internal/fips.h" +#include "internal/sizes.h" + +static OSSL_FUNC_keymgmt_new_fn dh_newdata; +static OSSL_FUNC_keymgmt_free_fn dh_freedata; +static OSSL_FUNC_keymgmt_gen_init_fn dh_gen_init; +static OSSL_FUNC_keymgmt_gen_init_fn dhx_gen_init; +static OSSL_FUNC_keymgmt_gen_set_template_fn dh_gen_set_template; +static OSSL_FUNC_keymgmt_gen_set_params_fn dh_gen_set_params; +static OSSL_FUNC_keymgmt_gen_settable_params_fn dh_gen_settable_params; +static OSSL_FUNC_keymgmt_gen_fn dh_gen; +static OSSL_FUNC_keymgmt_gen_cleanup_fn dh_gen_cleanup; +static OSSL_FUNC_keymgmt_load_fn dh_load; +static OSSL_FUNC_keymgmt_get_params_fn dh_get_params; +static OSSL_FUNC_keymgmt_gettable_params_fn dh_gettable_params; +static OSSL_FUNC_keymgmt_set_params_fn dh_set_params; +static OSSL_FUNC_keymgmt_settable_params_fn dh_settable_params; +static OSSL_FUNC_keymgmt_has_fn dh_has; +static OSSL_FUNC_keymgmt_match_fn dh_match; +static OSSL_FUNC_keymgmt_validate_fn dh_validate; +static OSSL_FUNC_keymgmt_import_fn dh_import; +static OSSL_FUNC_keymgmt_import_types_fn dh_import_types; +static OSSL_FUNC_keymgmt_export_fn dh_export; +static OSSL_FUNC_keymgmt_export_types_fn dh_export_types; +static OSSL_FUNC_keymgmt_dup_fn dh_dup; + +#define DH_POSSIBLE_SELECTIONS \ + (OSSL_KEYMGMT_SELECT_KEYPAIR | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) + +struct dh_gen_ctx { + OSSL_LIB_CTX *libctx; + + FFC_PARAMS *ffc_params; + int selection; + /* All these parameters are used for parameter generation only */ + /* If there is a group name then the remaining parameters are not needed */ + int group_nid; + size_t pbits; + size_t qbits; + unsigned char *seed; /* optional FIPS186-4 param for testing */ + size_t seedlen; + int gindex; /* optional FIPS186-4 generator index (ignored if -1) */ + int gen_type; /* see dhtype2id */ + int generator; /* Used by DH_PARAMGEN_TYPE_GENERATOR in non fips mode only */ + int pcounter; + int hindex; + int priv_len; + + char *mdname; + char *mdprops; + OSSL_CALLBACK *cb; + void *cbarg; + int dh_type; +}; + +static int dh_gen_type_name2id_w_default(const char *name, int type) +{ + if (strcmp(name, "default") == 0) { +#ifdef FIPS_MODULE + if (type == DH_FLAG_TYPE_DHX) + return DH_PARAMGEN_TYPE_FIPS_186_4; + + return DH_PARAMGEN_TYPE_GROUP; +#else + if (type == DH_FLAG_TYPE_DHX) + return DH_PARAMGEN_TYPE_FIPS_186_2; + + return DH_PARAMGEN_TYPE_GENERATOR; +#endif + } + + return ossl_dh_gen_type_name2id(name, type); +} + +static void *dh_newdata(void *provctx) +{ + DH *dh = NULL; + + if (ossl_prov_is_running()) { + dh = ossl_dh_new_ex(PROV_LIBCTX_OF(provctx)); + if (dh != NULL) { + DH_clear_flags(dh, DH_FLAG_TYPE_MASK); + DH_set_flags(dh, DH_FLAG_TYPE_DH); + } + } + return dh; +} + +static void *dhx_newdata(void *provctx) +{ + DH *dh = NULL; + + dh = ossl_dh_new_ex(PROV_LIBCTX_OF(provctx)); + if (dh != NULL) { + DH_clear_flags(dh, DH_FLAG_TYPE_MASK); + DH_set_flags(dh, DH_FLAG_TYPE_DHX); + } + return dh; +} + +static void dh_freedata(void *keydata) +{ + DH_free(keydata); +} + +static int dh_has(const void *keydata, int selection) +{ + const DH *dh = keydata; + int ok = 1; + + if (!ossl_prov_is_running() || dh == NULL) + return 0; + if ((selection & DH_POSSIBLE_SELECTIONS) == 0) + return 1; /* the selection is not missing */ + + if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) + ok = ok && (DH_get0_pub_key(dh) != NULL); + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + ok = ok && (DH_get0_priv_key(dh) != NULL); + if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) + ok = ok && (DH_get0_p(dh) != NULL && DH_get0_g(dh) != NULL); + return ok; +} + +static int dh_match(const void *keydata1, const void *keydata2, int selection) +{ + const DH *dh1 = keydata1; + const DH *dh2 = keydata2; + int ok = 1; + + if (!ossl_prov_is_running()) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) { + int key_checked = 0; + + if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) { + const BIGNUM *pa = DH_get0_pub_key(dh1); + const BIGNUM *pb = DH_get0_pub_key(dh2); + + if (pa != NULL && pb != NULL) { + ok = ok && BN_cmp(pa, pb) == 0; + key_checked = 1; + } + } + if (!key_checked + && (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) { + const BIGNUM *pa = DH_get0_priv_key(dh1); + const BIGNUM *pb = DH_get0_priv_key(dh2); + + if (pa != NULL && pb != NULL) { + ok = ok && BN_cmp(pa, pb) == 0; + key_checked = 1; + } + } + ok = ok && key_checked; + } + if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) { + FFC_PARAMS *dhparams1 = ossl_dh_get0_params((DH *)dh1); + FFC_PARAMS *dhparams2 = ossl_dh_get0_params((DH *)dh2); + + ok = ok && ossl_ffc_params_cmp(dhparams1, dhparams2, 1); + } + return ok; +} + +static int dh_import(void *keydata, int selection, const OSSL_PARAM params[]) +{ + DH *dh = keydata; + int ok = 1; + + if (!ossl_prov_is_running() || dh == NULL) + return 0; + + if ((selection & DH_POSSIBLE_SELECTIONS) == 0) + return 0; + + /* a key without parameters is meaningless */ + ok = ok && ossl_dh_params_fromdata(dh, params); + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) { + int include_private = + selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0; + + ok = ok && ossl_dh_key_fromdata(dh, params, include_private); + } + + return ok; +} + +static int dh_export(void *keydata, int selection, OSSL_CALLBACK *param_cb, + void *cbarg) +{ + DH *dh = keydata; + OSSL_PARAM_BLD *tmpl = NULL; + OSSL_PARAM *params = NULL; + int ok = 1; + + if (!ossl_prov_is_running() || dh == NULL) + return 0; + + if ((selection & DH_POSSIBLE_SELECTIONS) == 0) + return 0; + + tmpl = OSSL_PARAM_BLD_new(); + if (tmpl == NULL) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_ALL_PARAMETERS) != 0) + ok = ok && ossl_dh_params_todata(dh, tmpl, NULL); + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) { + int include_private = + selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0; + + ok = ok && ossl_dh_key_todata(dh, tmpl, NULL, include_private); + } + + if (!ok || (params = OSSL_PARAM_BLD_to_param(tmpl)) == NULL) { + ok = 0; + goto err; + } + + ok = param_cb(params, cbarg); + OSSL_PARAM_free(params); +err: + OSSL_PARAM_BLD_free(tmpl); + return ok; +} + +/* IMEXPORT = IMPORT + EXPORT */ + +# define DH_IMEXPORTABLE_PARAMETERS \ + OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_P, NULL, 0), \ + OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_Q, NULL, 0), \ + OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_G, NULL, 0), \ + OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_COFACTOR, NULL, 0), \ + OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_GINDEX, NULL), \ + OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_PCOUNTER, NULL), \ + OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_H, NULL), \ + OSSL_PARAM_int(OSSL_PKEY_PARAM_DH_PRIV_LEN, NULL), \ + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_FFC_SEED, NULL, 0), \ + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0) +# define DH_IMEXPORTABLE_PUBLIC_KEY \ + OSSL_PARAM_BN(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0) +# define DH_IMEXPORTABLE_PRIVATE_KEY \ + OSSL_PARAM_BN(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0) +static const OSSL_PARAM dh_all_types[] = { + DH_IMEXPORTABLE_PARAMETERS, + DH_IMEXPORTABLE_PUBLIC_KEY, + DH_IMEXPORTABLE_PRIVATE_KEY, + OSSL_PARAM_END +}; +static const OSSL_PARAM dh_parameter_types[] = { + DH_IMEXPORTABLE_PARAMETERS, + OSSL_PARAM_END +}; +static const OSSL_PARAM dh_key_types[] = { + DH_IMEXPORTABLE_PUBLIC_KEY, + DH_IMEXPORTABLE_PRIVATE_KEY, + OSSL_PARAM_END +}; +static const OSSL_PARAM *dh_types[] = { + NULL, /* Index 0 = none of them */ + dh_parameter_types, /* Index 1 = parameter types */ + dh_key_types, /* Index 2 = key types */ + dh_all_types /* Index 3 = 1 + 2 */ +}; + +static const OSSL_PARAM *dh_imexport_types(int selection) +{ + int type_select = 0; + + if ((selection & OSSL_KEYMGMT_SELECT_ALL_PARAMETERS) != 0) + type_select += 1; + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) + type_select += 2; + return dh_types[type_select]; +} + +static const OSSL_PARAM *dh_import_types(int selection) +{ + return dh_imexport_types(selection); +} + +static const OSSL_PARAM *dh_export_types(int selection) +{ + return dh_imexport_types(selection); +} + +static ossl_inline int dh_get_params(void *key, OSSL_PARAM params[]) +{ + DH *dh = key; + OSSL_PARAM *p; + + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS)) != NULL + && !OSSL_PARAM_set_int(p, DH_bits(dh))) + return 0; + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS)) != NULL + && !OSSL_PARAM_set_int(p, DH_security_bits(dh))) + return 0; + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE)) != NULL + && !OSSL_PARAM_set_int(p, DH_size(dh))) + return 0; + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY)) != NULL) { + if (p->data_type != OSSL_PARAM_OCTET_STRING) + return 0; + p->return_size = ossl_dh_key2buf(dh, (unsigned char **)&p->data, + p->data_size, 0); + if (p->return_size == 0) + return 0; + } + + return ossl_dh_params_todata(dh, NULL, params) + && ossl_dh_key_todata(dh, NULL, params, 1); +} + +static const OSSL_PARAM dh_params[] = { + OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0), + DH_IMEXPORTABLE_PARAMETERS, + DH_IMEXPORTABLE_PUBLIC_KEY, + DH_IMEXPORTABLE_PRIVATE_KEY, + OSSL_PARAM_END +}; + +static const OSSL_PARAM *dh_gettable_params(void *provctx) +{ + return dh_params; +} + +static const OSSL_PARAM dh_known_settable_params[] = { + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0), + OSSL_PARAM_END +}; + +static const OSSL_PARAM *dh_settable_params(void *provctx) +{ + return dh_known_settable_params; +} + +static int dh_set_params(void *key, const OSSL_PARAM params[]) +{ + DH *dh = key; + const OSSL_PARAM *p; + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY); + if (p != NULL + && (p->data_type != OSSL_PARAM_OCTET_STRING + || !ossl_dh_buf2key(dh, p->data, p->data_size))) + return 0; + + return 1; +} + +static int dh_validate_public(const DH *dh, int checktype) +{ + const BIGNUM *pub_key = NULL; + int res = 0; + + DH_get0_key(dh, &pub_key, NULL); + if (pub_key == NULL) + return 0; + + /* + * The partial test is only valid for named group's with q = (p - 1) / 2 + * but for that case it is also fully sufficient to check the key validity. + */ + if (ossl_dh_is_named_safe_prime_group(dh)) + return ossl_dh_check_pub_key_partial(dh, pub_key, &res); + + return DH_check_pub_key_ex(dh, pub_key); +} + +static int dh_validate_private(const DH *dh) +{ + int status = 0; + const BIGNUM *priv_key = NULL; + + DH_get0_key(dh, NULL, &priv_key); + if (priv_key == NULL) + return 0; + return ossl_dh_check_priv_key(dh, priv_key, &status); +} + +static int dh_validate(const void *keydata, int selection, int checktype) +{ + const DH *dh = keydata; + int ok = 1; + + if (!ossl_prov_is_running()) + return 0; + + if ((selection & DH_POSSIBLE_SELECTIONS) == 0) + return 1; /* nothing to validate */ + + if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) { + /* + * Both of these functions check parameters. DH_check_params_ex() + * performs a lightweight check (e.g. it does not check that p is a + * safe prime) + */ + if (checktype == OSSL_KEYMGMT_VALIDATE_QUICK_CHECK) + ok = ok && DH_check_params_ex(dh); + else + ok = ok && DH_check_ex(dh); + } + + if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) + ok = ok && dh_validate_public(dh, checktype); + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + ok = ok && dh_validate_private(dh); + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) + == OSSL_KEYMGMT_SELECT_KEYPAIR) + ok = ok && ossl_dh_check_pairwise(dh, 0); + return ok; +} + +static void *dh_gen_init_base(void *provctx, int selection, + const OSSL_PARAM params[], int type) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx); + struct dh_gen_ctx *gctx = NULL; + + if (!ossl_prov_is_running()) + return NULL; + + if ((selection & (OSSL_KEYMGMT_SELECT_KEYPAIR + | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS)) == 0) + return NULL; + + if ((gctx = OPENSSL_zalloc(sizeof(*gctx))) != NULL) { + gctx->selection = selection; + gctx->libctx = libctx; + gctx->pbits = 2048; + gctx->qbits = 224; + gctx->mdname = NULL; +#ifdef FIPS_MODULE + gctx->gen_type = (type == DH_FLAG_TYPE_DHX) + ? DH_PARAMGEN_TYPE_FIPS_186_4 + : DH_PARAMGEN_TYPE_GROUP; +#else + gctx->gen_type = (type == DH_FLAG_TYPE_DHX) + ? DH_PARAMGEN_TYPE_FIPS_186_2 + : DH_PARAMGEN_TYPE_GENERATOR; +#endif + gctx->gindex = -1; + gctx->hindex = 0; + gctx->pcounter = -1; + gctx->generator = DH_GENERATOR_2; + gctx->dh_type = type; + } + if (!dh_gen_set_params(gctx, params)) { + OPENSSL_free(gctx); + gctx = NULL; + } + return gctx; +} + +static void *dh_gen_init(void *provctx, int selection, + const OSSL_PARAM params[]) +{ + return dh_gen_init_base(provctx, selection, params, DH_FLAG_TYPE_DH); +} + +static void *dhx_gen_init(void *provctx, int selection, + const OSSL_PARAM params[]) +{ + return dh_gen_init_base(provctx, selection, params, DH_FLAG_TYPE_DHX); +} + +static int dh_gen_set_template(void *genctx, void *templ) +{ + struct dh_gen_ctx *gctx = genctx; + DH *dh = templ; + + if (!ossl_prov_is_running() || gctx == NULL || dh == NULL) + return 0; + gctx->ffc_params = ossl_dh_get0_params(dh); + return 1; +} + +static int dh_set_gen_seed(struct dh_gen_ctx *gctx, unsigned char *seed, + size_t seedlen) +{ + OPENSSL_clear_free(gctx->seed, gctx->seedlen); + gctx->seed = NULL; + gctx->seedlen = 0; + if (seed != NULL && seedlen > 0) { + gctx->seed = OPENSSL_memdup(seed, seedlen); + if (gctx->seed == NULL) + return 0; + gctx->seedlen = seedlen; + } + return 1; +} + +static int dh_gen_common_set_params(void *genctx, const OSSL_PARAM params[]) +{ + struct dh_gen_ctx *gctx = genctx; + const OSSL_PARAM *p; + int gen_type = -1; + + if (gctx == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_TYPE); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_UTF8_STRING + || ((gen_type = + dh_gen_type_name2id_w_default(p->data, gctx->dh_type)) == -1)) { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } + if (gen_type != -1) + gctx->gen_type = gen_type; + } + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_GROUP_NAME); + if (p != NULL) { + const DH_NAMED_GROUP *group = NULL; + + if (p->data_type != OSSL_PARAM_UTF8_STRING + || p->data == NULL + || (group = ossl_ffc_name_to_dh_named_group(p->data)) == NULL + || ((gctx->group_nid = + ossl_ffc_named_group_get_uid(group)) == NID_undef)) { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } + } + if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_PBITS)) != NULL + && !OSSL_PARAM_get_size_t(p, &gctx->pbits)) + return 0; + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_DH_PRIV_LEN); + if (p != NULL && !OSSL_PARAM_get_int(p, &gctx->priv_len)) + return 0; + return 1; +} + +static const OSSL_PARAM *dh_gen_settable_params(ossl_unused void *genctx, + ossl_unused void *provctx) +{ + static const OSSL_PARAM dh_gen_settable[] = { + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_FFC_TYPE, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0), + OSSL_PARAM_int(OSSL_PKEY_PARAM_DH_PRIV_LEN, NULL), + OSSL_PARAM_size_t(OSSL_PKEY_PARAM_FFC_PBITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_DH_GENERATOR, NULL), + OSSL_PARAM_END + }; + return dh_gen_settable; +} + +static const OSSL_PARAM *dhx_gen_settable_params(ossl_unused void *genctx, + ossl_unused void *provctx) +{ + static const OSSL_PARAM dhx_gen_settable[] = { + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_FFC_TYPE, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0), + OSSL_PARAM_int(OSSL_PKEY_PARAM_DH_PRIV_LEN, NULL), + OSSL_PARAM_size_t(OSSL_PKEY_PARAM_FFC_PBITS, NULL), + OSSL_PARAM_size_t(OSSL_PKEY_PARAM_FFC_QBITS, NULL), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_FFC_DIGEST, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_FFC_DIGEST_PROPS, NULL, 0), + OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_GINDEX, NULL), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_FFC_SEED, NULL, 0), + OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_PCOUNTER, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_H, NULL), + OSSL_PARAM_END + }; + return dhx_gen_settable; +} + +static int dhx_gen_set_params(void *genctx, const OSSL_PARAM params[]) +{ + struct dh_gen_ctx *gctx = genctx; + const OSSL_PARAM *p; + + if (!dh_gen_common_set_params(genctx, params)) + return 0; + + /* Parameters related to fips186-4 and fips186-2 */ + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_GINDEX); + if (p != NULL && !OSSL_PARAM_get_int(p, &gctx->gindex)) + return 0; + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_PCOUNTER); + if (p != NULL && !OSSL_PARAM_get_int(p, &gctx->pcounter)) + return 0; + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_H); + if (p != NULL && !OSSL_PARAM_get_int(p, &gctx->hindex)) + return 0; + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_SEED); + if (p != NULL + && (p->data_type != OSSL_PARAM_OCTET_STRING + || !dh_set_gen_seed(gctx, p->data, p->data_size))) + return 0; + if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_QBITS)) != NULL + && !OSSL_PARAM_get_size_t(p, &gctx->qbits)) + return 0; + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_DIGEST); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_UTF8_STRING) + return 0; + OPENSSL_free(gctx->mdname); + gctx->mdname = OPENSSL_strdup(p->data); + if (gctx->mdname == NULL) + return 0; + } + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_DIGEST_PROPS); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_UTF8_STRING) + return 0; + OPENSSL_free(gctx->mdprops); + gctx->mdprops = OPENSSL_strdup(p->data); + if (gctx->mdprops == NULL) + return 0; + } + + /* Parameters that are not allowed for DHX */ + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_DH_GENERATOR); + if (p != NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_UNSUPPORTED); + return 0; + } + return 1; +} + +static int dh_gen_set_params(void *genctx, const OSSL_PARAM params[]) +{ + struct dh_gen_ctx *gctx = genctx; + const OSSL_PARAM *p; + + if (!dh_gen_common_set_params(genctx, params)) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_DH_GENERATOR); + if (p != NULL && !OSSL_PARAM_get_int(p, &gctx->generator)) + return 0; + + /* Parameters that are not allowed for DH */ + if (OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_GINDEX) != NULL + || OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_PCOUNTER) != NULL + || OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_H) != NULL + || OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_SEED) != NULL + || OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_QBITS) != NULL + || OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_DIGEST) != NULL + || OSSL_PARAM_locate_const(params, + OSSL_PKEY_PARAM_FFC_DIGEST_PROPS) != NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } + return 1; +} + +static int dh_gencb(int p, int n, BN_GENCB *cb) +{ + struct dh_gen_ctx *gctx = BN_GENCB_get_arg(cb); + OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END }; + + params[0] = OSSL_PARAM_construct_int(OSSL_GEN_PARAM_POTENTIAL, &p); + params[1] = OSSL_PARAM_construct_int(OSSL_GEN_PARAM_ITERATION, &n); + + return gctx->cb(params, gctx->cbarg); +} + +static void *dh_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg) +{ + int ret = 0; + struct dh_gen_ctx *gctx = genctx; + DH *dh = NULL; + BN_GENCB *gencb = NULL; + FFC_PARAMS *ffc; + + if (!ossl_prov_is_running() || gctx == NULL) + return NULL; + + /* + * If a group name is selected then the type is group regardless of what + * the user selected. This overrides rather than errors for backwards + * compatibility. + */ + if (gctx->group_nid != NID_undef) + gctx->gen_type = DH_PARAMGEN_TYPE_GROUP; + + /* + * Do a bounds check on context gen_type. Must be in range: + * DH_PARAMGEN_TYPE_GENERATOR <= gen_type <= DH_PARAMGEN_TYPE_GROUP + * Noted here as this needs to be adjusted if a new group type is + * added. + */ + if (!ossl_assert((gctx->gen_type >= DH_PARAMGEN_TYPE_GENERATOR) + && (gctx->gen_type <= DH_PARAMGEN_TYPE_GROUP))) { + ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR, + "gen_type set to unsupported value %d", gctx->gen_type); + return NULL; + } + + /* For parameter generation - If there is a group name just create it */ + if (gctx->gen_type == DH_PARAMGEN_TYPE_GROUP + && gctx->ffc_params == NULL) { + /* Select a named group if there is not one already */ + if (gctx->group_nid == NID_undef) + gctx->group_nid = ossl_dh_get_named_group_uid_from_size(gctx->pbits); + if (gctx->group_nid == NID_undef) + return NULL; + dh = ossl_dh_new_by_nid_ex(gctx->libctx, gctx->group_nid); + if (dh == NULL) + return NULL; + ffc = ossl_dh_get0_params(dh); + } else { + dh = ossl_dh_new_ex(gctx->libctx); + if (dh == NULL) + return NULL; + ffc = ossl_dh_get0_params(dh); + + /* Copy the template value if one was passed */ + if (gctx->ffc_params != NULL + && !ossl_ffc_params_copy(ffc, gctx->ffc_params)) + goto end; + + if (!ossl_ffc_params_set_seed(ffc, gctx->seed, gctx->seedlen)) + goto end; + if (gctx->gindex != -1) { + ossl_ffc_params_set_gindex(ffc, gctx->gindex); + if (gctx->pcounter != -1) + ossl_ffc_params_set_pcounter(ffc, gctx->pcounter); + } else if (gctx->hindex != 0) { + ossl_ffc_params_set_h(ffc, gctx->hindex); + } + if (gctx->mdname != NULL) + ossl_ffc_set_digest(ffc, gctx->mdname, gctx->mdprops); + gctx->cb = osslcb; + gctx->cbarg = cbarg; + gencb = BN_GENCB_new(); + if (gencb != NULL) + BN_GENCB_set(gencb, dh_gencb, genctx); + + if ((gctx->selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) { + /* + * NOTE: The old safe prime generator code is not used in fips mode, + * (i.e internally it ignores the generator and chooses a named + * group based on pbits. + */ + if (gctx->gen_type == DH_PARAMGEN_TYPE_GENERATOR) + ret = DH_generate_parameters_ex(dh, gctx->pbits, + gctx->generator, gencb); + else + ret = ossl_dh_generate_ffc_parameters(dh, gctx->gen_type, + gctx->pbits, gctx->qbits, + gencb); + if (ret <= 0) + goto end; + } + } + + if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) { + if (ffc->p == NULL || ffc->g == NULL) + goto end; + if (gctx->priv_len > 0) + DH_set_length(dh, (long)gctx->priv_len); + ossl_ffc_params_enable_flags(ffc, FFC_PARAM_FLAG_VALIDATE_LEGACY, + gctx->gen_type == DH_PARAMGEN_TYPE_FIPS_186_2); + if (DH_generate_key(dh) <= 0) + goto end; +#ifdef FIPS_MODULE + if (!ossl_fips_self_testing()) { + ret = ossl_dh_check_pairwise(dh, 0); + if (ret <= 0) { + ossl_set_error_state(OSSL_SELF_TEST_TYPE_PCT); + goto end; + } + } +#endif /* FIPS_MODULE */ + } + DH_clear_flags(dh, DH_FLAG_TYPE_MASK); + DH_set_flags(dh, gctx->dh_type); + + ret = 1; +end: + if (ret <= 0) { + DH_free(dh); + dh = NULL; + } + BN_GENCB_free(gencb); + return dh; +} + +static void dh_gen_cleanup(void *genctx) +{ + struct dh_gen_ctx *gctx = genctx; + + if (gctx == NULL) + return; + + OPENSSL_free(gctx->mdname); + OPENSSL_free(gctx->mdprops); + OPENSSL_clear_free(gctx->seed, gctx->seedlen); + OPENSSL_free(gctx); +} + +static void *dh_load(const void *reference, size_t reference_sz) +{ + DH *dh = NULL; + + if (ossl_prov_is_running() && reference_sz == sizeof(dh)) { + /* The contents of the reference is the address to our object */ + dh = *(DH **)reference; + /* We grabbed, so we detach it */ + *(DH **)reference = NULL; + return dh; + } + return NULL; +} + +static void *dh_dup(const void *keydata_from, int selection) +{ + if (ossl_prov_is_running()) + return ossl_dh_dup(keydata_from, selection); + return NULL; +} + +const OSSL_DISPATCH ossl_dh_keymgmt_functions[] = { + { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))dh_newdata }, + { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))dh_gen_init }, + { OSSL_FUNC_KEYMGMT_GEN_SET_TEMPLATE, (void (*)(void))dh_gen_set_template }, + { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))dh_gen_set_params }, + { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, + (void (*)(void))dh_gen_settable_params }, + { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))dh_gen }, + { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))dh_gen_cleanup }, + { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))dh_load }, + { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))dh_freedata }, + { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))dh_get_params }, + { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))dh_gettable_params }, + { OSSL_FUNC_KEYMGMT_SET_PARAMS, (void (*) (void))dh_set_params }, + { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*) (void))dh_settable_params }, + { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))dh_has }, + { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))dh_match }, + { OSSL_FUNC_KEYMGMT_VALIDATE, (void (*)(void))dh_validate }, + { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))dh_import }, + { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))dh_import_types }, + { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))dh_export }, + { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))dh_export_types }, + { OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))dh_dup }, + OSSL_DISPATCH_END +}; + +/* For any DH key, we use the "DH" algorithms regardless of sub-type. */ +static const char *dhx_query_operation_name(int operation_id) +{ + return "DH"; +} + +const OSSL_DISPATCH ossl_dhx_keymgmt_functions[] = { + { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))dhx_newdata }, + { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))dhx_gen_init }, + { OSSL_FUNC_KEYMGMT_GEN_SET_TEMPLATE, (void (*)(void))dh_gen_set_template }, + { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))dhx_gen_set_params }, + { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, + (void (*)(void))dhx_gen_settable_params }, + { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))dh_gen }, + { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))dh_gen_cleanup }, + { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))dh_load }, + { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))dh_freedata }, + { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))dh_get_params }, + { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))dh_gettable_params }, + { OSSL_FUNC_KEYMGMT_SET_PARAMS, (void (*) (void))dh_set_params }, + { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*) (void))dh_settable_params }, + { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))dh_has }, + { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))dh_match }, + { OSSL_FUNC_KEYMGMT_VALIDATE, (void (*)(void))dh_validate }, + { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))dh_import }, + { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))dh_import_types }, + { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))dh_export }, + { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))dh_export_types }, + { OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME, + (void (*)(void))dhx_query_operation_name }, + { OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))dh_dup }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/keymgmt/dsa_kmgmt.c b/crypto/openssl/providers/implementations/keymgmt/dsa_kmgmt.c new file mode 100644 index 000000000000..92f661b1f3ab --- /dev/null +++ b/crypto/openssl/providers/implementations/keymgmt/dsa_kmgmt.c @@ -0,0 +1,748 @@ +/* + * Copyright 2019-2024 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 + */ + +/* + * DSA low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/bn.h> +#include <openssl/err.h> +#include "prov/securitycheck.h" +#include "prov/providercommon.h" +#include "prov/implementations.h" +#include "prov/provider_ctx.h" +#include "crypto/dsa.h" +#include "internal/sizes.h" +#include "internal/nelem.h" +#include "internal/param_build_set.h" + +static OSSL_FUNC_keymgmt_new_fn dsa_newdata; +static OSSL_FUNC_keymgmt_free_fn dsa_freedata; +static OSSL_FUNC_keymgmt_gen_init_fn dsa_gen_init; +static OSSL_FUNC_keymgmt_gen_set_template_fn dsa_gen_set_template; +static OSSL_FUNC_keymgmt_gen_set_params_fn dsa_gen_set_params; +static OSSL_FUNC_keymgmt_gen_settable_params_fn dsa_gen_settable_params; +static OSSL_FUNC_keymgmt_gen_get_params_fn dsa_gen_get_params; +static OSSL_FUNC_keymgmt_gen_gettable_params_fn dsa_gen_gettable_params; +static OSSL_FUNC_keymgmt_gen_fn dsa_gen; +static OSSL_FUNC_keymgmt_gen_cleanup_fn dsa_gen_cleanup; +static OSSL_FUNC_keymgmt_load_fn dsa_load; +static OSSL_FUNC_keymgmt_get_params_fn dsa_get_params; +static OSSL_FUNC_keymgmt_gettable_params_fn dsa_gettable_params; +static OSSL_FUNC_keymgmt_has_fn dsa_has; +static OSSL_FUNC_keymgmt_match_fn dsa_match; +static OSSL_FUNC_keymgmt_validate_fn dsa_validate; +static OSSL_FUNC_keymgmt_import_fn dsa_import; +static OSSL_FUNC_keymgmt_import_types_fn dsa_import_types; +static OSSL_FUNC_keymgmt_export_fn dsa_export; +static OSSL_FUNC_keymgmt_export_types_fn dsa_export_types; +static OSSL_FUNC_keymgmt_dup_fn dsa_dup; + +#define DSA_DEFAULT_MD "SHA256" +#define DSA_POSSIBLE_SELECTIONS \ + (OSSL_KEYMGMT_SELECT_KEYPAIR | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) + +struct dsa_gen_ctx { + OSSL_LIB_CTX *libctx; + + FFC_PARAMS *ffc_params; + int selection; + /* All these parameters are used for parameter generation only */ + size_t pbits; + size_t qbits; + unsigned char *seed; /* optional FIPS186-4 param for testing */ + size_t seedlen; + int gindex; /* optional FIPS186-4 generator index (ignored if -1) */ + int gen_type; /* DSA_PARAMGEN_TYPE_FIPS_186_2 or DSA_PARAMGEN_TYPE_FIPS_186_4 */ + int pcounter; + int hindex; + char *mdname; + char *mdprops; + OSSL_CALLBACK *cb; + void *cbarg; + OSSL_FIPS_IND_DECLARE +}; +typedef struct dh_name2id_st{ + const char *name; + int id; +} DSA_GENTYPE_NAME2ID; + +static const DSA_GENTYPE_NAME2ID dsatype2id[] = { +#ifdef FIPS_MODULE + { "default", DSA_PARAMGEN_TYPE_FIPS_186_4 }, +#else + { "default", DSA_PARAMGEN_TYPE_FIPS_DEFAULT }, +#endif + { "fips186_4", DSA_PARAMGEN_TYPE_FIPS_186_4 }, + { "fips186_2", DSA_PARAMGEN_TYPE_FIPS_186_2 }, +}; + +static int dsa_gen_type_name2id(const char *name) +{ + size_t i; + + for (i = 0; i < OSSL_NELEM(dsatype2id); ++i) { + if (OPENSSL_strcasecmp(dsatype2id[i].name, name) == 0) + return dsatype2id[i].id; + } + return -1; +} + +static int dsa_key_todata(DSA *dsa, OSSL_PARAM_BLD *bld, OSSL_PARAM params[], + int include_private) +{ + const BIGNUM *priv = NULL, *pub = NULL; + + if (dsa == NULL) + return 0; + + DSA_get0_key(dsa, &pub, &priv); + if (include_private + && priv != NULL + && !ossl_param_build_set_bn(bld, params, OSSL_PKEY_PARAM_PRIV_KEY, priv)) + return 0; + if (pub != NULL + && !ossl_param_build_set_bn(bld, params, OSSL_PKEY_PARAM_PUB_KEY, pub)) + return 0; + + return 1; +} + +static void *dsa_newdata(void *provctx) +{ + if (!ossl_prov_is_running()) + return NULL; + return ossl_dsa_new(PROV_LIBCTX_OF(provctx)); +} + +static void dsa_freedata(void *keydata) +{ + DSA_free(keydata); +} + +static int dsa_has(const void *keydata, int selection) +{ + const DSA *dsa = keydata; + int ok = 1; + + if (!ossl_prov_is_running() || dsa == NULL) + return 0; + if ((selection & DSA_POSSIBLE_SELECTIONS) == 0) + return 1; /* the selection is not missing */ + + if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) + ok = ok && (DSA_get0_pub_key(dsa) != NULL); + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + ok = ok && (DSA_get0_priv_key(dsa) != NULL); + if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) + ok = ok && (DSA_get0_p(dsa) != NULL && DSA_get0_g(dsa) != NULL); + return ok; +} + +static int dsa_match(const void *keydata1, const void *keydata2, int selection) +{ + const DSA *dsa1 = keydata1; + const DSA *dsa2 = keydata2; + int ok = 1; + + if (!ossl_prov_is_running()) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) { + int key_checked = 0; + + if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) { + const BIGNUM *pa = DSA_get0_pub_key(dsa1); + const BIGNUM *pb = DSA_get0_pub_key(dsa2); + + if (pa != NULL && pb != NULL) { + ok = ok && BN_cmp(pa, pb) == 0; + key_checked = 1; + } + } + if (!key_checked + && (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) { + const BIGNUM *pa = DSA_get0_priv_key(dsa1); + const BIGNUM *pb = DSA_get0_priv_key(dsa2); + + if (pa != NULL && pb != NULL) { + ok = ok && BN_cmp(pa, pb) == 0; + key_checked = 1; + } + } + ok = ok && key_checked; + } + if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) { + FFC_PARAMS *dsaparams1 = ossl_dsa_get0_params((DSA *)dsa1); + FFC_PARAMS *dsaparams2 = ossl_dsa_get0_params((DSA *)dsa2); + + ok = ok && ossl_ffc_params_cmp(dsaparams1, dsaparams2, 1); + } + return ok; +} + +static int dsa_import(void *keydata, int selection, const OSSL_PARAM params[]) +{ + DSA *dsa = keydata; + int ok = 1; + + if (!ossl_prov_is_running() || dsa == NULL) + return 0; + + if ((selection & DSA_POSSIBLE_SELECTIONS) == 0) + return 0; + + /* a key without parameters is meaningless */ + ok = ok && ossl_dsa_ffc_params_fromdata(dsa, params); + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) { + int include_private = + selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0; + + ok = ok && ossl_dsa_key_fromdata(dsa, params, include_private); + } + + return ok; +} + +static int dsa_export(void *keydata, int selection, OSSL_CALLBACK *param_cb, + void *cbarg) +{ + DSA *dsa = keydata; + OSSL_PARAM_BLD *tmpl; + OSSL_PARAM *params = NULL; + int ok = 1; + + if (!ossl_prov_is_running() || dsa == NULL) + return 0; + + if ((selection & DSA_POSSIBLE_SELECTIONS) == 0) + return 0; + + tmpl = OSSL_PARAM_BLD_new(); + if (tmpl == NULL) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_ALL_PARAMETERS) != 0) + ok = ok && ossl_ffc_params_todata(ossl_dsa_get0_params(dsa), tmpl, NULL); + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) { + int include_private = + selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0; + + ok = ok && dsa_key_todata(dsa, tmpl, NULL, include_private); + } + + if (!ok || (params = OSSL_PARAM_BLD_to_param(tmpl)) == NULL) { + ok = 0; + goto err; + } + + ok = param_cb(params, cbarg); + OSSL_PARAM_free(params); +err: + OSSL_PARAM_BLD_free(tmpl); + return ok; +} + +/* IMEXPORT = IMPORT + EXPORT */ + +# define DSA_IMEXPORTABLE_PARAMETERS \ + OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_P, NULL, 0), \ + OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_Q, NULL, 0), \ + OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_G, NULL, 0), \ + OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_COFACTOR, NULL, 0), \ + OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_GINDEX, NULL), \ + OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_PCOUNTER, NULL), \ + OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_H, NULL), \ + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_FFC_SEED, NULL, 0) +# define DSA_IMEXPORTABLE_PUBLIC_KEY \ + OSSL_PARAM_BN(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0) +# define DSA_IMEXPORTABLE_PRIVATE_KEY \ + OSSL_PARAM_BN(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0) +static const OSSL_PARAM dsa_all_types[] = { + DSA_IMEXPORTABLE_PARAMETERS, + DSA_IMEXPORTABLE_PUBLIC_KEY, + DSA_IMEXPORTABLE_PRIVATE_KEY, + OSSL_PARAM_END +}; +static const OSSL_PARAM dsa_parameter_types[] = { + DSA_IMEXPORTABLE_PARAMETERS, + OSSL_PARAM_END +}; +static const OSSL_PARAM dsa_key_types[] = { + DSA_IMEXPORTABLE_PUBLIC_KEY, + DSA_IMEXPORTABLE_PRIVATE_KEY, + OSSL_PARAM_END +}; +static const OSSL_PARAM *dsa_types[] = { + NULL, /* Index 0 = none of them */ + dsa_parameter_types, /* Index 1 = parameter types */ + dsa_key_types, /* Index 2 = key types */ + dsa_all_types /* Index 3 = 1 + 2 */ +}; + +static const OSSL_PARAM *dsa_imexport_types(int selection) +{ + int type_select = 0; + + if ((selection & OSSL_KEYMGMT_SELECT_ALL_PARAMETERS) != 0) + type_select += 1; + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) + type_select += 2; + return dsa_types[type_select]; +} + +static const OSSL_PARAM *dsa_import_types(int selection) +{ + return dsa_imexport_types(selection); +} + +static const OSSL_PARAM *dsa_export_types(int selection) +{ + return dsa_imexport_types(selection); +} + +static ossl_inline int dsa_get_params(void *key, OSSL_PARAM params[]) +{ + DSA *dsa = key; + OSSL_PARAM *p; + + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS)) != NULL + && !OSSL_PARAM_set_int(p, DSA_bits(dsa))) + return 0; + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS)) != NULL + && !OSSL_PARAM_set_int(p, DSA_security_bits(dsa))) + return 0; + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE)) != NULL + && !OSSL_PARAM_set_int(p, DSA_size(dsa))) + return 0; + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_DEFAULT_DIGEST)) != NULL + && !OSSL_PARAM_set_utf8_string(p, DSA_DEFAULT_MD)) + return 0; + return ossl_ffc_params_todata(ossl_dsa_get0_params(dsa), NULL, params) + && dsa_key_todata(dsa, NULL, params, 1); +} + +static const OSSL_PARAM dsa_params[] = { + OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_DEFAULT_DIGEST, NULL, 0), + DSA_IMEXPORTABLE_PARAMETERS, + DSA_IMEXPORTABLE_PUBLIC_KEY, + DSA_IMEXPORTABLE_PRIVATE_KEY, + OSSL_PARAM_END +}; + +static const OSSL_PARAM *dsa_gettable_params(void *provctx) +{ + return dsa_params; +} + +static int dsa_validate_domparams(const DSA *dsa, int checktype) +{ + int status = 0; + + return ossl_dsa_check_params(dsa, checktype, &status); +} + +static int dsa_validate_public(const DSA *dsa) +{ + int status = 0; + const BIGNUM *pub_key = NULL; + + DSA_get0_key(dsa, &pub_key, NULL); + if (pub_key == NULL) + return 0; + return ossl_dsa_check_pub_key(dsa, pub_key, &status); +} + +static int dsa_validate_private(const DSA *dsa) +{ + int status = 0; + const BIGNUM *priv_key = NULL; + + DSA_get0_key(dsa, NULL, &priv_key); + if (priv_key == NULL) + return 0; + return ossl_dsa_check_priv_key(dsa, priv_key, &status); +} + +static int dsa_validate(const void *keydata, int selection, int checktype) +{ + const DSA *dsa = keydata; + int ok = 1; + + if (!ossl_prov_is_running()) + return 0; + + if ((selection & DSA_POSSIBLE_SELECTIONS) == 0) + return 1; /* nothing to validate */ + + if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) + ok = ok && dsa_validate_domparams(dsa, checktype); + + if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) + ok = ok && dsa_validate_public(dsa); + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + ok = ok && dsa_validate_private(dsa); + + /* If the whole key is selected, we do a pairwise validation */ + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) + == OSSL_KEYMGMT_SELECT_KEYPAIR) + ok = ok && ossl_dsa_check_pairwise(dsa); + return ok; +} + +static void *dsa_gen_init(void *provctx, int selection, + const OSSL_PARAM params[]) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx); + struct dsa_gen_ctx *gctx = NULL; + + if (!ossl_prov_is_running() || (selection & DSA_POSSIBLE_SELECTIONS) == 0) + return NULL; + + if ((gctx = OPENSSL_zalloc(sizeof(*gctx))) != NULL) { + gctx->selection = selection; + gctx->libctx = libctx; + gctx->pbits = 2048; + gctx->qbits = 224; +#ifdef FIPS_MODULE + gctx->gen_type = DSA_PARAMGEN_TYPE_FIPS_186_4; +#else + gctx->gen_type = DSA_PARAMGEN_TYPE_FIPS_DEFAULT; +#endif + gctx->gindex = -1; + gctx->pcounter = -1; + gctx->hindex = 0; + OSSL_FIPS_IND_INIT(gctx) + } + if (!dsa_gen_set_params(gctx, params)) { + dsa_gen_cleanup(gctx); + gctx = NULL; + } + return gctx; +} + +static int dsa_gen_set_template(void *genctx, void *templ) +{ + struct dsa_gen_ctx *gctx = genctx; + DSA *dsa = templ; + + if (!ossl_prov_is_running() || gctx == NULL || dsa == NULL) + return 0; + gctx->ffc_params = ossl_dsa_get0_params(dsa); + return 1; +} + +static int dsa_set_gen_seed(struct dsa_gen_ctx *gctx, unsigned char *seed, + size_t seedlen) +{ + OPENSSL_clear_free(gctx->seed, gctx->seedlen); + gctx->seed = NULL; + gctx->seedlen = 0; + if (seed != NULL && seedlen > 0) { + gctx->seed = OPENSSL_memdup(seed, seedlen); + if (gctx->seed == NULL) + return 0; + gctx->seedlen = seedlen; + } + return 1; +} + +static int dsa_gen_set_params(void *genctx, const OSSL_PARAM params[]) +{ + struct dsa_gen_ctx *gctx = genctx; + const OSSL_PARAM *p; + int gen_type = -1; + + if (gctx == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 1; + + if (!OSSL_FIPS_IND_SET_CTX_PARAM(gctx, OSSL_FIPS_IND_SETTABLE0, params, + OSSL_PKEY_PARAM_FIPS_SIGN_CHECK)) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_TYPE); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_UTF8_STRING + || ((gen_type = dsa_gen_type_name2id(p->data)) == -1)) { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } + + /* + * Only assign context gen_type if it was set by dsa_gen_type_name2id + * must be in range: + * DSA_PARAMGEN_TYPE_FIPS_186_4 <= gen_type <= DSA_PARAMGEN_TYPE_FIPS_DEFAULT + */ + if (gen_type != -1) + gctx->gen_type = gen_type; + } + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_GINDEX); + if (p != NULL + && !OSSL_PARAM_get_int(p, &gctx->gindex)) + return 0; + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_PCOUNTER); + if (p != NULL + && !OSSL_PARAM_get_int(p, &gctx->pcounter)) + return 0; + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_H); + if (p != NULL + && !OSSL_PARAM_get_int(p, &gctx->hindex)) + return 0; + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_SEED); + if (p != NULL + && (p->data_type != OSSL_PARAM_OCTET_STRING + || !dsa_set_gen_seed(gctx, p->data, p->data_size))) + return 0; + if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_PBITS)) != NULL + && !OSSL_PARAM_get_size_t(p, &gctx->pbits)) + return 0; + if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_QBITS)) != NULL + && !OSSL_PARAM_get_size_t(p, &gctx->qbits)) + return 0; + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_DIGEST); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_UTF8_STRING) + return 0; + OPENSSL_free(gctx->mdname); + gctx->mdname = OPENSSL_strdup(p->data); + if (gctx->mdname == NULL) + return 0; + } + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_DIGEST_PROPS); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_UTF8_STRING) + return 0; + OPENSSL_free(gctx->mdprops); + gctx->mdprops = OPENSSL_strdup(p->data); + if (gctx->mdprops == NULL) + return 0; + } + return 1; +} + +static const OSSL_PARAM *dsa_gen_settable_params(ossl_unused void *genctx, + ossl_unused void *provctx) +{ + static OSSL_PARAM settable[] = { + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_FFC_TYPE, NULL, 0), + OSSL_PARAM_size_t(OSSL_PKEY_PARAM_FFC_PBITS, NULL), + OSSL_PARAM_size_t(OSSL_PKEY_PARAM_FFC_QBITS, NULL), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_FFC_DIGEST, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_FFC_DIGEST_PROPS, NULL, 0), + OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_GINDEX, NULL), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_FFC_SEED, NULL, 0), + OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_PCOUNTER, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_H, NULL), + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_PKEY_PARAM_FIPS_SIGN_CHECK) + OSSL_PARAM_END + }; + return settable; +} + +static int dsa_gen_get_params(void *genctx, OSSL_PARAM *params) +{ + struct dsa_gen_ctx *gctx = genctx; + + if (gctx == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 1; + if (!OSSL_FIPS_IND_GET_CTX_PARAM(gctx, params)) + return 0; + return 1; +} + +static const OSSL_PARAM *dsa_gen_gettable_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + static const OSSL_PARAM dsa_gen_gettable_params_table[] = { + OSSL_FIPS_IND_GETTABLE_CTX_PARAM() + OSSL_PARAM_END + }; + + return dsa_gen_gettable_params_table; +} + +static int dsa_gencb(int p, int n, BN_GENCB *cb) +{ + struct dsa_gen_ctx *gctx = BN_GENCB_get_arg(cb); + OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END }; + + params[0] = OSSL_PARAM_construct_int(OSSL_GEN_PARAM_POTENTIAL, &p); + params[1] = OSSL_PARAM_construct_int(OSSL_GEN_PARAM_ITERATION, &n); + + return gctx->cb(params, gctx->cbarg); +} + +static void *dsa_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg) +{ + struct dsa_gen_ctx *gctx = genctx; + DSA *dsa = NULL; + BN_GENCB *gencb = NULL; + int ret = 0; + FFC_PARAMS *ffc; + + if (!ossl_prov_is_running() || gctx == NULL) + return NULL; + +#ifdef FIPS_MODULE + /* + * DSA signing is not approved in FIPS 140-3, so there is no + * need for DSA keygen either. + */ + if (!OSSL_FIPS_IND_ON_UNAPPROVED(gctx, OSSL_FIPS_IND_SETTABLE0, + gctx->libctx, "DSA", "Keygen", + ossl_fips_config_dsa_sign_disallowed)) + return 0; +#endif + + dsa = ossl_dsa_new(gctx->libctx); + if (dsa == NULL) + return NULL; + + if (gctx->gen_type == DSA_PARAMGEN_TYPE_FIPS_DEFAULT) + gctx->gen_type = (gctx->pbits >= 2048 ? DSA_PARAMGEN_TYPE_FIPS_186_4 : + DSA_PARAMGEN_TYPE_FIPS_186_2); + + /* + * Do a bounds check on context gen_type. Must be in range: + * DSA_PARAMGEN_TYPE_FIPS_186_4 <= gen_type <= DSA_PARAMGEN_TYPE_FIPS_DEFAULT + * Noted here as this needs to be adjusted if a new type is + * added. + */ + if (!ossl_assert((gctx->gen_type >= DSA_PARAMGEN_TYPE_FIPS_186_4) + && (gctx->gen_type <= DSA_PARAMGEN_TYPE_FIPS_DEFAULT))) { + ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR, + "gen_type set to unsupported value %d", gctx->gen_type); + goto end; + } + + gctx->cb = osslcb; + gctx->cbarg = cbarg; + gencb = BN_GENCB_new(); + if (gencb != NULL) + BN_GENCB_set(gencb, dsa_gencb, genctx); + + ffc = ossl_dsa_get0_params(dsa); + /* Copy the template value if one was passed */ + if (gctx->ffc_params != NULL + && !ossl_ffc_params_copy(ffc, gctx->ffc_params)) + goto end; + + if (gctx->seed != NULL + && !ossl_ffc_params_set_seed(ffc, gctx->seed, gctx->seedlen)) + goto end; + if (gctx->gindex != -1) { + ossl_ffc_params_set_gindex(ffc, gctx->gindex); + if (gctx->pcounter != -1) + ossl_ffc_params_set_pcounter(ffc, gctx->pcounter); + } else if (gctx->hindex != 0) { + ossl_ffc_params_set_h(ffc, gctx->hindex); + } + if (gctx->mdname != NULL) + ossl_ffc_set_digest(ffc, gctx->mdname, gctx->mdprops); + + if ((gctx->selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) { + + if (ossl_dsa_generate_ffc_parameters(dsa, gctx->gen_type, + gctx->pbits, gctx->qbits, + gencb) <= 0) + goto end; + } + ossl_ffc_params_enable_flags(ffc, FFC_PARAM_FLAG_VALIDATE_LEGACY, + gctx->gen_type == DSA_PARAMGEN_TYPE_FIPS_186_2); + if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) { + if (ffc->p == NULL + || ffc->q == NULL + || ffc->g == NULL) + goto end; + if (DSA_generate_key(dsa) <= 0) + goto end; + } + ret = 1; +end: + if (ret <= 0) { + DSA_free(dsa); + dsa = NULL; + } + BN_GENCB_free(gencb); + return dsa; +} + +static void dsa_gen_cleanup(void *genctx) +{ + struct dsa_gen_ctx *gctx = genctx; + + if (gctx == NULL) + return; + + OPENSSL_free(gctx->mdname); + OPENSSL_free(gctx->mdprops); + OPENSSL_clear_free(gctx->seed, gctx->seedlen); + OPENSSL_free(gctx); +} + +static void *dsa_load(const void *reference, size_t reference_sz) +{ + DSA *dsa = NULL; + + if (ossl_prov_is_running() && reference_sz == sizeof(dsa)) { + /* The contents of the reference is the address to our object */ + dsa = *(DSA **)reference; + /* We grabbed, so we detach it */ + *(DSA **)reference = NULL; + return dsa; + } + return NULL; +} + +static void *dsa_dup(const void *keydata_from, int selection) +{ + if (ossl_prov_is_running()) + return ossl_dsa_dup(keydata_from, selection); + return NULL; +} + +const OSSL_DISPATCH ossl_dsa_keymgmt_functions[] = { + { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))dsa_newdata }, + { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))dsa_gen_init }, + { OSSL_FUNC_KEYMGMT_GEN_SET_TEMPLATE, (void (*)(void))dsa_gen_set_template }, + { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))dsa_gen_set_params }, + { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, + (void (*)(void))dsa_gen_settable_params }, + { OSSL_FUNC_KEYMGMT_GEN_GET_PARAMS, (void (*)(void))dsa_gen_get_params }, + { OSSL_FUNC_KEYMGMT_GEN_GETTABLE_PARAMS, + (void (*)(void))dsa_gen_gettable_params }, + { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))dsa_gen }, + { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))dsa_gen_cleanup }, + { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))dsa_load }, + { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))dsa_freedata }, + { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))dsa_get_params }, + { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))dsa_gettable_params }, + { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))dsa_has }, + { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))dsa_match }, + { OSSL_FUNC_KEYMGMT_VALIDATE, (void (*)(void))dsa_validate }, + { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))dsa_import }, + { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))dsa_import_types }, + { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))dsa_export }, + { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))dsa_export_types }, + { OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))dsa_dup }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/keymgmt/ec_kmgmt.c b/crypto/openssl/providers/implementations/keymgmt/ec_kmgmt.c new file mode 100644 index 000000000000..a1d04bc3fdd3 --- /dev/null +++ b/crypto/openssl/providers/implementations/keymgmt/ec_kmgmt.c @@ -0,0 +1,1536 @@ +/* + * Copyright 2020-2025 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 + */ + +/* + * ECDH/ECDSA low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include <string.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/bn.h> +#include <openssl/err.h> +#include <openssl/objects.h> +#include <openssl/proverr.h> +#include <openssl/self_test.h> +#include "crypto/bn.h" +#include "crypto/ec.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" +#include "prov/provider_ctx.h" +#include "prov/securitycheck.h" +#include "internal/fips.h" +#include "internal/param_build_set.h" + +#ifndef FIPS_MODULE +# ifndef OPENSSL_NO_SM2 +# include "crypto/sm2.h" +# endif +#endif + +static OSSL_FUNC_keymgmt_new_fn ec_newdata; +static OSSL_FUNC_keymgmt_gen_init_fn ec_gen_init; +static OSSL_FUNC_keymgmt_gen_set_template_fn ec_gen_set_template; +static OSSL_FUNC_keymgmt_gen_set_params_fn ec_gen_set_params; +static OSSL_FUNC_keymgmt_gen_settable_params_fn ec_gen_settable_params; +static OSSL_FUNC_keymgmt_gen_get_params_fn ec_gen_get_params; +static OSSL_FUNC_keymgmt_gen_gettable_params_fn ec_gen_gettable_params; +static OSSL_FUNC_keymgmt_gen_fn ec_gen; +static OSSL_FUNC_keymgmt_gen_cleanup_fn ec_gen_cleanup; +static OSSL_FUNC_keymgmt_load_fn ec_load; +static OSSL_FUNC_keymgmt_free_fn ec_freedata; +static OSSL_FUNC_keymgmt_get_params_fn ec_get_params; +static OSSL_FUNC_keymgmt_gettable_params_fn ec_gettable_params; +static OSSL_FUNC_keymgmt_set_params_fn ec_set_params; +static OSSL_FUNC_keymgmt_settable_params_fn ec_settable_params; +static OSSL_FUNC_keymgmt_has_fn ec_has; +static OSSL_FUNC_keymgmt_match_fn ec_match; +static OSSL_FUNC_keymgmt_validate_fn ec_validate; +static OSSL_FUNC_keymgmt_import_fn ec_import; +static OSSL_FUNC_keymgmt_import_types_fn ec_import_types; +static OSSL_FUNC_keymgmt_export_fn ec_export; +static OSSL_FUNC_keymgmt_export_types_fn ec_export_types; +static OSSL_FUNC_keymgmt_query_operation_name_fn ec_query_operation_name; +static OSSL_FUNC_keymgmt_dup_fn ec_dup; +#ifndef FIPS_MODULE +# ifndef OPENSSL_NO_SM2 +static OSSL_FUNC_keymgmt_new_fn sm2_newdata; +static OSSL_FUNC_keymgmt_gen_init_fn sm2_gen_init; +static OSSL_FUNC_keymgmt_gen_fn sm2_gen; +static OSSL_FUNC_keymgmt_get_params_fn sm2_get_params; +static OSSL_FUNC_keymgmt_gettable_params_fn sm2_gettable_params; +static OSSL_FUNC_keymgmt_settable_params_fn sm2_settable_params; +static OSSL_FUNC_keymgmt_import_fn sm2_import; +static OSSL_FUNC_keymgmt_query_operation_name_fn sm2_query_operation_name; +static OSSL_FUNC_keymgmt_validate_fn sm2_validate; +# endif +#endif + +#define EC_DEFAULT_MD "SHA256" +#define EC_POSSIBLE_SELECTIONS \ + (OSSL_KEYMGMT_SELECT_KEYPAIR | OSSL_KEYMGMT_SELECT_ALL_PARAMETERS) +#define SM2_DEFAULT_MD "SM3" + +static +const char *ec_query_operation_name(int operation_id) +{ + switch (operation_id) { + case OSSL_OP_KEYEXCH: + return "ECDH"; + case OSSL_OP_SIGNATURE: + return "ECDSA"; + } + return NULL; +} + +#ifndef FIPS_MODULE +# ifndef OPENSSL_NO_SM2 +static +const char *sm2_query_operation_name(int operation_id) +{ + switch (operation_id) { + case OSSL_OP_SIGNATURE: + return "SM2"; + } + return NULL; +} +# endif +#endif + +/* + * Callers of key_to_params MUST make sure that domparams_to_params is also + * called! + * + * This function only exports the bare keypair, domain parameters and other + * parameters are exported separately. + */ +static ossl_inline +int key_to_params(const EC_KEY *eckey, OSSL_PARAM_BLD *tmpl, + OSSL_PARAM params[], int include_private, + unsigned char **pub_key) +{ + BIGNUM *x = NULL, *y = NULL; + const BIGNUM *priv_key = NULL; + const EC_POINT *pub_point = NULL; + const EC_GROUP *ecg = NULL; + size_t pub_key_len = 0; + int ret = 0; + BN_CTX *bnctx = NULL; + + if (eckey == NULL + || (ecg = EC_KEY_get0_group(eckey)) == NULL) + return 0; + + priv_key = EC_KEY_get0_private_key(eckey); + pub_point = EC_KEY_get0_public_key(eckey); + + if (pub_point != NULL) { + OSSL_PARAM *p = NULL, *px = NULL, *py = NULL; + /* + * EC_POINT_point2buf() can generate random numbers in some + * implementations so we need to ensure we use the correct libctx. + */ + bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(eckey)); + if (bnctx == NULL) + goto err; + + + /* If we are doing a get then check first before decoding the point */ + if (tmpl == NULL) { + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PUB_KEY); + px = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_EC_PUB_X); + py = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_EC_PUB_Y); + } + + if (p != NULL || tmpl != NULL) { + /* convert pub_point to a octet string according to the SECG standard */ + point_conversion_form_t format = EC_KEY_get_conv_form(eckey); + + if ((pub_key_len = EC_POINT_point2buf(ecg, pub_point, + format, + pub_key, bnctx)) == 0 + || !ossl_param_build_set_octet_string(tmpl, p, + OSSL_PKEY_PARAM_PUB_KEY, + *pub_key, pub_key_len)) + goto err; + } + if (px != NULL || py != NULL) { + if (px != NULL) { + x = BN_CTX_get(bnctx); + if (x == NULL) + goto err; + } + if (py != NULL) { + y = BN_CTX_get(bnctx); + if (y == NULL) + goto err; + } + + if (!EC_POINT_get_affine_coordinates(ecg, pub_point, x, y, bnctx)) + goto err; + if (px != NULL + && !ossl_param_build_set_bn(tmpl, px, + OSSL_PKEY_PARAM_EC_PUB_X, x)) + goto err; + if (py != NULL + && !ossl_param_build_set_bn(tmpl, py, + OSSL_PKEY_PARAM_EC_PUB_Y, y)) + goto err; + } + } + + if (priv_key != NULL && include_private) { + size_t sz; + int ecbits; + + /* + * Key import/export should never leak the bit length of the secret + * scalar in the key. + * + * For this reason, on export we use padded BIGNUMs with fixed length. + * + * When importing we also should make sure that, even if short lived, + * the newly created BIGNUM is marked with the BN_FLG_CONSTTIME flag as + * soon as possible, so that any processing of this BIGNUM might opt for + * constant time implementations in the backend. + * + * Setting the BN_FLG_CONSTTIME flag alone is never enough, we also have + * to preallocate the BIGNUM internal buffer to a fixed public size big + * enough that operations performed during the processing never trigger + * a realloc which would leak the size of the scalar through memory + * accesses. + * + * Fixed Length + * ------------ + * + * The order of the large prime subgroup of the curve is our choice for + * a fixed public size, as that is generally the upper bound for + * generating a private key in EC cryptosystems and should fit all valid + * secret scalars. + * + * For padding on export we just use the bit length of the order + * converted to bytes (rounding up). + * + * For preallocating the BIGNUM storage we look at the number of "words" + * required for the internal representation of the order, and we + * preallocate 2 extra "words" in case any of the subsequent processing + * might temporarily overflow the order length. + */ + ecbits = EC_GROUP_order_bits(ecg); + if (ecbits <= 0) + goto err; + sz = (ecbits + 7) / 8; + + if (!ossl_param_build_set_bn_pad(tmpl, params, + OSSL_PKEY_PARAM_PRIV_KEY, + priv_key, sz)) + goto err; + } + ret = 1; + err: + BN_CTX_free(bnctx); + return ret; +} + +static ossl_inline +int otherparams_to_params(const EC_KEY *ec, OSSL_PARAM_BLD *tmpl, + OSSL_PARAM params[]) +{ + int ecdh_cofactor_mode = 0, group_check = 0; + const char *name = NULL; + point_conversion_form_t format; + + if (ec == NULL) + return 0; + + format = EC_KEY_get_conv_form(ec); + name = ossl_ec_pt_format_id2name((int)format); + if (name != NULL + && !ossl_param_build_set_utf8_string(tmpl, params, + OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, + name)) + return 0; + + group_check = EC_KEY_get_flags(ec) & EC_FLAG_CHECK_NAMED_GROUP_MASK; + name = ossl_ec_check_group_type_id2name(group_check); + if (name != NULL + && !ossl_param_build_set_utf8_string(tmpl, params, + OSSL_PKEY_PARAM_EC_GROUP_CHECK_TYPE, + name)) + return 0; + + if ((EC_KEY_get_enc_flags(ec) & EC_PKEY_NO_PUBKEY) != 0 + && !ossl_param_build_set_int(tmpl, params, + OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC, 0)) + return 0; + + ecdh_cofactor_mode = + (EC_KEY_get_flags(ec) & EC_FLAG_COFACTOR_ECDH) ? 1 : 0; + return ossl_param_build_set_int(tmpl, params, + OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, + ecdh_cofactor_mode); +} + +static +void *ec_newdata(void *provctx) +{ + if (!ossl_prov_is_running()) + return NULL; + return EC_KEY_new_ex(PROV_LIBCTX_OF(provctx), NULL); +} + +#ifndef FIPS_MODULE +# ifndef OPENSSL_NO_SM2 +static +void *sm2_newdata(void *provctx) +{ + if (!ossl_prov_is_running()) + return NULL; + return EC_KEY_new_by_curve_name_ex(PROV_LIBCTX_OF(provctx), NULL, NID_sm2); +} +# endif +#endif + +static +void ec_freedata(void *keydata) +{ + EC_KEY_free(keydata); +} + +static +int ec_has(const void *keydata, int selection) +{ + const EC_KEY *ec = keydata; + int ok = 1; + + if (!ossl_prov_is_running() || ec == NULL) + return 0; + if ((selection & EC_POSSIBLE_SELECTIONS) == 0) + return 1; /* the selection is not missing */ + + if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) + ok = ok && (EC_KEY_get0_public_key(ec) != NULL); + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + ok = ok && (EC_KEY_get0_private_key(ec) != NULL); + if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) + ok = ok && (EC_KEY_get0_group(ec) != NULL); + /* + * We consider OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS to always be + * available, so no extra check is needed other than the previous one + * against EC_POSSIBLE_SELECTIONS. + */ + return ok; +} + +static int ec_match(const void *keydata1, const void *keydata2, int selection) +{ + const EC_KEY *ec1 = keydata1; + const EC_KEY *ec2 = keydata2; + const EC_GROUP *group_a = EC_KEY_get0_group(ec1); + const EC_GROUP *group_b = EC_KEY_get0_group(ec2); + BN_CTX *ctx = NULL; + int ok = 1; + + if (!ossl_prov_is_running()) + return 0; + + ctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(ec1)); + if (ctx == NULL) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) + ok = ok && group_a != NULL && group_b != NULL + && EC_GROUP_cmp(group_a, group_b, ctx) == 0; + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) { + int key_checked = 0; + + if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) { + const EC_POINT *pa = EC_KEY_get0_public_key(ec1); + const EC_POINT *pb = EC_KEY_get0_public_key(ec2); + + if (pa != NULL && pb != NULL) { + ok = ok && EC_POINT_cmp(group_b, pa, pb, ctx) == 0; + key_checked = 1; + } + } + if (!key_checked + && (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) { + const BIGNUM *pa = EC_KEY_get0_private_key(ec1); + const BIGNUM *pb = EC_KEY_get0_private_key(ec2); + + if (pa != NULL && pb != NULL) { + ok = ok && BN_cmp(pa, pb) == 0; + key_checked = 1; + } + } + ok = ok && key_checked; + } + BN_CTX_free(ctx); + return ok; +} + +static int common_check_sm2(const EC_KEY *ec, int sm2_wanted) +{ + const EC_GROUP *ecg = NULL; + + /* + * sm2_wanted: import the keys or domparams only on SM2 Curve + * !sm2_wanted: import the keys or domparams only not on SM2 Curve + */ + if ((ecg = EC_KEY_get0_group(ec)) == NULL + || (sm2_wanted ^ (EC_GROUP_get_curve_name(ecg) == NID_sm2))) + return 0; + return 1; +} + +static +int common_import(void *keydata, int selection, const OSSL_PARAM params[], + int sm2_wanted) +{ + EC_KEY *ec = keydata; + int ok = 1; + + if (!ossl_prov_is_running() || ec == NULL) + return 0; + + /* + * In this implementation, we can export/import only keydata in the + * following combinations: + * - domain parameters (+optional other params) + * - public key with associated domain parameters (+optional other params) + * - private key with associated domain parameters and optional public key + * (+optional other params) + * + * This means: + * - domain parameters must always be requested + * - private key must be requested alongside public key + * - other parameters are always optional + */ + if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) == 0) + return 0; + + ok = ok && ossl_ec_group_fromdata(ec, params); + + if (!common_check_sm2(ec, sm2_wanted)) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) { + int include_private = + selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0; + + ok = ok && ossl_ec_key_fromdata(ec, params, include_private); + } + if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0) + ok = ok && ossl_ec_key_otherparams_fromdata(ec, params); + + return ok; +} + +static +int ec_import(void *keydata, int selection, const OSSL_PARAM params[]) +{ + return common_import(keydata, selection, params, 0); +} + +#ifndef FIPS_MODULE +# ifndef OPENSSL_NO_SM2 +static +int sm2_import(void *keydata, int selection, const OSSL_PARAM params[]) +{ + return common_import(keydata, selection, params, 1); +} +# endif +#endif + +static +int ec_export(void *keydata, int selection, OSSL_CALLBACK *param_cb, + void *cbarg) +{ + EC_KEY *ec = keydata; + OSSL_PARAM_BLD *tmpl = NULL; + OSSL_PARAM *params = NULL; + unsigned char *pub_key = NULL, *genbuf = NULL; + BN_CTX *bnctx = NULL; + int ok = 1; + + if (!ossl_prov_is_running() || ec == NULL) + return 0; + + /* + * In this implementation, we can export/import only keydata in the + * following combinations: + * - domain parameters (+optional other params) + * - public key with associated domain parameters (+optional other params) + * - private key with associated public key and domain parameters + * (+optional other params) + * + * This means: + * - domain parameters must always be requested + * - private key must be requested alongside public key + * - other parameters are always optional + */ + if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) == 0) + return 0; + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0 + && (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) == 0) + return 0; + + tmpl = OSSL_PARAM_BLD_new(); + if (tmpl == NULL) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) { + bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(ec)); + if (bnctx == NULL) { + ok = 0; + goto end; + } + BN_CTX_start(bnctx); + ok = ok && ossl_ec_group_todata(EC_KEY_get0_group(ec), tmpl, NULL, + ossl_ec_key_get_libctx(ec), + ossl_ec_key_get0_propq(ec), + bnctx, &genbuf); + } + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) { + int include_private = + selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0; + + ok = ok && key_to_params(ec, tmpl, NULL, include_private, &pub_key); + } + if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0) + ok = ok && otherparams_to_params(ec, tmpl, NULL); + + if (!ok || (params = OSSL_PARAM_BLD_to_param(tmpl)) == NULL) { + ok = 0; + goto end; + } + + ok = param_cb(params, cbarg); + OSSL_PARAM_free(params); +end: + OSSL_PARAM_BLD_free(tmpl); + OPENSSL_free(pub_key); + OPENSSL_free(genbuf); + BN_CTX_end(bnctx); + BN_CTX_free(bnctx); + return ok; +} + +/* IMEXPORT = IMPORT + EXPORT */ + +# define EC_IMEXPORTABLE_DOM_PARAMETERS \ + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0), \ + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_ENCODING, NULL, 0), \ + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, NULL, 0),\ + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_FIELD_TYPE, NULL, 0), \ + OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_P, NULL, 0), \ + OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_A, NULL, 0), \ + OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_B, NULL, 0), \ + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_EC_GENERATOR, NULL, 0), \ + OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_ORDER, NULL, 0), \ + OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_COFACTOR, NULL, 0), \ + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_EC_SEED, NULL, 0), \ + OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_DECODED_FROM_EXPLICIT_PARAMS, NULL) + +# define EC_IMEXPORTABLE_PUBLIC_KEY \ + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0) +# define EC_IMEXPORTABLE_PRIVATE_KEY \ + OSSL_PARAM_BN(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0) +# define EC_IMEXPORTABLE_OTHER_PARAMETERS \ + OSSL_PARAM_int(OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, NULL), \ + OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC, NULL) + +/* + * Include all the possible combinations of OSSL_PARAM arrays for + * ec_imexport_types(). + * + * They are in a separate file as it is ~100 lines of unreadable and + * uninteresting machine generated stuff. + */ +#include "ec_kmgmt_imexport.inc" + +static ossl_inline +const OSSL_PARAM *ec_imexport_types(int selection) +{ + int type_select = 0; + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + type_select += 1; + if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) + type_select += 2; + if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) + type_select += 4; + if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0) + type_select += 8; + return ec_types[type_select]; +} + +static +const OSSL_PARAM *ec_import_types(int selection) +{ + return ec_imexport_types(selection); +} + +static +const OSSL_PARAM *ec_export_types(int selection) +{ + return ec_imexport_types(selection); +} + +static int ec_get_ecm_params(const EC_GROUP *group, OSSL_PARAM params[]) +{ +#ifdef OPENSSL_NO_EC2M + return 1; +#else + int ret = 0, m; + unsigned int k1 = 0, k2 = 0, k3 = 0; + int basis_nid; + const char *basis_name = NULL; + int fid = EC_GROUP_get_field_type(group); + + if (fid != NID_X9_62_characteristic_two_field) + return 1; + + basis_nid = EC_GROUP_get_basis_type(group); + if (basis_nid == NID_X9_62_tpBasis) + basis_name = SN_X9_62_tpBasis; + else if (basis_nid == NID_X9_62_ppBasis) + basis_name = SN_X9_62_ppBasis; + else + goto err; + + m = EC_GROUP_get_degree(group); + if (!ossl_param_build_set_int(NULL, params, OSSL_PKEY_PARAM_EC_CHAR2_M, m) + || !ossl_param_build_set_utf8_string(NULL, params, + OSSL_PKEY_PARAM_EC_CHAR2_TYPE, + basis_name)) + goto err; + + if (basis_nid == NID_X9_62_tpBasis) { + if (!EC_GROUP_get_trinomial_basis(group, &k1) + || !ossl_param_build_set_int(NULL, params, + OSSL_PKEY_PARAM_EC_CHAR2_TP_BASIS, + (int)k1)) + goto err; + } else { + if (!EC_GROUP_get_pentanomial_basis(group, &k1, &k2, &k3) + || !ossl_param_build_set_int(NULL, params, + OSSL_PKEY_PARAM_EC_CHAR2_PP_K1, (int)k1) + || !ossl_param_build_set_int(NULL, params, + OSSL_PKEY_PARAM_EC_CHAR2_PP_K2, (int)k2) + || !ossl_param_build_set_int(NULL, params, + OSSL_PKEY_PARAM_EC_CHAR2_PP_K3, (int)k3)) + goto err; + } + ret = 1; +err: + return ret; +#endif /* OPENSSL_NO_EC2M */ +} + +static +int common_get_params(void *key, OSSL_PARAM params[], int sm2) +{ + int ret = 0; + EC_KEY *eck = key; + const EC_GROUP *ecg = NULL; + OSSL_PARAM *p; + unsigned char *pub_key = NULL, *genbuf = NULL; + OSSL_LIB_CTX *libctx; + const char *propq; + BN_CTX *bnctx = NULL; + + ecg = EC_KEY_get0_group(eck); + if (ecg == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_PARAMETERS_SET); + return 0; + } + + libctx = ossl_ec_key_get_libctx(eck); + propq = ossl_ec_key_get0_propq(eck); + + bnctx = BN_CTX_new_ex(libctx); + if (bnctx == NULL) + return 0; + BN_CTX_start(bnctx); + + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE)) != NULL + && !OSSL_PARAM_set_int(p, ECDSA_size(eck))) + goto err; + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS)) != NULL + && !OSSL_PARAM_set_int(p, EC_GROUP_order_bits(ecg))) + goto err; + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS)) != NULL) { + int ecbits, sec_bits; + + ecbits = EC_GROUP_order_bits(ecg); + + /* + * The following estimates are based on the values published + * in Table 2 of "NIST Special Publication 800-57 Part 1 Revision 4" + * at http://dx.doi.org/10.6028/NIST.SP.800-57pt1r4 . + * + * Note that the above reference explicitly categorizes algorithms in a + * discrete set of values {80, 112, 128, 192, 256}, and that it is + * relevant only for NIST approved Elliptic Curves, while OpenSSL + * applies the same logic also to other curves. + * + * Classifications produced by other standardazing bodies might differ, + * so the results provided for "bits of security" by this provider are + * to be considered merely indicative, and it is the users' + * responsibility to compare these values against the normative + * references that may be relevant for their intent and purposes. + */ + if (ecbits >= 512) + sec_bits = 256; + else if (ecbits >= 384) + sec_bits = 192; + else if (ecbits >= 256) + sec_bits = 128; + else if (ecbits >= 224) + sec_bits = 112; + else if (ecbits >= 160) + sec_bits = 80; + else + sec_bits = ecbits / 2; + + if (!OSSL_PARAM_set_int(p, sec_bits)) + goto err; + } + + if ((p = OSSL_PARAM_locate(params, + OSSL_PKEY_PARAM_EC_DECODED_FROM_EXPLICIT_PARAMS)) + != NULL) { + int explicitparams = EC_KEY_decoded_from_explicit_params(eck); + + if (explicitparams < 0 + || !OSSL_PARAM_set_int(p, explicitparams)) + goto err; + } + + if (!sm2) { + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_DEFAULT_DIGEST)) != NULL + && !OSSL_PARAM_set_utf8_string(p, EC_DEFAULT_MD)) + goto err; + } else { + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_DEFAULT_DIGEST)) != NULL + && !OSSL_PARAM_set_utf8_string(p, SM2_DEFAULT_MD)) + goto err; + } + + /* SM2 doesn't support this PARAM */ + if (!sm2) { + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_USE_COFACTOR_ECDH); + if (p != NULL) { + int ecdh_cofactor_mode = 0; + + ecdh_cofactor_mode = + (EC_KEY_get_flags(eck) & EC_FLAG_COFACTOR_ECDH) ? 1 : 0; + + if (!OSSL_PARAM_set_int(p, ecdh_cofactor_mode)) + goto err; + } + } + if ((p = OSSL_PARAM_locate(params, + OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY)) != NULL) { + const EC_POINT *ecp = EC_KEY_get0_public_key(key); + + if (ecp == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY); + goto err; + } + p->return_size = EC_POINT_point2oct(ecg, ecp, + POINT_CONVERSION_UNCOMPRESSED, + p->data, p->data_size, bnctx); + if (p->return_size == 0) + goto err; + } + + ret = ec_get_ecm_params(ecg, params) + && ossl_ec_group_todata(ecg, NULL, params, libctx, propq, bnctx, + &genbuf) + && key_to_params(eck, NULL, params, 1, &pub_key) + && otherparams_to_params(eck, NULL, params); +err: + OPENSSL_free(genbuf); + OPENSSL_free(pub_key); + BN_CTX_end(bnctx); + BN_CTX_free(bnctx); + return ret; +} + +static +int ec_get_params(void *key, OSSL_PARAM params[]) +{ + return common_get_params(key, params, 0); +} + +#ifndef OPENSSL_NO_EC2M +# define EC2M_GETTABLE_DOM_PARAMS \ + OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_CHAR2_M, NULL), \ + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_CHAR2_TYPE, NULL, 0), \ + OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_CHAR2_TP_BASIS, NULL), \ + OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_CHAR2_PP_K1, NULL), \ + OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_CHAR2_PP_K2, NULL), \ + OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_CHAR2_PP_K3, NULL), +#else +# define EC2M_GETTABLE_DOM_PARAMS +#endif + +static const OSSL_PARAM ec_known_gettable_params[] = { + OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_DEFAULT_DIGEST, NULL, 0), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0), + OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_DECODED_FROM_EXPLICIT_PARAMS, NULL), + EC_IMEXPORTABLE_DOM_PARAMETERS, + EC2M_GETTABLE_DOM_PARAMS + EC_IMEXPORTABLE_PUBLIC_KEY, + OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_PUB_X, NULL, 0), + OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_PUB_Y, NULL, 0), + EC_IMEXPORTABLE_PRIVATE_KEY, + EC_IMEXPORTABLE_OTHER_PARAMETERS, + OSSL_PARAM_END +}; + +static +const OSSL_PARAM *ec_gettable_params(void *provctx) +{ + return ec_known_gettable_params; +} + +static const OSSL_PARAM ec_known_settable_params[] = { + OSSL_PARAM_int(OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, NULL), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_ENCODING, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, NULL, 0), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_EC_SEED, NULL, 0), + OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC, NULL), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_GROUP_CHECK_TYPE, NULL, 0), + OSSL_PARAM_END +}; + +static +const OSSL_PARAM *ec_settable_params(void *provctx) +{ + return ec_known_settable_params; +} + +static +int ec_set_params(void *key, const OSSL_PARAM params[]) +{ + EC_KEY *eck = key; + const OSSL_PARAM *p; + + if (key == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 1; + + if (!ossl_ec_group_set_params((EC_GROUP *)EC_KEY_get0_group(key), params)) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY); + if (p != NULL) { + BN_CTX *ctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(key)); + int ret = 1; + + if (ctx == NULL + || p->data_type != OSSL_PARAM_OCTET_STRING + || !EC_KEY_oct2key(key, p->data, p->data_size, ctx)) + ret = 0; + BN_CTX_free(ctx); + if (!ret) + return 0; + } + + return ossl_ec_key_otherparams_fromdata(eck, params); +} + +#ifndef FIPS_MODULE +# ifndef OPENSSL_NO_SM2 +static +int sm2_get_params(void *key, OSSL_PARAM params[]) +{ + return common_get_params(key, params, 1); +} + +static const OSSL_PARAM sm2_known_gettable_params[] = { + OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_DEFAULT_DIGEST, NULL, 0), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0), + OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_DECODED_FROM_EXPLICIT_PARAMS, NULL), + EC_IMEXPORTABLE_DOM_PARAMETERS, + EC_IMEXPORTABLE_PUBLIC_KEY, + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_EC_PUB_X, NULL, 0), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_EC_PUB_Y, NULL, 0), + EC_IMEXPORTABLE_PRIVATE_KEY, + OSSL_PARAM_END +}; + +static +const OSSL_PARAM *sm2_gettable_params(ossl_unused void *provctx) +{ + return sm2_known_gettable_params; +} + +static const OSSL_PARAM sm2_known_settable_params[] = { + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0), + OSSL_PARAM_END +}; + +static +const OSSL_PARAM *sm2_settable_params(ossl_unused void *provctx) +{ + return sm2_known_settable_params; +} + +static +int sm2_validate(const void *keydata, int selection, int checktype) +{ + const EC_KEY *eck = keydata; + int ok = 1; + BN_CTX *ctx = NULL; + + if (!ossl_prov_is_running()) + return 0; + + if ((selection & EC_POSSIBLE_SELECTIONS) == 0) + return 1; /* nothing to validate */ + + ctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(eck)); + if (ctx == NULL) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) + ok = ok && EC_GROUP_check(EC_KEY_get0_group(eck), ctx); + + if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) { + if (checktype == OSSL_KEYMGMT_VALIDATE_QUICK_CHECK) + ok = ok && ossl_ec_key_public_check_quick(eck, ctx); + else + ok = ok && ossl_ec_key_public_check(eck, ctx); + } + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + ok = ok && ossl_sm2_key_private_check(eck); + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == OSSL_KEYMGMT_SELECT_KEYPAIR) + ok = ok && ossl_ec_key_pairwise_check(eck, ctx); + + BN_CTX_free(ctx); + return ok; +} +# endif +#endif + +static +int ec_validate(const void *keydata, int selection, int checktype) +{ + const EC_KEY *eck = keydata; + int ok = 1; + BN_CTX *ctx = NULL; + + if (!ossl_prov_is_running()) + return 0; + + if ((selection & EC_POSSIBLE_SELECTIONS) == 0) + return 1; /* nothing to validate */ + + ctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(eck)); + if (ctx == NULL) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) { + int flags = EC_KEY_get_flags(eck); + + if ((flags & EC_FLAG_CHECK_NAMED_GROUP) != 0) + ok = ok && EC_GROUP_check_named_curve(EC_KEY_get0_group(eck), + (flags & EC_FLAG_CHECK_NAMED_GROUP_NIST) != 0, ctx) > 0; + else + ok = ok && EC_GROUP_check(EC_KEY_get0_group(eck), ctx); + } + + if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) { + if (checktype == OSSL_KEYMGMT_VALIDATE_QUICK_CHECK) + ok = ok && ossl_ec_key_public_check_quick(eck, ctx); + else + ok = ok && ossl_ec_key_public_check(eck, ctx); + } + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + ok = ok && ossl_ec_key_private_check(eck); + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == OSSL_KEYMGMT_SELECT_KEYPAIR) + ok = ok && ossl_ec_key_pairwise_check(eck, ctx); + + BN_CTX_free(ctx); + return ok; +} + +struct ec_gen_ctx { + OSSL_LIB_CTX *libctx; + char *group_name; + char *encoding; + char *pt_format; + char *group_check; + char *field_type; + BIGNUM *p, *a, *b, *order, *cofactor; + unsigned char *gen, *seed; + size_t gen_len, seed_len; + int selection; + int ecdh_mode; + EC_GROUP *gen_group; + unsigned char *dhkem_ikm; + size_t dhkem_ikmlen; + OSSL_FIPS_IND_DECLARE +}; + +static void *ec_gen_init(void *provctx, int selection, + const OSSL_PARAM params[]) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx); + struct ec_gen_ctx *gctx = NULL; + + if (!ossl_prov_is_running() || (selection & (EC_POSSIBLE_SELECTIONS)) == 0) + return NULL; + + if ((gctx = OPENSSL_zalloc(sizeof(*gctx))) != NULL) { + gctx->libctx = libctx; + gctx->selection = selection; + gctx->ecdh_mode = 0; + OSSL_FIPS_IND_INIT(gctx) + if (!ec_gen_set_params(gctx, params)) { + OPENSSL_free(gctx); + gctx = NULL; + } + } + return gctx; +} + +#ifndef FIPS_MODULE +# ifndef OPENSSL_NO_SM2 +static void *sm2_gen_init(void *provctx, int selection, + const OSSL_PARAM params[]) +{ + struct ec_gen_ctx *gctx = ec_gen_init(provctx, selection, params); + + if (gctx != NULL) { + if (gctx->group_name != NULL) + return gctx; + if ((gctx->group_name = OPENSSL_strdup("sm2")) != NULL) + return gctx; + ec_gen_cleanup(gctx); + } + return NULL; +} +# endif +#endif + +static int ec_gen_set_group(void *genctx, const EC_GROUP *src) +{ + struct ec_gen_ctx *gctx = genctx; + EC_GROUP *group; + + group = EC_GROUP_dup(src); + if (group == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_CURVE); + return 0; + } + EC_GROUP_free(gctx->gen_group); + gctx->gen_group = group; + return 1; +} + +static int ec_gen_set_template(void *genctx, void *templ) +{ + struct ec_gen_ctx *gctx = genctx; + EC_KEY *ec = templ; + const EC_GROUP *ec_group; + + if (!ossl_prov_is_running() || gctx == NULL || ec == NULL) + return 0; + if ((ec_group = EC_KEY_get0_group(ec)) == NULL) + return 0; + return ec_gen_set_group(gctx, ec_group); +} + +#define COPY_INT_PARAM(params, key, val) \ +p = OSSL_PARAM_locate_const(params, key); \ +if (p != NULL && !OSSL_PARAM_get_int(p, &val)) \ + goto err; + +#define COPY_UTF8_PARAM(params, key, val) \ +p = OSSL_PARAM_locate_const(params, key); \ +if (p != NULL) { \ + if (p->data_type != OSSL_PARAM_UTF8_STRING) \ + goto err; \ + OPENSSL_free(val); \ + val = OPENSSL_strdup(p->data); \ + if (val == NULL) \ + goto err; \ +} + +#define COPY_OCTET_PARAM(params, key, val, len) \ +p = OSSL_PARAM_locate_const(params, key); \ +if (p != NULL) { \ + if (p->data_type != OSSL_PARAM_OCTET_STRING) \ + goto err; \ + OPENSSL_free(val); \ + len = p->data_size; \ + val = OPENSSL_memdup(p->data, p->data_size); \ + if (val == NULL) \ + goto err; \ +} + +#define COPY_BN_PARAM(params, key, bn) \ +p = OSSL_PARAM_locate_const(params, key); \ +if (p != NULL) { \ + if (bn == NULL) \ + bn = BN_new(); \ + if (bn == NULL || !OSSL_PARAM_get_BN(p, &bn)) \ + goto err; \ +} + +static int ec_gen_set_params(void *genctx, const OSSL_PARAM params[]) +{ + int ret = 0; + struct ec_gen_ctx *gctx = genctx; + const OSSL_PARAM *p; + + if (!OSSL_FIPS_IND_SET_CTX_PARAM(gctx, OSSL_FIPS_IND_SETTABLE0, params, + OSSL_PKEY_PARAM_FIPS_KEY_CHECK)) + goto err; + + COPY_INT_PARAM(params, OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, gctx->ecdh_mode); + + COPY_UTF8_PARAM(params, OSSL_PKEY_PARAM_GROUP_NAME, gctx->group_name); + COPY_UTF8_PARAM(params, OSSL_PKEY_PARAM_EC_FIELD_TYPE, gctx->field_type); + COPY_UTF8_PARAM(params, OSSL_PKEY_PARAM_EC_ENCODING, gctx->encoding); + COPY_UTF8_PARAM(params, OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, gctx->pt_format); + COPY_UTF8_PARAM(params, OSSL_PKEY_PARAM_EC_GROUP_CHECK_TYPE, gctx->group_check); + + COPY_BN_PARAM(params, OSSL_PKEY_PARAM_EC_P, gctx->p); + COPY_BN_PARAM(params, OSSL_PKEY_PARAM_EC_A, gctx->a); + COPY_BN_PARAM(params, OSSL_PKEY_PARAM_EC_B, gctx->b); + COPY_BN_PARAM(params, OSSL_PKEY_PARAM_EC_ORDER, gctx->order); + COPY_BN_PARAM(params, OSSL_PKEY_PARAM_EC_COFACTOR, gctx->cofactor); + + COPY_OCTET_PARAM(params, OSSL_PKEY_PARAM_EC_SEED, gctx->seed, gctx->seed_len); + COPY_OCTET_PARAM(params, OSSL_PKEY_PARAM_EC_GENERATOR, gctx->gen, + gctx->gen_len); + + COPY_OCTET_PARAM(params, OSSL_PKEY_PARAM_DHKEM_IKM, gctx->dhkem_ikm, + gctx->dhkem_ikmlen); + + ret = 1; +err: + return ret; +} + +static int ec_gen_set_group_from_params(struct ec_gen_ctx *gctx) +{ + int ret = 0; + OSSL_PARAM_BLD *bld; + OSSL_PARAM *params = NULL; + EC_GROUP *group = NULL; + + bld = OSSL_PARAM_BLD_new(); + if (bld == NULL) + return 0; + + if (gctx->encoding != NULL + && !OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_EC_ENCODING, + gctx->encoding, 0)) + goto err; + + if (gctx->pt_format != NULL + && !OSSL_PARAM_BLD_push_utf8_string(bld, + OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, + gctx->pt_format, 0)) + goto err; + + if (gctx->group_name != NULL) { + if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_GROUP_NAME, + gctx->group_name, 0)) + goto err; + /* Ignore any other parameters if there is a group name */ + goto build; + } else if (gctx->field_type != NULL) { + if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_EC_FIELD_TYPE, + gctx->field_type, 0)) + goto err; + } else { + goto err; + } + if (gctx->p == NULL + || gctx->a == NULL + || gctx->b == NULL + || gctx->order == NULL + || !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_P, gctx->p) + || !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_A, gctx->a) + || !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_B, gctx->b) + || !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_ORDER, gctx->order)) + goto err; + + if (gctx->cofactor != NULL + && !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_COFACTOR, + gctx->cofactor)) + goto err; + + if (gctx->seed != NULL + && !OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_EC_SEED, + gctx->seed, gctx->seed_len)) + goto err; + + if (gctx->gen == NULL + || !OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_EC_GENERATOR, + gctx->gen, gctx->gen_len)) + goto err; +build: + params = OSSL_PARAM_BLD_to_param(bld); + if (params == NULL) + goto err; + group = EC_GROUP_new_from_params(params, gctx->libctx, NULL); + if (group == NULL) + goto err; + + EC_GROUP_free(gctx->gen_group); + gctx->gen_group = group; + + ret = 1; +err: + OSSL_PARAM_free(params); + OSSL_PARAM_BLD_free(bld); + return ret; +} + +static const OSSL_PARAM *ec_gen_settable_params(ossl_unused void *genctx, + ossl_unused void *provctx) +{ + static OSSL_PARAM settable[] = { + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0), + OSSL_PARAM_int(OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, NULL), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_ENCODING, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_FIELD_TYPE, NULL, 0), + OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_P, NULL, 0), + OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_A, NULL, 0), + OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_B, NULL, 0), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_EC_GENERATOR, NULL, 0), + OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_ORDER, NULL, 0), + OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_COFACTOR, NULL, 0), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_EC_SEED, NULL, 0), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_DHKEM_IKM, NULL, 0), + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_PKEY_PARAM_FIPS_KEY_CHECK) + OSSL_PARAM_END + }; + return settable; +} + +static const OSSL_PARAM *ec_gen_gettable_params(ossl_unused void *genctx, + ossl_unused void *provctx) +{ + static const OSSL_PARAM known_ec_gen_gettable_ctx_params[] = { + OSSL_FIPS_IND_GETTABLE_CTX_PARAM() + OSSL_PARAM_END + }; + return known_ec_gen_gettable_ctx_params; +} + +static int ec_gen_get_params(void *genctx, OSSL_PARAM *params) +{ + struct ec_gen_ctx *gctx = genctx; + + if (gctx == NULL) + return 0; + + if (!OSSL_FIPS_IND_GET_CTX_PARAM(gctx, params)) + return 0; + + return 1; +} + +static int ec_gen_assign_group(EC_KEY *ec, EC_GROUP *group) +{ + if (group == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_PARAMETERS_SET); + return 0; + } + return EC_KEY_set_group(ec, group) > 0; +} + +/* + * The callback arguments (osslcb & cbarg) are not used by EC_KEY generation + */ +static void *ec_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg) +{ + struct ec_gen_ctx *gctx = genctx; + EC_KEY *ec = NULL; + int ret = 0; + + if (!ossl_prov_is_running() + || gctx == NULL + || (ec = EC_KEY_new_ex(gctx->libctx, NULL)) == NULL) + return NULL; + + if (gctx->gen_group == NULL) { + if (!ec_gen_set_group_from_params(gctx)) + goto err; + } else { + if (gctx->encoding != NULL) { + int flags = ossl_ec_encoding_name2id(gctx->encoding); + + if (flags < 0) + goto err; + EC_GROUP_set_asn1_flag(gctx->gen_group, flags); + } + if (gctx->pt_format != NULL) { + int format = ossl_ec_pt_format_name2id(gctx->pt_format); + + if (format < 0) + goto err; + EC_GROUP_set_point_conversion_form(gctx->gen_group, format); + } + } +#ifdef FIPS_MODULE + if (!ossl_fips_ind_ec_key_check(OSSL_FIPS_IND_GET(gctx), + OSSL_FIPS_IND_SETTABLE0, gctx->libctx, + gctx->gen_group, "EC KeyGen", 1)) + goto err; +#endif + + /* We must always assign a group, no matter what */ + ret = ec_gen_assign_group(ec, gctx->gen_group); + + /* Whether you want it or not, you get a keypair, not just one half */ + if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) { +#ifndef FIPS_MODULE + if (gctx->dhkem_ikm != NULL && gctx->dhkem_ikmlen != 0) + ret = ret && ossl_ec_generate_key_dhkem(ec, gctx->dhkem_ikm, + gctx->dhkem_ikmlen); + else +#endif + ret = ret && EC_KEY_generate_key(ec); + } + + if (gctx->ecdh_mode != -1) + ret = ret && ossl_ec_set_ecdh_cofactor_mode(ec, gctx->ecdh_mode); + + if (gctx->group_check != NULL) + ret = ret && ossl_ec_set_check_group_type_from_name(ec, + gctx->group_check); +#ifdef FIPS_MODULE + if (ret > 0 + && !ossl_fips_self_testing() + && EC_KEY_get0_public_key(ec) != NULL + && EC_KEY_get0_private_key(ec) != NULL + && EC_KEY_get0_group(ec) != NULL) { + BN_CTX *bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(ec)); + + ret = bnctx != NULL && ossl_ec_key_pairwise_check(ec, bnctx); + BN_CTX_free(bnctx); + if (ret <= 0) + ossl_set_error_state(OSSL_SELF_TEST_TYPE_PCT); + } +#endif /* FIPS_MODULE */ + + if (ret) + return ec; +err: + /* Something went wrong, throw the key away */ + EC_KEY_free(ec); + return NULL; +} + +#ifndef FIPS_MODULE +# ifndef OPENSSL_NO_SM2 +/* + * The callback arguments (osslcb & cbarg) are not used by EC_KEY generation + */ +static void *sm2_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg) +{ + struct ec_gen_ctx *gctx = genctx; + EC_KEY *ec = NULL; + int ret = 1; + + if (gctx == NULL + || (ec = EC_KEY_new_ex(gctx->libctx, NULL)) == NULL) + return NULL; + + if (gctx->gen_group == NULL) { + if (!ec_gen_set_group_from_params(gctx)) + goto err; + } else { + if (gctx->encoding) { + int flags = ossl_ec_encoding_name2id(gctx->encoding); + + if (flags < 0) + goto err; + EC_GROUP_set_asn1_flag(gctx->gen_group, flags); + } + if (gctx->pt_format != NULL) { + int format = ossl_ec_pt_format_name2id(gctx->pt_format); + + if (format < 0) + goto err; + EC_GROUP_set_point_conversion_form(gctx->gen_group, format); + } + } + + /* We must always assign a group, no matter what */ + ret = ec_gen_assign_group(ec, gctx->gen_group); + + /* Whether you want it or not, you get a keypair, not just one half */ + if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) + ret = ret && EC_KEY_generate_key(ec); + + if (ret) + return ec; +err: + /* Something went wrong, throw the key away */ + EC_KEY_free(ec); + return NULL; +} +# endif +#endif + +static void ec_gen_cleanup(void *genctx) +{ + struct ec_gen_ctx *gctx = genctx; + + if (gctx == NULL) + return; + + OPENSSL_clear_free(gctx->dhkem_ikm, gctx->dhkem_ikmlen); + EC_GROUP_free(gctx->gen_group); + BN_free(gctx->p); + BN_free(gctx->a); + BN_free(gctx->b); + BN_free(gctx->order); + BN_free(gctx->cofactor); + OPENSSL_free(gctx->group_name); + OPENSSL_free(gctx->field_type); + OPENSSL_free(gctx->pt_format); + OPENSSL_free(gctx->encoding); + OPENSSL_free(gctx->seed); + OPENSSL_free(gctx->gen); + OPENSSL_free(gctx); +} + +static void *common_load(const void *reference, size_t reference_sz, + int sm2_wanted) +{ + EC_KEY *ec = NULL; + + if (ossl_prov_is_running() && reference_sz == sizeof(ec)) { + /* The contents of the reference is the address to our object */ + ec = *(EC_KEY **)reference; + + if (!common_check_sm2(ec, sm2_wanted)) + return NULL; + + /* We grabbed, so we detach it */ + *(EC_KEY **)reference = NULL; + return ec; + } + return NULL; +} + +static void *ec_load(const void *reference, size_t reference_sz) +{ + return common_load(reference, reference_sz, 0); +} + +#ifndef FIPS_MODULE +# ifndef OPENSSL_NO_SM2 +static void *sm2_load(const void *reference, size_t reference_sz) +{ + return common_load(reference, reference_sz, 1); +} +# endif +#endif + +static void *ec_dup(const void *keydata_from, int selection) +{ + if (ossl_prov_is_running()) + return ossl_ec_key_dup(keydata_from, selection); + return NULL; +} + +const OSSL_DISPATCH ossl_ec_keymgmt_functions[] = { + { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))ec_newdata }, + { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))ec_gen_init }, + { OSSL_FUNC_KEYMGMT_GEN_SET_TEMPLATE, + (void (*)(void))ec_gen_set_template }, + { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))ec_gen_set_params }, + { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, + (void (*)(void))ec_gen_settable_params }, + { OSSL_FUNC_KEYMGMT_GEN_GET_PARAMS, (void (*)(void))ec_gen_get_params }, + { OSSL_FUNC_KEYMGMT_GEN_GETTABLE_PARAMS, + (void (*)(void))ec_gen_gettable_params }, + { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))ec_gen }, + { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))ec_gen_cleanup }, + { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))ec_load }, + { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))ec_freedata }, + { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))ec_get_params }, + { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))ec_gettable_params }, + { OSSL_FUNC_KEYMGMT_SET_PARAMS, (void (*) (void))ec_set_params }, + { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*) (void))ec_settable_params }, + { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))ec_has }, + { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))ec_match }, + { OSSL_FUNC_KEYMGMT_VALIDATE, (void (*)(void))ec_validate }, + { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))ec_import }, + { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))ec_import_types }, + { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))ec_export }, + { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))ec_export_types }, + { OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME, + (void (*)(void))ec_query_operation_name }, + { OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))ec_dup }, + OSSL_DISPATCH_END +}; + +#ifndef FIPS_MODULE +# ifndef OPENSSL_NO_SM2 +const OSSL_DISPATCH ossl_sm2_keymgmt_functions[] = { + { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))sm2_newdata }, + { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))sm2_gen_init }, + { OSSL_FUNC_KEYMGMT_GEN_SET_TEMPLATE, + (void (*)(void))ec_gen_set_template }, + { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))ec_gen_set_params }, + { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, + (void (*)(void))ec_gen_settable_params }, + { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))sm2_gen }, + { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))ec_gen_cleanup }, + { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))sm2_load }, + { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))ec_freedata }, + { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))sm2_get_params }, + { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))sm2_gettable_params }, + { OSSL_FUNC_KEYMGMT_SET_PARAMS, (void (*) (void))ec_set_params }, + { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*) (void))sm2_settable_params }, + { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))ec_has }, + { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))ec_match }, + { OSSL_FUNC_KEYMGMT_VALIDATE, (void (*)(void))sm2_validate }, + { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))sm2_import }, + { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))ec_import_types }, + { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))ec_export }, + { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))ec_export_types }, + { OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME, + (void (*)(void))sm2_query_operation_name }, + { OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))ec_dup }, + OSSL_DISPATCH_END +}; +# endif +#endif diff --git a/crypto/openssl/providers/implementations/keymgmt/ec_kmgmt_imexport.inc b/crypto/openssl/providers/implementations/keymgmt/ec_kmgmt_imexport.inc new file mode 100644 index 000000000000..b142e0df0c46 --- /dev/null +++ b/crypto/openssl/providers/implementations/keymgmt/ec_kmgmt_imexport.inc @@ -0,0 +1,109 @@ +/* + * 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 +*/ + +/* + * This file is meant to be included from ec_kmgmt.c + */ + +static const OSSL_PARAM ec_private_key_types[] = { + EC_IMEXPORTABLE_PRIVATE_KEY, + OSSL_PARAM_END +}; +static const OSSL_PARAM ec_public_key_types[] = { + EC_IMEXPORTABLE_PUBLIC_KEY, + OSSL_PARAM_END +}; +static const OSSL_PARAM ec_key_types[] = { + EC_IMEXPORTABLE_PRIVATE_KEY, + EC_IMEXPORTABLE_PUBLIC_KEY, + OSSL_PARAM_END +}; +static const OSSL_PARAM ec_dom_parameters_types[] = { + EC_IMEXPORTABLE_DOM_PARAMETERS, + OSSL_PARAM_END +}; +static const OSSL_PARAM ec_5_types[] = { + EC_IMEXPORTABLE_PRIVATE_KEY, + EC_IMEXPORTABLE_DOM_PARAMETERS, + OSSL_PARAM_END +}; +static const OSSL_PARAM ec_6_types[] = { + EC_IMEXPORTABLE_PUBLIC_KEY, + EC_IMEXPORTABLE_DOM_PARAMETERS, + OSSL_PARAM_END +}; +static const OSSL_PARAM ec_key_domp_types[] = { + EC_IMEXPORTABLE_PRIVATE_KEY, + EC_IMEXPORTABLE_PUBLIC_KEY, + EC_IMEXPORTABLE_DOM_PARAMETERS, + OSSL_PARAM_END +}; +static const OSSL_PARAM ec_other_parameters_types[] = { + EC_IMEXPORTABLE_OTHER_PARAMETERS, + OSSL_PARAM_END +}; +static const OSSL_PARAM ec_9_types[] = { + EC_IMEXPORTABLE_PRIVATE_KEY, + EC_IMEXPORTABLE_OTHER_PARAMETERS, + OSSL_PARAM_END +}; +static const OSSL_PARAM ec_10_types[] = { + EC_IMEXPORTABLE_PUBLIC_KEY, + EC_IMEXPORTABLE_OTHER_PARAMETERS, + OSSL_PARAM_END +}; +static const OSSL_PARAM ec_11_types[] = { + EC_IMEXPORTABLE_PRIVATE_KEY, + EC_IMEXPORTABLE_PUBLIC_KEY, + EC_IMEXPORTABLE_OTHER_PARAMETERS, + OSSL_PARAM_END +}; +static const OSSL_PARAM ec_all_parameters_types[] = { + EC_IMEXPORTABLE_DOM_PARAMETERS, + EC_IMEXPORTABLE_OTHER_PARAMETERS, + OSSL_PARAM_END +}; +static const OSSL_PARAM ec_13_types[] = { + EC_IMEXPORTABLE_PRIVATE_KEY, + EC_IMEXPORTABLE_DOM_PARAMETERS, + EC_IMEXPORTABLE_OTHER_PARAMETERS, + OSSL_PARAM_END +}; +static const OSSL_PARAM ec_14_types[] = { + EC_IMEXPORTABLE_PUBLIC_KEY, + EC_IMEXPORTABLE_DOM_PARAMETERS, + EC_IMEXPORTABLE_OTHER_PARAMETERS, + OSSL_PARAM_END +}; +static const OSSL_PARAM ec_all_types[] = { + EC_IMEXPORTABLE_PRIVATE_KEY, + EC_IMEXPORTABLE_PUBLIC_KEY, + EC_IMEXPORTABLE_DOM_PARAMETERS, + EC_IMEXPORTABLE_OTHER_PARAMETERS, + OSSL_PARAM_END +}; + +static const OSSL_PARAM *ec_types[] = { + NULL, + ec_private_key_types, + ec_public_key_types, + ec_key_types, + ec_dom_parameters_types, + ec_5_types, + ec_6_types, + ec_key_domp_types, + ec_other_parameters_types, + ec_9_types, + ec_10_types, + ec_11_types, + ec_all_parameters_types, + ec_13_types, + ec_14_types, + ec_all_types +}; diff --git a/crypto/openssl/providers/implementations/keymgmt/ecx_kmgmt.c b/crypto/openssl/providers/implementations/keymgmt/ecx_kmgmt.c new file mode 100644 index 000000000000..0ebe8b4d59b1 --- /dev/null +++ b/crypto/openssl/providers/implementations/keymgmt/ecx_kmgmt.c @@ -0,0 +1,1292 @@ +/* + * Copyright 2020-2025 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 + */ + +#include <assert.h> +#include <string.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/params.h> +#include <openssl/err.h> +#include <openssl/proverr.h> +#include <openssl/evp.h> +#include <openssl/rand.h> +#include <openssl/self_test.h> +#include "internal/fips.h" +#include "internal/param_build_set.h" +#include <openssl/param_build.h> +#include "crypto/ecx.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" +#include "prov/provider_ctx.h" +#include "prov/ecx.h" +#include "prov/securitycheck.h" +#ifdef S390X_EC_ASM +# include "s390x_arch.h" +# include <openssl/sha.h> /* For SHA512_DIGEST_LENGTH */ +#endif + +static OSSL_FUNC_keymgmt_new_fn x25519_new_key; +static OSSL_FUNC_keymgmt_new_fn x448_new_key; +static OSSL_FUNC_keymgmt_new_fn ed25519_new_key; +static OSSL_FUNC_keymgmt_new_fn ed448_new_key; +static OSSL_FUNC_keymgmt_gen_init_fn x25519_gen_init; +static OSSL_FUNC_keymgmt_gen_init_fn x448_gen_init; +static OSSL_FUNC_keymgmt_gen_init_fn ed25519_gen_init; +static OSSL_FUNC_keymgmt_gen_init_fn ed448_gen_init; +static OSSL_FUNC_keymgmt_gen_fn x25519_gen; +static OSSL_FUNC_keymgmt_gen_fn x448_gen; +static OSSL_FUNC_keymgmt_gen_fn ed25519_gen; +static OSSL_FUNC_keymgmt_gen_fn ed448_gen; +static OSSL_FUNC_keymgmt_gen_cleanup_fn ecx_gen_cleanup; +static OSSL_FUNC_keymgmt_gen_set_params_fn ecx_gen_set_params; +static OSSL_FUNC_keymgmt_gen_settable_params_fn ecx_gen_settable_params; +static OSSL_FUNC_keymgmt_load_fn ecx_load; +static OSSL_FUNC_keymgmt_get_params_fn x25519_get_params; +static OSSL_FUNC_keymgmt_get_params_fn x448_get_params; +static OSSL_FUNC_keymgmt_get_params_fn ed25519_get_params; +static OSSL_FUNC_keymgmt_get_params_fn ed448_get_params; +static OSSL_FUNC_keymgmt_gettable_params_fn x25519_gettable_params; +static OSSL_FUNC_keymgmt_gettable_params_fn x448_gettable_params; +static OSSL_FUNC_keymgmt_gettable_params_fn ed25519_gettable_params; +static OSSL_FUNC_keymgmt_gettable_params_fn ed448_gettable_params; +static OSSL_FUNC_keymgmt_set_params_fn x25519_set_params; +static OSSL_FUNC_keymgmt_set_params_fn x448_set_params; +static OSSL_FUNC_keymgmt_set_params_fn ed25519_set_params; +static OSSL_FUNC_keymgmt_set_params_fn ed448_set_params; +static OSSL_FUNC_keymgmt_settable_params_fn x25519_settable_params; +static OSSL_FUNC_keymgmt_settable_params_fn x448_settable_params; +static OSSL_FUNC_keymgmt_settable_params_fn ed25519_settable_params; +static OSSL_FUNC_keymgmt_settable_params_fn ed448_settable_params; +static OSSL_FUNC_keymgmt_has_fn ecx_has; +static OSSL_FUNC_keymgmt_match_fn ecx_match; +static OSSL_FUNC_keymgmt_validate_fn x25519_validate; +static OSSL_FUNC_keymgmt_validate_fn x448_validate; +static OSSL_FUNC_keymgmt_validate_fn ed25519_validate; +static OSSL_FUNC_keymgmt_validate_fn ed448_validate; +static OSSL_FUNC_keymgmt_import_fn ecx_import; +static OSSL_FUNC_keymgmt_import_types_fn ecx_imexport_types; +static OSSL_FUNC_keymgmt_export_fn ecx_export; +static OSSL_FUNC_keymgmt_export_types_fn ecx_imexport_types; +static OSSL_FUNC_keymgmt_dup_fn ecx_dup; + +#define ECX_POSSIBLE_SELECTIONS (OSSL_KEYMGMT_SELECT_KEYPAIR) + +struct ecx_gen_ctx { + OSSL_LIB_CTX *libctx; + char *propq; + ECX_KEY_TYPE type; + int selection; + unsigned char *dhkem_ikm; + size_t dhkem_ikmlen; +}; + +#ifdef S390X_EC_ASM +static void *s390x_ecx_keygen25519(struct ecx_gen_ctx *gctx); +static void *s390x_ecx_keygen448(struct ecx_gen_ctx *gctx); +static void *s390x_ecd_keygen25519(struct ecx_gen_ctx *gctx); +static void *s390x_ecd_keygen448(struct ecx_gen_ctx *gctx); +#endif + +#ifdef FIPS_MODULE +static int ecd_fips140_pairwise_test(const ECX_KEY *ecx, int type, int self_test); +#endif /* FIPS_MODULE */ + +static ossl_inline int ecx_key_type_is_ed(ECX_KEY_TYPE type) +{ + return type == ECX_KEY_TYPE_ED25519 || type == ECX_KEY_TYPE_ED448; +} + +static void *x25519_new_key(void *provctx) +{ + if (!ossl_prov_is_running()) + return 0; + return ossl_ecx_key_new(PROV_LIBCTX_OF(provctx), ECX_KEY_TYPE_X25519, 0, + NULL); +} + +static void *x448_new_key(void *provctx) +{ + if (!ossl_prov_is_running()) + return 0; + return ossl_ecx_key_new(PROV_LIBCTX_OF(provctx), ECX_KEY_TYPE_X448, 0, + NULL); +} + +static void *ed25519_new_key(void *provctx) +{ + if (!ossl_prov_is_running()) + return 0; + return ossl_ecx_key_new(PROV_LIBCTX_OF(provctx), ECX_KEY_TYPE_ED25519, 0, + NULL); +} + +static void *ed448_new_key(void *provctx) +{ + if (!ossl_prov_is_running()) + return 0; + return ossl_ecx_key_new(PROV_LIBCTX_OF(provctx), ECX_KEY_TYPE_ED448, 0, + NULL); +} + +static int ecx_has(const void *keydata, int selection) +{ + const ECX_KEY *key = keydata; + int ok = 0; + + if (ossl_prov_is_running() && key != NULL) { + /* + * ECX keys always have all the parameters they need (i.e. none). + * Therefore we always return with 1, if asked about parameters. + */ + ok = 1; + + if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) + ok = ok && key->haspubkey; + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + ok = ok && key->privkey != NULL; + } + return ok; +} + +static int ecx_match(const void *keydata1, const void *keydata2, int selection) +{ + const ECX_KEY *key1 = keydata1; + const ECX_KEY *key2 = keydata2; + int ok = 1; + + if (!ossl_prov_is_running()) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) + ok = ok && key1->type == key2->type; + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) { + int key_checked = 0; + + if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) { + const unsigned char *pa = key1->haspubkey ? key1->pubkey : NULL; + const unsigned char *pb = key2->haspubkey ? key2->pubkey : NULL; + size_t pal = key1->keylen; + size_t pbl = key2->keylen; + + if (pa != NULL && pb != NULL) { + ok = ok + && key1->type == key2->type + && pal == pbl + && CRYPTO_memcmp(pa, pb, pal) == 0; + key_checked = 1; + } + } + if (!key_checked + && (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) { + const unsigned char *pa = key1->privkey; + const unsigned char *pb = key2->privkey; + size_t pal = key1->keylen; + size_t pbl = key2->keylen; + + if (pa != NULL && pb != NULL) { + ok = ok + && key1->type == key2->type + && pal == pbl + && CRYPTO_memcmp(pa, pb, pal) == 0; + key_checked = 1; + } + } + ok = ok && key_checked; + } + return ok; +} + +static int ecx_import(void *keydata, int selection, const OSSL_PARAM params[]) +{ + ECX_KEY *key = keydata; + int ok = 1; + int include_private; + + if (!ossl_prov_is_running() || key == NULL) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) + return 0; + + include_private = selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0; + ok = ok && ossl_ecx_key_fromdata(key, params, include_private); + + return ok; +} + +static int key_to_params(ECX_KEY *key, OSSL_PARAM_BLD *tmpl, + OSSL_PARAM params[], int include_private) +{ + if (key == NULL) + return 0; + + if (!ossl_param_build_set_octet_string(tmpl, params, + OSSL_PKEY_PARAM_PUB_KEY, + key->pubkey, key->keylen)) + return 0; + + if (include_private + && key->privkey != NULL + && !ossl_param_build_set_octet_string(tmpl, params, + OSSL_PKEY_PARAM_PRIV_KEY, + key->privkey, key->keylen)) + return 0; + + return 1; +} + +static int ecx_export(void *keydata, int selection, OSSL_CALLBACK *param_cb, + void *cbarg) +{ + ECX_KEY *key = keydata; + OSSL_PARAM_BLD *tmpl; + OSSL_PARAM *params = NULL; + int ret = 0; + + if (!ossl_prov_is_running() || key == NULL) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) + return 0; + + tmpl = OSSL_PARAM_BLD_new(); + if (tmpl == NULL) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) { + int include_private = ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0); + + if (!key_to_params(key, tmpl, NULL, include_private)) + goto err; + } + + params = OSSL_PARAM_BLD_to_param(tmpl); + if (params == NULL) + goto err; + + ret = param_cb(params, cbarg); + OSSL_PARAM_free(params); +err: + OSSL_PARAM_BLD_free(tmpl); + return ret; +} + +#define ECX_KEY_TYPES() \ +OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0), \ +OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0) + +static const OSSL_PARAM ecx_key_types[] = { + ECX_KEY_TYPES(), + OSSL_PARAM_END +}; +static const OSSL_PARAM *ecx_imexport_types(int selection) +{ + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) + return ecx_key_types; + return NULL; +} + +static int ecx_get_params(void *key, OSSL_PARAM params[], int bits, int secbits, + int size) +{ + ECX_KEY *ecx = key; + OSSL_PARAM *p; + + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS)) != NULL + && !OSSL_PARAM_set_int(p, bits)) + return 0; + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS)) != NULL + && !OSSL_PARAM_set_int(p, secbits)) + return 0; + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE)) != NULL + && !OSSL_PARAM_set_int(p, size)) + return 0; + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY)) != NULL + && (ecx->type == ECX_KEY_TYPE_X25519 + || ecx->type == ECX_KEY_TYPE_X448)) { + if (!OSSL_PARAM_set_octet_string(p, ecx->pubkey, ecx->keylen)) + return 0; + } +#ifdef FIPS_MODULE + { + /* X25519 and X448 are not approved */ + int approved = 0; + + p = OSSL_PARAM_locate(params, OSSL_ALG_PARAM_FIPS_APPROVED_INDICATOR); + if (p != NULL && !OSSL_PARAM_set_int(p, approved)) + return 0; + } +#endif + + return key_to_params(ecx, NULL, params, 1); +} + +static int ed_get_params(void *key, OSSL_PARAM params[]) +{ + OSSL_PARAM *p; + + if ((p = OSSL_PARAM_locate(params, + OSSL_PKEY_PARAM_MANDATORY_DIGEST)) != NULL + && !OSSL_PARAM_set_utf8_string(p, "")) + return 0; + return 1; +} + +static int x25519_get_params(void *key, OSSL_PARAM params[]) +{ + return ecx_get_params(key, params, X25519_BITS, X25519_SECURITY_BITS, + X25519_KEYLEN); +} + +static int x448_get_params(void *key, OSSL_PARAM params[]) +{ + return ecx_get_params(key, params, X448_BITS, X448_SECURITY_BITS, + X448_KEYLEN); +} + +static int ed25519_get_params(void *key, OSSL_PARAM params[]) +{ + return ecx_get_params(key, params, ED25519_BITS, ED25519_SECURITY_BITS, + ED25519_SIGSIZE) + && ed_get_params(key, params); +} + +static int ed448_get_params(void *key, OSSL_PARAM params[]) +{ + return ecx_get_params(key, params, ED448_BITS, ED448_SECURITY_BITS, + ED448_SIGSIZE) + && ed_get_params(key, params); +} + +static const OSSL_PARAM ecx_gettable_params[] = { + OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0), + ECX_KEY_TYPES(), + OSSL_FIPS_IND_GETTABLE_CTX_PARAM() + OSSL_PARAM_END +}; + +static const OSSL_PARAM ed_gettable_params[] = { + OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_MANDATORY_DIGEST, NULL, 0), + ECX_KEY_TYPES(), + OSSL_PARAM_END +}; + +static const OSSL_PARAM *x25519_gettable_params(void *provctx) +{ + return ecx_gettable_params; +} + +static const OSSL_PARAM *x448_gettable_params(void *provctx) +{ + return ecx_gettable_params; +} + +static const OSSL_PARAM *ed25519_gettable_params(void *provctx) +{ + return ed_gettable_params; +} + +static const OSSL_PARAM *ed448_gettable_params(void *provctx) +{ + return ed_gettable_params; +} + +static int set_property_query(ECX_KEY *ecxkey, const char *propq) +{ + OPENSSL_free(ecxkey->propq); + ecxkey->propq = NULL; + if (propq != NULL) { + ecxkey->propq = OPENSSL_strdup(propq); + if (ecxkey->propq == NULL) + return 0; + } + return 1; +} + +static int ecx_set_params(void *key, const OSSL_PARAM params[]) +{ + ECX_KEY *ecxkey = key; + const OSSL_PARAM *p; + + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY); + if (p != NULL) { + void *buf = ecxkey->pubkey; + + if (p->data_size != ecxkey->keylen + || !OSSL_PARAM_get_octet_string(p, &buf, sizeof(ecxkey->pubkey), + NULL)) + return 0; + OPENSSL_clear_free(ecxkey->privkey, ecxkey->keylen); + ecxkey->privkey = NULL; + ecxkey->haspubkey = 1; + } + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PROPERTIES); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_UTF8_STRING + || !set_property_query(ecxkey, p->data)) + return 0; + } + + return 1; +} + +static int x25519_set_params(void *key, const OSSL_PARAM params[]) +{ + return ecx_set_params(key, params); +} + +static int x448_set_params(void *key, const OSSL_PARAM params[]) +{ + return ecx_set_params(key, params); +} + +static int ed25519_set_params(void *key, const OSSL_PARAM params[]) +{ + return 1; +} + +static int ed448_set_params(void *key, const OSSL_PARAM params[]) +{ + return 1; +} + +static const OSSL_PARAM ecx_settable_params[] = { + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_END +}; + +static const OSSL_PARAM ed_settable_params[] = { + OSSL_PARAM_END +}; + +static const OSSL_PARAM *x25519_settable_params(void *provctx) +{ + return ecx_settable_params; +} + +static const OSSL_PARAM *x448_settable_params(void *provctx) +{ + return ecx_settable_params; +} + +static const OSSL_PARAM *ed25519_settable_params(void *provctx) +{ + return ed_settable_params; +} + +static const OSSL_PARAM *ed448_settable_params(void *provctx) +{ + return ed_settable_params; +} + +static void *ecx_gen_init(void *provctx, int selection, + const OSSL_PARAM params[], ECX_KEY_TYPE type, + const char *algdesc) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx); + struct ecx_gen_ctx *gctx = NULL; + + if (!ossl_prov_is_running()) + return NULL; + + if ((gctx = OPENSSL_zalloc(sizeof(*gctx))) != NULL) { + gctx->libctx = libctx; + gctx->type = type; + gctx->selection = selection; +#ifdef FIPS_MODULE + /* X25519/X448 are not FIPS approved, (ED25519/ED448 are approved) */ + if (algdesc != NULL + && !ossl_FIPS_IND_callback(libctx, algdesc, "KeyGen Init")) { + OPENSSL_free(gctx); + return NULL; + } +#endif + } else { + return NULL; + } + if (!ecx_gen_set_params(gctx, params)) { + ecx_gen_cleanup(gctx); + gctx = NULL; + } + return gctx; +} + +static void *x25519_gen_init(void *provctx, int selection, + const OSSL_PARAM params[]) +{ + return ecx_gen_init(provctx, selection, params, ECX_KEY_TYPE_X25519, "X25519"); +} + +static void *x448_gen_init(void *provctx, int selection, + const OSSL_PARAM params[]) +{ + return ecx_gen_init(provctx, selection, params, ECX_KEY_TYPE_X448, "X448"); +} + +static void *ed25519_gen_init(void *provctx, int selection, + const OSSL_PARAM params[]) +{ + return ecx_gen_init(provctx, selection, params, ECX_KEY_TYPE_ED25519, NULL); +} + +static void *ed448_gen_init(void *provctx, int selection, + const OSSL_PARAM params[]) +{ + return ecx_gen_init(provctx, selection, params, ECX_KEY_TYPE_ED448, NULL); +} + +static int ecx_gen_set_params(void *genctx, const OSSL_PARAM params[]) +{ + struct ecx_gen_ctx *gctx = genctx; + const OSSL_PARAM *p; + + if (gctx == NULL) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_GROUP_NAME); + if (p != NULL) { + const char *groupname = NULL; + + /* + * We optionally allow setting a group name - but each algorithm only + * support one such name, so all we do is verify that it is the one we + * expected. + */ + switch (gctx->type) { + case ECX_KEY_TYPE_X25519: + groupname = "x25519"; + break; + case ECX_KEY_TYPE_X448: + groupname = "x448"; + break; + default: + /* We only support this for key exchange at the moment */ + break; + } + if (p->data_type != OSSL_PARAM_UTF8_STRING + || groupname == NULL + || OPENSSL_strcasecmp(p->data, groupname) != 0) { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } + } + p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PROPERTIES); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_UTF8_STRING) + return 0; + OPENSSL_free(gctx->propq); + gctx->propq = OPENSSL_strdup(p->data); + if (gctx->propq == NULL) + return 0; + } + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_DHKEM_IKM); + if (p != NULL) { + if (p->data_size != 0 && p->data != NULL) { + OPENSSL_free(gctx->dhkem_ikm); + gctx->dhkem_ikm = NULL; + if (!OSSL_PARAM_get_octet_string(p, (void **)&gctx->dhkem_ikm, 0, + &gctx->dhkem_ikmlen)) + return 0; + } + } + + return 1; +} + +static const OSSL_PARAM *ecx_gen_settable_params(ossl_unused void *genctx, + ossl_unused void *provctx) +{ + static OSSL_PARAM settable[] = { + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_DHKEM_IKM, NULL, 0), + OSSL_PARAM_END + }; + return settable; +} + +#ifdef FIPS_MODULE +/* + * Refer: FIPS 140-3 IG 10.3.A Additional Comment 1 + * Perform a pairwise test for EDDSA by signing and verifying signature. + * + * The parameter `self_test` is used to indicate whether to create OSSL_SELF_TEST + * instance. + */ +static int ecd_fips140_pairwise_test(const ECX_KEY *ecx, int type, int self_test) +{ + int ret = 0; + OSSL_SELF_TEST *st = NULL; + OSSL_CALLBACK *cb = NULL; + void *cbarg = NULL; + + unsigned char msg[16] = {0}; + size_t msg_len = sizeof(msg); + unsigned char sig[ED448_SIGSIZE] = {0}; + + int is_ed25519 = (type == ECX_KEY_TYPE_ED25519) ? 1 : 0; + int operation_result = 0; + + /* + * The functions `OSSL_SELF_TEST_*` will return directly if parameter `st` + * is NULL. + */ + if (self_test) { + OSSL_SELF_TEST_get_callback(ecx->libctx, &cb, &cbarg); + + st = OSSL_SELF_TEST_new(cb, cbarg); + if (st == NULL) + return 0; + } + + OSSL_SELF_TEST_onbegin(st, OSSL_SELF_TEST_TYPE_PCT, + OSSL_SELF_TEST_DESC_PCT_EDDSA); + + if (is_ed25519) + operation_result = ossl_ed25519_sign(sig, msg, msg_len, ecx->pubkey, + ecx->privkey, 0, 0, 0, NULL, 0, + ecx->libctx, ecx->propq); + else + operation_result = ossl_ed448_sign(ecx->libctx, sig, msg, msg_len, + ecx->pubkey, ecx->privkey, NULL, 0, + 0, ecx->propq); + if (operation_result != 1) + goto err; + + OSSL_SELF_TEST_oncorrupt_byte(st, sig); + + if (is_ed25519) + operation_result = ossl_ed25519_verify(msg, msg_len, sig, ecx->pubkey, + 0, 0, 0, NULL, 0, ecx->libctx, + ecx->propq); + else + operation_result = ossl_ed448_verify(ecx->libctx, msg, msg_len, sig, + ecx->pubkey, NULL, 0, 0, ecx->propq); + if (operation_result != 1) + goto err; + + ret = 1; +err: + OSSL_SELF_TEST_onend(st, ret); + OSSL_SELF_TEST_free(st); + return ret; +} +#endif + +static void *ecx_gen(struct ecx_gen_ctx *gctx) +{ + ECX_KEY *key; + unsigned char *privkey; + + if (gctx == NULL) + return NULL; + if ((key = ossl_ecx_key_new(gctx->libctx, gctx->type, 0, + gctx->propq)) == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_EC_LIB); + return NULL; + } + + /* If we're doing parameter generation then we just return a blank key */ + if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) + return key; + + if ((privkey = ossl_ecx_key_allocate_privkey(key)) == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_EC_LIB); + goto err; + } +#ifndef FIPS_MODULE + if (gctx->dhkem_ikm != NULL && gctx->dhkem_ikmlen != 0) { + if (ecx_key_type_is_ed(gctx->type)) + goto err; + if (!ossl_ecx_dhkem_derive_private(key, privkey, + gctx->dhkem_ikm, gctx->dhkem_ikmlen)) + goto err; + } else +#endif + { + if (RAND_priv_bytes_ex(gctx->libctx, privkey, key->keylen, 0) <= 0) + goto err; + } + + switch (gctx->type) { + case ECX_KEY_TYPE_X25519: + privkey[0] &= 248; + privkey[X25519_KEYLEN - 1] &= 127; + privkey[X25519_KEYLEN - 1] |= 64; + ossl_x25519_public_from_private(key->pubkey, privkey); + break; + case ECX_KEY_TYPE_X448: + privkey[0] &= 252; + privkey[X448_KEYLEN - 1] |= 128; + ossl_x448_public_from_private(key->pubkey, privkey); + break; + case ECX_KEY_TYPE_ED25519: + if (!ossl_ed25519_public_from_private(gctx->libctx, key->pubkey, privkey, + gctx->propq)) + goto err; + break; + case ECX_KEY_TYPE_ED448: + if (!ossl_ed448_public_from_private(gctx->libctx, key->pubkey, privkey, + gctx->propq)) + goto err; + break; + } + key->haspubkey = 1; + return key; +err: + ossl_ecx_key_free(key); + return NULL; +} + +static void *x25519_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg) +{ + struct ecx_gen_ctx *gctx = genctx; + + if (!ossl_prov_is_running()) + return 0; + +#ifdef S390X_EC_ASM + if (OPENSSL_s390xcap_P.pcc[1] & S390X_CAPBIT(S390X_SCALAR_MULTIPLY_X25519)) + return s390x_ecx_keygen25519(gctx); +#endif + return ecx_gen(gctx); +} + +static void *x448_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg) +{ + struct ecx_gen_ctx *gctx = genctx; + + if (!ossl_prov_is_running()) + return 0; + +#ifdef S390X_EC_ASM + if (OPENSSL_s390xcap_P.pcc[1] & S390X_CAPBIT(S390X_SCALAR_MULTIPLY_X448)) + return s390x_ecx_keygen448(gctx); +#endif + return ecx_gen(gctx); +} + +static void *ed25519_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg) +{ + ECX_KEY *key = NULL; + struct ecx_gen_ctx *gctx = genctx; + + if (!ossl_prov_is_running()) + return 0; + +#ifdef S390X_EC_ASM + if (OPENSSL_s390xcap_P.pcc[1] & S390X_CAPBIT(S390X_SCALAR_MULTIPLY_ED25519) + && OPENSSL_s390xcap_P.kdsa[0] & S390X_CAPBIT(S390X_EDDSA_SIGN_ED25519) + && OPENSSL_s390xcap_P.kdsa[0] + & S390X_CAPBIT(S390X_EDDSA_VERIFY_ED25519)) { + key = s390x_ecd_keygen25519(gctx); + } else +#endif + { + key = ecx_gen(gctx); + } + +#ifdef FIPS_MODULE + /* Exit if keygen failed OR we are doing parameter generation (blank key) */ + if (!key || ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)) + return key; + if (ecd_fips140_pairwise_test(key, ECX_KEY_TYPE_ED25519, 1) != 1) { + ossl_set_error_state(OSSL_SELF_TEST_TYPE_PCT); + ossl_ecx_key_free(key); + return NULL; + } +#endif + + return key; +} + +static void *ed448_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg) +{ + ECX_KEY *key = NULL; + struct ecx_gen_ctx *gctx = genctx; + + if (!ossl_prov_is_running()) + return 0; + +#ifdef S390X_EC_ASM + if (OPENSSL_s390xcap_P.pcc[1] & S390X_CAPBIT(S390X_SCALAR_MULTIPLY_ED448) + && OPENSSL_s390xcap_P.kdsa[0] & S390X_CAPBIT(S390X_EDDSA_SIGN_ED448) + && OPENSSL_s390xcap_P.kdsa[0] & S390X_CAPBIT(S390X_EDDSA_VERIFY_ED448)) { + key = s390x_ecd_keygen448(gctx); + } else +#endif + { + key = ecx_gen(gctx); + } + +#ifdef FIPS_MODULE + /* Exit if keygen failed OR we are doing parameter generation (blank key) */ + if (!key || ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)) + return key; + if (ecd_fips140_pairwise_test(key, ECX_KEY_TYPE_ED448, 1) != 1) { + ossl_set_error_state(OSSL_SELF_TEST_TYPE_PCT); + ossl_ecx_key_free(key); + return NULL; + } +#endif + + return key; +} + +static void ecx_gen_cleanup(void *genctx) +{ + struct ecx_gen_ctx *gctx = genctx; + + if (gctx == NULL) + return; + + OPENSSL_clear_free(gctx->dhkem_ikm, gctx->dhkem_ikmlen); + OPENSSL_free(gctx->propq); + OPENSSL_free(gctx); +} + +void *ecx_load(const void *reference, size_t reference_sz) +{ + ECX_KEY *key = NULL; + + if (ossl_prov_is_running() && reference_sz == sizeof(key)) { + /* The contents of the reference is the address to our object */ + key = *(ECX_KEY **)reference; + /* We grabbed, so we detach it */ + *(ECX_KEY **)reference = NULL; + return key; + } + return NULL; +} + +static void *ecx_dup(const void *keydata_from, int selection) +{ + if (ossl_prov_is_running()) + return ossl_ecx_key_dup(keydata_from, selection); + return NULL; +} + +static int ecx_key_pairwise_check(const ECX_KEY *ecx, int type) +{ + uint8_t pub[64]; + + switch (type) { + case ECX_KEY_TYPE_X25519: + ossl_x25519_public_from_private(pub, ecx->privkey); + break; + case ECX_KEY_TYPE_X448: + ossl_x448_public_from_private(pub, ecx->privkey); + break; + default: + return 0; + } + return CRYPTO_memcmp(ecx->pubkey, pub, ecx->keylen) == 0; +} + +#ifdef FIPS_MODULE +/* + * FIPS ACVP testing requires the ability to check if the public key is valid + * This is not required normally since the ED signature verify does the test + * internally. + */ +static int ecd_key_pub_check(const ECX_KEY *ecx, int type) +{ + switch (type) { + case ECX_KEY_TYPE_ED25519: + return ossl_ed25519_pubkey_verify(ecx->pubkey, ecx->keylen); + case ECX_KEY_TYPE_ED448: + return ossl_ed448_pubkey_verify(ecx->pubkey, ecx->keylen); + default: + return 1; + } +} +#endif + +#ifdef FIPS_MODULE +static int ecd_key_pairwise_check(const ECX_KEY *ecx, int type) +{ + return ecd_fips140_pairwise_test(ecx, type, 0); +} +#else +static int ecd_key_pairwise_check(const ECX_KEY *ecx, int type) +{ + uint8_t pub[64]; + + switch (type) { + case ECX_KEY_TYPE_ED25519: + if (!ossl_ed25519_public_from_private(ecx->libctx, pub, ecx->privkey, + ecx->propq)) + return 0; + break; + case ECX_KEY_TYPE_ED448: + if (!ossl_ed448_public_from_private(ecx->libctx, pub, ecx->privkey, + ecx->propq)) + return 0; + break; + default: + return 0; + } + return CRYPTO_memcmp(ecx->pubkey, pub, ecx->keylen) == 0; +} +#endif + +static int ecx_validate(const void *keydata, int selection, int type, + size_t keylen) +{ + const ECX_KEY *ecx = keydata; + int ok = keylen == ecx->keylen; + + if (!ossl_prov_is_running()) + return 0; + + if ((selection & ECX_POSSIBLE_SELECTIONS) == 0) + return 1; /* nothing to validate */ + + if (!ok) { + ERR_raise(ERR_LIB_PROV, PROV_R_ALGORITHM_MISMATCH); + return 0; + } + + if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) { + ok = ok && ecx->haspubkey; +#ifdef FIPS_MODULE + ok = ok && ecd_key_pub_check(ecx, type); +#endif + } + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + ok = ok && ecx->privkey != NULL; + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != OSSL_KEYMGMT_SELECT_KEYPAIR) + return ok; + + if (ecx_key_type_is_ed(type)) + ok = ok && ecd_key_pairwise_check(ecx, type); + else + ok = ok && ecx_key_pairwise_check(ecx, type); + + return ok; +} + +static int x25519_validate(const void *keydata, int selection, int checktype) +{ + return ecx_validate(keydata, selection, ECX_KEY_TYPE_X25519, X25519_KEYLEN); +} + +static int x448_validate(const void *keydata, int selection, int checktype) +{ + return ecx_validate(keydata, selection, ECX_KEY_TYPE_X448, X448_KEYLEN); +} + +static int ed25519_validate(const void *keydata, int selection, int checktype) +{ + return ecx_validate(keydata, selection, ECX_KEY_TYPE_ED25519, ED25519_KEYLEN); +} + +static int ed448_validate(const void *keydata, int selection, int checktype) +{ + return ecx_validate(keydata, selection, ECX_KEY_TYPE_ED448, ED448_KEYLEN); +} + +#define MAKE_KEYMGMT_FUNCTIONS(alg) \ + const OSSL_DISPATCH ossl_##alg##_keymgmt_functions[] = { \ + { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))alg##_new_key }, \ + { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))ossl_ecx_key_free }, \ + { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))alg##_get_params }, \ + { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))alg##_gettable_params }, \ + { OSSL_FUNC_KEYMGMT_SET_PARAMS, (void (*) (void))alg##_set_params }, \ + { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*) (void))alg##_settable_params }, \ + { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))ecx_has }, \ + { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))ecx_match }, \ + { OSSL_FUNC_KEYMGMT_VALIDATE, (void (*)(void))alg##_validate }, \ + { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))ecx_import }, \ + { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))ecx_imexport_types }, \ + { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))ecx_export }, \ + { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))ecx_imexport_types }, \ + { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))alg##_gen_init }, \ + { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))ecx_gen_set_params }, \ + { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, \ + (void (*)(void))ecx_gen_settable_params }, \ + { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))alg##_gen }, \ + { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))ecx_gen_cleanup }, \ + { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))ecx_load }, \ + { OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))ecx_dup }, \ + OSSL_DISPATCH_END \ + }; + +MAKE_KEYMGMT_FUNCTIONS(x25519) +MAKE_KEYMGMT_FUNCTIONS(x448) +MAKE_KEYMGMT_FUNCTIONS(ed25519) +MAKE_KEYMGMT_FUNCTIONS(ed448) + +#ifdef S390X_EC_ASM +# include "s390x_arch.h" + +static void *s390x_ecx_keygen25519(struct ecx_gen_ctx *gctx) +{ + static const unsigned char generator[] = { + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + ECX_KEY *key = ossl_ecx_key_new(gctx->libctx, ECX_KEY_TYPE_X25519, 1, + gctx->propq); + unsigned char *privkey = NULL, *pubkey; + + if (key == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_EC_LIB); + goto err; + } + + /* If we're doing parameter generation then we just return a blank key */ + if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) + return key; + + pubkey = key->pubkey; + + privkey = ossl_ecx_key_allocate_privkey(key); + if (privkey == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_EC_LIB); + goto err; + } + +#ifndef FIPS_MODULE + if (gctx->dhkem_ikm != NULL && gctx->dhkem_ikmlen != 0) { + if (gctx->type != ECX_KEY_TYPE_X25519) + goto err; + if (!ossl_ecx_dhkem_derive_private(key, privkey, + gctx->dhkem_ikm, gctx->dhkem_ikmlen)) + goto err; + } else +#endif + { + if (RAND_priv_bytes_ex(gctx->libctx, privkey, X25519_KEYLEN, 0) <= 0) + goto err; + } + + privkey[0] &= 248; + privkey[31] &= 127; + privkey[31] |= 64; + + if (s390x_x25519_mul(pubkey, generator, privkey) != 1) + goto err; + key->haspubkey = 1; + return key; + err: + ossl_ecx_key_free(key); + return NULL; +} + +static void *s390x_ecx_keygen448(struct ecx_gen_ctx *gctx) +{ + static const unsigned char generator[] = { + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + ECX_KEY *key = ossl_ecx_key_new(gctx->libctx, ECX_KEY_TYPE_X448, 1, + gctx->propq); + unsigned char *privkey = NULL, *pubkey; + + if (key == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_EC_LIB); + goto err; + } + + /* If we're doing parameter generation then we just return a blank key */ + if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) + return key; + + pubkey = key->pubkey; + + privkey = ossl_ecx_key_allocate_privkey(key); + if (privkey == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_EC_LIB); + goto err; + } + +#ifndef FIPS_MODULE + if (gctx->dhkem_ikm != NULL && gctx->dhkem_ikmlen != 0) { + if (gctx->type != ECX_KEY_TYPE_X448) + goto err; + if (!ossl_ecx_dhkem_derive_private(key, privkey, + gctx->dhkem_ikm, gctx->dhkem_ikmlen)) + goto err; + } else +#endif + { + if (RAND_priv_bytes_ex(gctx->libctx, privkey, X448_KEYLEN, 0) <= 0) + goto err; + } + + privkey[0] &= 252; + privkey[55] |= 128; + + if (s390x_x448_mul(pubkey, generator, privkey) != 1) + goto err; + key->haspubkey = 1; + return key; + err: + ossl_ecx_key_free(key); + return NULL; +} + +static void *s390x_ecd_keygen25519(struct ecx_gen_ctx *gctx) +{ + static const unsigned char generator_x[] = { + 0x1a, 0xd5, 0x25, 0x8f, 0x60, 0x2d, 0x56, 0xc9, 0xb2, 0xa7, 0x25, 0x95, + 0x60, 0xc7, 0x2c, 0x69, 0x5c, 0xdc, 0xd6, 0xfd, 0x31, 0xe2, 0xa4, 0xc0, + 0xfe, 0x53, 0x6e, 0xcd, 0xd3, 0x36, 0x69, 0x21 + }; + static const unsigned char generator_y[] = { + 0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, + }; + unsigned char x_dst[32], buff[SHA512_DIGEST_LENGTH]; + ECX_KEY *key = ossl_ecx_key_new(gctx->libctx, ECX_KEY_TYPE_ED25519, 1, + gctx->propq); + unsigned char *privkey = NULL, *pubkey; + unsigned int sz; + EVP_MD *sha = NULL; + int j; + + if (key == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_EC_LIB); + goto err; + } + + /* If we're doing parameter generation then we just return a blank key */ + if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) + return key; + + pubkey = key->pubkey; + + privkey = ossl_ecx_key_allocate_privkey(key); + if (privkey == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_EC_LIB); + goto err; + } + + if (RAND_priv_bytes_ex(gctx->libctx, privkey, ED25519_KEYLEN, 0) <= 0) + goto err; + + sha = EVP_MD_fetch(gctx->libctx, "SHA512", gctx->propq); + if (sha == NULL) + goto err; + j = EVP_Digest(privkey, 32, buff, &sz, sha, NULL); + EVP_MD_free(sha); + if (!j) + goto err; + + buff[0] &= 248; + buff[31] &= 63; + buff[31] |= 64; + + if (s390x_ed25519_mul(x_dst, pubkey, + generator_x, generator_y, buff) != 1) + goto err; + + pubkey[31] |= ((x_dst[0] & 0x01) << 7); + key->haspubkey = 1; + return key; + err: + ossl_ecx_key_free(key); + return NULL; +} + +static void *s390x_ecd_keygen448(struct ecx_gen_ctx *gctx) +{ + static const unsigned char generator_x[] = { + 0x5e, 0xc0, 0x0c, 0xc7, 0x2b, 0xa8, 0x26, 0x26, 0x8e, 0x93, 0x00, 0x8b, + 0xe1, 0x80, 0x3b, 0x43, 0x11, 0x65, 0xb6, 0x2a, 0xf7, 0x1a, 0xae, 0x12, + 0x64, 0xa4, 0xd3, 0xa3, 0x24, 0xe3, 0x6d, 0xea, 0x67, 0x17, 0x0f, 0x47, + 0x70, 0x65, 0x14, 0x9e, 0xda, 0x36, 0xbf, 0x22, 0xa6, 0x15, 0x1d, 0x22, + 0xed, 0x0d, 0xed, 0x6b, 0xc6, 0x70, 0x19, 0x4f, 0x00 + }; + static const unsigned char generator_y[] = { + 0x14, 0xfa, 0x30, 0xf2, 0x5b, 0x79, 0x08, 0x98, 0xad, 0xc8, 0xd7, 0x4e, + 0x2c, 0x13, 0xbd, 0xfd, 0xc4, 0x39, 0x7c, 0xe6, 0x1c, 0xff, 0xd3, 0x3a, + 0xd7, 0xc2, 0xa0, 0x05, 0x1e, 0x9c, 0x78, 0x87, 0x40, 0x98, 0xa3, 0x6c, + 0x73, 0x73, 0xea, 0x4b, 0x62, 0xc7, 0xc9, 0x56, 0x37, 0x20, 0x76, 0x88, + 0x24, 0xbc, 0xb6, 0x6e, 0x71, 0x46, 0x3f, 0x69, 0x00 + }; + unsigned char x_dst[57], buff[114]; + ECX_KEY *key = ossl_ecx_key_new(gctx->libctx, ECX_KEY_TYPE_ED448, 1, + gctx->propq); + unsigned char *privkey = NULL, *pubkey; + EVP_MD_CTX *hashctx = NULL; + EVP_MD *shake = NULL; + + if (key == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_EC_LIB); + goto err; + } + + /* If we're doing parameter generation then we just return a blank key */ + if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) + return key; + + pubkey = key->pubkey; + + privkey = ossl_ecx_key_allocate_privkey(key); + if (privkey == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_EC_LIB); + goto err; + } + + shake = EVP_MD_fetch(gctx->libctx, "SHAKE256", gctx->propq); + if (shake == NULL) + goto err; + if (RAND_priv_bytes_ex(gctx->libctx, privkey, ED448_KEYLEN, 0) <= 0) + goto err; + + hashctx = EVP_MD_CTX_new(); + if (hashctx == NULL) + goto err; + if (EVP_DigestInit_ex(hashctx, shake, NULL) != 1) + goto err; + if (EVP_DigestUpdate(hashctx, privkey, 57) != 1) + goto err; + if (EVP_DigestFinalXOF(hashctx, buff, sizeof(buff)) != 1) + goto err; + + buff[0] &= -4; + buff[55] |= 0x80; + buff[56] = 0; + + if (s390x_ed448_mul(x_dst, pubkey, + generator_x, generator_y, buff) != 1) + goto err; + + pubkey[56] |= ((x_dst[0] & 0x01) << 7); + EVP_MD_CTX_free(hashctx); + EVP_MD_free(shake); + key->haspubkey = 1; + return key; + err: + ossl_ecx_key_free(key); + EVP_MD_CTX_free(hashctx); + EVP_MD_free(shake); + return NULL; +} +#endif diff --git a/crypto/openssl/providers/implementations/keymgmt/kdf_legacy_kmgmt.c b/crypto/openssl/providers/implementations/keymgmt/kdf_legacy_kmgmt.c new file mode 100644 index 000000000000..deb49600066d --- /dev/null +++ b/crypto/openssl/providers/implementations/keymgmt/kdf_legacy_kmgmt.c @@ -0,0 +1,102 @@ +/* + * Copyright 2019-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 + */ + +/* + * This implemments a dummy key manager for legacy KDFs that still support the + * old way of performing a KDF via EVP_PKEY_derive(). New KDFs should not be + * implemented this way. In reality there is no key data for such KDFs, so this + * key manager does very little. + */ + +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/err.h> +#include "prov/implementations.h" +#include "prov/providercommon.h" +#include "prov/provider_ctx.h" +#include "prov/kdfexchange.h" + +static OSSL_FUNC_keymgmt_new_fn kdf_newdata; +static OSSL_FUNC_keymgmt_free_fn kdf_freedata; +static OSSL_FUNC_keymgmt_has_fn kdf_has; + +KDF_DATA *ossl_kdf_data_new(void *provctx) +{ + KDF_DATA *kdfdata; + + if (!ossl_prov_is_running()) + return NULL; + + kdfdata = OPENSSL_zalloc(sizeof(*kdfdata)); + if (kdfdata == NULL) + return NULL; + + if (!CRYPTO_NEW_REF(&kdfdata->refcnt, 1)) { + OPENSSL_free(kdfdata); + return NULL; + } + kdfdata->libctx = PROV_LIBCTX_OF(provctx); + + return kdfdata; +} + +void ossl_kdf_data_free(KDF_DATA *kdfdata) +{ + int ref = 0; + + if (kdfdata == NULL) + return; + + CRYPTO_DOWN_REF(&kdfdata->refcnt, &ref); + if (ref > 0) + return; + + CRYPTO_FREE_REF(&kdfdata->refcnt); + OPENSSL_free(kdfdata); +} + +int ossl_kdf_data_up_ref(KDF_DATA *kdfdata) +{ + int ref = 0; + + /* This is effectively doing a new operation on the KDF_DATA and should be + * adequately guarded again modules' error states. However, both current + * calls here are guarded properly in exchange/kdf_exch.c. Thus, it + * could be removed here. The concern is that something in the future + * might call this function without adequate guards. It's a cheap call, + * it seems best to leave it even though it is currently redundant. + */ + if (!ossl_prov_is_running()) + return 0; + + CRYPTO_UP_REF(&kdfdata->refcnt, &ref); + return 1; +} + +static void *kdf_newdata(void *provctx) +{ + return ossl_kdf_data_new(provctx); +} + +static void kdf_freedata(void *kdfdata) +{ + ossl_kdf_data_free(kdfdata); +} + +static int kdf_has(const void *keydata, int selection) +{ + return 1; /* nothing is missing */ +} + +const OSSL_DISPATCH ossl_kdf_keymgmt_functions[] = { + { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))kdf_newdata }, + { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))kdf_freedata }, + { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))kdf_has }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/keymgmt/mac_legacy_kmgmt.c b/crypto/openssl/providers/implementations/keymgmt/mac_legacy_kmgmt.c new file mode 100644 index 000000000000..a83017e3aebf --- /dev/null +++ b/crypto/openssl/providers/implementations/keymgmt/mac_legacy_kmgmt.c @@ -0,0 +1,573 @@ +/* + * Copyright 2020-2025 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 + */ + +/* We need to use some engine deprecated APIs */ +#define OPENSSL_SUPPRESS_DEPRECATED + +#include <string.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/params.h> +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/proverr.h> +#include <openssl/param_build.h> +#ifndef FIPS_MODULE +# include <openssl/engine.h> +#endif +#include "internal/param_build_set.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" +#include "prov/provider_ctx.h" +#include "prov/macsignature.h" + +static OSSL_FUNC_keymgmt_new_fn mac_new; +static OSSL_FUNC_keymgmt_free_fn mac_free; +static OSSL_FUNC_keymgmt_gen_init_fn mac_gen_init; +static OSSL_FUNC_keymgmt_gen_fn mac_gen; +static OSSL_FUNC_keymgmt_gen_cleanup_fn mac_gen_cleanup; +static OSSL_FUNC_keymgmt_gen_set_params_fn mac_gen_set_params; +static OSSL_FUNC_keymgmt_gen_settable_params_fn mac_gen_settable_params; +static OSSL_FUNC_keymgmt_get_params_fn mac_get_params; +static OSSL_FUNC_keymgmt_gettable_params_fn mac_gettable_params; +static OSSL_FUNC_keymgmt_set_params_fn mac_set_params; +static OSSL_FUNC_keymgmt_settable_params_fn mac_settable_params; +static OSSL_FUNC_keymgmt_has_fn mac_has; +static OSSL_FUNC_keymgmt_match_fn mac_match; +static OSSL_FUNC_keymgmt_import_fn mac_import; +static OSSL_FUNC_keymgmt_import_types_fn mac_imexport_types; +static OSSL_FUNC_keymgmt_export_fn mac_export; +static OSSL_FUNC_keymgmt_export_types_fn mac_imexport_types; + +static OSSL_FUNC_keymgmt_new_fn mac_new_cmac; +static OSSL_FUNC_keymgmt_gettable_params_fn cmac_gettable_params; +static OSSL_FUNC_keymgmt_import_types_fn cmac_imexport_types; +static OSSL_FUNC_keymgmt_export_types_fn cmac_imexport_types; +static OSSL_FUNC_keymgmt_gen_init_fn cmac_gen_init; +static OSSL_FUNC_keymgmt_gen_set_params_fn cmac_gen_set_params; +static OSSL_FUNC_keymgmt_gen_settable_params_fn cmac_gen_settable_params; + +struct mac_gen_ctx { + OSSL_LIB_CTX *libctx; + int selection; + unsigned char *priv_key; + size_t priv_key_len; + PROV_CIPHER cipher; +}; + +MAC_KEY *ossl_mac_key_new(OSSL_LIB_CTX *libctx, int cmac) +{ + MAC_KEY *mackey; + + if (!ossl_prov_is_running()) + return NULL; + + mackey = OPENSSL_zalloc(sizeof(*mackey)); + if (mackey == NULL) + return NULL; + + if (!CRYPTO_NEW_REF(&mackey->refcnt, 1)) { + OPENSSL_free(mackey); + return NULL; + } + mackey->libctx = libctx; + mackey->cmac = cmac; + + return mackey; +} + +void ossl_mac_key_free(MAC_KEY *mackey) +{ + int ref = 0; + + if (mackey == NULL) + return; + + CRYPTO_DOWN_REF(&mackey->refcnt, &ref); + if (ref > 0) + return; + + OPENSSL_secure_clear_free(mackey->priv_key, mackey->priv_key_len); + OPENSSL_free(mackey->properties); + ossl_prov_cipher_reset(&mackey->cipher); + CRYPTO_FREE_REF(&mackey->refcnt); + OPENSSL_free(mackey); +} + +int ossl_mac_key_up_ref(MAC_KEY *mackey) +{ + int ref = 0; + + /* This is effectively doing a new operation on the MAC_KEY and should be + * adequately guarded again modules' error states. However, both current + * calls here are guarded properly in signature/mac_legacy.c. Thus, it + * could be removed here. The concern is that something in the future + * might call this function without adequate guards. It's a cheap call, + * it seems best to leave it even though it is currently redundant. + */ + if (!ossl_prov_is_running()) + return 0; + + CRYPTO_UP_REF(&mackey->refcnt, &ref); + return 1; +} + +static void *mac_new(void *provctx) +{ + return ossl_mac_key_new(PROV_LIBCTX_OF(provctx), 0); +} + +static void *mac_new_cmac(void *provctx) +{ + return ossl_mac_key_new(PROV_LIBCTX_OF(provctx), 1); +} + +static void mac_free(void *mackey) +{ + ossl_mac_key_free(mackey); +} + +static int mac_has(const void *keydata, int selection) +{ + const MAC_KEY *key = keydata; + int ok = 0; + + if (ossl_prov_is_running() && key != NULL) { + /* + * MAC keys always have all the parameters they need (i.e. none). + * Therefore we always return with 1, if asked about parameters. + * Similarly for public keys. + */ + ok = 1; + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + ok = key->priv_key != NULL; + } + return ok; +} + +static int mac_match(const void *keydata1, const void *keydata2, int selection) +{ + const MAC_KEY *key1 = keydata1; + const MAC_KEY *key2 = keydata2; + int ok = 1; + + if (!ossl_prov_is_running()) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) { + if ((key1->priv_key == NULL && key2->priv_key != NULL) + || (key1->priv_key != NULL && key2->priv_key == NULL) + || key1->priv_key_len != key2->priv_key_len + || (key1->cipher.cipher == NULL && key2->cipher.cipher != NULL) + || (key1->cipher.cipher != NULL && key2->cipher.cipher == NULL)) + ok = 0; + else + ok = ok && (key1->priv_key == NULL /* implies key2->privkey == NULL */ + || CRYPTO_memcmp(key1->priv_key, key2->priv_key, + key1->priv_key_len) == 0); + if (key1->cipher.cipher != NULL) + ok = ok && EVP_CIPHER_is_a(key1->cipher.cipher, + EVP_CIPHER_get0_name(key2->cipher.cipher)); + } + return ok; +} + +static int mac_key_fromdata(MAC_KEY *key, const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p; + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_OCTET_STRING) { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } + OPENSSL_secure_clear_free(key->priv_key, key->priv_key_len); + /* allocate at least one byte to distinguish empty key from no key set */ + key->priv_key = OPENSSL_secure_malloc(p->data_size > 0 ? p->data_size : 1); + if (key->priv_key == NULL) + return 0; + memcpy(key->priv_key, p->data, p->data_size); + key->priv_key_len = p->data_size; + } + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PROPERTIES); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_UTF8_STRING) { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } + OPENSSL_free(key->properties); + key->properties = OPENSSL_strdup(p->data); + if (key->properties == NULL) + return 0; + } + + if (key->cmac && !ossl_prov_cipher_load_from_params(&key->cipher, params, + key->libctx)) { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } + + if (key->priv_key != NULL) + return 1; + + return 0; +} + +static int mac_import(void *keydata, int selection, const OSSL_PARAM params[]) +{ + MAC_KEY *key = keydata; + + if (!ossl_prov_is_running() || key == NULL) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) == 0) + return 0; + + return mac_key_fromdata(key, params); +} + +static int key_to_params(MAC_KEY *key, OSSL_PARAM_BLD *tmpl, + OSSL_PARAM params[]) +{ + if (key == NULL) + return 0; + + if (key->priv_key != NULL + && !ossl_param_build_set_octet_string(tmpl, params, + OSSL_PKEY_PARAM_PRIV_KEY, + key->priv_key, key->priv_key_len)) + return 0; + + if (key->cipher.cipher != NULL + && !ossl_param_build_set_utf8_string(tmpl, params, + OSSL_PKEY_PARAM_CIPHER, + EVP_CIPHER_get0_name(key->cipher.cipher))) + return 0; + +#if !defined(OPENSSL_NO_ENGINE) && !defined(FIPS_MODULE) + if (key->cipher.engine != NULL + && !ossl_param_build_set_utf8_string(tmpl, params, + OSSL_PKEY_PARAM_ENGINE, + ENGINE_get_id(key->cipher.engine))) + return 0; +#endif + + return 1; +} + +static int mac_export(void *keydata, int selection, OSSL_CALLBACK *param_cb, + void *cbarg) +{ + MAC_KEY *key = keydata; + OSSL_PARAM_BLD *tmpl; + OSSL_PARAM *params = NULL; + int ret = 0; + + if (!ossl_prov_is_running() || key == NULL) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) == 0) + return 0; + + tmpl = OSSL_PARAM_BLD_new(); + if (tmpl == NULL) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0 + && !key_to_params(key, tmpl, NULL)) + goto err; + + params = OSSL_PARAM_BLD_to_param(tmpl); + if (params == NULL) + goto err; + + ret = param_cb(params, cbarg); + OSSL_PARAM_free(params); +err: + OSSL_PARAM_BLD_free(tmpl); + return ret; +} + +static const OSSL_PARAM mac_key_types[] = { + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_END +}; +static const OSSL_PARAM *mac_imexport_types(int selection) +{ + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + return mac_key_types; + return NULL; +} + +static const OSSL_PARAM cmac_key_types[] = { + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_CIPHER, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_ENGINE, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_END +}; +static const OSSL_PARAM *cmac_imexport_types(int selection) +{ + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + return cmac_key_types; + return NULL; +} + +static int mac_get_params(void *key, OSSL_PARAM params[]) +{ + return key_to_params(key, NULL, params); +} + +static const OSSL_PARAM *mac_gettable_params(void *provctx) +{ + static const OSSL_PARAM gettable_params[] = { + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0), + OSSL_PARAM_END + }; + return gettable_params; +} + +static const OSSL_PARAM *cmac_gettable_params(void *provctx) +{ + static const OSSL_PARAM gettable_params[] = { + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_CIPHER, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_ENGINE, NULL, 0), + OSSL_PARAM_END + }; + return gettable_params; +} + +static int mac_set_params(void *keydata, const OSSL_PARAM params[]) +{ + MAC_KEY *key = keydata; + const OSSL_PARAM *p; + + if (key == NULL) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY); + if (p != NULL) + return mac_key_fromdata(key, params); + + return 1; +} + +static const OSSL_PARAM *mac_settable_params(void *provctx) +{ + static const OSSL_PARAM settable_params[] = { + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0), + OSSL_PARAM_END + }; + return settable_params; +} + +static void *mac_gen_init_common(void *provctx, int selection) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx); + struct mac_gen_ctx *gctx = NULL; + + if (!ossl_prov_is_running()) + return NULL; + + if ((gctx = OPENSSL_zalloc(sizeof(*gctx))) != NULL) { + gctx->libctx = libctx; + gctx->selection = selection; + } + return gctx; +} + +static void *mac_gen_init(void *provctx, int selection, + const OSSL_PARAM params[]) +{ + struct mac_gen_ctx *gctx = mac_gen_init_common(provctx, selection); + + if (gctx != NULL && !mac_gen_set_params(gctx, params)) { + mac_gen_cleanup(gctx); + gctx = NULL; + } + return gctx; +} + +static void *cmac_gen_init(void *provctx, int selection, + const OSSL_PARAM params[]) +{ + struct mac_gen_ctx *gctx = mac_gen_init_common(provctx, selection); + + if (gctx != NULL && !cmac_gen_set_params(gctx, params)) { + mac_gen_cleanup(gctx); + gctx = NULL; + } + return gctx; +} + +static int mac_gen_set_params(void *genctx, const OSSL_PARAM params[]) +{ + struct mac_gen_ctx *gctx = genctx; + const OSSL_PARAM *p; + + if (gctx == NULL) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_OCTET_STRING) { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } + gctx->priv_key = OPENSSL_secure_malloc(p->data_size); + if (gctx->priv_key == NULL) + return 0; + memcpy(gctx->priv_key, p->data, p->data_size); + gctx->priv_key_len = p->data_size; + } + + return 1; +} + +static int cmac_gen_set_params(void *genctx, const OSSL_PARAM params[]) +{ + struct mac_gen_ctx *gctx = genctx; + + if (!mac_gen_set_params(genctx, params)) + return 0; + + if (!ossl_prov_cipher_load_from_params(&gctx->cipher, params, + gctx->libctx)) { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } + + return 1; +} + +static const OSSL_PARAM *mac_gen_settable_params(ossl_unused void *genctx, + ossl_unused void *provctx) +{ + static OSSL_PARAM settable[] = { + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0), + OSSL_PARAM_END + }; + return settable; +} + +static const OSSL_PARAM *cmac_gen_settable_params(ossl_unused void *genctx, + ossl_unused void *provctx) +{ + static OSSL_PARAM settable[] = { + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_CIPHER, NULL, 0), + OSSL_PARAM_END + }; + return settable; +} + +static void *mac_gen(void *genctx, OSSL_CALLBACK *cb, void *cbarg) +{ + struct mac_gen_ctx *gctx = genctx; + MAC_KEY *key; + + if (!ossl_prov_is_running() || gctx == NULL) + return NULL; + + if ((key = ossl_mac_key_new(gctx->libctx, 0)) == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_PROV_LIB); + return NULL; + } + + /* If we're doing parameter generation then we just return a blank key */ + if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) + return key; + + if (gctx->priv_key == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY); + ossl_mac_key_free(key); + return NULL; + } + + /* + * This is horrible but required for backwards compatibility. We don't + * actually do real key generation at all. We simply copy the key that was + * previously set in the gctx. Hopefully at some point in the future all + * of this can be removed and we will only support the EVP_KDF APIs. + */ + if (!ossl_prov_cipher_copy(&key->cipher, &gctx->cipher)) { + ossl_mac_key_free(key); + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + return NULL; + } + ossl_prov_cipher_reset(&gctx->cipher); + key->priv_key = gctx->priv_key; + key->priv_key_len = gctx->priv_key_len; + gctx->priv_key_len = 0; + gctx->priv_key = NULL; + + return key; +} + +static void mac_gen_cleanup(void *genctx) +{ + struct mac_gen_ctx *gctx = genctx; + + if (gctx == NULL) + return; + + OPENSSL_secure_clear_free(gctx->priv_key, gctx->priv_key_len); + ossl_prov_cipher_reset(&gctx->cipher); + OPENSSL_free(gctx); +} + +const OSSL_DISPATCH ossl_mac_legacy_keymgmt_functions[] = { + { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))mac_new }, + { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))mac_free }, + { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))mac_get_params }, + { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))mac_gettable_params }, + { OSSL_FUNC_KEYMGMT_SET_PARAMS, (void (*) (void))mac_set_params }, + { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*) (void))mac_settable_params }, + { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))mac_has }, + { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))mac_match }, + { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))mac_import }, + { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))mac_imexport_types }, + { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))mac_export }, + { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))mac_imexport_types }, + { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))mac_gen_init }, + { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))mac_gen_set_params }, + { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, + (void (*)(void))mac_gen_settable_params }, + { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))mac_gen }, + { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))mac_gen_cleanup }, + OSSL_DISPATCH_END +}; + +const OSSL_DISPATCH ossl_cmac_legacy_keymgmt_functions[] = { + { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))mac_new_cmac }, + { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))mac_free }, + { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))mac_get_params }, + { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))cmac_gettable_params }, + { OSSL_FUNC_KEYMGMT_SET_PARAMS, (void (*) (void))mac_set_params }, + { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*) (void))mac_settable_params }, + { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))mac_has }, + { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))mac_match }, + { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))mac_import }, + { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))cmac_imexport_types }, + { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))mac_export }, + { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))cmac_imexport_types }, + { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))cmac_gen_init }, + { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))cmac_gen_set_params }, + { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, + (void (*)(void))cmac_gen_settable_params }, + { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))mac_gen }, + { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))mac_gen_cleanup }, + OSSL_DISPATCH_END +}; + diff --git a/crypto/openssl/providers/implementations/keymgmt/ml_dsa_kmgmt.c b/crypto/openssl/providers/implementations/keymgmt/ml_dsa_kmgmt.c new file mode 100644 index 000000000000..6b99e093c6d5 --- /dev/null +++ b/crypto/openssl/providers/implementations/keymgmt/ml_dsa_kmgmt.c @@ -0,0 +1,595 @@ +/* + * Copyright 2024-2025 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 + */ + +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/evp.h> +#include <openssl/param_build.h> +#include <openssl/proverr.h> +#include <openssl/self_test.h> +#include "crypto/ml_dsa.h" +#include "internal/fips.h" +#include "internal/param_build_set.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" +#include "prov/provider_ctx.h" +#include "prov/ml_dsa.h" + +static OSSL_FUNC_keymgmt_free_fn ml_dsa_free_key; +static OSSL_FUNC_keymgmt_has_fn ml_dsa_has; +static OSSL_FUNC_keymgmt_match_fn ml_dsa_match; +static OSSL_FUNC_keymgmt_import_fn ml_dsa_import; +static OSSL_FUNC_keymgmt_export_fn ml_dsa_export; +static OSSL_FUNC_keymgmt_import_types_fn ml_dsa_imexport_types; +static OSSL_FUNC_keymgmt_export_types_fn ml_dsa_imexport_types; +static OSSL_FUNC_keymgmt_dup_fn ml_dsa_dup_key; +static OSSL_FUNC_keymgmt_get_params_fn ml_dsa_get_params; +static OSSL_FUNC_keymgmt_gettable_params_fn ml_dsa_gettable_params; +static OSSL_FUNC_keymgmt_validate_fn ml_dsa_validate; +static OSSL_FUNC_keymgmt_gen_init_fn ml_dsa_gen_init; +static OSSL_FUNC_keymgmt_gen_cleanup_fn ml_dsa_gen_cleanup; +static OSSL_FUNC_keymgmt_gen_set_params_fn ml_dsa_gen_set_params; +static OSSL_FUNC_keymgmt_gen_settable_params_fn ml_dsa_gen_settable_params; +#ifndef FIPS_MODULE +static OSSL_FUNC_keymgmt_load_fn ml_dsa_load; +#endif + +struct ml_dsa_gen_ctx { + PROV_CTX *provctx; + char *propq; + uint8_t entropy[32]; + size_t entropy_len; +}; + +#ifdef FIPS_MODULE +static int ml_dsa_pairwise_test(const ML_DSA_KEY *key) +{ + OSSL_SELF_TEST *st = NULL; + OSSL_CALLBACK *cb = NULL; + OSSL_LIB_CTX *ctx; + void *cbarg = NULL; + static const uint8_t msg[] = { 80, 108, 117, 103, 104 }; + uint8_t rnd[ML_DSA_ENTROPY_LEN]; + uint8_t sig[ML_DSA_87_SIG_LEN]; + size_t sig_len = 0; + int ret = 0; + + if (!ml_dsa_has(key, OSSL_KEYMGMT_SELECT_KEYPAIR) + || ossl_fips_self_testing()) + return 1; + + /* + * The functions `OSSL_SELF_TEST_*` will return directly if parameter `st` + * is NULL. + */ + ctx = ossl_ml_dsa_key_get0_libctx(key); + OSSL_SELF_TEST_get_callback(ctx, &cb, &cbarg); + + if ((st = OSSL_SELF_TEST_new(cb, cbarg)) == NULL) + return 0; + + OSSL_SELF_TEST_onbegin(st, OSSL_SELF_TEST_TYPE_PCT, + OSSL_SELF_TEST_DESC_PCT_ML_DSA); + + memset(rnd, 0, sizeof(rnd)); + memset(sig, 0, sizeof(sig)); + + if (ossl_ml_dsa_sign(key, 0, msg, sizeof(msg), NULL, 0, rnd, sizeof(rnd), 0, + sig, &sig_len, sizeof(sig)) <= 0) + goto err; + + OSSL_SELF_TEST_oncorrupt_byte(st, sig); + + if (ossl_ml_dsa_verify(key, 0, msg, sizeof(msg), NULL, 0, 0, + sig, sig_len) <= 0) + goto err; + + ret = 1; + err: + OSSL_SELF_TEST_onend(st, ret); + OSSL_SELF_TEST_free(st); + return ret; +} +#endif + +ML_DSA_KEY *ossl_prov_ml_dsa_new(PROV_CTX *ctx, const char *propq, int evp_type) +{ + ML_DSA_KEY *key; + + if (!ossl_prov_is_running()) + return 0; + + key = ossl_ml_dsa_key_new(PROV_LIBCTX_OF(ctx), propq, evp_type); + /* + * When decoding, if the key ends up "loaded" into the same provider, these + * are the correct config settings, otherwise, new values will be assigned + * on import into a different provider. The "load" API does not pass along + * the provider context. + */ + if (key != NULL) { + int flags_set = 0, flags_clr = 0; + + if (ossl_prov_ctx_get_bool_param( + ctx, OSSL_PKEY_PARAM_ML_DSA_RETAIN_SEED, 1)) + flags_set |= ML_DSA_KEY_RETAIN_SEED; + else + flags_clr = ML_DSA_KEY_RETAIN_SEED; + + if (ossl_prov_ctx_get_bool_param( + ctx, OSSL_PKEY_PARAM_ML_DSA_PREFER_SEED, 1)) + flags_set |= ML_DSA_KEY_PREFER_SEED; + else + flags_clr |= ML_DSA_KEY_PREFER_SEED; + + ossl_ml_dsa_set_prekey(key, flags_set, flags_clr, NULL, 0, NULL, 0); + } + return key; +} + +static void ml_dsa_free_key(void *keydata) +{ + ossl_ml_dsa_key_free((ML_DSA_KEY *)keydata); +} + +static void *ml_dsa_dup_key(const void *keydata_from, int selection) +{ + if (ossl_prov_is_running()) + return ossl_ml_dsa_key_dup(keydata_from, selection); + return NULL; +} + +static int ml_dsa_has(const void *keydata, int selection) +{ + const ML_DSA_KEY *key = keydata; + + if (!ossl_prov_is_running() || key == NULL) + return 0; + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) + return 1; /* the selection is not missing */ + + return ossl_ml_dsa_key_has(key, selection); +} + +static int ml_dsa_match(const void *keydata1, const void *keydata2, int selection) +{ + const ML_DSA_KEY *key1 = keydata1; + const ML_DSA_KEY *key2 = keydata2; + + if (!ossl_prov_is_running()) + return 0; + if (key1 == NULL || key2 == NULL) + return 0; + return ossl_ml_dsa_key_equal(key1, key2, selection); +} + +static int ml_dsa_validate(const void *key_data, int selection, int check_type) +{ + const ML_DSA_KEY *key = key_data; + + if (!ml_dsa_has(key, selection)) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == OSSL_KEYMGMT_SELECT_KEYPAIR) + return ossl_ml_dsa_key_pairwise_check(key); + return 1; +} + +/** + * @brief Load a ML_DSA key from raw data. + * + * @param key An ML_DSA key to load into + * @param params An array of parameters containing key data. + * @param include_private Set to 1 to optionally include the private key data + * if it exists. + * @returns 1 on success, or 0 on failure. + */ +static int ml_dsa_key_fromdata(ML_DSA_KEY *key, const OSSL_PARAM params[], + int include_private) +{ + const OSSL_PARAM *p = NULL; + const ML_DSA_PARAMS *key_params = ossl_ml_dsa_key_params(key); + const uint8_t *pk = NULL, *sk = NULL, *seed = NULL; + size_t pk_len = 0, sk_len = 0, seed_len = 0; + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY); + if (p != NULL + && !OSSL_PARAM_get_octet_string_ptr(p, (const void **)&pk, &pk_len)) + return 0; + if (pk != NULL && pk_len != key_params->pk_len) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH, + "Invalid %s public key length", key_params->alg); + return 0; + } + + /* Private key is optional */ + if (include_private) { + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ML_DSA_SEED); + if (p != NULL + && !OSSL_PARAM_get_octet_string_ptr(p, (const void **)&seed, + &seed_len)) + return 0; + if (seed != NULL && seed_len != ML_DSA_SEED_BYTES) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SEED_LENGTH); + return 0; + } + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY); + if (p != NULL + && !OSSL_PARAM_get_octet_string_ptr(p, (const void **)&sk, &sk_len)) + return 0; + if (sk != NULL && sk_len != key_params->sk_len) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH, + "Invalid %s private key length", key_params->alg); + return 0; + } + } + + /* The caller MUST specify at least one of seed, private or public keys. */ + if (seed_len == 0 && pk_len == 0 && sk_len == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + return 0; + } + + if (seed_len != 0 + && (sk_len == 0 + || (ossl_ml_dsa_key_get_prov_flags(key) & ML_DSA_KEY_PREFER_SEED))) { + if (!ossl_ml_dsa_set_prekey(key, 0, 0, seed, seed_len, sk, sk_len)) + return 0; + if (!ossl_ml_dsa_generate_key(key)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GENERATE_KEY); + return 0; + } + } else if (sk_len > 0) { + if (!ossl_ml_dsa_sk_decode(key, sk, sk_len)) + return 0; + } else if (pk_len > 0) { + if (!ossl_ml_dsa_pk_decode(key, pk, pk_len)) + return 0; + } + + /* Error if the supplied public key does not match the generated key */ + if (pk_len == 0 + || seed_len + sk_len == 0 + || memcmp(ossl_ml_dsa_key_get_pub(key), pk, pk_len) == 0) + return 1; + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, + "explicit %s public key does not match private", + key_params->alg); + ossl_ml_dsa_key_reset(key); + return 0; +} + +static int ml_dsa_import(void *keydata, int selection, const OSSL_PARAM params[]) +{ + ML_DSA_KEY *key = keydata; + int include_priv; + int res; + + if (!ossl_prov_is_running() || key == NULL) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) + return 0; + + include_priv = ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0); + res = ml_dsa_key_fromdata(key, params, include_priv); +#ifdef FIPS_MODULE + if (res > 0) { + res = ml_dsa_pairwise_test(key); + if (!res) { + ossl_ml_dsa_key_reset(key); + ossl_set_error_state(OSSL_SELF_TEST_TYPE_PCT_IMPORT); + } + } +#endif /* FIPS_MODULE */ + return res; +} + +#define ML_DSA_IMEXPORTABLE_PARAMETERS \ + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ML_DSA_SEED, NULL, 0), \ + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0), \ + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0) + +static const OSSL_PARAM ml_dsa_key_types[] = { + ML_DSA_IMEXPORTABLE_PARAMETERS, + OSSL_PARAM_END +}; +static const OSSL_PARAM *ml_dsa_imexport_types(int selection) +{ + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) + return NULL; + return ml_dsa_key_types; +} + +static const OSSL_PARAM ml_dsa_params[] = { + OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_MANDATORY_DIGEST, NULL, 0), + ML_DSA_IMEXPORTABLE_PARAMETERS, + OSSL_PARAM_END +}; +static const OSSL_PARAM *ml_dsa_gettable_params(void *provctx) +{ + return ml_dsa_params; +} + +static int ml_dsa_get_params(void *keydata, OSSL_PARAM params[]) +{ + ML_DSA_KEY *key = keydata; + OSSL_PARAM *p; + const uint8_t *pub, *priv, *seed; + + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS)) != NULL + && !OSSL_PARAM_set_int(p, 8 * ossl_ml_dsa_key_get_pub_len(key))) + return 0; + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS)) != NULL + && !OSSL_PARAM_set_int(p, ossl_ml_dsa_key_get_collision_strength_bits(key))) + return 0; + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE)) != NULL + && !OSSL_PARAM_set_int(p, ossl_ml_dsa_key_get_sig_len(key))) + return 0; + + pub = ossl_ml_dsa_key_get_pub(key); + priv = ossl_ml_dsa_key_get_priv(key); + seed = ossl_ml_dsa_key_get_seed(key); + + if (seed != NULL + && (p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_ML_DSA_SEED)) != NULL + && !OSSL_PARAM_set_octet_string(p, seed, ML_DSA_SEED_BYTES)) + return 0; + if (priv != NULL + && (p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PRIV_KEY)) != NULL + && !OSSL_PARAM_set_octet_string(p, priv, + ossl_ml_dsa_key_get_priv_len(key))) + return 0; + if (pub != NULL + && (p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PUB_KEY)) != NULL + && !OSSL_PARAM_set_octet_string(p, pub, + ossl_ml_dsa_key_get_pub_len(key))) + return 0; + /* + * This allows apps to use an empty digest, so that the old API + * for digest signing can be used. + */ + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MANDATORY_DIGEST); + if (p != NULL && !OSSL_PARAM_set_utf8_string(p, "")) + return 0; + + return 1; +} + +static int ml_dsa_export(void *keydata, int selection, + OSSL_CALLBACK *param_cb, void *cbarg) +{ + ML_DSA_KEY *key = keydata; + OSSL_PARAM params[4]; + const uint8_t *buf; + int include_private, pnum = 0; + + if (!ossl_prov_is_running() || key == NULL) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) + return 0; + + include_private = ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0); + + /* + * Note that if the seed is present, both the seed and the private key are + * exported. The recipient will have a choice. + */ + if (include_private) { + if ((buf = ossl_ml_dsa_key_get_seed(key)) != NULL) { + params[pnum++] = OSSL_PARAM_construct_octet_string + (OSSL_PKEY_PARAM_ML_DSA_SEED, (void *)buf, ML_DSA_SEED_BYTES); + } + if ((buf = ossl_ml_dsa_key_get_priv(key)) != NULL) { + params[pnum++] = OSSL_PARAM_construct_octet_string + (OSSL_PKEY_PARAM_PRIV_KEY, (void *)buf, + ossl_ml_dsa_key_get_priv_len(key)); + } + } + if (((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) + && ((buf = ossl_ml_dsa_key_get_pub(key)) != NULL)) { + params[pnum++] = OSSL_PARAM_construct_octet_string + (OSSL_PKEY_PARAM_PUB_KEY, (void *)buf, + ossl_ml_dsa_key_get_pub_len(key)); + } + if (pnum == 0) + return 0; + params[pnum] = OSSL_PARAM_construct_end(); + return param_cb(params, cbarg); +} + +#ifndef FIPS_MODULE +static void *ml_dsa_load(const void *reference, size_t reference_sz) +{ + ML_DSA_KEY *key = NULL; + const ML_DSA_PARAMS *key_params; + const uint8_t *sk, *seed; + + if (ossl_prov_is_running() && reference_sz == sizeof(key)) { + /* The contents of the reference is the address to our object */ + key = *(ML_DSA_KEY **)reference; + /* We grabbed, so we detach it */ + *(ML_DSA_KEY **)reference = NULL; + /* All done, if the pubkey is present. */ + if (key == NULL || ossl_ml_dsa_key_get_pub(key) != NULL) + return key; + /* Handle private prekey inputs. */ + sk = ossl_ml_dsa_key_get_priv(key); + seed = ossl_ml_dsa_key_get_seed(key); + if (seed != NULL + && (sk == NULL || (ossl_ml_dsa_key_get_prov_flags(key) + & ML_DSA_KEY_PREFER_SEED))) { + if (ossl_ml_dsa_generate_key(key)) + return key; + } else if (sk != NULL) { + if (ossl_ml_dsa_sk_decode(key, sk, + ossl_ml_dsa_key_get_priv_len(key))) + return key; + key_params = ossl_ml_dsa_key_params(key); + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, + "error parsing %s private key", + key_params->alg); + } else { + return key; + } + } + + ossl_ml_dsa_key_free(key); + return NULL; +} +#endif + +static void *ml_dsa_gen_init(void *provctx, int selection, + const OSSL_PARAM params[]) +{ + struct ml_dsa_gen_ctx *gctx = NULL; + + if (!ossl_prov_is_running()) + return NULL; + + if ((gctx = OPENSSL_zalloc(sizeof(*gctx))) != NULL) { + gctx->provctx = provctx; + if (!ml_dsa_gen_set_params(gctx, params)) { + OPENSSL_free(gctx); + gctx = NULL; + } + } + return gctx; +} + +static void *ml_dsa_gen(void *genctx, int evp_type) +{ + struct ml_dsa_gen_ctx *gctx = genctx; + ML_DSA_KEY *key = NULL; + + if (!ossl_prov_is_running()) + return NULL; + key = ossl_prov_ml_dsa_new(gctx->provctx, gctx->propq, evp_type); + if (key == NULL) + return NULL; + if (gctx->entropy_len != 0 + && !ossl_ml_dsa_set_prekey(key, 0, 0, + gctx->entropy, gctx->entropy_len, NULL, 0)) + goto err; + if (!ossl_ml_dsa_generate_key(key)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GENERATE_KEY); + goto err; + } +#ifdef FIPS_MODULE + if (!ml_dsa_pairwise_test(key)) { + ossl_set_error_state(OSSL_SELF_TEST_TYPE_PCT); + goto err; + } +#endif + return key; + err: + ossl_ml_dsa_key_free(key); + return NULL; +} + +static int ml_dsa_gen_set_params(void *genctx, const OSSL_PARAM params[]) +{ + struct ml_dsa_gen_ctx *gctx = genctx; + const OSSL_PARAM *p; + + if (gctx == NULL) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ML_DSA_SEED); + if (p != NULL) { + void *vp = gctx->entropy; + size_t len = sizeof(gctx->entropy); + + if (!OSSL_PARAM_get_octet_string(p, &vp, len, &(gctx->entropy_len))) { + gctx->entropy_len = 0; + return 0; + } + } + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PROPERTIES); + if (p != NULL) { + OPENSSL_free(gctx->propq); + gctx->propq = NULL; + if (!OSSL_PARAM_get_utf8_string(p, &gctx->propq, 0)) + return 0; + } + return 1; +} + +static const OSSL_PARAM *ml_dsa_gen_settable_params(ossl_unused void *genctx, + ossl_unused void *provctx) +{ + static OSSL_PARAM settable[] = { + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ML_DSA_SEED, NULL, 0), + OSSL_PARAM_END + }; + return settable; +} + +static void ml_dsa_gen_cleanup(void *genctx) +{ + struct ml_dsa_gen_ctx *gctx = genctx; + + if (gctx == NULL) + return; + + OPENSSL_cleanse(gctx->entropy, gctx->entropy_len); + OPENSSL_free(gctx->propq); + OPENSSL_free(gctx); +} + +#ifndef FIPS_MODULE +# define DISPATCH_LOAD_FN \ + { OSSL_FUNC_KEYMGMT_LOAD, (OSSL_FUNC) ml_dsa_load }, +#else +# define DISPATCH_LOAD_FN /* Non-FIPS only */ +#endif + +#define MAKE_KEYMGMT_FUNCTIONS(alg) \ + static OSSL_FUNC_keymgmt_new_fn ml_dsa_##alg##_new_key; \ + static OSSL_FUNC_keymgmt_gen_fn ml_dsa_##alg##_gen; \ + static void *ml_dsa_##alg##_new_key(void *provctx) \ + { \ + return ossl_prov_ml_dsa_new(provctx, NULL, EVP_PKEY_ML_DSA_##alg); \ + } \ + static void *ml_dsa_##alg##_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg)\ + { \ + return ml_dsa_gen(genctx, EVP_PKEY_ML_DSA_##alg); \ + } \ + const OSSL_DISPATCH ossl_ml_dsa_##alg##_keymgmt_functions[] = { \ + { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))ml_dsa_##alg##_new_key }, \ + { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))ml_dsa_free_key }, \ + { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))ml_dsa_has }, \ + { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))ml_dsa_match }, \ + { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))ml_dsa_import }, \ + { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))ml_dsa_imexport_types },\ + { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))ml_dsa_export }, \ + { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))ml_dsa_imexport_types },\ + DISPATCH_LOAD_FN \ + { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))ml_dsa_get_params }, \ + { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))ml_dsa_gettable_params },\ + { OSSL_FUNC_KEYMGMT_VALIDATE, (void (*)(void))ml_dsa_validate }, \ + { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))ml_dsa_gen_init }, \ + { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))ml_dsa_##alg##_gen }, \ + { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))ml_dsa_gen_cleanup }, \ + { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, \ + (void (*)(void))ml_dsa_gen_set_params }, \ + { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, \ + (void (*)(void))ml_dsa_gen_settable_params }, \ + { OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))ml_dsa_dup_key }, \ + OSSL_DISPATCH_END \ + } + +MAKE_KEYMGMT_FUNCTIONS(44); +MAKE_KEYMGMT_FUNCTIONS(65); +MAKE_KEYMGMT_FUNCTIONS(87); diff --git a/crypto/openssl/providers/implementations/keymgmt/ml_kem_kmgmt.c b/crypto/openssl/providers/implementations/keymgmt/ml_kem_kmgmt.c new file mode 100644 index 000000000000..9b34fe1c0331 --- /dev/null +++ b/crypto/openssl/providers/implementations/keymgmt/ml_kem_kmgmt.c @@ -0,0 +1,859 @@ +/* + * Copyright 2024-2025 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 + */ + +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/params.h> +#include <openssl/err.h> +#include <openssl/proverr.h> +#include <openssl/provider.h> +#include <openssl/rand.h> +#include <openssl/self_test.h> +#include <openssl/param_build.h> +#include "crypto/ml_kem.h" +#include "internal/fips.h" +#include "internal/param_build_set.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" +#include "prov/provider_ctx.h" +#include "prov/securitycheck.h" +#include "prov/ml_kem.h" + +static OSSL_FUNC_keymgmt_new_fn ml_kem_512_new; +static OSSL_FUNC_keymgmt_new_fn ml_kem_768_new; +static OSSL_FUNC_keymgmt_new_fn ml_kem_1024_new; +static OSSL_FUNC_keymgmt_gen_fn ml_kem_gen; +static OSSL_FUNC_keymgmt_gen_init_fn ml_kem_512_gen_init; +static OSSL_FUNC_keymgmt_gen_init_fn ml_kem_768_gen_init; +static OSSL_FUNC_keymgmt_gen_init_fn ml_kem_1024_gen_init; +static OSSL_FUNC_keymgmt_gen_cleanup_fn ml_kem_gen_cleanup; +static OSSL_FUNC_keymgmt_gen_set_params_fn ml_kem_gen_set_params; +static OSSL_FUNC_keymgmt_gen_settable_params_fn ml_kem_gen_settable_params; +#ifndef FIPS_MODULE +static OSSL_FUNC_keymgmt_load_fn ml_kem_load; +#endif +static OSSL_FUNC_keymgmt_get_params_fn ml_kem_get_params; +static OSSL_FUNC_keymgmt_gettable_params_fn ml_kem_gettable_params; +static OSSL_FUNC_keymgmt_set_params_fn ml_kem_set_params; +static OSSL_FUNC_keymgmt_settable_params_fn ml_kem_settable_params; +static OSSL_FUNC_keymgmt_has_fn ml_kem_has; +static OSSL_FUNC_keymgmt_match_fn ml_kem_match; +static OSSL_FUNC_keymgmt_validate_fn ml_kem_validate; +static OSSL_FUNC_keymgmt_import_fn ml_kem_import; +static OSSL_FUNC_keymgmt_export_fn ml_kem_export; +static OSSL_FUNC_keymgmt_import_types_fn ml_kem_imexport_types; +static OSSL_FUNC_keymgmt_export_types_fn ml_kem_imexport_types; +static OSSL_FUNC_keymgmt_dup_fn ml_kem_dup; + +static const int minimal_selection = OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS + | OSSL_KEYMGMT_SELECT_PRIVATE_KEY; + +typedef struct ml_kem_gen_ctx_st { + PROV_CTX *provctx; + char *propq; + int selection; + int evp_type; + uint8_t seedbuf[ML_KEM_SEED_BYTES]; + uint8_t *seed; +} PROV_ML_KEM_GEN_CTX; + +static int ml_kem_pairwise_test(const ML_KEM_KEY *key, int key_flags) +{ +#ifdef FIPS_MODULE + OSSL_SELF_TEST *st = NULL; + OSSL_CALLBACK *cb = NULL; + void *cbarg = NULL; +#endif + unsigned char entropy[ML_KEM_RANDOM_BYTES]; + unsigned char secret[ML_KEM_SHARED_SECRET_BYTES]; + unsigned char out[ML_KEM_SHARED_SECRET_BYTES]; + unsigned char *ctext = NULL; + const ML_KEM_VINFO *v = ossl_ml_kem_key_vinfo(key); + int operation_result = 0; + int ret = 0; + + /* Unless we have both a public and private key, we can't do the test */ + if (!ossl_ml_kem_have_prvkey(key) + || !ossl_ml_kem_have_pubkey(key) + || (key_flags & ML_KEM_KEY_PCT_TYPE) == 0) + return 1; +#ifdef FIPS_MODULE + /* During self test, it is a waste to do this test */ + if (ossl_fips_self_testing()) + return 1; + + /* + * The functions `OSSL_SELF_TEST_*` will return directly if parameter `st` + * is NULL. + */ + OSSL_SELF_TEST_get_callback(key->libctx, &cb, &cbarg); + + st = OSSL_SELF_TEST_new(cb, cbarg); + if (st == NULL) + return 0; + + OSSL_SELF_TEST_onbegin(st, OSSL_SELF_TEST_TYPE_PCT, + OSSL_SELF_TEST_DESC_PCT_ML_KEM); +#endif /* FIPS_MODULE */ + + ctext = OPENSSL_malloc(v->ctext_bytes); + if (ctext == NULL) + goto err; + + memset(out, 0, sizeof(out)); + + /* + * The pairwise test is skipped unless either RANDOM or FIXED entropy PCTs + * are enabled. + */ + if (key_flags & ML_KEM_KEY_RANDOM_PCT) { + operation_result = ossl_ml_kem_encap_rand(ctext, v->ctext_bytes, + secret, sizeof(secret), key); + } else { + memset(entropy, 0125, sizeof(entropy)); + operation_result = ossl_ml_kem_encap_seed(ctext, v->ctext_bytes, + secret, sizeof(secret), + entropy, sizeof(entropy), + key); + } + if (operation_result != 1) + goto err; + +#ifdef FIPS_MODULE + OSSL_SELF_TEST_oncorrupt_byte(st, ctext); +#endif + + operation_result = ossl_ml_kem_decap(out, sizeof(out), ctext, v->ctext_bytes, + key); + if (operation_result != 1 || memcmp(out, secret, sizeof(out)) != 0) + goto err; + + ret = 1; +err: +#ifdef FIPS_MODULE + OSSL_SELF_TEST_onend(st, ret); + OSSL_SELF_TEST_free(st); +#else + if (ret == 0) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, + "public part of %s private key fails to match private", + v->algorithm_name); + } +#endif + OPENSSL_free(ctext); + return ret; +} + +ML_KEM_KEY *ossl_prov_ml_kem_new(PROV_CTX *ctx, const char *propq, int evp_type) +{ + ML_KEM_KEY *key; + + if (!ossl_prov_is_running()) + return NULL; + /* + * When decoding, if the key ends up "loaded" into the same provider, these + * are the correct config settings, otherwise, new values will be assigned + * on import into a different provider. The "load" API does not pass along + * the provider context. + */ + if ((key = ossl_ml_kem_key_new(PROV_LIBCTX_OF(ctx), propq, evp_type)) != NULL) { + const char *pct_type = ossl_prov_ctx_get_param( + ctx, OSSL_PKEY_PARAM_ML_KEM_IMPORT_PCT_TYPE, "random"); + + if (ossl_prov_ctx_get_bool_param( + ctx, OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED, 1)) + key->prov_flags |= ML_KEM_KEY_RETAIN_SEED; + else + key->prov_flags &= ~ML_KEM_KEY_RETAIN_SEED; + if (ossl_prov_ctx_get_bool_param( + ctx, OSSL_PKEY_PARAM_ML_KEM_PREFER_SEED, 1)) + key->prov_flags |= ML_KEM_KEY_PREFER_SEED; + else + key->prov_flags &= ~ML_KEM_KEY_PREFER_SEED; + if (OPENSSL_strcasecmp(pct_type, "random") == 0) + key->prov_flags |= ML_KEM_KEY_RANDOM_PCT; + else if (OPENSSL_strcasecmp(pct_type, "fixed") == 0) + key->prov_flags |= ML_KEM_KEY_FIXED_PCT; + else + key->prov_flags &= ~ML_KEM_KEY_PCT_TYPE; + } + return key; +} + +static int ml_kem_has(const void *vkey, int selection) +{ + const ML_KEM_KEY *key = vkey; + + /* A NULL key MUST fail to have anything */ + if (!ossl_prov_is_running() || key == NULL) + return 0; + + switch (selection & OSSL_KEYMGMT_SELECT_KEYPAIR) { + case 0: + return 1; + case OSSL_KEYMGMT_SELECT_PUBLIC_KEY: + return ossl_ml_kem_have_pubkey(key); + default: + return ossl_ml_kem_have_prvkey(key); + } +} + +static int ml_kem_match(const void *vkey1, const void *vkey2, int selection) +{ + const ML_KEM_KEY *key1 = vkey1; + const ML_KEM_KEY *key2 = vkey2; + + if (!ossl_prov_is_running()) + return 0; + + /* All we have that can be compared is key material */ + if (!(selection & OSSL_KEYMGMT_SELECT_KEYPAIR)) + return 1; + + return ossl_ml_kem_pubkey_cmp(key1, key2); +} + +static int ml_kem_validate(const void *vkey, int selection, int check_type) +{ + const ML_KEM_KEY *key = vkey; + + if (!ml_kem_has(key, selection)) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == OSSL_KEYMGMT_SELECT_KEYPAIR) + return ml_kem_pairwise_test(key, ML_KEM_KEY_RANDOM_PCT); + return 1; +} + +static int ml_kem_export(void *vkey, int selection, OSSL_CALLBACK *param_cb, + void *cbarg) +{ + ML_KEM_KEY *key = vkey; + OSSL_PARAM_BLD *tmpl = NULL; + OSSL_PARAM *params = NULL; + const ML_KEM_VINFO *v; + uint8_t *pubenc = NULL, *prvenc = NULL, *seedenc = NULL; + size_t prvlen = 0, seedlen = 0; + int ret = 0; + + if (!ossl_prov_is_running() || key == NULL) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) + return 0; + + v = ossl_ml_kem_key_vinfo(key); + if (!ossl_ml_kem_have_pubkey(key)) { + /* Fail when no key material can be returned */ + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) == 0 + || !ossl_ml_kem_decoded_key(key)) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + return 0; + } + } else if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) { + pubenc = OPENSSL_malloc(v->pubkey_bytes); + if (pubenc == NULL + || !ossl_ml_kem_encode_public_key(pubenc, v->pubkey_bytes, key)) + goto err; + } + + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) { + /* + * The seed and/or private key material are allocated on the secure + * heap if configured, ossl_param_build_set_octet_string(), will then + * also use the secure heap. + */ + if (ossl_ml_kem_have_seed(key)) { + seedlen = ML_KEM_SEED_BYTES; + if ((seedenc = OPENSSL_secure_zalloc(seedlen)) == NULL + || !ossl_ml_kem_encode_seed(seedenc, seedlen, key)) + goto err; + } + if (ossl_ml_kem_have_prvkey(key)) { + prvlen = v->prvkey_bytes; + if ((prvenc = OPENSSL_secure_zalloc(prvlen)) == NULL + || !ossl_ml_kem_encode_private_key(prvenc, prvlen, key)) + goto err; + } else if (ossl_ml_kem_have_dkenc(key)) { + prvlen = v->prvkey_bytes; + if ((prvenc = OPENSSL_secure_zalloc(prvlen)) == NULL) + goto err; + memcpy(prvenc, key->encoded_dk, prvlen); + } + } + + tmpl = OSSL_PARAM_BLD_new(); + if (tmpl == NULL) + goto err; + + /* The (d, z) seed, when available and private keys are requested. */ + if (seedenc != NULL + && !ossl_param_build_set_octet_string( + tmpl, params, OSSL_PKEY_PARAM_ML_KEM_SEED, seedenc, seedlen)) + goto err; + + /* The private key in the FIPS 203 |dk| format, when requested. */ + if (prvenc != NULL + && !ossl_param_build_set_octet_string( + tmpl, params, OSSL_PKEY_PARAM_PRIV_KEY, prvenc, prvlen)) + goto err; + + /* The public key on request; it is always available when either is */ + if (pubenc != NULL + && !ossl_param_build_set_octet_string( + tmpl, params, OSSL_PKEY_PARAM_PUB_KEY, pubenc, v->pubkey_bytes)) + goto err; + + params = OSSL_PARAM_BLD_to_param(tmpl); + if (params == NULL) + goto err; + + ret = param_cb(params, cbarg); + OSSL_PARAM_free(params); + +err: + OSSL_PARAM_BLD_free(tmpl); + OPENSSL_secure_clear_free(seedenc, seedlen); + OPENSSL_secure_clear_free(prvenc, prvlen); + OPENSSL_free(pubenc); + return ret; +} + +static const OSSL_PARAM *ml_kem_imexport_types(int selection) +{ + static const OSSL_PARAM key_types[] = { + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ML_KEM_SEED, NULL, 0), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0), + OSSL_PARAM_END + }; + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) + return key_types; + return NULL; +} + +static int check_seed(const uint8_t *seed, const uint8_t *prvenc, + ML_KEM_KEY *key) +{ + size_t zlen = ML_KEM_RANDOM_BYTES; + + if (memcmp(seed + ML_KEM_SEED_BYTES - zlen, + prvenc + key->vinfo->prvkey_bytes - zlen, zlen) == 0) + return 1; + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, + "private %s key implicit rejection secret does" + " not match seed", key->vinfo->algorithm_name); + return 0; +} + +static int check_prvenc(const uint8_t *prvenc, ML_KEM_KEY *key) +{ + size_t len = key->vinfo->prvkey_bytes; + uint8_t *buf = OPENSSL_malloc(len); + int ret = 0; + + if (buf != NULL + && ossl_ml_kem_encode_private_key(buf, len, key)) + ret = memcmp(buf, prvenc, len) == 0; + OPENSSL_clear_free(buf, len); + if (ret) + return 1; + + if (buf != NULL) + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, + "explicit %s private key does not match seed", + key->vinfo->algorithm_name); + ossl_ml_kem_key_reset(key); + return 0; +} + +static int ml_kem_key_fromdata(ML_KEM_KEY *key, + const OSSL_PARAM params[], + int include_private) +{ + const OSSL_PARAM *p = NULL; + const void *pubenc = NULL, *prvenc = NULL, *seedenc = NULL; + size_t publen = 0, prvlen = 0, seedlen = 0, puboff; + const ML_KEM_VINFO *v; + + /* Invalid attempt to mutate a key, what is the right error to report? */ + if (key == NULL || ossl_ml_kem_have_pubkey(key)) + return 0; + v = ossl_ml_kem_key_vinfo(key); + + /* + * When a private key is provided, without a seed, any public key also + * provided will be ignored (apart from length), just as with the seed. + */ + if (include_private) { + /* + * When a seed is provided, the private and public keys may be ignored, + * after validating just their lengths. Comparing encodings or hashes + * when applicable is possible, but not currently implemented. + */ + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ML_KEM_SEED); + if (p != NULL + && OSSL_PARAM_get_octet_string_ptr(p, &seedenc, &seedlen) != 1) + return 0; + if (seedlen != 0 && seedlen != ML_KEM_SEED_BYTES) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SEED_LENGTH); + return 0; + } + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY); + if (p != NULL + && OSSL_PARAM_get_octet_string_ptr(p, &prvenc, &prvlen) != 1) + return 0; + if (prvlen != 0 && prvlen != v->prvkey_bytes) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + } + + /* Used only when no seed or private key is provided. */ + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY); + if (p != NULL + && OSSL_PARAM_get_octet_string_ptr(p, &pubenc, &publen) != 1) + return 0; + if (publen != 0 && publen != v->pubkey_bytes) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + + /* The caller MUST specify at least one of seed, private or public keys. */ + if (seedlen == 0 && publen == 0 && prvlen == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + return 0; + } + + /* Check any explicit public key against embedded value in private key */ + if (publen > 0 && prvlen > 0) { + /* point to the ek offset in dk = DKpke||ek||H(ek)||z */ + puboff = prvlen - ML_KEM_RANDOM_BYTES - ML_KEM_PKHASH_BYTES - publen; + if (memcmp(pubenc, (unsigned char *)prvenc + puboff, publen) != 0) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, + "explicit %s public key does not match private", + v->algorithm_name); + return 0; + } + } + + if (seedlen != 0 + && (prvlen == 0 || (key->prov_flags & ML_KEM_KEY_PREFER_SEED))) { + if (prvlen != 0 && !check_seed(seedenc, prvenc, key)) + return 0; + if (!ossl_ml_kem_set_seed(seedenc, seedlen, key) + || !ossl_ml_kem_genkey(NULL, 0, key)) + return 0; + return prvlen == 0 || check_prvenc(prvenc, key); + } else if (prvlen != 0) { + return ossl_ml_kem_parse_private_key(prvenc, prvlen, key); + } + return ossl_ml_kem_parse_public_key(pubenc, publen, key); +} + +static int ml_kem_import(void *vkey, int selection, const OSSL_PARAM params[]) +{ + ML_KEM_KEY *key = vkey; + int include_private; + int res; + + if (!ossl_prov_is_running() || key == NULL) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) + return 0; + + include_private = selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0; + res = ml_kem_key_fromdata(key, params, include_private); + if (res > 0 && include_private + && !ml_kem_pairwise_test(key, key->prov_flags)) { +#ifdef FIPS_MODULE + ossl_set_error_state(OSSL_SELF_TEST_TYPE_PCT_IMPORT); +#endif + ossl_ml_kem_key_reset(key); + res = 0; + } + return res; +} + +static const OSSL_PARAM *ml_kem_gettable_params(void *provctx) +{ + static const OSSL_PARAM arr[] = { + OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL), + /* Exported for import */ + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ML_KEM_SEED, NULL, 0), + /* Exported to EVP_PKEY_get_raw_private_key() */ + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0), + /* Exported to EVP_PKEY_get_raw_public_key() */ + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0), + /* Needed by EVP_PKEY_get1_encoded_public_key() */ + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0), + OSSL_PARAM_END + }; + + return arr; +} + +#ifndef FIPS_MODULE +static void *ml_kem_load(const void *reference, size_t reference_sz) +{ + ML_KEM_KEY *key = NULL; + uint8_t *encoded_dk = NULL; + uint8_t seed[ML_KEM_SEED_BYTES]; + + if (ossl_prov_is_running() && reference_sz == sizeof(key)) { + /* The contents of the reference is the address to our object */ + key = *(ML_KEM_KEY **)reference; + encoded_dk = key->encoded_dk; + key->encoded_dk = NULL; + /* We grabbed, so we detach it */ + *(ML_KEM_KEY **)reference = NULL; + if (encoded_dk != NULL + && ossl_ml_kem_encode_seed(seed, sizeof(seed), key) + && !check_seed(seed, encoded_dk, key)) + goto err; + /* Generate the key now, if it holds only a stashed seed. */ + if (ossl_ml_kem_have_seed(key) + && (encoded_dk == NULL + || (key->prov_flags & ML_KEM_KEY_PREFER_SEED))) { + if (!ossl_ml_kem_genkey(NULL, 0, key) + || (encoded_dk != NULL && !check_prvenc(encoded_dk, key))) + goto err; + } else if (encoded_dk != NULL) { + if (!ossl_ml_kem_parse_private_key(encoded_dk, + key->vinfo->prvkey_bytes, key)) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, + "error parsing %s private key", + key->vinfo->algorithm_name); + goto err; + } + if (!ml_kem_pairwise_test(key, key->prov_flags)) + goto err; + } + OPENSSL_free(encoded_dk); + return key; + } + + err: + OPENSSL_free(encoded_dk); + ossl_ml_kem_key_free(key); + return NULL; +} +#endif + +/* + * It is assumed the key is guaranteed non-NULL here, and is from this provider + */ +static int ml_kem_get_params(void *vkey, OSSL_PARAM params[]) +{ + ML_KEM_KEY *key = vkey; + const ML_KEM_VINFO *v = ossl_ml_kem_key_vinfo(key); + OSSL_PARAM *p; + const char *pubparams[] = { + OSSL_PKEY_PARAM_PUB_KEY, + OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY + }; + int i; + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS); + if (p != NULL) + if (!OSSL_PARAM_set_int(p, v->bits)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS); + if (p != NULL) + if (!OSSL_PARAM_set_int(p, v->secbits)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE); + if (p != NULL) + if (!OSSL_PARAM_set_int(p, v->ctext_bytes)) + return 0; + + if (ossl_ml_kem_have_pubkey(key)) { + uint8_t *pubenc = NULL; + + for (i = 0; i < 2; ++i) { + p = OSSL_PARAM_locate(params, pubparams[i]); + if (p == NULL) + continue; + if (p->data_type != OSSL_PARAM_OCTET_STRING) + return 0; + p->return_size = v->pubkey_bytes; + if (p->data == NULL) + continue; + if (p->data_size < p->return_size) + return 0; + if (pubenc != NULL) { + memcpy(p->data, pubenc, p->return_size); + continue; + } + if (!ossl_ml_kem_encode_public_key(p->data, p->return_size, key)) + return 0; + pubenc = p->data; + } + } + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PRIV_KEY); + if (p != NULL && ossl_ml_kem_have_prvkey(key)) { + if (p->data_type != OSSL_PARAM_OCTET_STRING) + return 0; + p->return_size = v->prvkey_bytes; + if (p->data != NULL) { + if (p->data_size < p->return_size) + return 0; + if (!ossl_ml_kem_encode_private_key(p->data, p->return_size, key)) + return 0; + } + } + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_ML_KEM_SEED); + if (p != NULL && ossl_ml_kem_have_seed(key)) { + if (p->data_type != OSSL_PARAM_OCTET_STRING) + return 0; + p->return_size = ML_KEM_SEED_BYTES; + if (p->data != NULL) { + if (p->data_size < p->return_size) + return 0; + if (!ossl_ml_kem_encode_seed(p->data, p->return_size, key)) + return 0; + } + } + + return 1; +} + +static const OSSL_PARAM *ml_kem_settable_params(void *provctx) +{ + static const OSSL_PARAM arr[] = { + /* Used in TLS via EVP_PKEY_set1_encoded_public_key(). */ + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0), + OSSL_PARAM_END + }; + + return arr; +} + +static int ml_kem_set_params(void *vkey, const OSSL_PARAM params[]) +{ + ML_KEM_KEY *key = vkey; + const OSSL_PARAM *p; + const void *pubenc = NULL; + size_t publen = 0; + + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY); + if (p != NULL + && (OSSL_PARAM_get_octet_string_ptr(p, &pubenc, &publen) != 1 + || publen != key->vinfo->pubkey_bytes)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY); + return 0; + } + + if (publen == 0) + return 1; + + /* Key mutation is reportedly generally not allowed */ + if (ossl_ml_kem_have_pubkey(key)) { + ERR_raise_data(ERR_LIB_PROV, + PROV_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE, + "ML-KEM keys cannot be mutated"); + return 0; + } + + return ossl_ml_kem_parse_public_key(pubenc, publen, key); +} + +static int ml_kem_gen_set_params(void *vgctx, const OSSL_PARAM params[]) +{ + PROV_ML_KEM_GEN_CTX *gctx = vgctx; + const OSSL_PARAM *p; + + if (gctx == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PROPERTIES); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_UTF8_STRING) + return 0; + OPENSSL_free(gctx->propq); + if ((gctx->propq = OPENSSL_strdup(p->data)) == NULL) + return 0; + } + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ML_KEM_SEED); + if (p != NULL) { + size_t len = ML_KEM_SEED_BYTES; + + gctx->seed = gctx->seedbuf; + if (OSSL_PARAM_get_octet_string(p, (void **)&gctx->seed, len, &len) + && len == ML_KEM_SEED_BYTES) + return 1; + + /* Possibly, but less likely wrong data type */ + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SEED_LENGTH); + gctx->seed = NULL; + return 0; + } + + return 1; +} + +static void *ml_kem_gen_init(void *provctx, int selection, + const OSSL_PARAM params[], int evp_type) +{ + PROV_ML_KEM_GEN_CTX *gctx = NULL; + + /* + * We can only generate private keys, check that the selection is + * appropriate. + */ + if (!ossl_prov_is_running() + || (selection & minimal_selection) == 0 + || (gctx = OPENSSL_zalloc(sizeof(*gctx))) == NULL) + return NULL; + + gctx->selection = selection; + gctx->evp_type = evp_type; + gctx->provctx = provctx; + if (ml_kem_gen_set_params(gctx, params)) + return gctx; + + ml_kem_gen_cleanup(gctx); + return NULL; +} + +static const OSSL_PARAM *ml_kem_gen_settable_params(ossl_unused void *vgctx, + ossl_unused void *provctx) +{ + static OSSL_PARAM settable[] = { + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ML_KEM_SEED, NULL, 0), + OSSL_PARAM_END + }; + return settable; +} + +static void *ml_kem_gen(void *vgctx, OSSL_CALLBACK *osslcb, void *cbarg) +{ + PROV_ML_KEM_GEN_CTX *gctx = vgctx; + ML_KEM_KEY *key; + uint8_t *nopub = NULL; + uint8_t *seed; + int genok = 0; + + if (gctx == NULL + || (gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == + OSSL_KEYMGMT_SELECT_PUBLIC_KEY) + return NULL; + seed = gctx->seed; + key = ossl_prov_ml_kem_new(gctx->provctx, gctx->propq, gctx->evp_type); + if (key == NULL) + return NULL; + + if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) + return key; + + if (seed != NULL && !ossl_ml_kem_set_seed(seed, ML_KEM_SEED_BYTES, key)) + return NULL; + genok = ossl_ml_kem_genkey(nopub, 0, key); + + /* Erase the single-use seed */ + if (seed != NULL) + OPENSSL_cleanse(seed, ML_KEM_SEED_BYTES); + gctx->seed = NULL; + + if (genok) { +#ifdef FIPS_MODULE + if (!ml_kem_pairwise_test(key, ML_KEM_KEY_FIXED_PCT)) { + ossl_set_error_state(OSSL_SELF_TEST_TYPE_PCT); + ossl_ml_kem_key_free(key); + return NULL; + } +#endif /* FIPS_MODULE */ + return key; + } + + ossl_ml_kem_key_free(key); + return NULL; +} + +static void ml_kem_gen_cleanup(void *vgctx) +{ + PROV_ML_KEM_GEN_CTX *gctx = vgctx; + + if (gctx == NULL) + return; + + if (gctx->seed != NULL) + OPENSSL_cleanse(gctx->seed, ML_KEM_RANDOM_BYTES); + OPENSSL_free(gctx->propq); + OPENSSL_free(gctx); +} + +static void *ml_kem_dup(const void *vkey, int selection) +{ + const ML_KEM_KEY *key = vkey; + + if (!ossl_prov_is_running()) + return NULL; + + return ossl_ml_kem_key_dup(key, selection); +} + +#ifndef FIPS_MODULE +# define DISPATCH_LOAD_FN \ + { OSSL_FUNC_KEYMGMT_LOAD, (OSSL_FUNC) ml_kem_load }, +#else +# define DISPATCH_LOAD_FN /* Non-FIPS only */ +#endif + +#define DECLARE_VARIANT(bits) \ + static void *ml_kem_##bits##_new(void *provctx) \ + { \ + return ossl_prov_ml_kem_new(provctx, NULL, EVP_PKEY_ML_KEM_##bits); \ + } \ + static void *ml_kem_##bits##_gen_init(void *provctx, int selection, \ + const OSSL_PARAM params[]) \ + { \ + return ml_kem_gen_init(provctx, selection, params, \ + EVP_PKEY_ML_KEM_##bits); \ + } \ + const OSSL_DISPATCH ossl_ml_kem_##bits##_keymgmt_functions[] = { \ + { OSSL_FUNC_KEYMGMT_NEW, (OSSL_FUNC) ml_kem_##bits##_new }, \ + { OSSL_FUNC_KEYMGMT_FREE, (OSSL_FUNC) ossl_ml_kem_key_free }, \ + { OSSL_FUNC_KEYMGMT_GET_PARAMS, (OSSL_FUNC) ml_kem_get_params }, \ + { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (OSSL_FUNC) ml_kem_gettable_params }, \ + { OSSL_FUNC_KEYMGMT_SET_PARAMS, (OSSL_FUNC) ml_kem_set_params }, \ + { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (OSSL_FUNC) ml_kem_settable_params }, \ + { OSSL_FUNC_KEYMGMT_HAS, (OSSL_FUNC) ml_kem_has }, \ + { OSSL_FUNC_KEYMGMT_MATCH, (OSSL_FUNC) ml_kem_match }, \ + { OSSL_FUNC_KEYMGMT_VALIDATE, (OSSL_FUNC) ml_kem_validate }, \ + { OSSL_FUNC_KEYMGMT_GEN_INIT, (OSSL_FUNC) ml_kem_##bits##_gen_init }, \ + { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (OSSL_FUNC) ml_kem_gen_set_params }, \ + { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, (OSSL_FUNC) ml_kem_gen_settable_params }, \ + { OSSL_FUNC_KEYMGMT_GEN, (OSSL_FUNC) ml_kem_gen }, \ + { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (OSSL_FUNC) ml_kem_gen_cleanup }, \ + DISPATCH_LOAD_FN \ + { OSSL_FUNC_KEYMGMT_DUP, (OSSL_FUNC) ml_kem_dup }, \ + { OSSL_FUNC_KEYMGMT_IMPORT, (OSSL_FUNC) ml_kem_import }, \ + { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (OSSL_FUNC) ml_kem_imexport_types }, \ + { OSSL_FUNC_KEYMGMT_EXPORT, (OSSL_FUNC) ml_kem_export }, \ + { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (OSSL_FUNC) ml_kem_imexport_types }, \ + OSSL_DISPATCH_END \ + } +DECLARE_VARIANT(512); +DECLARE_VARIANT(768); +DECLARE_VARIANT(1024); diff --git a/crypto/openssl/providers/implementations/keymgmt/mlx_kmgmt.c b/crypto/openssl/providers/implementations/keymgmt/mlx_kmgmt.c new file mode 100644 index 000000000000..bea878327609 --- /dev/null +++ b/crypto/openssl/providers/implementations/keymgmt/mlx_kmgmt.c @@ -0,0 +1,820 @@ +/* + * Copyright 2024-2025 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 + */ + +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/err.h> +#include <openssl/param_build.h> +#include <openssl/params.h> +#include <openssl/proverr.h> +#include <openssl/rand.h> +#include <openssl/self_test.h> +#include "internal/nelem.h" +#include "internal/param_build_set.h" +#include "prov/implementations.h" +#include "prov/mlx_kem.h" +#include "prov/provider_ctx.h" +#include "prov/providercommon.h" +#include "prov/securitycheck.h" + +static OSSL_FUNC_keymgmt_gen_fn mlx_kem_gen; +static OSSL_FUNC_keymgmt_gen_cleanup_fn mlx_kem_gen_cleanup; +static OSSL_FUNC_keymgmt_gen_set_params_fn mlx_kem_gen_set_params; +static OSSL_FUNC_keymgmt_gen_settable_params_fn mlx_kem_gen_settable_params; +static OSSL_FUNC_keymgmt_get_params_fn mlx_kem_get_params; +static OSSL_FUNC_keymgmt_gettable_params_fn mlx_kem_gettable_params; +static OSSL_FUNC_keymgmt_set_params_fn mlx_kem_set_params; +static OSSL_FUNC_keymgmt_settable_params_fn mlx_kem_settable_params; +static OSSL_FUNC_keymgmt_has_fn mlx_kem_has; +static OSSL_FUNC_keymgmt_match_fn mlx_kem_match; +static OSSL_FUNC_keymgmt_import_fn mlx_kem_import; +static OSSL_FUNC_keymgmt_export_fn mlx_kem_export; +static OSSL_FUNC_keymgmt_import_types_fn mlx_kem_imexport_types; +static OSSL_FUNC_keymgmt_export_types_fn mlx_kem_imexport_types; +static OSSL_FUNC_keymgmt_dup_fn mlx_kem_dup; + +static const int minimal_selection = OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS + | OSSL_KEYMGMT_SELECT_PRIVATE_KEY; + +/* Must match DECLARE_DISPATCH invocations at the end of the file */ +static const ECDH_VINFO hybrid_vtable[] = { + { "EC", "P-256", 65, 32, 32, 1, EVP_PKEY_ML_KEM_768 }, + { "EC", "P-384", 97, 48, 48, 1, EVP_PKEY_ML_KEM_1024 }, +#if !defined(OPENSSL_NO_ECX) + { "X25519", NULL, 32, 32, 32, 0, EVP_PKEY_ML_KEM_768 }, + { "X448", NULL, 56, 56, 56, 0, EVP_PKEY_ML_KEM_1024 }, +#endif +}; + +typedef struct mlx_kem_gen_ctx_st { + OSSL_LIB_CTX *libctx; + char *propq; + int selection; + unsigned int evp_type; +} PROV_ML_KEM_GEN_CTX; + +static void mlx_kem_key_free(void *vkey) +{ + MLX_KEY *key = vkey; + + if (key == NULL) + return; + OPENSSL_free(key->propq); + EVP_PKEY_free(key->mkey); + EVP_PKEY_free(key->xkey); + OPENSSL_free(key); +} + +/* Takes ownership of propq */ +static void * +mlx_kem_key_new(unsigned int v, OSSL_LIB_CTX *libctx, char *propq) +{ + MLX_KEY *key = NULL; + unsigned int ml_kem_variant; + + if (!ossl_prov_is_running() + || v >= OSSL_NELEM(hybrid_vtable) + || (key = OPENSSL_malloc(sizeof(*key))) == NULL) + goto err; + + ml_kem_variant = hybrid_vtable[v].ml_kem_variant; + key->libctx = libctx; + key->minfo = ossl_ml_kem_get_vinfo(ml_kem_variant); + key->xinfo = &hybrid_vtable[v]; + key->xkey = key->mkey = NULL; + key->state = MLX_HAVE_NOKEYS; + key->propq = propq; + return key; + + err: + OPENSSL_free(propq); + return NULL; +} + + +static int mlx_kem_has(const void *vkey, int selection) +{ + const MLX_KEY *key = vkey; + + /* A NULL key MUST fail to have anything */ + if (!ossl_prov_is_running() || key == NULL) + return 0; + + switch (selection & OSSL_KEYMGMT_SELECT_KEYPAIR) { + case 0: + return 1; + case OSSL_KEYMGMT_SELECT_PUBLIC_KEY: + return mlx_kem_have_pubkey(key); + default: + return mlx_kem_have_prvkey(key); + } +} + +static int mlx_kem_match(const void *vkey1, const void *vkey2, int selection) +{ + const MLX_KEY *key1 = vkey1; + const MLX_KEY *key2 = vkey2; + int have_pub1 = mlx_kem_have_pubkey(key1); + int have_pub2 = mlx_kem_have_pubkey(key2); + + if (!ossl_prov_is_running()) + return 0; + + /* Compare domain parameters */ + if (key1->xinfo != key2->xinfo) + return 0; + + if (!(selection & OSSL_KEYMGMT_SELECT_KEYPAIR)) + return 1; + + if (have_pub1 ^ have_pub2) + return 0; + + /* As in other providers, equal when both have no key material. */ + if (!have_pub1) + return 1; + + return EVP_PKEY_eq(key1->mkey, key2->mkey) + && EVP_PKEY_eq(key1->xkey, key2->xkey); +} + +typedef struct export_cb_arg_st { + const char *algorithm_name; + uint8_t *pubenc; + uint8_t *prvenc; + int pubcount; + int prvcount; + size_t puboff; + size_t prvoff; + size_t publen; + size_t prvlen; +} EXPORT_CB_ARG; + +/* Copy any exported key material into its storage slot */ +static int export_sub_cb(const OSSL_PARAM *params, void *varg) +{ + EXPORT_CB_ARG *sub_arg = varg; + const OSSL_PARAM *p = NULL; + size_t len; + + /* + * The caller will decide whether anything essential is missing, but, if + * some key material was returned, it should have the right (parameter) + * data type and length. + */ + if (ossl_param_is_empty(params)) + return 1; + if (sub_arg->pubenc != NULL + && (p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY)) != NULL) { + void *pub = sub_arg->pubenc + sub_arg->puboff; + + if (OSSL_PARAM_get_octet_string(p, &pub, sub_arg->publen, &len) != 1) + return 0; + if (len != sub_arg->publen) { + ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR, + "Unexpected %s public key length %lu != %lu", + sub_arg->algorithm_name, (unsigned long) len, + sub_arg->publen); + return 0; + } + ++sub_arg->pubcount; + } + if (sub_arg->prvenc != NULL + && (p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY)) != NULL) { + void *prv = sub_arg->prvenc + sub_arg->prvoff; + + if (OSSL_PARAM_get_octet_string(p, &prv, sub_arg->prvlen, &len) != 1) + return 0; + if (len != sub_arg->prvlen) { + ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR, + "Unexpected %s private key length %lu != %lu", + sub_arg->algorithm_name, (unsigned long) len, + (unsigned long) sub_arg->publen); + return 0; + } + ++sub_arg->prvcount; + } + return 1; +} + +static int +export_sub(EXPORT_CB_ARG *sub_arg, int selection, MLX_KEY *key) +{ + int slot; + + /* + * The caller is responsible for initialising only the pubenc and prvenc + * pointer fields, the rest are set here or in the callback. + */ + sub_arg->pubcount = 0; + sub_arg->prvcount = 0; + + for (slot = 0; slot < 2; ++slot) { + int ml_kem_slot = key->xinfo->ml_kem_slot; + EVP_PKEY *pkey; + + /* Export the parts of each component into its storage slot */ + if (slot == ml_kem_slot) { + pkey = key->mkey; + sub_arg->algorithm_name = key->minfo->algorithm_name; + sub_arg->puboff = slot * key->xinfo->pubkey_bytes; + sub_arg->prvoff = slot * key->xinfo->prvkey_bytes; + sub_arg->publen = key->minfo->pubkey_bytes; + sub_arg->prvlen = key->minfo->prvkey_bytes; + } else { + pkey = key->xkey; + sub_arg->algorithm_name = key->xinfo->algorithm_name; + sub_arg->puboff = (1 - ml_kem_slot) * key->minfo->pubkey_bytes; + sub_arg->prvoff = (1 - ml_kem_slot) * key->minfo->prvkey_bytes; + sub_arg->publen = key->xinfo->pubkey_bytes; + sub_arg->prvlen = key->xinfo->prvkey_bytes; + } + if (!EVP_PKEY_export(pkey, selection, export_sub_cb, (void *)sub_arg)) + return 0; + } + return 1; +} + +static int mlx_kem_export(void *vkey, int selection, OSSL_CALLBACK *param_cb, + void *cbarg) +{ + MLX_KEY *key = vkey; + OSSL_PARAM_BLD *tmpl = NULL; + OSSL_PARAM *params = NULL; + size_t publen; + size_t prvlen; + int ret = 0; + EXPORT_CB_ARG sub_arg; + + if (!ossl_prov_is_running() || key == NULL) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) + return 0; + + /* Fail when no key material has yet been provided */ + if (!mlx_kem_have_pubkey(key)) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + return 0; + } + publen = key->minfo->pubkey_bytes + key->xinfo->pubkey_bytes; + prvlen = key->minfo->prvkey_bytes + key->xinfo->prvkey_bytes; + memset(&sub_arg, 0, sizeof(sub_arg)); + + if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) { + sub_arg.pubenc = OPENSSL_malloc(publen); + if (sub_arg.pubenc == NULL) + goto err; + } + + if (mlx_kem_have_prvkey(key) + && (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) { + /* + * Allocated on the secure heap if configured, this is detected in + * ossl_param_build_set_octet_string(), which will then also use the + * secure heap. + */ + sub_arg.prvenc = OPENSSL_secure_zalloc(prvlen); + if (sub_arg.prvenc == NULL) + goto err; + } + + tmpl = OSSL_PARAM_BLD_new(); + if (tmpl == NULL) + goto err; + + /* Extract sub-component key material */ + if (!export_sub(&sub_arg, selection, key)) + goto err; + + if (sub_arg.pubenc != NULL && sub_arg.pubcount == 2 + && !ossl_param_build_set_octet_string( + tmpl, NULL, OSSL_PKEY_PARAM_PUB_KEY, sub_arg.pubenc, publen)) + goto err; + + if (sub_arg.prvenc != NULL && sub_arg.prvcount == 2 + && !ossl_param_build_set_octet_string( + tmpl, NULL, OSSL_PKEY_PARAM_PRIV_KEY, sub_arg.prvenc, prvlen)) + goto err; + + params = OSSL_PARAM_BLD_to_param(tmpl); + if (params == NULL) + goto err; + + ret = param_cb(params, cbarg); + OSSL_PARAM_free(params); + + err: + OSSL_PARAM_BLD_free(tmpl); + OPENSSL_secure_clear_free(sub_arg.prvenc, prvlen); + OPENSSL_free(sub_arg.pubenc); + return ret; +} + +static const OSSL_PARAM *mlx_kem_imexport_types(int selection) +{ + static const OSSL_PARAM key_types[] = { + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0), + OSSL_PARAM_END + }; + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) + return key_types; + return NULL; +} + +static int +load_slot(OSSL_LIB_CTX *libctx, const char *propq, const char *pname, + int selection, MLX_KEY *key, int slot, const uint8_t *in, + int mbytes, int xbytes) +{ + EVP_PKEY_CTX *ctx; + EVP_PKEY **ppkey; + OSSL_PARAM parr[] = { OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END }; + const char *alg; + char *group = NULL; + size_t off, len; + void *val; + int ml_kem_slot = key->xinfo->ml_kem_slot; + int ret = 0; + + if (slot == ml_kem_slot) { + alg = key->minfo->algorithm_name; + ppkey = &key->mkey; + off = slot * xbytes; + len = mbytes; + } else { + alg = key->xinfo->algorithm_name; + group = (char *) key->xinfo->group_name; + ppkey = &key->xkey; + off = (1 - ml_kem_slot) * mbytes; + len = xbytes; + } + val = (void *)(in + off); + + if ((ctx = EVP_PKEY_CTX_new_from_name(libctx, alg, propq)) == NULL + || EVP_PKEY_fromdata_init(ctx) <= 0) + goto err; + parr[0] = OSSL_PARAM_construct_octet_string(pname, val, len); + if (group != NULL) + parr[1] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, + group, 0); + if (EVP_PKEY_fromdata(ctx, ppkey, selection, parr) > 0) + ret = 1; + + err: + EVP_PKEY_CTX_free(ctx); + return ret; +} + +static int +load_keys(MLX_KEY *key, + const uint8_t *pubenc, size_t publen, + const uint8_t *prvenc, size_t prvlen) +{ + int slot; + + for (slot = 0; slot < 2; ++slot) { + if (prvlen) { + /* Ignore public keys when private provided */ + if (!load_slot(key->libctx, key->propq, OSSL_PKEY_PARAM_PRIV_KEY, + minimal_selection, key, slot, prvenc, + key->minfo->prvkey_bytes, key->xinfo->prvkey_bytes)) + goto err; + } else if (publen) { + /* Absent private key data, import public keys */ + if (!load_slot(key->libctx, key->propq, OSSL_PKEY_PARAM_PUB_KEY, + minimal_selection, key, slot, pubenc, + key->minfo->pubkey_bytes, key->xinfo->pubkey_bytes)) + goto err; + } + } + key->state = prvlen ? MLX_HAVE_PRVKEY : MLX_HAVE_PUBKEY; + return 1; + + err: + EVP_PKEY_free(key->mkey); + EVP_PKEY_free(key->xkey); + key->xkey = key->mkey = NULL; + key->state = MLX_HAVE_NOKEYS; + return 0; +} + +static int mlx_kem_key_fromdata(MLX_KEY *key, + const OSSL_PARAM params[], + int include_private) +{ + const OSSL_PARAM *param_prv_key = NULL, *param_pub_key; + const void *pubenc = NULL, *prvenc = NULL; + size_t pubkey_bytes, prvkey_bytes; + size_t publen = 0, prvlen = 0; + + /* Invalid attempt to mutate a key, what is the right error to report? */ + if (key == NULL || mlx_kem_have_pubkey(key)) + return 0; + pubkey_bytes = key->minfo->pubkey_bytes + key->xinfo->pubkey_bytes; + prvkey_bytes = key->minfo->prvkey_bytes + key->xinfo->prvkey_bytes; + + /* What does the caller want to set? */ + param_pub_key = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY); + if (param_pub_key != NULL && + OSSL_PARAM_get_octet_string_ptr(param_pub_key, &pubenc, &publen) != 1) + return 0; + if (include_private) + param_prv_key = OSSL_PARAM_locate_const(params, + OSSL_PKEY_PARAM_PRIV_KEY); + if (param_prv_key != NULL && + OSSL_PARAM_get_octet_string_ptr(param_prv_key, &prvenc, &prvlen) != 1) + return 0; + + /* The caller MUST specify at least one of the public or private keys. */ + if (publen == 0 && prvlen == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY); + return 0; + } + + /* + * When a pubkey is provided, its length MUST be correct, if a private key + * is also provided, the public key will be otherwise ignored. We could + * look for a matching encoded block, but unclear this is useful. + */ + if (publen != 0 && publen != pubkey_bytes) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + if (prvlen != 0 && prvlen != prvkey_bytes) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + + return load_keys(key, pubenc, publen, prvenc, prvlen); +} + +static int mlx_kem_import(void *vkey, int selection, const OSSL_PARAM params[]) +{ + MLX_KEY *key = vkey; + int include_private; + + if (!ossl_prov_is_running() || key == NULL) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) + return 0; + + include_private = selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0; + return mlx_kem_key_fromdata(key, params, include_private); +} + +static const OSSL_PARAM *mlx_kem_gettable_params(void *provctx) +{ + static const OSSL_PARAM arr[] = { + OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0), + OSSL_PARAM_END + }; + + return arr; +} + +/* + * It is assumed the key is guaranteed non-NULL here, and is from this provider + */ +static int mlx_kem_get_params(void *vkey, OSSL_PARAM params[]) +{ + MLX_KEY *key = vkey; + OSSL_PARAM *p, *pub, *prv = NULL; + EXPORT_CB_ARG sub_arg; + int selection; + size_t publen = key->minfo->pubkey_bytes + key->xinfo->pubkey_bytes; + size_t prvlen = key->minfo->prvkey_bytes + key->xinfo->prvkey_bytes; + + /* The reported "bit" count is those of the ML-KEM key */ + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS); + if (p != NULL) + if (!OSSL_PARAM_set_int(p, key->minfo->bits)) + return 0; + + /* The reported security bits are those of the ML-KEM key */ + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS); + if (p != NULL) + if (!OSSL_PARAM_set_int(p, key->minfo->secbits)) + return 0; + + /* The ciphertext sizes are additive */ + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE); + if (p != NULL) + if (!OSSL_PARAM_set_int(p, key->minfo->ctext_bytes + key->xinfo->pubkey_bytes)) + return 0; + + if (!mlx_kem_have_pubkey(key)) + return 1; + + memset(&sub_arg, 0, sizeof(sub_arg)); + pub = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY); + if (pub != NULL) { + if (pub->data_type != OSSL_PARAM_OCTET_STRING) + return 0; + pub->return_size = publen; + if (pub->data == NULL) { + pub = NULL; + } else if (pub->data_size < publen) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL, + "public key output buffer too short: %lu < %lu", + (unsigned long) pub->data_size, + (unsigned long) publen); + return 0; + } else { + sub_arg.pubenc = pub->data; + } + } + if (mlx_kem_have_prvkey(key)) { + prv = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PRIV_KEY); + if (prv != NULL) { + if (prv->data_type != OSSL_PARAM_OCTET_STRING) + return 0; + prv->return_size = prvlen; + if (prv->data == NULL) { + prv = NULL; + } else if (prv->data_size < prvlen) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL, + "private key output buffer too short: %lu < %lu", + (unsigned long) prv->data_size, + (unsigned long) prvlen); + return 0; + } else { + sub_arg.prvenc = prv->data; + } + } + } + if (pub == NULL && prv == NULL) + return 1; + + selection = prv == NULL ? 0 : OSSL_KEYMGMT_SELECT_PRIVATE_KEY; + selection |= pub == NULL ? 0 : OSSL_KEYMGMT_SELECT_PUBLIC_KEY; + if (key->xinfo->group_name != NULL) + selection |= OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS; + + /* Extract sub-component key material */ + if (!export_sub(&sub_arg, selection, key)) + return 0; + + if ((pub != NULL && sub_arg.pubcount != 2) + || (prv != NULL && sub_arg.prvcount != 2)) + return 0; + + return 1; +} + +static const OSSL_PARAM *mlx_kem_settable_params(void *provctx) +{ + static const OSSL_PARAM arr[] = { + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0), + OSSL_PARAM_END + }; + + return arr; +} + +static int mlx_kem_set_params(void *vkey, const OSSL_PARAM params[]) +{ + MLX_KEY *key = vkey; + const OSSL_PARAM *p; + const void *pubenc = NULL; + size_t publen = 0; + + if (ossl_param_is_empty(params)) + return 1; + + /* Only one settable parameter is supported */ + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY); + if (p == NULL) + return 1; + + /* Key mutation is reportedly generally not allowed */ + if (mlx_kem_have_pubkey(key)) { + ERR_raise_data(ERR_LIB_PROV, + PROV_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE, + "keys cannot be mutated"); + return 0; + } + /* An unlikely failure mode is the parameter having some unexpected type */ + if (!OSSL_PARAM_get_octet_string_ptr(p, &pubenc, &publen)) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PROPERTIES); + if (p != NULL) { + OPENSSL_free(key->propq); + key->propq = NULL; + if (!OSSL_PARAM_get_utf8_string(p, &key->propq, 0)) + return 0; + } + + if (publen != key->minfo->pubkey_bytes + key->xinfo->pubkey_bytes) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY); + return 0; + } + + return load_keys(key, pubenc, publen, NULL, 0); +} + +static int mlx_kem_gen_set_params(void *vgctx, const OSSL_PARAM params[]) +{ + PROV_ML_KEM_GEN_CTX *gctx = vgctx; + const OSSL_PARAM *p; + + if (gctx == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PROPERTIES); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_UTF8_STRING) + return 0; + OPENSSL_free(gctx->propq); + if ((gctx->propq = OPENSSL_strdup(p->data)) == NULL) + return 0; + } + return 1; +} + +static void *mlx_kem_gen_init(int evp_type, OSSL_LIB_CTX *libctx, + int selection, const OSSL_PARAM params[]) +{ + PROV_ML_KEM_GEN_CTX *gctx = NULL; + + /* + * We can only generate private keys, check that the selection is + * appropriate. + */ + if (!ossl_prov_is_running() + || (selection & minimal_selection) == 0 + || (gctx = OPENSSL_zalloc(sizeof(*gctx))) == NULL) + return NULL; + + gctx->evp_type = evp_type; + gctx->libctx = libctx; + gctx->selection = selection; + if (mlx_kem_gen_set_params(gctx, params)) + return gctx; + + mlx_kem_gen_cleanup(gctx); + return NULL; +} + +static const OSSL_PARAM *mlx_kem_gen_settable_params(ossl_unused void *vgctx, + ossl_unused void *provctx) +{ + static OSSL_PARAM settable[] = { + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_END + }; + + return settable; +} + +static void *mlx_kem_gen(void *vgctx, OSSL_CALLBACK *osslcb, void *cbarg) +{ + PROV_ML_KEM_GEN_CTX *gctx = vgctx; + MLX_KEY *key; + char *propq; + + if (gctx == NULL + || (gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == + OSSL_KEYMGMT_SELECT_PUBLIC_KEY) + return NULL; + + /* Lose ownership of propq */ + propq = gctx->propq; + gctx->propq = NULL; + if ((key = mlx_kem_key_new(gctx->evp_type, gctx->libctx, propq)) == NULL) + return NULL; + + if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) + return key; + + /* For now, using the same "propq" for all components */ + key->mkey = EVP_PKEY_Q_keygen(key->libctx, key->propq, + key->minfo->algorithm_name); + key->xkey = EVP_PKEY_Q_keygen(key->libctx, key->propq, + key->xinfo->algorithm_name, + key->xinfo->group_name); + if (key->mkey != NULL && key->xkey != NULL) { + key->state = MLX_HAVE_PRVKEY; + return key; + } + + mlx_kem_key_free(key); + return NULL; +} + +static void mlx_kem_gen_cleanup(void *vgctx) +{ + PROV_ML_KEM_GEN_CTX *gctx = vgctx; + + if (gctx == NULL) + return; + OPENSSL_free(gctx->propq); + OPENSSL_free(gctx); +} + +static void *mlx_kem_dup(const void *vkey, int selection) +{ + const MLX_KEY *key = vkey; + MLX_KEY *ret; + + if (!ossl_prov_is_running() + || (ret = OPENSSL_memdup(key, sizeof(*ret))) == NULL) + return NULL; + + if (ret->propq != NULL + && (ret->propq = OPENSSL_strdup(ret->propq)) == NULL) { + OPENSSL_free(ret); + return NULL; + } + + /* Absent key material, nothing left to do */ + if (ret->mkey == NULL) { + if (ret->xkey == NULL) + return ret; + /* Fail if the source key is an inconsistent state */ + OPENSSL_free(ret); + return NULL; + } + + switch (selection & OSSL_KEYMGMT_SELECT_KEYPAIR) { + case 0: + ret->xkey = ret->mkey = NULL; + return ret; + case OSSL_KEYMGMT_SELECT_KEYPAIR: + ret->mkey = EVP_PKEY_dup(key->mkey); + ret->xkey = EVP_PKEY_dup(key->xkey); + if (ret->xkey != NULL && ret->mkey != NULL) + return ret; + break; + default: + ERR_raise_data(ERR_LIB_PROV, PROV_R_UNSUPPORTED_SELECTION, + "duplication of partial key material not supported"); + break; + } + + mlx_kem_key_free(ret); + return NULL; +} + +#define DECLARE_DISPATCH(name, variant) \ + static OSSL_FUNC_keymgmt_new_fn mlx_##name##_kem_new; \ + static void *mlx_##name##_kem_new(void *provctx) \ + { \ + OSSL_LIB_CTX *libctx; \ + \ + libctx = provctx == NULL ? NULL : PROV_LIBCTX_OF(provctx); \ + return mlx_kem_key_new(variant, libctx, NULL); \ + } \ + static OSSL_FUNC_keymgmt_gen_init_fn mlx_##name##_kem_gen_init; \ + static void *mlx_##name##_kem_gen_init(void *provctx, int selection, \ + const OSSL_PARAM params[]) \ + { \ + OSSL_LIB_CTX *libctx; \ + \ + libctx = provctx == NULL ? NULL : PROV_LIBCTX_OF(provctx); \ + return mlx_kem_gen_init(variant, libctx, selection, params); \ + } \ + const OSSL_DISPATCH ossl_mlx_##name##_kem_kmgmt_functions[] = { \ + { OSSL_FUNC_KEYMGMT_NEW, (OSSL_FUNC) mlx_##name##_kem_new }, \ + { OSSL_FUNC_KEYMGMT_FREE, (OSSL_FUNC) mlx_kem_key_free }, \ + { OSSL_FUNC_KEYMGMT_GET_PARAMS, (OSSL_FUNC) mlx_kem_get_params }, \ + { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (OSSL_FUNC) mlx_kem_gettable_params }, \ + { OSSL_FUNC_KEYMGMT_SET_PARAMS, (OSSL_FUNC) mlx_kem_set_params }, \ + { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (OSSL_FUNC) mlx_kem_settable_params }, \ + { OSSL_FUNC_KEYMGMT_HAS, (OSSL_FUNC) mlx_kem_has }, \ + { OSSL_FUNC_KEYMGMT_MATCH, (OSSL_FUNC) mlx_kem_match }, \ + { OSSL_FUNC_KEYMGMT_GEN_INIT, (OSSL_FUNC) mlx_##name##_kem_gen_init }, \ + { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (OSSL_FUNC) mlx_kem_gen_set_params }, \ + { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, (OSSL_FUNC) mlx_kem_gen_settable_params }, \ + { OSSL_FUNC_KEYMGMT_GEN, (OSSL_FUNC) mlx_kem_gen }, \ + { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (OSSL_FUNC) mlx_kem_gen_cleanup }, \ + { OSSL_FUNC_KEYMGMT_DUP, (OSSL_FUNC) mlx_kem_dup }, \ + { OSSL_FUNC_KEYMGMT_IMPORT, (OSSL_FUNC) mlx_kem_import }, \ + { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (OSSL_FUNC) mlx_kem_imexport_types }, \ + { OSSL_FUNC_KEYMGMT_EXPORT, (OSSL_FUNC) mlx_kem_export }, \ + { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (OSSL_FUNC) mlx_kem_imexport_types }, \ + OSSL_DISPATCH_END \ + } +/* See |hybrid_vtable| above */ +DECLARE_DISPATCH(p256, 0); +DECLARE_DISPATCH(p384, 1); +#if !defined(OPENSSL_NO_ECX) +DECLARE_DISPATCH(x25519, 2); +DECLARE_DISPATCH(x448, 3); +#endif diff --git a/crypto/openssl/providers/implementations/keymgmt/rsa_kmgmt.c b/crypto/openssl/providers/implementations/keymgmt/rsa_kmgmt.c new file mode 100644 index 000000000000..cd74275d604b --- /dev/null +++ b/crypto/openssl/providers/implementations/keymgmt/rsa_kmgmt.c @@ -0,0 +1,744 @@ +/* + * Copyright 2019-2025 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 + */ + +/* + * RSA low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/bn.h> +#include <openssl/err.h> +#include <openssl/rsa.h> +#include <openssl/evp.h> +#include <openssl/proverr.h> +#include "prov/implementations.h" +#include "prov/providercommon.h" +#include "prov/provider_ctx.h" +#include "crypto/rsa.h" +#include "crypto/cryptlib.h" +#include "internal/fips.h" +#include "internal/param_build_set.h" + +static OSSL_FUNC_keymgmt_new_fn rsa_newdata; +static OSSL_FUNC_keymgmt_new_fn rsapss_newdata; +static OSSL_FUNC_keymgmt_gen_init_fn rsa_gen_init; +static OSSL_FUNC_keymgmt_gen_init_fn rsapss_gen_init; +static OSSL_FUNC_keymgmt_gen_set_params_fn rsa_gen_set_params; +static OSSL_FUNC_keymgmt_gen_settable_params_fn rsa_gen_settable_params; +static OSSL_FUNC_keymgmt_gen_settable_params_fn rsapss_gen_settable_params; +static OSSL_FUNC_keymgmt_gen_fn rsa_gen; +static OSSL_FUNC_keymgmt_gen_cleanup_fn rsa_gen_cleanup; +static OSSL_FUNC_keymgmt_load_fn rsa_load; +static OSSL_FUNC_keymgmt_load_fn rsapss_load; +static OSSL_FUNC_keymgmt_free_fn rsa_freedata; +static OSSL_FUNC_keymgmt_get_params_fn rsa_get_params; +static OSSL_FUNC_keymgmt_gettable_params_fn rsa_gettable_params; +static OSSL_FUNC_keymgmt_has_fn rsa_has; +static OSSL_FUNC_keymgmt_match_fn rsa_match; +static OSSL_FUNC_keymgmt_validate_fn rsa_validate; +static OSSL_FUNC_keymgmt_import_fn rsa_import; +static OSSL_FUNC_keymgmt_import_types_fn rsa_import_types; +static OSSL_FUNC_keymgmt_export_fn rsa_export; +static OSSL_FUNC_keymgmt_export_types_fn rsa_export_types; +static OSSL_FUNC_keymgmt_query_operation_name_fn rsa_query_operation_name; +static OSSL_FUNC_keymgmt_dup_fn rsa_dup; + +#define RSA_DEFAULT_MD "SHA256" +#define RSA_POSSIBLE_SELECTIONS \ + (OSSL_KEYMGMT_SELECT_KEYPAIR | OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) + +DEFINE_STACK_OF(BIGNUM) +DEFINE_SPECIAL_STACK_OF_CONST(BIGNUM_const, BIGNUM) + +static int pss_params_fromdata(RSA_PSS_PARAMS_30 *pss_params, int *defaults_set, + const OSSL_PARAM params[], int rsa_type, + OSSL_LIB_CTX *libctx) +{ + if (!ossl_rsa_pss_params_30_fromdata(pss_params, defaults_set, + params, libctx)) + return 0; + + /* If not a PSS type RSA, sending us PSS parameters is wrong */ + if (rsa_type != RSA_FLAG_TYPE_RSASSAPSS + && !ossl_rsa_pss_params_30_is_unrestricted(pss_params)) + return 0; + + return 1; +} + +static void *rsa_newdata(void *provctx) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx); + RSA *rsa; + + if (!ossl_prov_is_running()) + return NULL; + + rsa = ossl_rsa_new_with_ctx(libctx); + if (rsa != NULL) { + RSA_clear_flags(rsa, RSA_FLAG_TYPE_MASK); + RSA_set_flags(rsa, RSA_FLAG_TYPE_RSA); + } + return rsa; +} + +static void *rsapss_newdata(void *provctx) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx); + RSA *rsa; + + if (!ossl_prov_is_running()) + return NULL; + + rsa = ossl_rsa_new_with_ctx(libctx); + if (rsa != NULL) { + RSA_clear_flags(rsa, RSA_FLAG_TYPE_MASK); + RSA_set_flags(rsa, RSA_FLAG_TYPE_RSASSAPSS); + } + return rsa; +} + +static void rsa_freedata(void *keydata) +{ + RSA_free(keydata); +} + +static int rsa_has(const void *keydata, int selection) +{ + const RSA *rsa = keydata; + int ok = 1; + + if (rsa == NULL || !ossl_prov_is_running()) + return 0; + if ((selection & RSA_POSSIBLE_SELECTIONS) == 0) + return 1; /* the selection is not missing */ + + /* OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS are always available even if empty */ + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) + ok = ok && (RSA_get0_n(rsa) != NULL); + if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) + ok = ok && (RSA_get0_e(rsa) != NULL); + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + ok = ok && (RSA_get0_d(rsa) != NULL); + return ok; +} + +static int rsa_match(const void *keydata1, const void *keydata2, int selection) +{ + const RSA *rsa1 = keydata1; + const RSA *rsa2 = keydata2; + int ok = 1; + + if (!ossl_prov_is_running()) + return 0; + + /* There is always an |e| */ + ok = ok && BN_cmp(RSA_get0_e(rsa1), RSA_get0_e(rsa2)) == 0; + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) { + int key_checked = 0; + + if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) { + const BIGNUM *pa = RSA_get0_n(rsa1); + const BIGNUM *pb = RSA_get0_n(rsa2); + + if (pa != NULL && pb != NULL) { + ok = ok && BN_cmp(pa, pb) == 0; + key_checked = 1; + } + } + if (!key_checked + && (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) { + const BIGNUM *pa = RSA_get0_d(rsa1); + const BIGNUM *pb = RSA_get0_d(rsa2); + + if (pa != NULL && pb != NULL) { + ok = ok && BN_cmp(pa, pb) == 0; + key_checked = 1; + } + } + ok = ok && key_checked; + } + return ok; +} + +static int rsa_import(void *keydata, int selection, const OSSL_PARAM params[]) +{ + RSA *rsa = keydata; + int rsa_type; + int ok = 1; + int pss_defaults_set = 0; + + if (!ossl_prov_is_running() || rsa == NULL) + return 0; + + if ((selection & RSA_POSSIBLE_SELECTIONS) == 0) + return 0; + + rsa_type = RSA_test_flags(rsa, RSA_FLAG_TYPE_MASK); + + if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0) + ok = ok && pss_params_fromdata(ossl_rsa_get0_pss_params_30(rsa), + &pss_defaults_set, + params, rsa_type, + ossl_rsa_get0_libctx(rsa)); + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) { + int include_private = + selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0; + + ok = ok && ossl_rsa_fromdata(rsa, params, include_private); + } + + return ok; +} + +static int rsa_export(void *keydata, int selection, + OSSL_CALLBACK *param_callback, void *cbarg) +{ + RSA *rsa = keydata; + const RSA_PSS_PARAMS_30 *pss_params = ossl_rsa_get0_pss_params_30(rsa); + OSSL_PARAM_BLD *tmpl; + OSSL_PARAM *params = NULL; + int ok = 1; + + if (!ossl_prov_is_running() || rsa == NULL) + return 0; + + if ((selection & RSA_POSSIBLE_SELECTIONS) == 0) + return 0; + + tmpl = OSSL_PARAM_BLD_new(); + if (tmpl == NULL) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0) + ok = ok && (ossl_rsa_pss_params_30_is_unrestricted(pss_params) + || ossl_rsa_pss_params_30_todata(pss_params, tmpl, NULL)); + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) { + int include_private = + selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0; + + ok = ok && ossl_rsa_todata(rsa, tmpl, NULL, include_private); + } + + if (!ok || (params = OSSL_PARAM_BLD_to_param(tmpl)) == NULL) { + ok = 0; + goto err; + } + + ok = param_callback(params, cbarg); + OSSL_PARAM_free(params); +err: + OSSL_PARAM_BLD_free(tmpl); + return ok; +} + +#ifdef FIPS_MODULE +/* In fips mode there are no multi-primes. */ +# define RSA_KEY_MP_TYPES() \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR1, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR2, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT1, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT2, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_COEFFICIENT1, NULL, 0), +#else +/* + * We allow up to 10 prime factors (starting with p, q). + * NOTE: there is only 9 OSSL_PKEY_PARAM_RSA_COEFFICIENT + */ +# define RSA_KEY_MP_TYPES() \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR1, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR2, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR3, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR4, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR5, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR6, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR7, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR8, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR9, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR10, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT1, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT2, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT3, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT4, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT5, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT6, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT7, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT8, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT9, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT10, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_COEFFICIENT1, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_COEFFICIENT2, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_COEFFICIENT3, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_COEFFICIENT4, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_COEFFICIENT5, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_COEFFICIENT6, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_COEFFICIENT7, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_COEFFICIENT8, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_COEFFICIENT9, NULL, 0), +#endif + +#define RSA_KEY_TYPES() \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_N, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_E, NULL, 0), \ +OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_D, NULL, 0), \ +RSA_KEY_MP_TYPES() + +/* + * This provider can export everything in an RSA key, so we use the exact + * same type description for export as for import. Other providers might + * choose to import full keys, but only export the public parts, and will + * therefore have the importkey_types and importkey_types functions return + * different arrays. + */ +static const OSSL_PARAM rsa_key_types[] = { + RSA_KEY_TYPES() + OSSL_PARAM_END +}; +/* + * We lied about the amount of factors, exponents and coefficients, the + * export and import functions can really deal with an infinite amount + * of these numbers. However, RSA keys with too many primes are futile, + * so we at least pretend to have some limits. + */ + +static const OSSL_PARAM *rsa_imexport_types(int selection) +{ + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) + return rsa_key_types; + return NULL; +} + +static const OSSL_PARAM *rsa_import_types(int selection) +{ + return rsa_imexport_types(selection); +} + +static const OSSL_PARAM *rsa_export_types(int selection) +{ + return rsa_imexport_types(selection); +} + +static int rsa_get_params(void *key, OSSL_PARAM params[]) +{ + RSA *rsa = key; + const RSA_PSS_PARAMS_30 *pss_params = ossl_rsa_get0_pss_params_30(rsa); + int rsa_type = RSA_test_flags(rsa, RSA_FLAG_TYPE_MASK); + OSSL_PARAM *p; + int empty = RSA_get0_n(rsa) == NULL; + + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS)) != NULL + && (empty || !OSSL_PARAM_set_int(p, RSA_bits(rsa)))) + return 0; + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS)) != NULL + && (empty || !OSSL_PARAM_set_int(p, RSA_security_bits(rsa)))) + return 0; + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE)) != NULL + && (empty || !OSSL_PARAM_set_int(p, RSA_size(rsa)))) + return 0; + + /* + * For restricted RSA-PSS keys, we ignore the default digest request. + * With RSA-OAEP keys, this may need to be amended. + */ + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_DEFAULT_DIGEST)) != NULL + && (rsa_type != RSA_FLAG_TYPE_RSASSAPSS + || ossl_rsa_pss_params_30_is_unrestricted(pss_params))) { + if (!OSSL_PARAM_set_utf8_string(p, RSA_DEFAULT_MD)) + return 0; + } + + /* + * For non-RSA-PSS keys, we ignore the mandatory digest request. + * With RSA-OAEP keys, this may need to be amended. + */ + if ((p = OSSL_PARAM_locate(params, + OSSL_PKEY_PARAM_MANDATORY_DIGEST)) != NULL + && rsa_type == RSA_FLAG_TYPE_RSASSAPSS + && !ossl_rsa_pss_params_30_is_unrestricted(pss_params)) { + const char *mdname = + ossl_rsa_oaeppss_nid2name(ossl_rsa_pss_params_30_hashalg(pss_params)); + + if (mdname == NULL || !OSSL_PARAM_set_utf8_string(p, mdname)) + return 0; + } + return (rsa_type != RSA_FLAG_TYPE_RSASSAPSS + || ossl_rsa_pss_params_30_todata(pss_params, NULL, params)) + && ossl_rsa_todata(rsa, NULL, params, 1); +} + +static const OSSL_PARAM rsa_params[] = { + OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_DEFAULT_DIGEST, NULL, 0), + RSA_KEY_TYPES() + OSSL_PARAM_END +}; + +static const OSSL_PARAM *rsa_gettable_params(void *provctx) +{ + return rsa_params; +} + +static int rsa_validate(const void *keydata, int selection, int checktype) +{ + const RSA *rsa = keydata; + int ok = 1; + + if (!ossl_prov_is_running()) + return 0; + + if ((selection & RSA_POSSIBLE_SELECTIONS) == 0) + return 1; /* nothing to validate */ + + /* If the whole key is selected, we do a pairwise validation */ + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) + == OSSL_KEYMGMT_SELECT_KEYPAIR) { + ok = ok && ossl_rsa_validate_pairwise(rsa); + } else { + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + ok = ok && ossl_rsa_validate_private(rsa); + if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) + ok = ok && ossl_rsa_validate_public(rsa); + } + return ok; +} + +struct rsa_gen_ctx { + OSSL_LIB_CTX *libctx; + const char *propq; + + int rsa_type; + + size_t nbits; + BIGNUM *pub_exp; + size_t primes; + + /* For PSS */ + RSA_PSS_PARAMS_30 pss_params; + int pss_defaults_set; + + /* For generation callback */ + OSSL_CALLBACK *cb; + void *cbarg; + +#if defined(FIPS_MODULE) && !defined(OPENSSL_NO_ACVP_TESTS) + /* ACVP test parameters */ + OSSL_PARAM *acvp_test_params; +#endif +}; + +static int rsa_gencb(int p, int n, BN_GENCB *cb) +{ + struct rsa_gen_ctx *gctx = BN_GENCB_get_arg(cb); + OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END }; + + params[0] = OSSL_PARAM_construct_int(OSSL_GEN_PARAM_POTENTIAL, &p); + params[1] = OSSL_PARAM_construct_int(OSSL_GEN_PARAM_ITERATION, &n); + return gctx->cb(params, gctx->cbarg); +} + +static void *gen_init(void *provctx, int selection, int rsa_type, + const OSSL_PARAM params[]) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx); + struct rsa_gen_ctx *gctx = NULL; + + if (!ossl_prov_is_running()) + return NULL; + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) + return NULL; + + if ((gctx = OPENSSL_zalloc(sizeof(*gctx))) != NULL) { + gctx->libctx = libctx; + if ((gctx->pub_exp = BN_new()) == NULL + || !BN_set_word(gctx->pub_exp, RSA_F4)) { + goto err; + } + gctx->nbits = 2048; + gctx->primes = RSA_DEFAULT_PRIME_NUM; + gctx->rsa_type = rsa_type; + } else { + goto err; + } + + if (!rsa_gen_set_params(gctx, params)) + goto err; + return gctx; + +err: + if (gctx != NULL) + BN_free(gctx->pub_exp); + OPENSSL_free(gctx); + return NULL; +} + +static void *rsa_gen_init(void *provctx, int selection, + const OSSL_PARAM params[]) +{ + return gen_init(provctx, selection, RSA_FLAG_TYPE_RSA, params); +} + +static void *rsapss_gen_init(void *provctx, int selection, + const OSSL_PARAM params[]) +{ + return gen_init(provctx, selection, RSA_FLAG_TYPE_RSASSAPSS, params); +} + +/* + * This function is common for all RSA sub-types, to detect possible + * misuse, such as PSS parameters being passed when a plain RSA key + * is generated. + */ +static int rsa_gen_set_params(void *genctx, const OSSL_PARAM params[]) +{ + struct rsa_gen_ctx *gctx = genctx; + const OSSL_PARAM *p; + + if (ossl_param_is_empty(params)) + return 1; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_BITS)) != NULL) { + if (!OSSL_PARAM_get_size_t(p, &gctx->nbits)) + return 0; + if (gctx->nbits < RSA_MIN_MODULUS_BITS) { + ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SIZE_TOO_SMALL); + return 0; + } + } + if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_PRIMES)) != NULL + && !OSSL_PARAM_get_size_t(p, &gctx->primes)) + return 0; + if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_E)) != NULL + && !OSSL_PARAM_get_BN(p, &gctx->pub_exp)) + return 0; + /* Only attempt to get PSS parameters when generating an RSA-PSS key */ + if (gctx->rsa_type == RSA_FLAG_TYPE_RSASSAPSS + && !pss_params_fromdata(&gctx->pss_params, &gctx->pss_defaults_set, params, + gctx->rsa_type, gctx->libctx)) + return 0; +#if defined(FIPS_MODULE) && !defined(OPENSSL_NO_ACVP_TESTS) + /* Any ACVP test related parameters are copied into a params[] */ + if (!ossl_rsa_acvp_test_gen_params_new(&gctx->acvp_test_params, params)) + return 0; +#endif + return 1; +} + +#define rsa_gen_basic \ + OSSL_PARAM_size_t(OSSL_PKEY_PARAM_RSA_BITS, NULL), \ + OSSL_PARAM_size_t(OSSL_PKEY_PARAM_RSA_PRIMES, NULL), \ + OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_E, NULL, 0) + +/* + * The following must be kept in sync with ossl_rsa_pss_params_30_fromdata() + * in crypto/rsa/rsa_backend.c + */ +#define rsa_gen_pss \ + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_DIGEST, NULL, 0), \ + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_DIGEST_PROPS, NULL, 0), \ + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_MASKGENFUNC, NULL, 0), \ + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_MGF1_DIGEST, NULL, 0), \ + OSSL_PARAM_int(OSSL_PKEY_PARAM_RSA_PSS_SALTLEN, NULL) + +static const OSSL_PARAM *rsa_gen_settable_params(ossl_unused void *genctx, + ossl_unused void *provctx) +{ + static OSSL_PARAM settable[] = { + rsa_gen_basic, + OSSL_PARAM_END + }; + + return settable; +} + +static const OSSL_PARAM *rsapss_gen_settable_params(ossl_unused void *genctx, + ossl_unused void *provctx) +{ + static OSSL_PARAM settable[] = { + rsa_gen_basic, + rsa_gen_pss, + OSSL_PARAM_END + }; + + return settable; +} + +static void *rsa_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg) +{ + struct rsa_gen_ctx *gctx = genctx; + RSA *rsa = NULL, *rsa_tmp = NULL; + BN_GENCB *gencb = NULL; + + if (!ossl_prov_is_running() || gctx == NULL) + return NULL; + + switch (gctx->rsa_type) { + case RSA_FLAG_TYPE_RSA: + /* For plain RSA keys, PSS parameters must not be set */ + if (!ossl_rsa_pss_params_30_is_unrestricted(&gctx->pss_params)) + goto err; + break; + case RSA_FLAG_TYPE_RSASSAPSS: + /* + * For plain RSA-PSS keys, PSS parameters may be set but don't have + * to, so not check. + */ + break; + default: + /* Unsupported RSA key sub-type... */ + return NULL; + } + + if ((rsa_tmp = ossl_rsa_new_with_ctx(gctx->libctx)) == NULL) + return NULL; + + gctx->cb = osslcb; + gctx->cbarg = cbarg; + gencb = BN_GENCB_new(); + if (gencb != NULL) + BN_GENCB_set(gencb, rsa_gencb, genctx); + +#if defined(FIPS_MODULE) && !defined(OPENSSL_NO_ACVP_TESTS) + if (gctx->acvp_test_params != NULL) { + if (!ossl_rsa_acvp_test_set_params(rsa_tmp, gctx->acvp_test_params)) + goto err; + } +#endif + + if (!RSA_generate_multi_prime_key(rsa_tmp, + (int)gctx->nbits, (int)gctx->primes, + gctx->pub_exp, gencb)) + goto err; + + if (!ossl_rsa_pss_params_30_copy(ossl_rsa_get0_pss_params_30(rsa_tmp), + &gctx->pss_params)) + goto err; + + RSA_clear_flags(rsa_tmp, RSA_FLAG_TYPE_MASK); + RSA_set_flags(rsa_tmp, gctx->rsa_type); + + rsa = rsa_tmp; + rsa_tmp = NULL; + err: + BN_GENCB_free(gencb); + RSA_free(rsa_tmp); + return rsa; +} + +static void rsa_gen_cleanup(void *genctx) +{ + struct rsa_gen_ctx *gctx = genctx; + + if (gctx == NULL) + return; +#if defined(FIPS_MODULE) && !defined(OPENSSL_NO_ACVP_TESTS) + ossl_rsa_acvp_test_gen_params_free(gctx->acvp_test_params); + gctx->acvp_test_params = NULL; +#endif + BN_clear_free(gctx->pub_exp); + OPENSSL_free(gctx); +} + +static void *common_load(const void *reference, size_t reference_sz, + int expected_rsa_type) +{ + RSA *rsa = NULL; + + if (ossl_prov_is_running() && reference_sz == sizeof(rsa)) { + /* The contents of the reference is the address to our object */ + rsa = *(RSA **)reference; + + if (RSA_test_flags(rsa, RSA_FLAG_TYPE_MASK) != expected_rsa_type) + return NULL; + + /* We grabbed, so we detach it */ + *(RSA **)reference = NULL; + return rsa; + } + return NULL; +} + +static void *rsa_load(const void *reference, size_t reference_sz) +{ + return common_load(reference, reference_sz, RSA_FLAG_TYPE_RSA); +} + +static void *rsapss_load(const void *reference, size_t reference_sz) +{ + return common_load(reference, reference_sz, RSA_FLAG_TYPE_RSASSAPSS); +} + +static void *rsa_dup(const void *keydata_from, int selection) +{ + if (ossl_prov_is_running() + /* do not allow creating empty keys by duplication */ + && (selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) + return ossl_rsa_dup(keydata_from, selection); + return NULL; +} + +/* For any RSA key, we use the "RSA" algorithms regardless of sub-type. */ +static const char *rsa_query_operation_name(int operation_id) +{ + return "RSA"; +} + +const OSSL_DISPATCH ossl_rsa_keymgmt_functions[] = { + { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))rsa_newdata }, + { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))rsa_gen_init }, + { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, + (void (*)(void))rsa_gen_set_params }, + { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, + (void (*)(void))rsa_gen_settable_params }, + { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))rsa_gen }, + { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))rsa_gen_cleanup }, + { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))rsa_load }, + { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))rsa_freedata }, + { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))rsa_get_params }, + { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))rsa_gettable_params }, + { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))rsa_has }, + { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))rsa_match }, + { OSSL_FUNC_KEYMGMT_VALIDATE, (void (*)(void))rsa_validate }, + { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))rsa_import }, + { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))rsa_import_types }, + { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))rsa_export }, + { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))rsa_export_types }, + { OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))rsa_dup }, + OSSL_DISPATCH_END +}; + +const OSSL_DISPATCH ossl_rsapss_keymgmt_functions[] = { + { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))rsapss_newdata }, + { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))rsapss_gen_init }, + { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))rsa_gen_set_params }, + { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, + (void (*)(void))rsapss_gen_settable_params }, + { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))rsa_gen }, + { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))rsa_gen_cleanup }, + { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))rsapss_load }, + { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))rsa_freedata }, + { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))rsa_get_params }, + { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))rsa_gettable_params }, + { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))rsa_has }, + { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))rsa_match }, + { OSSL_FUNC_KEYMGMT_VALIDATE, (void (*)(void))rsa_validate }, + { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))rsa_import }, + { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))rsa_import_types }, + { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))rsa_export }, + { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))rsa_export_types }, + { OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME, + (void (*)(void))rsa_query_operation_name }, + { OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))rsa_dup }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/keymgmt/slh_dsa_kmgmt.c b/crypto/openssl/providers/implementations/keymgmt/slh_dsa_kmgmt.c new file mode 100644 index 000000000000..721617229467 --- /dev/null +++ b/crypto/openssl/providers/implementations/keymgmt/slh_dsa_kmgmt.c @@ -0,0 +1,480 @@ +/* + * Copyright 2024-2025 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 + */ + +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/param_build.h> +#include <openssl/self_test.h> +#include <openssl/proverr.h> +#include "crypto/slh_dsa.h" +#include "internal/fips.h" +#include "internal/param_build_set.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" +#include "prov/provider_ctx.h" + +#ifdef FIPS_MODULE +static int slh_dsa_fips140_pairwise_test(const SLH_DSA_KEY *key, + SLH_DSA_HASH_CTX *ctx); +#endif /* FIPS_MODULE */ + +static OSSL_FUNC_keymgmt_free_fn slh_dsa_free_key; +static OSSL_FUNC_keymgmt_has_fn slh_dsa_has; +static OSSL_FUNC_keymgmt_match_fn slh_dsa_match; +static OSSL_FUNC_keymgmt_import_fn slh_dsa_import; +static OSSL_FUNC_keymgmt_export_fn slh_dsa_export; +static OSSL_FUNC_keymgmt_import_types_fn slh_dsa_imexport_types; +static OSSL_FUNC_keymgmt_export_types_fn slh_dsa_imexport_types; +static OSSL_FUNC_keymgmt_load_fn slh_dsa_load; +static OSSL_FUNC_keymgmt_get_params_fn slh_dsa_get_params; +static OSSL_FUNC_keymgmt_gettable_params_fn slh_dsa_gettable_params; +static OSSL_FUNC_keymgmt_validate_fn slh_dsa_validate; +static OSSL_FUNC_keymgmt_gen_init_fn slh_dsa_gen_init; +static OSSL_FUNC_keymgmt_gen_cleanup_fn slh_dsa_gen_cleanup; +static OSSL_FUNC_keymgmt_gen_set_params_fn slh_dsa_gen_set_params; +static OSSL_FUNC_keymgmt_gen_settable_params_fn slh_dsa_gen_settable_params; +static OSSL_FUNC_keymgmt_dup_fn slh_dsa_dup_key; + +#define SLH_DSA_POSSIBLE_SELECTIONS (OSSL_KEYMGMT_SELECT_KEYPAIR) + +struct slh_dsa_gen_ctx { + SLH_DSA_HASH_CTX *ctx; + OSSL_LIB_CTX *libctx; + char *propq; + uint8_t entropy[SLH_DSA_MAX_N * 3]; + size_t entropy_len; +}; + +static void *slh_dsa_new_key(void *provctx, const char *alg) +{ + if (!ossl_prov_is_running()) + return 0; + + return ossl_slh_dsa_key_new(PROV_LIBCTX_OF(provctx), NULL, alg); +} + +static void slh_dsa_free_key(void *keydata) +{ + ossl_slh_dsa_key_free((SLH_DSA_KEY *)keydata); +} + +static void *slh_dsa_dup_key(const void *keydata_from, int selection) +{ + if (ossl_prov_is_running()) + return ossl_slh_dsa_key_dup(keydata_from, selection); + return NULL; +} + +static int slh_dsa_has(const void *keydata, int selection) +{ + const SLH_DSA_KEY *key = keydata; + + if (!ossl_prov_is_running() || key == NULL) + return 0; + if ((selection & SLH_DSA_POSSIBLE_SELECTIONS) == 0) + return 1; /* the selection is not missing */ + + return ossl_slh_dsa_key_has(key, selection); +} + +static int slh_dsa_match(const void *keydata1, const void *keydata2, int selection) +{ + const SLH_DSA_KEY *key1 = keydata1; + const SLH_DSA_KEY *key2 = keydata2; + + if (!ossl_prov_is_running()) + return 0; + if (key1 == NULL || key2 == NULL) + return 0; + return ossl_slh_dsa_key_equal(key1, key2, selection); +} + +static int slh_dsa_validate(const void *key_data, int selection, int check_type) +{ + const SLH_DSA_KEY *key = key_data; + + if (!slh_dsa_has(key, selection)) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == OSSL_KEYMGMT_SELECT_KEYPAIR) + return ossl_slh_dsa_key_pairwise_check(key); + return 1; +} + +static int slh_dsa_import(void *keydata, int selection, const OSSL_PARAM params[]) +{ + SLH_DSA_KEY *key = keydata; + int include_priv; + + if (!ossl_prov_is_running() || key == NULL) + return 0; + + if ((selection & SLH_DSA_POSSIBLE_SELECTIONS) == 0) + return 0; + + include_priv = ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0); + return ossl_slh_dsa_key_fromdata(key, params, include_priv); +} + +#define SLH_DSA_IMEXPORTABLE_PARAMETERS \ + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0), \ + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0) + +static const OSSL_PARAM slh_dsa_key_types[] = { + SLH_DSA_IMEXPORTABLE_PARAMETERS, + OSSL_PARAM_END +}; +static const OSSL_PARAM *slh_dsa_imexport_types(int selection) +{ + if ((selection & SLH_DSA_POSSIBLE_SELECTIONS) == 0) + return NULL; + return slh_dsa_key_types; +} + +static const OSSL_PARAM slh_dsa_params[] = { + OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_MANDATORY_DIGEST, NULL, 0), + SLH_DSA_IMEXPORTABLE_PARAMETERS, + OSSL_PARAM_END +}; +static const OSSL_PARAM *slh_dsa_gettable_params(void *provctx) +{ + return slh_dsa_params; +} + +static int key_to_params(SLH_DSA_KEY *key, OSSL_PARAM_BLD *tmpl, + int selection) +{ + /* Error if there is no key or public key */ + if (key == NULL || ossl_slh_dsa_key_get_pub(key) == NULL) + return 0; + + if (((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) + && ossl_slh_dsa_key_get_priv(key) != NULL) + if (ossl_param_build_set_octet_string(tmpl, NULL, + OSSL_PKEY_PARAM_PRIV_KEY, + ossl_slh_dsa_key_get_priv(key), + ossl_slh_dsa_key_get_priv_len(key)) != 1) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) == 0) + return 1; + + return ossl_param_build_set_octet_string(tmpl, NULL, + OSSL_PKEY_PARAM_PUB_KEY, + ossl_slh_dsa_key_get_pub(key), + ossl_slh_dsa_key_get_pub_len(key)); +} + +static int slh_dsa_get_params(void *keydata, OSSL_PARAM params[]) +{ + SLH_DSA_KEY *key = keydata; + OSSL_PARAM *p; + const uint8_t *pub, *priv; + + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS)) != NULL + && !OSSL_PARAM_set_int(p, 8 * ossl_slh_dsa_key_get_pub_len(key))) + return 0; + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS)) != NULL + && !OSSL_PARAM_set_int(p, 8 * ossl_slh_dsa_key_get_n(key))) + return 0; + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE)) != NULL + && !OSSL_PARAM_set_int(p, ossl_slh_dsa_key_get_sig_len(key))) + return 0; + + priv = ossl_slh_dsa_key_get_priv(key); + if (priv != NULL) { + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PRIV_KEY); + /* Note: ossl_slh_dsa_key_get_priv_len() includes the public key */ + if (p != NULL + && !OSSL_PARAM_set_octet_string(p, priv, + ossl_slh_dsa_key_get_priv_len(key))) + return 0; + } + pub = ossl_slh_dsa_key_get_pub(key); + if (pub != NULL) { + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PUB_KEY); + if (p != NULL + && !OSSL_PARAM_set_octet_string(p, pub, + ossl_slh_dsa_key_get_pub_len(key))) + return 0; + } + /* + * This allows apps to use an empty digest, so that the old API + * for digest signing can be used. + */ + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MANDATORY_DIGEST); + if (p != NULL && !OSSL_PARAM_set_utf8_string(p, "")) + return 0; + return 1; +} + +static int slh_dsa_export(void *keydata, int selection, OSSL_CALLBACK *param_cb, + void *cbarg) +{ + SLH_DSA_KEY *key = keydata; + OSSL_PARAM_BLD *tmpl; + OSSL_PARAM *params = NULL; + int ret = 0; + + if (!ossl_prov_is_running() || key == NULL) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) + return 0; + + tmpl = OSSL_PARAM_BLD_new(); + if (tmpl == NULL) + return 0; + + if (!key_to_params(key, tmpl, selection)) + goto err; + + params = OSSL_PARAM_BLD_to_param(tmpl); + if (params == NULL) + goto err; + + ret = param_cb(params, cbarg); + OSSL_PARAM_free(params); +err: + OSSL_PARAM_BLD_free(tmpl); + return ret; +} + +static void *slh_dsa_load(const void *reference, size_t reference_sz) +{ + SLH_DSA_KEY *key = NULL; + + if (ossl_prov_is_running() && reference_sz == sizeof(key)) { + /* The contents of the reference is the address to our object */ + key = *(SLH_DSA_KEY **)reference; + /* We grabbed, so we detach it */ + *(SLH_DSA_KEY **)reference = NULL; + return key; + } + return NULL; +} + +static void *slh_dsa_gen_init(void *provctx, int selection, + const OSSL_PARAM params[]) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx); + struct slh_dsa_gen_ctx *gctx = NULL; + + if (!ossl_prov_is_running()) + return NULL; + + if ((gctx = OPENSSL_zalloc(sizeof(*gctx))) != NULL) { + gctx->libctx = libctx; + if (!slh_dsa_gen_set_params(gctx, params)) { + OPENSSL_free(gctx); + gctx = NULL; + } + } + return gctx; +} + +#ifdef FIPS_MODULE +/* + * Refer to FIPS 140-3 IG 10.3.A Additional Comment 1 + * Perform a pairwise test for SLH_DSA by signing and verifying a signature. + */ +static int slh_dsa_fips140_pairwise_test(const SLH_DSA_KEY *key, + SLH_DSA_HASH_CTX *ctx) +{ + int ret = 0; + OSSL_SELF_TEST *st = NULL; + OSSL_CALLBACK *cb = NULL; + void *cb_arg = NULL; + uint8_t msg[16] = {0}; + size_t msg_len = sizeof(msg); + uint8_t *sig = NULL; + size_t sig_len; + OSSL_LIB_CTX *lib_ctx; + int alloc_ctx = 0; + + /* During self test, it is a waste to do this test */ + if (ossl_fips_self_testing()) + return 1; + + if (ctx == NULL) { + ctx = ossl_slh_dsa_hash_ctx_new(key); + if (ctx == NULL) + return 0; + alloc_ctx = 1; + } + lib_ctx = ossl_slh_dsa_key_get0_libctx(key); + + OSSL_SELF_TEST_get_callback(lib_ctx, &cb, &cb_arg); + st = OSSL_SELF_TEST_new(cb, cb_arg); + if (st == NULL) + goto err; + + OSSL_SELF_TEST_onbegin(st, OSSL_SELF_TEST_TYPE_PCT, + OSSL_SELF_TEST_DESC_PCT_SLH_DSA); + + sig_len = ossl_slh_dsa_key_get_sig_len(key); + sig = OPENSSL_malloc(sig_len); + if (sig == NULL) + goto err; + + if (ossl_slh_dsa_sign(ctx, msg, msg_len, NULL, 0, NULL, 0, + sig, &sig_len, sig_len) != 1) + goto err; + + OSSL_SELF_TEST_oncorrupt_byte(st, sig); + + if (ossl_slh_dsa_verify(ctx, msg, msg_len, NULL, 0, 0, sig, sig_len) != 1) + goto err; + + ret = 1; +err: + if (alloc_ctx) + ossl_slh_dsa_hash_ctx_free(ctx); + OPENSSL_free(sig); + OSSL_SELF_TEST_onend(st, ret); + OSSL_SELF_TEST_free(st); + return ret; +} +#endif /* FIPS_MODULE */ + +static void *slh_dsa_gen(void *genctx, const char *alg) +{ + struct slh_dsa_gen_ctx *gctx = genctx; + SLH_DSA_KEY *key = NULL; + SLH_DSA_HASH_CTX *ctx = NULL; + + if (!ossl_prov_is_running()) + return NULL; + key = ossl_slh_dsa_key_new(gctx->libctx, gctx->propq, alg); + if (key == NULL) + return NULL; + ctx = ossl_slh_dsa_hash_ctx_new(key); + if (ctx == NULL) + goto err; + if (!ossl_slh_dsa_generate_key(ctx, key, gctx->libctx, + gctx->entropy, gctx->entropy_len)) + goto err; +#ifdef FIPS_MODULE + if (!slh_dsa_fips140_pairwise_test(key, ctx)) { + ossl_set_error_state(OSSL_SELF_TEST_TYPE_PCT); + goto err; + } +#endif /* FIPS_MODULE */ + ossl_slh_dsa_hash_ctx_free(ctx); + return key; + err: + ossl_slh_dsa_hash_ctx_free(ctx); + ossl_slh_dsa_key_free(key); + return NULL; +} + +static int slh_dsa_gen_set_params(void *genctx, const OSSL_PARAM params[]) +{ + struct slh_dsa_gen_ctx *gctx = genctx; + const OSSL_PARAM *p; + + if (gctx == NULL) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_SLH_DSA_SEED); + if (p != NULL) { + void *vp = gctx->entropy; + size_t len = sizeof(gctx->entropy); + + if (!OSSL_PARAM_get_octet_string(p, &vp, len, &(gctx->entropy_len))) { + gctx->entropy_len = 0; + return 0; + } + } + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PROPERTIES); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_UTF8_STRING) + return 0; + OPENSSL_free(gctx->propq); + gctx->propq = OPENSSL_strdup(p->data); + if (gctx->propq == NULL) + return 0; + } + return 1; +} + +static const OSSL_PARAM *slh_dsa_gen_settable_params(ossl_unused void *genctx, + ossl_unused void *provctx) +{ + static OSSL_PARAM settable[] = { + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_SLH_DSA_SEED, NULL, 0), + OSSL_PARAM_END + }; + return settable; +} + +static void slh_dsa_gen_cleanup(void *genctx) +{ + struct slh_dsa_gen_ctx *gctx = genctx; + + if (gctx == NULL) + return; + + OPENSSL_cleanse(gctx->entropy, gctx->entropy_len); + OPENSSL_free(gctx->propq); + OPENSSL_free(gctx); +} + +#define MAKE_KEYMGMT_FUNCTIONS(alg, fn) \ + static OSSL_FUNC_keymgmt_new_fn slh_dsa_##fn##_new_key; \ + static OSSL_FUNC_keymgmt_gen_fn slh_dsa_##fn##_gen; \ + static void *slh_dsa_##fn##_new_key(void *provctx) \ + { \ + return slh_dsa_new_key(provctx, alg); \ + } \ + static void *slh_dsa_##fn##_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg)\ + { \ + return slh_dsa_gen(genctx, alg); \ + } \ + const OSSL_DISPATCH ossl_slh_dsa_##fn##_keymgmt_functions[] = { \ + { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))slh_dsa_##fn##_new_key }, \ + { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))slh_dsa_free_key }, \ + { OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))slh_dsa_dup_key }, \ + { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))slh_dsa_has }, \ + { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))slh_dsa_match }, \ + { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))slh_dsa_import }, \ + { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))slh_dsa_imexport_types },\ + { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))slh_dsa_export }, \ + { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))slh_dsa_imexport_types },\ + { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))slh_dsa_load }, \ + { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))slh_dsa_get_params }, \ + { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))slh_dsa_gettable_params },\ + { OSSL_FUNC_KEYMGMT_VALIDATE, (void (*)(void))slh_dsa_validate }, \ + { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))slh_dsa_gen_init }, \ + { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))slh_dsa_##fn##_gen }, \ + { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))slh_dsa_gen_cleanup },\ + { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, \ + (void (*)(void))slh_dsa_gen_set_params }, \ + { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, \ + (void (*)(void))slh_dsa_gen_settable_params }, \ + OSSL_DISPATCH_END \ + } + +MAKE_KEYMGMT_FUNCTIONS("SLH-DSA-SHA2-128s", sha2_128s); +MAKE_KEYMGMT_FUNCTIONS("SLH-DSA-SHA2-128f", sha2_128f); +MAKE_KEYMGMT_FUNCTIONS("SLH-DSA-SHA2-192s", sha2_192s); +MAKE_KEYMGMT_FUNCTIONS("SLH-DSA-SHA2-192f", sha2_192f); +MAKE_KEYMGMT_FUNCTIONS("SLH-DSA-SHA2-256s", sha2_256s); +MAKE_KEYMGMT_FUNCTIONS("SLH-DSA-SHA2-256f", sha2_256f); +MAKE_KEYMGMT_FUNCTIONS("SLH-DSA-SHAKE-128s", shake_128s); +MAKE_KEYMGMT_FUNCTIONS("SLH-DSA-SHAKE-128f", shake_128f); +MAKE_KEYMGMT_FUNCTIONS("SLH-DSA-SHAKE-192s", shake_192s); +MAKE_KEYMGMT_FUNCTIONS("SLH-DSA-SHAKE-192f", shake_192f); +MAKE_KEYMGMT_FUNCTIONS("SLH-DSA-SHAKE-256s", shake_256s); +MAKE_KEYMGMT_FUNCTIONS("SLH-DSA-SHAKE-256f", shake_256f); diff --git a/crypto/openssl/providers/implementations/keymgmt/template_kmgmt.c b/crypto/openssl/providers/implementations/keymgmt/template_kmgmt.c new file mode 100644 index 000000000000..0d62f556ecd5 --- /dev/null +++ b/crypto/openssl/providers/implementations/keymgmt/template_kmgmt.c @@ -0,0 +1,440 @@ +/* + * Copyright 2024-2025 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 + */ + +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/params.h> +#include <openssl/err.h> +#include <openssl/proverr.h> +#include <openssl/evp.h> +#include <openssl/rand.h> +#include <openssl/self_test.h> +#include "internal/param_build_set.h" +#include <openssl/param_build.h> +#include "prov/implementations.h" +#include "prov/providercommon.h" +#include "prov/provider_ctx.h" +#include "prov/securitycheck.h" + +extern const OSSL_DISPATCH ossl_template_keymgmt_functions[]; + +#define BUFSIZE 1000 +#if defined(NDEBUG) || defined(OPENSSL_NO_STDIO) +static void debug_print(char *fmt, ...) +{ +} +#else +static void debug_print(char *fmt, ...) +{ + char out[BUFSIZE]; + va_list argptr; + + va_start(argptr, fmt); + vsnprintf(out, BUFSIZE, fmt, argptr); + va_end(argptr); + if (getenv("TEMPLATEKM")) + fprintf(stderr, "TEMPLATE_KM: %s", out); +} +#endif + +static OSSL_FUNC_keymgmt_new_fn template_new; +static OSSL_FUNC_keymgmt_free_fn template_free; +static OSSL_FUNC_keymgmt_gen_init_fn template_gen_init; +static OSSL_FUNC_keymgmt_gen_fn template_gen; +static OSSL_FUNC_keymgmt_gen_cleanup_fn template_gen_cleanup; +static OSSL_FUNC_keymgmt_gen_set_params_fn template_gen_set_params; +static OSSL_FUNC_keymgmt_gen_settable_params_fn template_gen_settable_params; +static OSSL_FUNC_keymgmt_get_params_fn template_get_params; +static OSSL_FUNC_keymgmt_gettable_params_fn template_gettable_params; +static OSSL_FUNC_keymgmt_set_params_fn template_set_params; +static OSSL_FUNC_keymgmt_settable_params_fn template_settable_params; +static OSSL_FUNC_keymgmt_has_fn template_has; +static OSSL_FUNC_keymgmt_match_fn template_match; +static OSSL_FUNC_keymgmt_import_fn template_import; +static OSSL_FUNC_keymgmt_export_fn template_export; +static OSSL_FUNC_keymgmt_import_types_fn template_imexport_types; +static OSSL_FUNC_keymgmt_export_types_fn template_imexport_types; + +static OSSL_FUNC_keymgmt_dup_fn template_dup; + +struct template_gen_ctx { + void *provctx; + int selection; +}; + +static void *template_new(void *provctx) +{ + void *key = NULL; + + debug_print("new key req\n"); + if (!ossl_prov_is_running()) + return 0; + + /* add logic to create new key */ + + debug_print("new key = %p\n", key); + return key; +} + +static void template_free(void *vkey) +{ + debug_print("free key %p\n", vkey); + if (vkey == NULL) + return; + + /* add logic to free all key components */ + + OPENSSL_free(vkey); +} + +static int template_has(const void *keydata, int selection) +{ + int ok = 0; + + debug_print("has %p\n", keydata); + if (ossl_prov_is_running() && keydata != NULL) { + /* add logic to check whether this key has the requested parameters */ + } + debug_print("has result %d\n", ok); + return ok; +} + +static int template_match(const void *keydata1, const void *keydata2, int selection) +{ + int ok = 1; + + debug_print("matching %p and %p\n", keydata1, keydata2); + if (!ossl_prov_is_running()) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) + ok = ok && keydata1 != NULL && keydata2 != NULL; + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) { + int key_checked = 0; + + if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) { + /* validate whether the public keys match */ + } + if (!key_checked + && (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) { + /* validate whether the private keys match */ + } + ok = ok && key_checked; + } + debug_print("match result %d\n", ok); + return ok; +} + +static int key_to_params(void *key, OSSL_PARAM_BLD *tmpl, + OSSL_PARAM params[], int include_private) +{ + if (key == NULL) + return 0; + + /* add public and/or private key parts to templ as possible */ + + return 1; +} + +static int template_export(void *key, int selection, OSSL_CALLBACK *param_cb, + void *cbarg) +{ + OSSL_PARAM_BLD *tmpl; + OSSL_PARAM *params = NULL; + int ret = 0; + + debug_print("export %p\n", key); + if (!ossl_prov_is_running() || key == NULL) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) + return 0; + + tmpl = OSSL_PARAM_BLD_new(); + if (tmpl == NULL) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) { + int include_private = ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0); + + if (!key_to_params(key, tmpl, NULL, include_private)) + goto err; + } + + params = OSSL_PARAM_BLD_to_param(tmpl); + if (params == NULL) + goto err; + + ret = param_cb(params, cbarg); + OSSL_PARAM_free(params); +err: + OSSL_PARAM_BLD_free(tmpl); + debug_print("export result %d\n", ret); + return ret; +} + +static int ossl_template_key_fromdata(void *key, + const OSSL_PARAM params[], + int include_private) +{ + const OSSL_PARAM *param_priv_key = NULL, *param_pub_key; + + if (key == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 0; + + /* validate integrity of key (algorithm type specific) */ + + param_pub_key = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY); + if (include_private) + param_priv_key = + OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY); + + if (param_pub_key == NULL && param_priv_key == NULL) + return 0; + + if (param_priv_key != NULL) { + /* retrieve private key and check integrity */ + } + + if (param_pub_key != NULL) { + /* retrieve public key and check integrity */ + } + + return 1; +} + +static int template_import(void *key, int selection, const OSSL_PARAM params[]) +{ + int ok = 1; + int include_private; + + debug_print("import %p\n", key); + if (!ossl_prov_is_running() || key == NULL) + return 0; + + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0) + return 0; + + include_private = selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0; + ok = ok && ossl_template_key_fromdata(key, params, include_private); + + debug_print("import result %d\n", ok); + return ok; +} + +#define TEMPLATE_KEY_TYPES() \ + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0), \ + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0) + +static const OSSL_PARAM template_key_types[] = { + TEMPLATE_KEY_TYPES(), + OSSL_PARAM_END +}; + +static const OSSL_PARAM *template_imexport_types(int selection) +{ + debug_print("getting imexport types\n"); + if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) + return template_key_types; + return NULL; +} + +static int template_get_params(void *key, OSSL_PARAM params[]) +{ + OSSL_PARAM *p; + + debug_print("get params %p\n", key); + + if (ossl_param_is_empty(params)) + return 0; + + /* return sensible values for at least these parameters */ + + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS)) != NULL + && !OSSL_PARAM_set_int(p, 0)) + return 0; + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS)) != NULL + && !OSSL_PARAM_set_int(p, 0)) + return 0; + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE)) != NULL + && !OSSL_PARAM_set_int(p, 0)) + return 0; + if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY)) != NULL) { + if (!OSSL_PARAM_set_octet_string(p, NULL, 0)) + return 0; + } + + debug_print("get params OK\n"); + return 1; +} + +static const OSSL_PARAM template_gettable_params_arr[] = { + OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0), + OSSL_PARAM_END +}; + +static const OSSL_PARAM *template_gettable_params(void *provctx) +{ + debug_print("gettable params called\n"); + return template_gettable_params_arr; +} + +static int template_set_params(void *key, const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p; + + debug_print("set params called for %p\n", key); + if (ossl_param_is_empty(params)) + return 1; /* OK not to set anything */ + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY); + if (p != NULL) { + /* load public key structure */ + } + + debug_print("set params OK\n"); + return 1; +} + +static const OSSL_PARAM template_settable_params_arr[] = { + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0), + OSSL_PARAM_END +}; + +static const OSSL_PARAM *template_settable_params(void *provctx) +{ + debug_print("settable params called\n"); + return template_settable_params_arr; +} + +static int template_gen_set_params(void *genctx, const OSSL_PARAM params[]) +{ + struct template_gen_ctx *gctx = genctx; + + if (gctx == NULL) + return 0; + + debug_print("empty gen_set params called for %p\n", gctx); + return 1; +} + +static void *template_gen_init(void *provctx, int selection, + const OSSL_PARAM params[]) +{ + struct template_gen_ctx *gctx = NULL; + + debug_print("gen init called for %p\n", provctx); + + /* perform algorithm type specific sanity checks */ + + if (!ossl_prov_is_running()) + return NULL; + + if ((gctx = OPENSSL_zalloc(sizeof(*gctx))) != NULL) { + gctx->provctx = provctx; + gctx->selection = selection; + } + if (!template_gen_set_params(gctx, params)) { + OPENSSL_free(gctx); + gctx = NULL; + } + debug_print("gen init returns %p\n", gctx); + return gctx; +} + +static const OSSL_PARAM *template_gen_settable_params(ossl_unused void *genctx, + ossl_unused void *provctx) +{ + static OSSL_PARAM settable[] = { + OSSL_PARAM_END + }; + return settable; +} + +static void *template_gen(void *vctx, OSSL_CALLBACK *osslcb, void *cbarg) +{ + struct template_gen_ctx *gctx = (struct template_gen_ctx *)vctx; + void *key = NULL; + + debug_print("gen called for %p\n", gctx); + + if (gctx == NULL) + goto err; + + /* generate and return new key */ + + debug_print("gen returns set %p\n", key); + return key; +err: + template_free(key); + debug_print("gen returns NULL\n"); + return NULL; +} + +static void template_gen_cleanup(void *genctx) +{ + struct template_gen_ctx *gctx = genctx; + + if (gctx == NULL) + return; + + debug_print("gen cleanup for %p\n", gctx); + OPENSSL_free(gctx); +} + +static void *template_dup(const void *vsrckey, int selection) +{ + void *dstkey = NULL; + + debug_print("dup called for %p\n", vsrckey); + if (!ossl_prov_is_running()) + return NULL; + + dstkey = template_new(NULL); + if (dstkey == NULL) + goto err; + + /* populate dstkey from vsrckey material */ + + debug_print("dup returns %p\n", dstkey); + return dstkey; + err: + template_free(dstkey); + debug_print("dup returns NULL\n"); + return NULL; +} + +const OSSL_DISPATCH ossl_template_keymgmt_functions[] = { + { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))template_new }, + { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))template_free }, + { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))template_get_params }, + { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))template_gettable_params }, + { OSSL_FUNC_KEYMGMT_SET_PARAMS, (void (*) (void))template_set_params }, + { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*) (void))template_settable_params }, + { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))template_has }, + { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))template_match }, + { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))template_imexport_types }, + { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))template_imexport_types }, + { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))template_import }, + { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))template_export }, + { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))template_gen_init }, + { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))template_gen_set_params }, + { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, + (void (*)(void))template_gen_settable_params }, + { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))template_gen }, + { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))template_gen_cleanup }, + { OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))template_dup }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/macs/blake2_mac_impl.c b/crypto/openssl/providers/implementations/macs/blake2_mac_impl.c new file mode 100644 index 000000000000..2ca800fad9bc --- /dev/null +++ b/crypto/openssl/providers/implementations/macs/blake2_mac_impl.c @@ -0,0 +1,254 @@ +/* + * Copyright 2018-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 + */ + +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/params.h> +#include <openssl/proverr.h> + +#include "prov/blake2.h" +#include "internal/cryptlib.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" + +/* + * Forward declaration of everything implemented here. This is not strictly + * necessary for the compiler, but provides an assurance that the signatures + * of the functions in the dispatch table are correct. + */ +static OSSL_FUNC_mac_newctx_fn blake2_mac_new; +static OSSL_FUNC_mac_dupctx_fn blake2_mac_dup; +static OSSL_FUNC_mac_freectx_fn blake2_mac_free; +static OSSL_FUNC_mac_gettable_ctx_params_fn blake2_gettable_ctx_params; +static OSSL_FUNC_mac_get_ctx_params_fn blake2_get_ctx_params; +static OSSL_FUNC_mac_settable_ctx_params_fn blake2_mac_settable_ctx_params; +static OSSL_FUNC_mac_set_ctx_params_fn blake2_mac_set_ctx_params; +static OSSL_FUNC_mac_init_fn blake2_mac_init; +static OSSL_FUNC_mac_update_fn blake2_mac_update; +static OSSL_FUNC_mac_final_fn blake2_mac_final; + +struct blake2_mac_data_st { + BLAKE2_CTX ctx; + BLAKE2_PARAM params; + unsigned char key[BLAKE2_KEYBYTES]; +}; + +static void *blake2_mac_new(void *unused_provctx) +{ + struct blake2_mac_data_st *macctx; + + if (!ossl_prov_is_running()) + return NULL; + + macctx = OPENSSL_zalloc(sizeof(*macctx)); + if (macctx != NULL) { + BLAKE2_PARAM_INIT(&macctx->params); + /* ctx initialization is deferred to BLAKE2b_Init() */ + } + return macctx; +} + +static void *blake2_mac_dup(void *vsrc) +{ + struct blake2_mac_data_st *dst; + struct blake2_mac_data_st *src = vsrc; + + if (!ossl_prov_is_running()) + return NULL; + + dst = OPENSSL_zalloc(sizeof(*dst)); + if (dst == NULL) + return NULL; + + *dst = *src; + return dst; +} + +static void blake2_mac_free(void *vmacctx) +{ + struct blake2_mac_data_st *macctx = vmacctx; + + if (macctx != NULL) { + OPENSSL_cleanse(macctx->key, sizeof(macctx->key)); + OPENSSL_free(macctx); + } +} + +static size_t blake2_mac_size(void *vmacctx) +{ + struct blake2_mac_data_st *macctx = vmacctx; + + return macctx->params.digest_length; +} + +static int blake2_setkey(struct blake2_mac_data_st *macctx, + const unsigned char *key, size_t keylen) +{ + if (keylen > BLAKE2_KEYBYTES || keylen == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + memcpy(macctx->key, key, keylen); + /* Pad with zeroes at the end if required */ + if (keylen < BLAKE2_KEYBYTES) + memset(macctx->key + keylen, 0, BLAKE2_KEYBYTES - keylen); + BLAKE2_PARAM_SET_KEY_LENGTH(&macctx->params, (uint8_t)keylen); + return 1; +} + +static int blake2_mac_init(void *vmacctx, const unsigned char *key, + size_t keylen, const OSSL_PARAM params[]) +{ + struct blake2_mac_data_st *macctx = vmacctx; + + if (!ossl_prov_is_running() || !blake2_mac_set_ctx_params(macctx, params)) + return 0; + if (key != NULL) { + if (!blake2_setkey(macctx, key, keylen)) + return 0; + } else if (macctx->params.key_length == 0) { + /* Check key has been set */ + ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET); + return 0; + } + return BLAKE2_INIT_KEY(&macctx->ctx, &macctx->params, macctx->key); +} + +static int blake2_mac_update(void *vmacctx, + const unsigned char *data, size_t datalen) +{ + struct blake2_mac_data_st *macctx = vmacctx; + + if (datalen == 0) + return 1; + + return BLAKE2_UPDATE(&macctx->ctx, data, datalen); +} + +static int blake2_mac_final(void *vmacctx, + unsigned char *out, size_t *outl, + size_t outsize) +{ + struct blake2_mac_data_st *macctx = vmacctx; + + if (!ossl_prov_is_running()) + return 0; + + *outl = blake2_mac_size(macctx); + return BLAKE2_FINAL(out, &macctx->ctx); +} + +static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_MAC_PARAM_SIZE, NULL), + OSSL_PARAM_size_t(OSSL_MAC_PARAM_BLOCK_SIZE, NULL), + OSSL_PARAM_END +}; +static const OSSL_PARAM *blake2_gettable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + return known_gettable_ctx_params; +} + +static int blake2_get_ctx_params(void *vmacctx, OSSL_PARAM params[]) +{ + OSSL_PARAM *p; + + if ((p = OSSL_PARAM_locate(params, OSSL_MAC_PARAM_SIZE)) != NULL + && !OSSL_PARAM_set_size_t(p, blake2_mac_size(vmacctx))) + return 0; + + if ((p = OSSL_PARAM_locate(params, OSSL_MAC_PARAM_BLOCK_SIZE)) != NULL + && !OSSL_PARAM_set_size_t(p, BLAKE2_BLOCKBYTES)) + return 0; + + return 1; +} + +static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_MAC_PARAM_SIZE, NULL), + OSSL_PARAM_octet_string(OSSL_MAC_PARAM_KEY, NULL, 0), + OSSL_PARAM_octet_string(OSSL_MAC_PARAM_CUSTOM, NULL, 0), + OSSL_PARAM_octet_string(OSSL_MAC_PARAM_SALT, NULL, 0), + OSSL_PARAM_END +}; +static const OSSL_PARAM *blake2_mac_settable_ctx_params( + ossl_unused void *ctx, ossl_unused void *p_ctx) +{ + return known_settable_ctx_params; +} + +/* + * ALL parameters should be set before init(). + */ +static int blake2_mac_set_ctx_params(void *vmacctx, const OSSL_PARAM params[]) +{ + struct blake2_mac_data_st *macctx = vmacctx; + const OSSL_PARAM *p; + + if (ossl_param_is_empty(params)) + return 1; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_SIZE)) != NULL) { + size_t size; + + if (!OSSL_PARAM_get_size_t(p, &size) + || size < 1 + || size > BLAKE2_OUTBYTES) { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_XOF_OR_INVALID_LENGTH); + return 0; + } + BLAKE2_PARAM_SET_DIGEST_LENGTH(&macctx->params, (uint8_t)size); + } + + if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_KEY)) != NULL + && !blake2_setkey(macctx, p->data, p->data_size)) + return 0; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_CUSTOM)) + != NULL) { + /* + * The OSSL_PARAM API doesn't provide direct pointer use, so we + * must handle the OSSL_PARAM structure ourselves here + */ + if (p->data_size > BLAKE2_PERSONALBYTES) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_CUSTOM_LENGTH); + return 0; + } + BLAKE2_PARAM_SET_PERSONAL(&macctx->params, p->data, p->data_size); + } + + if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_SALT)) != NULL) { + /* + * The OSSL_PARAM API doesn't provide direct pointer use, so we + * must handle the OSSL_PARAM structure ourselves here as well + */ + if (p->data_size > BLAKE2_SALTBYTES) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SALT_LENGTH); + return 0; + } + BLAKE2_PARAM_SET_SALT(&macctx->params, p->data, p->data_size); + } + return 1; +} + +const OSSL_DISPATCH BLAKE2_FUNCTIONS[] = { + { OSSL_FUNC_MAC_NEWCTX, (void (*)(void))blake2_mac_new }, + { OSSL_FUNC_MAC_DUPCTX, (void (*)(void))blake2_mac_dup }, + { OSSL_FUNC_MAC_FREECTX, (void (*)(void))blake2_mac_free }, + { OSSL_FUNC_MAC_INIT, (void (*)(void))blake2_mac_init }, + { OSSL_FUNC_MAC_UPDATE, (void (*)(void))blake2_mac_update }, + { OSSL_FUNC_MAC_FINAL, (void (*)(void))blake2_mac_final }, + { OSSL_FUNC_MAC_GETTABLE_CTX_PARAMS, + (void (*)(void))blake2_gettable_ctx_params }, + { OSSL_FUNC_MAC_GET_CTX_PARAMS, (void (*)(void))blake2_get_ctx_params }, + { OSSL_FUNC_MAC_SETTABLE_CTX_PARAMS, + (void (*)(void))blake2_mac_settable_ctx_params }, + { OSSL_FUNC_MAC_SET_CTX_PARAMS, (void (*)(void))blake2_mac_set_ctx_params }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/macs/blake2b_mac.c b/crypto/openssl/providers/implementations/macs/blake2b_mac.c new file mode 100644 index 000000000000..b445cbd57875 --- /dev/null +++ b/crypto/openssl/providers/implementations/macs/blake2b_mac.c @@ -0,0 +1,33 @@ +/* + * Copyright 2018-2021 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 + */ + +/* Constants */ +#define BLAKE2_CTX BLAKE2B_CTX +#define BLAKE2_PARAM BLAKE2B_PARAM +#define BLAKE2_KEYBYTES BLAKE2B_KEYBYTES +#define BLAKE2_OUTBYTES BLAKE2B_OUTBYTES +#define BLAKE2_PERSONALBYTES BLAKE2B_PERSONALBYTES +#define BLAKE2_SALTBYTES BLAKE2B_SALTBYTES +#define BLAKE2_BLOCKBYTES BLAKE2B_BLOCKBYTES + +/* Function names */ +#define BLAKE2_PARAM_INIT ossl_blake2b_param_init +#define BLAKE2_INIT_KEY ossl_blake2b_init_key +#define BLAKE2_UPDATE ossl_blake2b_update +#define BLAKE2_FINAL ossl_blake2b_final +#define BLAKE2_PARAM_SET_DIGEST_LENGTH ossl_blake2b_param_set_digest_length +#define BLAKE2_PARAM_SET_KEY_LENGTH ossl_blake2b_param_set_key_length +#define BLAKE2_PARAM_SET_PERSONAL ossl_blake2b_param_set_personal +#define BLAKE2_PARAM_SET_SALT ossl_blake2b_param_set_salt + +/* OSSL_DISPATCH symbol */ +#define BLAKE2_FUNCTIONS ossl_blake2bmac_functions + +#include "blake2_mac_impl.c" + diff --git a/crypto/openssl/providers/implementations/macs/blake2s_mac.c b/crypto/openssl/providers/implementations/macs/blake2s_mac.c new file mode 100644 index 000000000000..6b3fa28bd36b --- /dev/null +++ b/crypto/openssl/providers/implementations/macs/blake2s_mac.c @@ -0,0 +1,32 @@ +/* + * Copyright 2018-2021 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 + */ + +/* Constants */ +#define BLAKE2_CTX BLAKE2S_CTX +#define BLAKE2_PARAM BLAKE2S_PARAM +#define BLAKE2_KEYBYTES BLAKE2S_KEYBYTES +#define BLAKE2_OUTBYTES BLAKE2S_OUTBYTES +#define BLAKE2_PERSONALBYTES BLAKE2S_PERSONALBYTES +#define BLAKE2_SALTBYTES BLAKE2S_SALTBYTES +#define BLAKE2_BLOCKBYTES BLAKE2S_BLOCKBYTES + +/* Function names */ +#define BLAKE2_PARAM_INIT ossl_blake2s_param_init +#define BLAKE2_INIT_KEY ossl_blake2s_init_key +#define BLAKE2_UPDATE ossl_blake2s_update +#define BLAKE2_FINAL ossl_blake2s_final +#define BLAKE2_PARAM_SET_DIGEST_LENGTH ossl_blake2s_param_set_digest_length +#define BLAKE2_PARAM_SET_KEY_LENGTH ossl_blake2s_param_set_key_length +#define BLAKE2_PARAM_SET_PERSONAL ossl_blake2s_param_set_personal +#define BLAKE2_PARAM_SET_SALT ossl_blake2s_param_set_salt + +/* OSSL_DISPATCH symbol */ +#define BLAKE2_FUNCTIONS ossl_blake2smac_functions + +#include "blake2_mac_impl.c" diff --git a/crypto/openssl/providers/implementations/macs/build.info b/crypto/openssl/providers/implementations/macs/build.info new file mode 100644 index 000000000000..35db66bf23bd --- /dev/null +++ b/crypto/openssl/providers/implementations/macs/build.info @@ -0,0 +1,30 @@ +# We make separate GOAL variables for each algorithm, to make it easy to +# switch each to the Legacy provider when needed. + +$GMAC_GOAL=../../libdefault.a ../../libfips.a +$HMAC_GOAL=../../libdefault.a ../../libfips.a +$KMAC_GOAL=../../libdefault.a ../../libfips.a +$CMAC_GOAL=../../libdefault.a ../../libfips.a +$BLAKE2_GOAL=../../libdefault.a +$SIPHASH_GOAL=../../libdefault.a +$POLY1305_GOAL=../../libdefault.a + +SOURCE[$GMAC_GOAL]=gmac_prov.c +SOURCE[$HMAC_GOAL]=hmac_prov.c +SOURCE[$KMAC_GOAL]=kmac_prov.c + +IF[{- !$disabled{cmac} -}] + SOURCE[$CMAC_GOAL]=cmac_prov.c +ENDIF + +IF[{- !$disabled{blake2} -}] + SOURCE[$BLAKE2_GOAL]=blake2b_mac.c blake2s_mac.c +ENDIF + +IF[{- !$disabled{siphash} -}] + SOURCE[$SIPHASH_GOAL]=siphash_prov.c +ENDIF + +IF[{- !$disabled{poly1305} -}] + SOURCE[$POLY1305_GOAL]=poly1305_prov.c +ENDIF diff --git a/crypto/openssl/providers/implementations/macs/cmac_prov.c b/crypto/openssl/providers/implementations/macs/cmac_prov.c new file mode 100644 index 000000000000..36acf4d81b8a --- /dev/null +++ b/crypto/openssl/providers/implementations/macs/cmac_prov.c @@ -0,0 +1,307 @@ +/* + * Copyright 2018-2024 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 + */ + +/* + * CMAC low level APIs are deprecated for public use, but still ok for internal + * use. + */ +#include "internal/deprecated.h" + +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/params.h> +#include <openssl/evp.h> +#include <openssl/cmac.h> +#include <openssl/err.h> +#include <openssl/proverr.h> + +#include "prov/securitycheck.h" +#include "prov/implementations.h" +#include "prov/provider_ctx.h" +#include "prov/provider_util.h" +#include "prov/providercommon.h" +#include "crypto/cmac.h" + +/* + * Forward declaration of everything implemented here. This is not strictly + * necessary for the compiler, but provides an assurance that the signatures + * of the functions in the dispatch table are correct. + */ +static OSSL_FUNC_mac_newctx_fn cmac_new; +static OSSL_FUNC_mac_dupctx_fn cmac_dup; +static OSSL_FUNC_mac_freectx_fn cmac_free; +static OSSL_FUNC_mac_gettable_ctx_params_fn cmac_gettable_ctx_params; +static OSSL_FUNC_mac_get_ctx_params_fn cmac_get_ctx_params; +static OSSL_FUNC_mac_settable_ctx_params_fn cmac_settable_ctx_params; +static OSSL_FUNC_mac_set_ctx_params_fn cmac_set_ctx_params; +static OSSL_FUNC_mac_init_fn cmac_init; +static OSSL_FUNC_mac_update_fn cmac_update; +static OSSL_FUNC_mac_final_fn cmac_final; + +/* local CMAC data */ + +struct cmac_data_st { + void *provctx; + CMAC_CTX *ctx; + PROV_CIPHER cipher; + OSSL_FIPS_IND_DECLARE +}; + +static void *cmac_new(void *provctx) +{ + struct cmac_data_st *macctx; + + if (!ossl_prov_is_running()) + return NULL; + + if ((macctx = OPENSSL_zalloc(sizeof(*macctx))) == NULL + || (macctx->ctx = CMAC_CTX_new()) == NULL) { + OPENSSL_free(macctx); + macctx = NULL; + } else { + macctx->provctx = provctx; + OSSL_FIPS_IND_INIT(macctx) + } + + return macctx; +} + +static void cmac_free(void *vmacctx) +{ + struct cmac_data_st *macctx = vmacctx; + + if (macctx != NULL) { + CMAC_CTX_free(macctx->ctx); + ossl_prov_cipher_reset(&macctx->cipher); + OPENSSL_free(macctx); + } +} + +static void *cmac_dup(void *vsrc) +{ + struct cmac_data_st *src = vsrc; + struct cmac_data_st *dst; + + if (!ossl_prov_is_running()) + return NULL; + + dst = cmac_new(src->provctx); + if (dst == NULL) + return NULL; + if (!CMAC_CTX_copy(dst->ctx, src->ctx) + || !ossl_prov_cipher_copy(&dst->cipher, &src->cipher)) { + cmac_free(dst); + return NULL; + } + OSSL_FIPS_IND_COPY(dst, src) + return dst; +} + +static size_t cmac_size(void *vmacctx) +{ + struct cmac_data_st *macctx = vmacctx; + const EVP_CIPHER_CTX *cipherctx = CMAC_CTX_get0_cipher_ctx(macctx->ctx); + + if (EVP_CIPHER_CTX_get0_cipher(cipherctx) == NULL) + return 0; + + return EVP_CIPHER_CTX_get_block_size(cipherctx); +} + +#ifdef FIPS_MODULE +/* + * TDES Encryption is not approved in FIPS 140-3. + * + * In strict approved mode we just fail here (by returning 0). + * If we are going to bypass it using a FIPS indicator then we need to pass that + * information down to the cipher also. + * This function returns the param to pass down in 'p'. + * state will return OSSL_FIPS_IND_STATE_UNKNOWN if the param has not been set. + * + * The name 'OSSL_CIPHER_PARAM_FIPS_ENCRYPT_CHECK' used below matches the + * key name used by the Triple-DES. + */ +static int tdes_check_param(struct cmac_data_st *macctx, OSSL_PARAM *p, + int *state) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(macctx->provctx); + const EVP_CIPHER *cipher = ossl_prov_cipher_cipher(&macctx->cipher); + + *state = OSSL_FIPS_IND_STATE_UNKNOWN; + if (EVP_CIPHER_is_a(cipher, "DES-EDE3-CBC")) { + if (!OSSL_FIPS_IND_ON_UNAPPROVED(macctx, OSSL_FIPS_IND_SETTABLE0, + libctx, "CMAC", "Triple-DES", + ossl_fips_config_tdes_encrypt_disallowed)) + return 0; + OSSL_FIPS_IND_GET_PARAM(macctx, p, state, OSSL_FIPS_IND_SETTABLE0, + OSSL_CIPHER_PARAM_FIPS_ENCRYPT_CHECK) + } + return 1; +} +#endif + +static int cmac_setkey(struct cmac_data_st *macctx, + const unsigned char *key, size_t keylen) +{ + int rv; + OSSL_PARAM *p = NULL; +#ifdef FIPS_MODULE + int state = OSSL_FIPS_IND_STATE_UNKNOWN; + OSSL_PARAM prms[2] = { OSSL_PARAM_END, OSSL_PARAM_END }; + + if (!tdes_check_param(macctx, &prms[0], &state)) + return 0; + if (state != OSSL_FIPS_IND_STATE_UNKNOWN) + p = prms; +#endif + rv = ossl_cmac_init(macctx->ctx, key, keylen, + ossl_prov_cipher_cipher(&macctx->cipher), + ossl_prov_cipher_engine(&macctx->cipher), p); + ossl_prov_cipher_reset(&macctx->cipher); + return rv; +} + +static int cmac_init(void *vmacctx, const unsigned char *key, + size_t keylen, const OSSL_PARAM params[]) +{ + struct cmac_data_st *macctx = vmacctx; + + if (!ossl_prov_is_running() || !cmac_set_ctx_params(macctx, params)) + return 0; + if (key != NULL) + return cmac_setkey(macctx, key, keylen); + /* Reinitialize the CMAC context */ + return CMAC_Init(macctx->ctx, NULL, 0, NULL, NULL); +} + +static int cmac_update(void *vmacctx, const unsigned char *data, + size_t datalen) +{ + struct cmac_data_st *macctx = vmacctx; + + return CMAC_Update(macctx->ctx, data, datalen); +} + +static int cmac_final(void *vmacctx, unsigned char *out, size_t *outl, + size_t outsize) +{ + struct cmac_data_st *macctx = vmacctx; + + if (!ossl_prov_is_running()) + return 0; + + return CMAC_Final(macctx->ctx, out, outl); +} + +static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_MAC_PARAM_SIZE, NULL), + OSSL_PARAM_size_t(OSSL_MAC_PARAM_BLOCK_SIZE, NULL), + OSSL_FIPS_IND_GETTABLE_CTX_PARAM() + OSSL_PARAM_END +}; +static const OSSL_PARAM *cmac_gettable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + return known_gettable_ctx_params; +} + +static int cmac_get_ctx_params(void *vmacctx, OSSL_PARAM params[]) +{ + OSSL_PARAM *p; + + if ((p = OSSL_PARAM_locate(params, OSSL_MAC_PARAM_SIZE)) != NULL + && !OSSL_PARAM_set_size_t(p, cmac_size(vmacctx))) + return 0; + + if ((p = OSSL_PARAM_locate(params, OSSL_MAC_PARAM_BLOCK_SIZE)) != NULL + && !OSSL_PARAM_set_size_t(p, cmac_size(vmacctx))) + return 0; + + if (!OSSL_FIPS_IND_GET_CTX_PARAM((struct cmac_data_st *)vmacctx, params)) + return 0; + return 1; +} + +static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_CIPHER, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_octet_string(OSSL_MAC_PARAM_KEY, NULL, 0), + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_CIPHER_PARAM_FIPS_ENCRYPT_CHECK) + OSSL_PARAM_END +}; +static const OSSL_PARAM *cmac_settable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + return known_settable_ctx_params; +} + +/* + * ALL parameters should be set before init(). + */ +static int cmac_set_ctx_params(void *vmacctx, const OSSL_PARAM params[]) +{ + struct cmac_data_st *macctx = vmacctx; + OSSL_LIB_CTX *ctx = PROV_LIBCTX_OF(macctx->provctx); + const OSSL_PARAM *p; + + if (ossl_param_is_empty(params)) + return 1; + + if (!OSSL_FIPS_IND_SET_CTX_PARAM(macctx, + OSSL_FIPS_IND_SETTABLE0, params, + OSSL_CIPHER_PARAM_FIPS_ENCRYPT_CHECK)) + return 0; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_CIPHER)) != NULL) { + if (!ossl_prov_cipher_load_from_params(&macctx->cipher, params, ctx)) + return 0; + + if (EVP_CIPHER_get_mode(ossl_prov_cipher_cipher(&macctx->cipher)) + != EVP_CIPH_CBC_MODE) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE); + return 0; + } +#ifdef FIPS_MODULE + { + const EVP_CIPHER *cipher = ossl_prov_cipher_cipher(&macctx->cipher); + + if (!EVP_CIPHER_is_a(cipher, "AES-256-CBC") + && !EVP_CIPHER_is_a(cipher, "AES-192-CBC") + && !EVP_CIPHER_is_a(cipher, "AES-128-CBC") + && !EVP_CIPHER_is_a(cipher, "DES-EDE3-CBC")) { + ERR_raise(ERR_LIB_PROV, EVP_R_UNSUPPORTED_CIPHER); + return 0; + } + } +#endif + } + + if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_KEY)) != NULL) { + if (p->data_type != OSSL_PARAM_OCTET_STRING) + return 0; + return cmac_setkey(macctx, p->data, p->data_size); + } + return 1; +} + +const OSSL_DISPATCH ossl_cmac_functions[] = { + { OSSL_FUNC_MAC_NEWCTX, (void (*)(void))cmac_new }, + { OSSL_FUNC_MAC_DUPCTX, (void (*)(void))cmac_dup }, + { OSSL_FUNC_MAC_FREECTX, (void (*)(void))cmac_free }, + { OSSL_FUNC_MAC_INIT, (void (*)(void))cmac_init }, + { OSSL_FUNC_MAC_UPDATE, (void (*)(void))cmac_update }, + { OSSL_FUNC_MAC_FINAL, (void (*)(void))cmac_final }, + { OSSL_FUNC_MAC_GETTABLE_CTX_PARAMS, + (void (*)(void))cmac_gettable_ctx_params }, + { OSSL_FUNC_MAC_GET_CTX_PARAMS, (void (*)(void))cmac_get_ctx_params }, + { OSSL_FUNC_MAC_SETTABLE_CTX_PARAMS, + (void (*)(void))cmac_settable_ctx_params }, + { OSSL_FUNC_MAC_SET_CTX_PARAMS, (void (*)(void))cmac_set_ctx_params }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/macs/gmac_prov.c b/crypto/openssl/providers/implementations/macs/gmac_prov.c new file mode 100644 index 000000000000..ca9382b0a82f --- /dev/null +++ b/crypto/openssl/providers/implementations/macs/gmac_prov.c @@ -0,0 +1,259 @@ +/* + * Copyright 2018-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 + */ + +#include <stdlib.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/params.h> +#include <openssl/evp.h> +#include <openssl/err.h> +#include <openssl/proverr.h> + +#include "prov/implementations.h" +#include "prov/provider_ctx.h" +#include "prov/provider_util.h" +#include "prov/providercommon.h" + +/* + * Forward declaration of everything implemented here. This is not strictly + * necessary for the compiler, but provides an assurance that the signatures + * of the functions in the dispatch table are correct. + */ +static OSSL_FUNC_mac_newctx_fn gmac_new; +static OSSL_FUNC_mac_dupctx_fn gmac_dup; +static OSSL_FUNC_mac_freectx_fn gmac_free; +static OSSL_FUNC_mac_gettable_params_fn gmac_gettable_params; +static OSSL_FUNC_mac_get_params_fn gmac_get_params; +static OSSL_FUNC_mac_settable_ctx_params_fn gmac_settable_ctx_params; +static OSSL_FUNC_mac_set_ctx_params_fn gmac_set_ctx_params; +static OSSL_FUNC_mac_init_fn gmac_init; +static OSSL_FUNC_mac_update_fn gmac_update; +static OSSL_FUNC_mac_final_fn gmac_final; + +/* local GMAC pkey structure */ + +struct gmac_data_st { + void *provctx; + EVP_CIPHER_CTX *ctx; /* Cipher context */ + PROV_CIPHER cipher; +}; + +static void gmac_free(void *vmacctx) +{ + struct gmac_data_st *macctx = vmacctx; + + if (macctx != NULL) { + EVP_CIPHER_CTX_free(macctx->ctx); + ossl_prov_cipher_reset(&macctx->cipher); + OPENSSL_free(macctx); + } +} + +static void *gmac_new(void *provctx) +{ + struct gmac_data_st *macctx; + + if (!ossl_prov_is_running()) + return NULL; + + if ((macctx = OPENSSL_zalloc(sizeof(*macctx))) == NULL + || (macctx->ctx = EVP_CIPHER_CTX_new()) == NULL) { + gmac_free(macctx); + return NULL; + } + macctx->provctx = provctx; + + return macctx; +} + +static void *gmac_dup(void *vsrc) +{ + struct gmac_data_st *src = vsrc; + struct gmac_data_st *dst; + + if (!ossl_prov_is_running()) + return NULL; + + dst = gmac_new(src->provctx); + if (dst == NULL) + return NULL; + + if (!EVP_CIPHER_CTX_copy(dst->ctx, src->ctx) + || !ossl_prov_cipher_copy(&dst->cipher, &src->cipher)) { + gmac_free(dst); + return NULL; + } + return dst; +} + +static size_t gmac_size(void) +{ + return EVP_GCM_TLS_TAG_LEN; +} + +static int gmac_setkey(struct gmac_data_st *macctx, + const unsigned char *key, size_t keylen) +{ + EVP_CIPHER_CTX *ctx = macctx->ctx; + + if (keylen != (size_t)EVP_CIPHER_CTX_get_key_length(ctx)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + if (!EVP_EncryptInit_ex(ctx, NULL, NULL, key, NULL)) + return 0; + return 1; +} + +static int gmac_init(void *vmacctx, const unsigned char *key, + size_t keylen, const OSSL_PARAM params[]) +{ + struct gmac_data_st *macctx = vmacctx; + + if (!ossl_prov_is_running() || !gmac_set_ctx_params(macctx, params)) + return 0; + if (key != NULL) + return gmac_setkey(macctx, key, keylen); + return EVP_EncryptInit_ex(macctx->ctx, NULL, NULL, NULL, NULL); +} + +static int gmac_update(void *vmacctx, const unsigned char *data, + size_t datalen) +{ + struct gmac_data_st *macctx = vmacctx; + EVP_CIPHER_CTX *ctx = macctx->ctx; + int outlen; + + if (datalen == 0) + return 1; + + while (datalen > INT_MAX) { + if (!EVP_EncryptUpdate(ctx, NULL, &outlen, data, INT_MAX)) + return 0; + data += INT_MAX; + datalen -= INT_MAX; + } + return EVP_EncryptUpdate(ctx, NULL, &outlen, data, datalen); +} + +static int gmac_final(void *vmacctx, unsigned char *out, size_t *outl, + size_t outsize) +{ + OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END }; + struct gmac_data_st *macctx = vmacctx; + int hlen = 0; + + if (!ossl_prov_is_running()) + return 0; + + if (!EVP_EncryptFinal_ex(macctx->ctx, out, &hlen)) + return 0; + + hlen = gmac_size(); + params[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, + out, (size_t)hlen); + if (!EVP_CIPHER_CTX_get_params(macctx->ctx, params)) + return 0; + + *outl = hlen; + return 1; +} + +static const OSSL_PARAM known_gettable_params[] = { + OSSL_PARAM_size_t(OSSL_MAC_PARAM_SIZE, NULL), + OSSL_PARAM_END +}; +static const OSSL_PARAM *gmac_gettable_params(void *provctx) +{ + return known_gettable_params; +} + +static int gmac_get_params(OSSL_PARAM params[]) +{ + OSSL_PARAM *p; + + if ((p = OSSL_PARAM_locate(params, OSSL_MAC_PARAM_SIZE)) != NULL) + return OSSL_PARAM_set_size_t(p, gmac_size()); + + return 1; +} + +static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_CIPHER, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_octet_string(OSSL_MAC_PARAM_KEY, NULL, 0), + OSSL_PARAM_octet_string(OSSL_MAC_PARAM_IV, NULL, 0), + OSSL_PARAM_END +}; +static const OSSL_PARAM *gmac_settable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + return known_settable_ctx_params; +} + +/* + * ALL parameters should be set before init(). + */ +static int gmac_set_ctx_params(void *vmacctx, const OSSL_PARAM params[]) +{ + struct gmac_data_st *macctx = vmacctx; + EVP_CIPHER_CTX *ctx = macctx->ctx; + OSSL_LIB_CTX *provctx = PROV_LIBCTX_OF(macctx->provctx); + const OSSL_PARAM *p; + + if (ossl_param_is_empty(params)) + return 1; + if (ctx == NULL) + return 0; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_CIPHER)) != NULL) { + if (!ossl_prov_cipher_load_from_params(&macctx->cipher, params, provctx)) + return 0; + if (EVP_CIPHER_get_mode(ossl_prov_cipher_cipher(&macctx->cipher)) + != EVP_CIPH_GCM_MODE) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE); + return 0; + } + if (!EVP_EncryptInit_ex(ctx, ossl_prov_cipher_cipher(&macctx->cipher), + ossl_prov_cipher_engine(&macctx->cipher), NULL, + NULL)) + return 0; + } + + if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_KEY)) != NULL) + if (p->data_type != OSSL_PARAM_OCTET_STRING + || !gmac_setkey(macctx, p->data, p->data_size)) + return 0; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_IV)) != NULL) { + if (p->data_type != OSSL_PARAM_OCTET_STRING) + return 0; + + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, + p->data_size, NULL) <= 0 + || !EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, p->data)) + return 0; + } + return 1; +} + +const OSSL_DISPATCH ossl_gmac_functions[] = { + { OSSL_FUNC_MAC_NEWCTX, (void (*)(void))gmac_new }, + { OSSL_FUNC_MAC_DUPCTX, (void (*)(void))gmac_dup }, + { OSSL_FUNC_MAC_FREECTX, (void (*)(void))gmac_free }, + { OSSL_FUNC_MAC_INIT, (void (*)(void))gmac_init }, + { OSSL_FUNC_MAC_UPDATE, (void (*)(void))gmac_update }, + { OSSL_FUNC_MAC_FINAL, (void (*)(void))gmac_final }, + { OSSL_FUNC_MAC_GETTABLE_PARAMS, (void (*)(void))gmac_gettable_params }, + { OSSL_FUNC_MAC_GET_PARAMS, (void (*)(void))gmac_get_params }, + { OSSL_FUNC_MAC_SETTABLE_CTX_PARAMS, + (void (*)(void))gmac_settable_ctx_params }, + { OSSL_FUNC_MAC_SET_CTX_PARAMS, (void (*)(void))gmac_set_ctx_params }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/macs/hmac_prov.c b/crypto/openssl/providers/implementations/macs/hmac_prov.c new file mode 100644 index 000000000000..eb5ecaa300ef --- /dev/null +++ b/crypto/openssl/providers/implementations/macs/hmac_prov.c @@ -0,0 +1,404 @@ +/* + * Copyright 2018-2025 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 + */ + +/* + * HMAC low level APIs are deprecated for public use, but still ok for internal + * use. + */ +#include "internal/deprecated.h" + +#include <string.h> + +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/params.h> +#include <openssl/evp.h> +#include <openssl/hmac.h> +#include <openssl/proverr.h> +#include <openssl/err.h> + +#include "internal/ssl3_cbc.h" + +#include "prov/implementations.h" +#include "prov/provider_ctx.h" +#include "prov/provider_util.h" +#include "prov/providercommon.h" +#include "prov/securitycheck.h" + +/* + * Forward declaration of everything implemented here. This is not strictly + * necessary for the compiler, but provides an assurance that the signatures + * of the functions in the dispatch table are correct. + */ +static OSSL_FUNC_mac_newctx_fn hmac_new; +static OSSL_FUNC_mac_dupctx_fn hmac_dup; +static OSSL_FUNC_mac_freectx_fn hmac_free; +static OSSL_FUNC_mac_gettable_ctx_params_fn hmac_gettable_ctx_params; +static OSSL_FUNC_mac_get_ctx_params_fn hmac_get_ctx_params; +static OSSL_FUNC_mac_settable_ctx_params_fn hmac_settable_ctx_params; +static OSSL_FUNC_mac_set_ctx_params_fn hmac_set_ctx_params; +static OSSL_FUNC_mac_init_fn hmac_init; +static OSSL_FUNC_mac_update_fn hmac_update; +static OSSL_FUNC_mac_final_fn hmac_final; + +/* local HMAC context structure */ + +/* typedef EVP_MAC_IMPL */ +struct hmac_data_st { + void *provctx; + HMAC_CTX *ctx; /* HMAC context */ + PROV_DIGEST digest; + unsigned char *key; + size_t keylen; + /* Length of full TLS record including the MAC and any padding */ + size_t tls_data_size; + unsigned char tls_header[13]; + int tls_header_set; + unsigned char tls_mac_out[EVP_MAX_MD_SIZE]; + size_t tls_mac_out_size; +#ifdef FIPS_MODULE + /* + * 'internal' is set to 1 if HMAC is used inside another algorithm such as a + * KDF. In this case it is the parent algorithm that is responsible for + * performing any conditional FIPS indicator related checks for the HMAC. + */ + int internal; +#endif + OSSL_FIPS_IND_DECLARE +}; + +static void *hmac_new(void *provctx) +{ + struct hmac_data_st *macctx; + + if (!ossl_prov_is_running()) + return NULL; + + if ((macctx = OPENSSL_zalloc(sizeof(*macctx))) == NULL + || (macctx->ctx = HMAC_CTX_new()) == NULL) { + OPENSSL_free(macctx); + return NULL; + } + macctx->provctx = provctx; + OSSL_FIPS_IND_INIT(macctx) + + return macctx; +} + +static void hmac_free(void *vmacctx) +{ + struct hmac_data_st *macctx = vmacctx; + + if (macctx != NULL) { + HMAC_CTX_free(macctx->ctx); + ossl_prov_digest_reset(&macctx->digest); + OPENSSL_clear_free(macctx->key, macctx->keylen); + OPENSSL_free(macctx); + } +} + +static void *hmac_dup(void *vsrc) +{ + struct hmac_data_st *src = vsrc; + struct hmac_data_st *dst; + HMAC_CTX *ctx; + + if (!ossl_prov_is_running()) + return NULL; + dst = hmac_new(src->provctx); + if (dst == NULL) + return NULL; + + ctx = dst->ctx; + *dst = *src; + dst->ctx = ctx; + dst->key = NULL; + memset(&dst->digest, 0, sizeof(dst->digest)); + + if (!HMAC_CTX_copy(dst->ctx, src->ctx) + || !ossl_prov_digest_copy(&dst->digest, &src->digest)) { + hmac_free(dst); + return NULL; + } + if (src->key != NULL) { + dst->key = OPENSSL_malloc(src->keylen > 0 ? src->keylen : 1); + if (dst->key == NULL) { + hmac_free(dst); + return 0; + } + if (src->keylen > 0) + memcpy(dst->key, src->key, src->keylen); + } + return dst; +} + +static size_t hmac_size(struct hmac_data_st *macctx) +{ + return HMAC_size(macctx->ctx); +} + +static int hmac_block_size(struct hmac_data_st *macctx) +{ + const EVP_MD *md = ossl_prov_digest_md(&macctx->digest); + + if (md == NULL) + return 0; + return EVP_MD_block_size(md); +} + +static int hmac_setkey(struct hmac_data_st *macctx, + const unsigned char *key, size_t keylen) +{ + const EVP_MD *digest; + +#ifdef FIPS_MODULE + /* + * KDF's pass a salt rather than a key, + * which is why it skips the key check unless "HMAC" is fetched directly. + */ + if (!macctx->internal) { + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(macctx->provctx); + int approved = ossl_mac_check_key_size(keylen); + + if (!approved) { + if (!OSSL_FIPS_IND_ON_UNAPPROVED(macctx, OSSL_FIPS_IND_SETTABLE0, + libctx, "HMAC", "keysize", + ossl_fips_config_hmac_key_check)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + } + } +#endif + + if (macctx->key != NULL) + OPENSSL_clear_free(macctx->key, macctx->keylen); + /* Keep a copy of the key in case we need it for TLS HMAC */ + macctx->key = OPENSSL_malloc(keylen > 0 ? keylen : 1); + if (macctx->key == NULL) + return 0; + + if (keylen > 0) + memcpy(macctx->key, key, keylen); + macctx->keylen = keylen; + + digest = ossl_prov_digest_md(&macctx->digest); + /* HMAC_Init_ex doesn't tolerate all zero params, so we must be careful */ + if (key != NULL || (macctx->tls_data_size == 0 && digest != NULL)) + return HMAC_Init_ex(macctx->ctx, key, keylen, digest, + ossl_prov_digest_engine(&macctx->digest)); + return 1; +} + +static int hmac_init(void *vmacctx, const unsigned char *key, + size_t keylen, const OSSL_PARAM params[]) +{ + struct hmac_data_st *macctx = vmacctx; + + if (!ossl_prov_is_running() || !hmac_set_ctx_params(macctx, params)) + return 0; + + if (key != NULL) + return hmac_setkey(macctx, key, keylen); + + /* Just reinit the HMAC context */ + return HMAC_Init_ex(macctx->ctx, NULL, 0, NULL, NULL); +} + +static int hmac_update(void *vmacctx, const unsigned char *data, + size_t datalen) +{ + struct hmac_data_st *macctx = vmacctx; + + if (macctx->tls_data_size > 0) { + /* We're doing a TLS HMAC */ + if (!macctx->tls_header_set) { + /* We expect the first update call to contain the TLS header */ + if (datalen != sizeof(macctx->tls_header)) + return 0; + memcpy(macctx->tls_header, data, datalen); + macctx->tls_header_set = 1; + return 1; + } + /* macctx->tls_data_size is datalen plus the padding length */ + if (macctx->tls_data_size < datalen) + return 0; + + return ssl3_cbc_digest_record(ossl_prov_digest_md(&macctx->digest), + macctx->tls_mac_out, + &macctx->tls_mac_out_size, + macctx->tls_header, + data, + datalen, + macctx->tls_data_size, + macctx->key, + macctx->keylen, + 0); + } + + return HMAC_Update(macctx->ctx, data, datalen); +} + +static int hmac_final(void *vmacctx, unsigned char *out, size_t *outl, + size_t outsize) +{ + unsigned int hlen; + struct hmac_data_st *macctx = vmacctx; + + if (!ossl_prov_is_running()) + return 0; + if (macctx->tls_data_size > 0) { + if (macctx->tls_mac_out_size == 0) + return 0; + if (outl != NULL) + *outl = macctx->tls_mac_out_size; + memcpy(out, macctx->tls_mac_out, macctx->tls_mac_out_size); + return 1; + } + if (!HMAC_Final(macctx->ctx, out, &hlen)) + return 0; + *outl = hlen; + return 1; +} + +static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_MAC_PARAM_SIZE, NULL), + OSSL_PARAM_size_t(OSSL_MAC_PARAM_BLOCK_SIZE, NULL), + OSSL_FIPS_IND_GETTABLE_CTX_PARAM() + OSSL_PARAM_END +}; +static const OSSL_PARAM *hmac_gettable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + return known_gettable_ctx_params; +} + +static int hmac_get_ctx_params(void *vmacctx, OSSL_PARAM params[]) +{ + struct hmac_data_st *macctx = vmacctx; + OSSL_PARAM *p; + + if ((p = OSSL_PARAM_locate(params, OSSL_MAC_PARAM_SIZE)) != NULL + && !OSSL_PARAM_set_size_t(p, hmac_size(macctx))) + return 0; + + if ((p = OSSL_PARAM_locate(params, OSSL_MAC_PARAM_BLOCK_SIZE)) != NULL + && !OSSL_PARAM_set_int(p, hmac_block_size(macctx))) + return 0; + +#ifdef FIPS_MODULE + p = OSSL_PARAM_locate(params, OSSL_MAC_PARAM_FIPS_APPROVED_INDICATOR); + if (p != NULL) { + int approved = 0; + + if (!macctx->internal) + approved = OSSL_FIPS_IND_GET(macctx)->approved; + if (!OSSL_PARAM_set_int(p, approved)) + return 0; + } +#endif + return 1; +} + +static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_octet_string(OSSL_MAC_PARAM_KEY, NULL, 0), + OSSL_PARAM_int(OSSL_MAC_PARAM_DIGEST_NOINIT, NULL), + OSSL_PARAM_int(OSSL_MAC_PARAM_DIGEST_ONESHOT, NULL), + OSSL_PARAM_size_t(OSSL_MAC_PARAM_TLS_DATA_SIZE, NULL), + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_MAC_PARAM_FIPS_KEY_CHECK) + OSSL_PARAM_END +}; +static const OSSL_PARAM *hmac_settable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + return known_settable_ctx_params; +} + +/* + * ALL parameters should be set before init(). + */ +static int hmac_set_ctx_params(void *vmacctx, const OSSL_PARAM params[]) +{ + struct hmac_data_st *macctx = vmacctx; + OSSL_LIB_CTX *ctx = PROV_LIBCTX_OF(macctx->provctx); + const OSSL_PARAM *p; + + if (ossl_param_is_empty(params)) + return 1; + + if (!OSSL_FIPS_IND_SET_CTX_PARAM(macctx, OSSL_FIPS_IND_SETTABLE0, params, + OSSL_MAC_PARAM_FIPS_KEY_CHECK)) + return 0; + + if (!ossl_prov_digest_load_from_params(&macctx->digest, params, ctx)) + return 0; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_KEY)) != NULL) { + if (p->data_type != OSSL_PARAM_OCTET_STRING) + return 0; + + if (!hmac_setkey(macctx, p->data, p->data_size)) + return 0; + } + + if ((p = OSSL_PARAM_locate_const(params, + OSSL_MAC_PARAM_TLS_DATA_SIZE)) != NULL) { + if (!OSSL_PARAM_get_size_t(p, &macctx->tls_data_size)) + return 0; + } + return 1; +} + +const OSSL_DISPATCH ossl_hmac_functions[] = { + { OSSL_FUNC_MAC_NEWCTX, (void (*)(void))hmac_new }, + { OSSL_FUNC_MAC_DUPCTX, (void (*)(void))hmac_dup }, + { OSSL_FUNC_MAC_FREECTX, (void (*)(void))hmac_free }, + { OSSL_FUNC_MAC_INIT, (void (*)(void))hmac_init }, + { OSSL_FUNC_MAC_UPDATE, (void (*)(void))hmac_update }, + { OSSL_FUNC_MAC_FINAL, (void (*)(void))hmac_final }, + { OSSL_FUNC_MAC_GETTABLE_CTX_PARAMS, + (void (*)(void))hmac_gettable_ctx_params }, + { OSSL_FUNC_MAC_GET_CTX_PARAMS, (void (*)(void))hmac_get_ctx_params }, + { OSSL_FUNC_MAC_SETTABLE_CTX_PARAMS, + (void (*)(void))hmac_settable_ctx_params }, + { OSSL_FUNC_MAC_SET_CTX_PARAMS, (void (*)(void))hmac_set_ctx_params }, + OSSL_DISPATCH_END +}; + +#ifdef FIPS_MODULE +static OSSL_FUNC_mac_newctx_fn hmac_internal_new; + +static void *hmac_internal_new(void *provctx) +{ + struct hmac_data_st *macctx = hmac_new(provctx); + + if (macctx != NULL) + macctx->internal = 1; + return macctx; +} + +const OSSL_DISPATCH ossl_hmac_internal_functions[] = { + { OSSL_FUNC_MAC_NEWCTX, (void (*)(void))hmac_internal_new }, + { OSSL_FUNC_MAC_DUPCTX, (void (*)(void))hmac_dup }, + { OSSL_FUNC_MAC_FREECTX, (void (*)(void))hmac_free }, + { OSSL_FUNC_MAC_INIT, (void (*)(void))hmac_init }, + { OSSL_FUNC_MAC_UPDATE, (void (*)(void))hmac_update }, + { OSSL_FUNC_MAC_FINAL, (void (*)(void))hmac_final }, + { OSSL_FUNC_MAC_GETTABLE_CTX_PARAMS, + (void (*)(void))hmac_gettable_ctx_params }, + { OSSL_FUNC_MAC_GET_CTX_PARAMS, (void (*)(void))hmac_get_ctx_params }, + { OSSL_FUNC_MAC_SETTABLE_CTX_PARAMS, + (void (*)(void))hmac_settable_ctx_params }, + { OSSL_FUNC_MAC_SET_CTX_PARAMS, (void (*)(void))hmac_set_ctx_params }, + OSSL_DISPATCH_END +}; + +#endif /* FIPS_MODULE */ diff --git a/crypto/openssl/providers/implementations/macs/kmac_prov.c b/crypto/openssl/providers/implementations/macs/kmac_prov.c new file mode 100644 index 000000000000..8e583ed8f323 --- /dev/null +++ b/crypto/openssl/providers/implementations/macs/kmac_prov.c @@ -0,0 +1,697 @@ +/* + * Copyright 2018-2024 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 + */ + +/* + * See SP800-185 "Appendix A - KMAC, .... in Terms of Keccak[c]" + * + * Inputs are: + * K = Key (len(K) < 2^2040 bits) + * X = Input + * L = Output length (0 <= L < 2^2040 bits) + * S = Customization String Default="" (len(S) < 2^2040 bits) + * + * KMAC128(K, X, L, S) + * { + * newX = bytepad(encode_string(K), 168) || X || right_encode(L). + * T = bytepad(encode_string("KMAC") || encode_string(S), 168). + * return KECCAK[256](T || newX || 00, L). + * } + * + * KMAC256(K, X, L, S) + * { + * newX = bytepad(encode_string(K), 136) || X || right_encode(L). + * T = bytepad(encode_string("KMAC") || encode_string(S), 136). + * return KECCAK[512](T || newX || 00, L). + * } + * + * KMAC128XOF(K, X, L, S) + * { + * newX = bytepad(encode_string(K), 168) || X || right_encode(0). + * T = bytepad(encode_string("KMAC") || encode_string(S), 168). + * return KECCAK[256](T || newX || 00, L). + * } + * + * KMAC256XOF(K, X, L, S) + * { + * newX = bytepad(encode_string(K), 136) || X || right_encode(0). + * T = bytepad(encode_string("KMAC") || encode_string(S), 136). + * return KECCAK[512](T || newX || 00, L). + * } + * + */ + +#include <stdlib.h> +#include <string.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/params.h> +#include <openssl/evp.h> +#include <openssl/err.h> +#include <openssl/proverr.h> +#include <openssl/fips_names.h> +#include "prov/securitycheck.h" +#include "prov/implementations.h" +#include "prov/provider_ctx.h" +#include "prov/provider_util.h" +#include "prov/providercommon.h" +#include "internal/cryptlib.h" /* ossl_assert */ + +/* + * Forward declaration of everything implemented here. This is not strictly + * necessary for the compiler, but provides an assurance that the signatures + * of the functions in the dispatch table are correct. + */ +static OSSL_FUNC_mac_newctx_fn kmac128_new; +static OSSL_FUNC_mac_newctx_fn kmac256_new; +static OSSL_FUNC_mac_dupctx_fn kmac_dup; +static OSSL_FUNC_mac_freectx_fn kmac_free; +static OSSL_FUNC_mac_gettable_ctx_params_fn kmac_gettable_ctx_params; +static OSSL_FUNC_mac_get_ctx_params_fn kmac_get_ctx_params; +static OSSL_FUNC_mac_settable_ctx_params_fn kmac_settable_ctx_params; +static OSSL_FUNC_mac_set_ctx_params_fn kmac_set_ctx_params; +static OSSL_FUNC_mac_init_fn kmac_init; +static OSSL_FUNC_mac_update_fn kmac_update; +static OSSL_FUNC_mac_final_fn kmac_final; + +#define KMAC_MAX_BLOCKSIZE ((1600 - 128 * 2) / 8) /* 168 */ + +/* + * Length encoding will be a 1 byte size + length in bits (3 bytes max) + * This gives a range of 0..0XFFFFFF bits = 2097151 bytes). + */ +#define KMAC_MAX_OUTPUT_LEN (0xFFFFFF / 8) +#define KMAC_MAX_ENCODED_HEADER_LEN (1 + 3) + +/* + * Restrict the maximum length of the customisation string. This must not + * exceed 64 bits = 8k bytes. + */ +#define KMAC_MAX_CUSTOM 512 + +/* Maximum size of encoded custom string */ +#define KMAC_MAX_CUSTOM_ENCODED (KMAC_MAX_CUSTOM + KMAC_MAX_ENCODED_HEADER_LEN) + +/* Maximum key size in bytes = 512 (4096 bits) */ +#define KMAC_MAX_KEY 512 +#define KMAC_MIN_KEY 4 + +/* + * Maximum Encoded Key size will be padded to a multiple of the blocksize + * i.e KMAC_MAX_KEY + KMAC_MAX_ENCODED_HEADER_LEN = 512 + 4 + * Padded to a multiple of KMAC_MAX_BLOCKSIZE + */ +#define KMAC_MAX_KEY_ENCODED (KMAC_MAX_BLOCKSIZE * 4) + +/* Fixed value of encode_string("KMAC") */ +static const unsigned char kmac_string[] = { + 0x01, 0x20, 0x4B, 0x4D, 0x41, 0x43 +}; + +#define KMAC_FLAG_XOF_MODE 1 + +struct kmac_data_st { + void *provctx; + EVP_MD_CTX *ctx; + PROV_DIGEST digest; + size_t out_len; + size_t key_len; + size_t custom_len; + /* If xof_mode = 1 then we use right_encode(0) */ + int xof_mode; + /* key and custom are stored in encoded form */ + unsigned char key[KMAC_MAX_KEY_ENCODED]; + unsigned char custom[KMAC_MAX_CUSTOM_ENCODED]; +#ifdef FIPS_MODULE + /* + * 'internal' is set to 1 if KMAC is used inside another algorithm such as a + * KDF. In this case it is the parent algorithm that is responsible for + * performing any conditional FIPS indicator related checks for KMAC. + */ + int internal; +#endif + OSSL_FIPS_IND_DECLARE +}; + +static int encode_string(unsigned char *out, size_t out_max_len, size_t *out_len, + const unsigned char *in, size_t in_len); +static int right_encode(unsigned char *out, size_t out_max_len, size_t *out_len, + size_t bits); +static int bytepad(unsigned char *out, size_t *out_len, + const unsigned char *in1, size_t in1_len, + const unsigned char *in2, size_t in2_len, + size_t w); +static int kmac_bytepad_encode_key(unsigned char *out, size_t out_max_len, + size_t *out_len, + const unsigned char *in, size_t in_len, + size_t w); + +static void kmac_free(void *vmacctx) +{ + struct kmac_data_st *kctx = vmacctx; + + if (kctx != NULL) { + EVP_MD_CTX_free(kctx->ctx); + ossl_prov_digest_reset(&kctx->digest); + OPENSSL_cleanse(kctx->key, kctx->key_len); + OPENSSL_cleanse(kctx->custom, kctx->custom_len); + OPENSSL_free(kctx); + } +} + +/* + * We have KMAC implemented as a hash, which we can use instead of + * reimplementing the EVP functionality with direct use of + * keccak_mac_init() and friends. + */ +static struct kmac_data_st *kmac_new(void *provctx) +{ + struct kmac_data_st *kctx; + + if (!ossl_prov_is_running()) + return NULL; + + if ((kctx = OPENSSL_zalloc(sizeof(*kctx))) == NULL + || (kctx->ctx = EVP_MD_CTX_new()) == NULL) { + kmac_free(kctx); + return NULL; + } + kctx->provctx = provctx; + OSSL_FIPS_IND_INIT(kctx) + return kctx; +} + +static void *kmac_fetch_new(void *provctx, const OSSL_PARAM *params) +{ + struct kmac_data_st *kctx = kmac_new(provctx); + int md_size; + + if (kctx == NULL) + return 0; + if (!ossl_prov_digest_load_from_params(&kctx->digest, params, + PROV_LIBCTX_OF(provctx))) { + kmac_free(kctx); + return 0; + } + + md_size = EVP_MD_get_size(ossl_prov_digest_md(&kctx->digest)); + if (md_size <= 0) { + kmac_free(kctx); + return 0; + } + kctx->out_len = (size_t)md_size; + return kctx; +} + +static void *kmac128_new(void *provctx) +{ + static const OSSL_PARAM kmac128_params[] = { + OSSL_PARAM_utf8_string("digest", OSSL_DIGEST_NAME_KECCAK_KMAC128, + sizeof(OSSL_DIGEST_NAME_KECCAK_KMAC128)), + OSSL_PARAM_END + }; + return kmac_fetch_new(provctx, kmac128_params); +} + +static void *kmac256_new(void *provctx) +{ + static const OSSL_PARAM kmac256_params[] = { + OSSL_PARAM_utf8_string("digest", OSSL_DIGEST_NAME_KECCAK_KMAC256, + sizeof(OSSL_DIGEST_NAME_KECCAK_KMAC256)), + OSSL_PARAM_END + }; + return kmac_fetch_new(provctx, kmac256_params); +} + +static void *kmac_dup(void *vsrc) +{ + struct kmac_data_st *src = vsrc; + struct kmac_data_st *dst; + + if (!ossl_prov_is_running()) + return NULL; + + dst = kmac_new(src->provctx); + if (dst == NULL) + return NULL; + + if (!EVP_MD_CTX_copy(dst->ctx, src->ctx) + || !ossl_prov_digest_copy(&dst->digest, &src->digest)) { + kmac_free(dst); + return NULL; + } +#ifdef FIPS_MODULE + dst->internal = src->internal; +#endif + dst->out_len = src->out_len; + dst->key_len = src->key_len; + dst->custom_len = src->custom_len; + dst->xof_mode = src->xof_mode; + memcpy(dst->key, src->key, src->key_len); + memcpy(dst->custom, src->custom, dst->custom_len); + OSSL_FIPS_IND_COPY(dst, src) + + return dst; +} + +static int kmac_setkey(struct kmac_data_st *kctx, const unsigned char *key, + size_t keylen) +{ + const EVP_MD *digest = ossl_prov_digest_md(&kctx->digest); + int w = EVP_MD_get_block_size(digest); + + if (keylen < KMAC_MIN_KEY || keylen > KMAC_MAX_KEY) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } +#ifdef FIPS_MODULE + /* + * Only do the key check if KMAC is fetched directly. + * Other algorithms that embed KMAC such as SSKDF will ignore this check. + */ + if (!kctx->internal) { + int approved = ossl_mac_check_key_size(keylen); + + if (!approved) { + if (!OSSL_FIPS_IND_ON_UNAPPROVED(kctx, OSSL_FIPS_IND_SETTABLE1, + PROV_LIBCTX_OF(kctx->provctx), + "KMAC", "Key size", + ossl_fips_config_kmac_key_check)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + } + } +#endif + if (w <= 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_LENGTH); + return 0; + } + if (!kmac_bytepad_encode_key(kctx->key, sizeof(kctx->key), &kctx->key_len, + key, keylen, (size_t)w)) + return 0; + return 1; +} + +/* + * The init() assumes that any ctrl methods are set beforehand for + * md, key and custom. Setting the fields afterwards will have no + * effect on the output mac. + */ +static int kmac_init(void *vmacctx, const unsigned char *key, + size_t keylen, const OSSL_PARAM params[]) +{ + struct kmac_data_st *kctx = vmacctx; + EVP_MD_CTX *ctx = kctx->ctx; + unsigned char *out; + size_t out_len, block_len; + int res, t; + + if (!ossl_prov_is_running() || !kmac_set_ctx_params(kctx, params)) + return 0; + + if (key != NULL) { + if (!kmac_setkey(kctx, key, keylen)) + return 0; + } else if (kctx->key_len == 0) { + /* Check key has been set */ + ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET); + return 0; + } + if (!EVP_DigestInit_ex(kctx->ctx, ossl_prov_digest_md(&kctx->digest), + NULL)) + return 0; + + t = EVP_MD_get_block_size(ossl_prov_digest_md(&kctx->digest)); + if (t <= 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_LENGTH); + return 0; + } + block_len = t; + + /* Set default custom string if it is not already set */ + if (kctx->custom_len == 0) { + const OSSL_PARAM cparams[] = { + OSSL_PARAM_octet_string(OSSL_MAC_PARAM_CUSTOM, "", 0), + OSSL_PARAM_END + }; + (void)kmac_set_ctx_params(kctx, cparams); + } + + if (!bytepad(NULL, &out_len, kmac_string, sizeof(kmac_string), + kctx->custom, kctx->custom_len, block_len)) { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + return 0; + } + out = OPENSSL_malloc(out_len); + if (out == NULL) + return 0; + res = bytepad(out, NULL, kmac_string, sizeof(kmac_string), + kctx->custom, kctx->custom_len, block_len) + && EVP_DigestUpdate(ctx, out, out_len) + && EVP_DigestUpdate(ctx, kctx->key, kctx->key_len); + OPENSSL_free(out); + return res; +} + +static int kmac_update(void *vmacctx, const unsigned char *data, + size_t datalen) +{ + struct kmac_data_st *kctx = vmacctx; + + return EVP_DigestUpdate(kctx->ctx, data, datalen); +} + +static int kmac_final(void *vmacctx, unsigned char *out, size_t *outl, + size_t outsize) +{ + struct kmac_data_st *kctx = vmacctx; + EVP_MD_CTX *ctx = kctx->ctx; + size_t lbits, len; + unsigned char encoded_outlen[KMAC_MAX_ENCODED_HEADER_LEN]; + int ok; + + if (!ossl_prov_is_running()) + return 0; + + /* KMAC XOF mode sets the encoded length to 0 */ + lbits = (kctx->xof_mode ? 0 : (kctx->out_len * 8)); + + ok = right_encode(encoded_outlen, sizeof(encoded_outlen), &len, lbits) + && EVP_DigestUpdate(ctx, encoded_outlen, len) + && EVP_DigestFinalXOF(ctx, out, kctx->out_len); + *outl = kctx->out_len; + return ok; +} + +static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_MAC_PARAM_SIZE, NULL), + OSSL_PARAM_size_t(OSSL_MAC_PARAM_BLOCK_SIZE, NULL), + OSSL_FIPS_IND_GETTABLE_CTX_PARAM() + OSSL_PARAM_END +}; +static const OSSL_PARAM *kmac_gettable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + return known_gettable_ctx_params; +} + +static int kmac_get_ctx_params(void *vmacctx, OSSL_PARAM params[]) +{ + struct kmac_data_st *kctx = vmacctx; + OSSL_PARAM *p; + int sz; + + if ((p = OSSL_PARAM_locate(params, OSSL_MAC_PARAM_SIZE)) != NULL + && !OSSL_PARAM_set_size_t(p, kctx->out_len)) + return 0; + + if ((p = OSSL_PARAM_locate(params, OSSL_MAC_PARAM_BLOCK_SIZE)) != NULL) { + sz = EVP_MD_block_size(ossl_prov_digest_md(&kctx->digest)); + if (!OSSL_PARAM_set_int(p, sz)) + return 0; + } + + if (!OSSL_FIPS_IND_GET_CTX_PARAM(kctx, params)) + return 0; + + return 1; +} + +static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_int(OSSL_MAC_PARAM_XOF, NULL), + OSSL_PARAM_size_t(OSSL_MAC_PARAM_SIZE, NULL), + OSSL_PARAM_octet_string(OSSL_MAC_PARAM_KEY, NULL, 0), + OSSL_PARAM_octet_string(OSSL_MAC_PARAM_CUSTOM, NULL, 0), + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_MAC_PARAM_FIPS_NO_SHORT_MAC) + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_MAC_PARAM_FIPS_KEY_CHECK) + OSSL_PARAM_END +}; +static const OSSL_PARAM *kmac_settable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + return known_settable_ctx_params; +} + +/* + * The following params can be set any time before final(): + * - "outlen" or "size": The requested output length. + * - "xof": If set, this indicates that right_encoded(0) + * is part of the digested data, otherwise it + * uses right_encoded(requested output length). + * + * All other params should be set before init(). + */ +static int kmac_set_ctx_params(void *vmacctx, const OSSL_PARAM *params) +{ + struct kmac_data_st *kctx = vmacctx; + const OSSL_PARAM *p; + + if (ossl_param_is_empty(params)) + return 1; + + if (!OSSL_FIPS_IND_SET_CTX_PARAM(kctx, OSSL_FIPS_IND_SETTABLE0, params, + OSSL_MAC_PARAM_FIPS_NO_SHORT_MAC)) + return 0; + if (!OSSL_FIPS_IND_SET_CTX_PARAM(kctx, OSSL_FIPS_IND_SETTABLE1, params, + OSSL_MAC_PARAM_FIPS_KEY_CHECK)) + return 0; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_XOF)) != NULL + && !OSSL_PARAM_get_int(p, &kctx->xof_mode)) + return 0; + if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_SIZE)) != NULL) { + size_t sz = 0; + + if (!OSSL_PARAM_get_size_t(p, &sz)) + return 0; + if (sz > KMAC_MAX_OUTPUT_LEN) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_OUTPUT_LENGTH); + return 0; + } +#ifdef FIPS_MODULE + /* SP 800-185 8.4.2 mandates a minimum of 32 bits of output */ + if (sz < 32 / 8) { + if (!OSSL_FIPS_IND_ON_UNAPPROVED(kctx, OSSL_FIPS_IND_SETTABLE0, + PROV_LIBCTX_OF(kctx->provctx), + "KMAC", "length", + ossl_fips_config_no_short_mac)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_OUTPUT_LENGTH); + return 0; + } + } +#endif + kctx->out_len = sz; + } + if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_KEY)) != NULL + && !kmac_setkey(kctx, p->data, p->data_size)) + return 0; + if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_CUSTOM)) + != NULL) { + if (p->data_size > KMAC_MAX_CUSTOM) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_CUSTOM_LENGTH); + return 0; + } + if (!encode_string(kctx->custom, sizeof(kctx->custom), &kctx->custom_len, + p->data, p->data_size)) + return 0; + } + return 1; +} + +/* Encoding/Padding Methods. */ + +/* Returns the number of bytes required to store 'bits' into a byte array */ +static unsigned int get_encode_size(size_t bits) +{ + unsigned int cnt = 0, sz = sizeof(size_t); + + while (bits && (cnt < sz)) { + ++cnt; + bits >>= 8; + } + /* If bits is zero 1 byte is required */ + if (cnt == 0) + cnt = 1; + return cnt; +} + +/* + * Convert an integer into bytes . The number of bytes is appended + * to the end of the buffer. Returns an array of bytes 'out' of size + * *out_len. + * + * e.g if bits = 32, out[2] = { 0x20, 0x01 } + */ +static int right_encode(unsigned char *out, size_t out_max_len, size_t *out_len, + size_t bits) +{ + unsigned int len = get_encode_size(bits); + int i; + + if (len >= out_max_len) { + ERR_raise(ERR_LIB_PROV, PROV_R_LENGTH_TOO_LARGE); + return 0; + } + + /* MSB's are at the start of the bytes array */ + for (i = len - 1; i >= 0; --i) { + out[i] = (unsigned char)(bits & 0xFF); + bits >>= 8; + } + /* Tack the length onto the end */ + out[len] = (unsigned char)len; + + /* The Returned length includes the tacked on byte */ + *out_len = len + 1; + return 1; +} + +/* + * Encodes a string with a left encoded length added. Note that the + * in_len is converted to bits (*8). + * + * e.g- in="KMAC" gives out[6] = { 0x01, 0x20, 0x4B, 0x4D, 0x41, 0x43 } + * len bits K M A C + */ +static int encode_string(unsigned char *out, size_t out_max_len, size_t *out_len, + const unsigned char *in, size_t in_len) +{ + if (in == NULL) { + *out_len = 0; + } else { + size_t i, bits, len, sz; + + bits = 8 * in_len; + len = get_encode_size(bits); + sz = 1 + len + in_len; + + if (sz > out_max_len) { + ERR_raise(ERR_LIB_PROV, PROV_R_LENGTH_TOO_LARGE); + return 0; + } + + out[0] = (unsigned char)len; + for (i = len; i > 0; --i) { + out[i] = (bits & 0xFF); + bits >>= 8; + } + memcpy(out + len + 1, in, in_len); + *out_len = sz; + } + return 1; +} + +/* + * Returns a zero padded encoding of the inputs in1 and an optional + * in2 (can be NULL). The padded output must be a multiple of the blocksize 'w'. + * The value of w is in bytes (< 256). + * + * The returned output is: + * zero_padded(multiple of w, (left_encode(w) || in1 [|| in2]) + */ +static int bytepad(unsigned char *out, size_t *out_len, + const unsigned char *in1, size_t in1_len, + const unsigned char *in2, size_t in2_len, size_t w) +{ + int len; + unsigned char *p = out; + int sz = w; + + if (out == NULL) { + if (out_len == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + sz = 2 + in1_len + (in2 != NULL ? in2_len : 0); + *out_len = (sz + w - 1) / w * w; + return 1; + } + + if (!ossl_assert(w <= 255)) + return 0; + + /* Left encoded w */ + *p++ = 1; + *p++ = (unsigned char)w; + /* || in1 */ + memcpy(p, in1, in1_len); + p += in1_len; + /* [ || in2 ] */ + if (in2 != NULL && in2_len > 0) { + memcpy(p, in2, in2_len); + p += in2_len; + } + /* Figure out the pad size (divisible by w) */ + len = p - out; + sz = (len + w - 1) / w * w; + /* zero pad the end of the buffer */ + if (sz != len) + memset(p, 0, sz - len); + if (out_len != NULL) + *out_len = sz; + return 1; +} + +/* Returns out = bytepad(encode_string(in), w) */ +static int kmac_bytepad_encode_key(unsigned char *out, size_t out_max_len, + size_t *out_len, + const unsigned char *in, size_t in_len, + size_t w) +{ + unsigned char tmp[KMAC_MAX_KEY + KMAC_MAX_ENCODED_HEADER_LEN]; + size_t tmp_len; + + if (!encode_string(tmp, sizeof(tmp), &tmp_len, in, in_len)) + return 0; + if (!bytepad(NULL, out_len, tmp, tmp_len, NULL, 0, w)) + return 0; + if (!ossl_assert(*out_len <= out_max_len)) + return 0; + return bytepad(out, NULL, tmp, tmp_len, NULL, 0, w); +} + +#define IMPLEMENT_KMAC_TABLE(size, funcname, newname) \ +const OSSL_DISPATCH ossl_kmac##size##_##funcname[] = \ +{ \ + { OSSL_FUNC_MAC_NEWCTX, (void (*)(void))kmac##size##_##newname }, \ + { OSSL_FUNC_MAC_DUPCTX, (void (*)(void))kmac_dup }, \ + { OSSL_FUNC_MAC_FREECTX, (void (*)(void))kmac_free }, \ + { OSSL_FUNC_MAC_INIT, (void (*)(void))kmac_init }, \ + { OSSL_FUNC_MAC_UPDATE, (void (*)(void))kmac_update }, \ + { OSSL_FUNC_MAC_FINAL, (void (*)(void))kmac_final }, \ + { OSSL_FUNC_MAC_GETTABLE_CTX_PARAMS, \ + (void (*)(void))kmac_gettable_ctx_params }, \ + { OSSL_FUNC_MAC_GET_CTX_PARAMS, (void (*)(void))kmac_get_ctx_params }, \ + { OSSL_FUNC_MAC_SETTABLE_CTX_PARAMS, \ + (void (*)(void))kmac_settable_ctx_params }, \ + { OSSL_FUNC_MAC_SET_CTX_PARAMS, (void (*)(void))kmac_set_ctx_params }, \ + OSSL_DISPATCH_END \ +} + +#define KMAC_TABLE(size) IMPLEMENT_KMAC_TABLE(size, functions, new) + +KMAC_TABLE(128); +KMAC_TABLE(256); + +#ifdef FIPS_MODULE +# define KMAC_INTERNAL_TABLE(size) \ +static OSSL_FUNC_mac_newctx_fn kmac##size##_internal_new; \ +static void *kmac##size##_internal_new(void *provctx) \ +{ \ + struct kmac_data_st *macctx = kmac##size##_new(provctx); \ + \ + if (macctx != NULL) \ + macctx->internal = 1; \ + return macctx; \ +} \ +IMPLEMENT_KMAC_TABLE(size, internal_functions, internal_new) + +KMAC_INTERNAL_TABLE(128); +KMAC_INTERNAL_TABLE(256); +#endif /* FIPS_MODULE */ diff --git a/crypto/openssl/providers/implementations/macs/poly1305_prov.c b/crypto/openssl/providers/implementations/macs/poly1305_prov.c new file mode 100644 index 000000000000..19974f9289b1 --- /dev/null +++ b/crypto/openssl/providers/implementations/macs/poly1305_prov.c @@ -0,0 +1,187 @@ +/* + * Copyright 2018-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 + */ + +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/params.h> +#include <openssl/evp.h> +#include <openssl/err.h> +#include <openssl/proverr.h> + +#include "crypto/poly1305.h" + +#include "prov/implementations.h" +#include "prov/providercommon.h" + +/* + * Forward declaration of everything implemented here. This is not strictly + * necessary for the compiler, but provides an assurance that the signatures + * of the functions in the dispatch table are correct. + */ +static OSSL_FUNC_mac_newctx_fn poly1305_new; +static OSSL_FUNC_mac_dupctx_fn poly1305_dup; +static OSSL_FUNC_mac_freectx_fn poly1305_free; +static OSSL_FUNC_mac_gettable_params_fn poly1305_gettable_params; +static OSSL_FUNC_mac_get_params_fn poly1305_get_params; +static OSSL_FUNC_mac_settable_ctx_params_fn poly1305_settable_ctx_params; +static OSSL_FUNC_mac_set_ctx_params_fn poly1305_set_ctx_params; +static OSSL_FUNC_mac_init_fn poly1305_init; +static OSSL_FUNC_mac_update_fn poly1305_update; +static OSSL_FUNC_mac_final_fn poly1305_final; + +struct poly1305_data_st { + void *provctx; + int updated; + POLY1305 poly1305; /* Poly1305 data */ +}; + +static void *poly1305_new(void *provctx) +{ + struct poly1305_data_st *ctx; + + if (!ossl_prov_is_running()) + return NULL; + ctx = OPENSSL_zalloc(sizeof(*ctx)); + if (ctx != NULL) + ctx->provctx = provctx; + return ctx; +} + +static void poly1305_free(void *vmacctx) +{ + OPENSSL_free(vmacctx); +} + +static void *poly1305_dup(void *vsrc) +{ + struct poly1305_data_st *src = vsrc; + struct poly1305_data_st *dst; + + if (!ossl_prov_is_running()) + return NULL; + dst = OPENSSL_malloc(sizeof(*dst)); + if (dst == NULL) + return NULL; + + *dst = *src; + return dst; +} + +static size_t poly1305_size(void) +{ + return POLY1305_DIGEST_SIZE; +} + +static int poly1305_setkey(struct poly1305_data_st *ctx, + const unsigned char *key, size_t keylen) +{ + if (keylen != POLY1305_KEY_SIZE) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + Poly1305_Init(&ctx->poly1305, key); + ctx->updated = 0; + return 1; +} + +static int poly1305_init(void *vmacctx, const unsigned char *key, + size_t keylen, const OSSL_PARAM params[]) +{ + struct poly1305_data_st *ctx = vmacctx; + + /* initialize the context in MAC_ctrl function */ + if (!ossl_prov_is_running() || !poly1305_set_ctx_params(ctx, params)) + return 0; + if (key != NULL) + return poly1305_setkey(ctx, key, keylen); + /* no reinitialization of context with the same key is allowed */ + return ctx->updated == 0; +} + +static int poly1305_update(void *vmacctx, const unsigned char *data, + size_t datalen) +{ + struct poly1305_data_st *ctx = vmacctx; + + ctx->updated = 1; + if (datalen == 0) + return 1; + + /* poly1305 has nothing to return in its update function */ + Poly1305_Update(&ctx->poly1305, data, datalen); + return 1; +} + +static int poly1305_final(void *vmacctx, unsigned char *out, size_t *outl, + size_t outsize) +{ + struct poly1305_data_st *ctx = vmacctx; + + if (!ossl_prov_is_running()) + return 0; + ctx->updated = 1; + Poly1305_Final(&ctx->poly1305, out); + *outl = poly1305_size(); + return 1; +} + +static const OSSL_PARAM known_gettable_params[] = { + OSSL_PARAM_size_t(OSSL_MAC_PARAM_SIZE, NULL), + OSSL_PARAM_END +}; +static const OSSL_PARAM *poly1305_gettable_params(void *provctx) +{ + return known_gettable_params; +} + +static int poly1305_get_params(OSSL_PARAM params[]) +{ + OSSL_PARAM *p; + + if ((p = OSSL_PARAM_locate(params, OSSL_MAC_PARAM_SIZE)) != NULL) + return OSSL_PARAM_set_size_t(p, poly1305_size()); + + return 1; +} + +static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_octet_string(OSSL_MAC_PARAM_KEY, NULL, 0), + OSSL_PARAM_END +}; +static const OSSL_PARAM *poly1305_settable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + return known_settable_ctx_params; +} + +static int poly1305_set_ctx_params(void *vmacctx, const OSSL_PARAM *params) +{ + struct poly1305_data_st *ctx = vmacctx; + const OSSL_PARAM *p; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_KEY)) != NULL + && !poly1305_setkey(ctx, p->data, p->data_size)) + return 0; + return 1; +} + +const OSSL_DISPATCH ossl_poly1305_functions[] = { + { OSSL_FUNC_MAC_NEWCTX, (void (*)(void))poly1305_new }, + { OSSL_FUNC_MAC_DUPCTX, (void (*)(void))poly1305_dup }, + { OSSL_FUNC_MAC_FREECTX, (void (*)(void))poly1305_free }, + { OSSL_FUNC_MAC_INIT, (void (*)(void))poly1305_init }, + { OSSL_FUNC_MAC_UPDATE, (void (*)(void))poly1305_update }, + { OSSL_FUNC_MAC_FINAL, (void (*)(void))poly1305_final }, + { OSSL_FUNC_MAC_GETTABLE_PARAMS, (void (*)(void))poly1305_gettable_params }, + { OSSL_FUNC_MAC_GET_PARAMS, (void (*)(void))poly1305_get_params }, + { OSSL_FUNC_MAC_SETTABLE_CTX_PARAMS, + (void (*)(void))poly1305_settable_ctx_params }, + { OSSL_FUNC_MAC_SET_CTX_PARAMS, (void (*)(void))poly1305_set_ctx_params }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/macs/siphash_prov.c b/crypto/openssl/providers/implementations/macs/siphash_prov.c new file mode 100644 index 000000000000..08a393c39c69 --- /dev/null +++ b/crypto/openssl/providers/implementations/macs/siphash_prov.c @@ -0,0 +1,237 @@ +/* + * Copyright 2018-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 + */ + +#include <string.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/params.h> +#include <openssl/evp.h> +#include <openssl/err.h> +#include <openssl/proverr.h> + +#include "crypto/siphash.h" + +#include "prov/implementations.h" +#include "prov/providercommon.h" + +/* + * Forward declaration of everything implemented here. This is not strictly + * necessary for the compiler, but provides an assurance that the signatures + * of the functions in the dispatch table are correct. + */ +static OSSL_FUNC_mac_newctx_fn siphash_new; +static OSSL_FUNC_mac_dupctx_fn siphash_dup; +static OSSL_FUNC_mac_freectx_fn siphash_free; +static OSSL_FUNC_mac_gettable_ctx_params_fn siphash_gettable_ctx_params; +static OSSL_FUNC_mac_get_ctx_params_fn siphash_get_ctx_params; +static OSSL_FUNC_mac_settable_ctx_params_fn siphash_settable_ctx_params; +static OSSL_FUNC_mac_set_ctx_params_fn siphash_set_params; +static OSSL_FUNC_mac_init_fn siphash_init; +static OSSL_FUNC_mac_update_fn siphash_update; +static OSSL_FUNC_mac_final_fn siphash_final; + +struct siphash_data_st { + void *provctx; + SIPHASH siphash; /* Siphash data */ + SIPHASH sipcopy; /* Siphash data copy for reinitialization */ + unsigned int crounds, drounds; +}; + +static unsigned int crounds(struct siphash_data_st *ctx) +{ + return ctx->crounds != 0 ? ctx->crounds : SIPHASH_C_ROUNDS; +} + +static unsigned int drounds(struct siphash_data_st *ctx) +{ + return ctx->drounds != 0 ? ctx->drounds : SIPHASH_D_ROUNDS; +} + +static void *siphash_new(void *provctx) +{ + struct siphash_data_st *ctx; + + if (!ossl_prov_is_running()) + return NULL; + ctx = OPENSSL_zalloc(sizeof(*ctx)); + if (ctx != NULL) + ctx->provctx = provctx; + return ctx; +} + +static void siphash_free(void *vmacctx) +{ + OPENSSL_free(vmacctx); +} + +static void *siphash_dup(void *vsrc) +{ + struct siphash_data_st *ssrc = vsrc; + struct siphash_data_st *sdst; + + if (!ossl_prov_is_running()) + return NULL; + sdst = OPENSSL_malloc(sizeof(*sdst)); + if (sdst == NULL) + return NULL; + + *sdst = *ssrc; + return sdst; +} + +static size_t siphash_size(void *vmacctx) +{ + struct siphash_data_st *ctx = vmacctx; + + return SipHash_hash_size(&ctx->siphash); +} + +static int siphash_setkey(struct siphash_data_st *ctx, + const unsigned char *key, size_t keylen) +{ + int ret; + + if (keylen != SIPHASH_KEY_SIZE) + return 0; + ret = SipHash_Init(&ctx->siphash, key, crounds(ctx), drounds(ctx)); + if (ret) + ctx->sipcopy = ctx->siphash; + return ret; +} + +static int siphash_init(void *vmacctx, const unsigned char *key, size_t keylen, + const OSSL_PARAM params[]) +{ + struct siphash_data_st *ctx = vmacctx; + + if (!ossl_prov_is_running() || !siphash_set_params(ctx, params)) + return 0; + /* + * Without a key, there is not much to do here, + * The actual initialization happens through controls. + */ + if (key == NULL) { + ctx->siphash = ctx->sipcopy; + return 1; + } + return siphash_setkey(ctx, key, keylen); +} + +static int siphash_update(void *vmacctx, const unsigned char *data, + size_t datalen) +{ + struct siphash_data_st *ctx = vmacctx; + + if (datalen == 0) + return 1; + + SipHash_Update(&ctx->siphash, data, datalen); + return 1; +} + +static int siphash_final(void *vmacctx, unsigned char *out, size_t *outl, + size_t outsize) +{ + struct siphash_data_st *ctx = vmacctx; + size_t hlen = siphash_size(ctx); + + if (!ossl_prov_is_running() || outsize < hlen) + return 0; + + *outl = hlen; + return SipHash_Final(&ctx->siphash, out, hlen); +} + +static const OSSL_PARAM *siphash_gettable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_MAC_PARAM_SIZE, NULL), + OSSL_PARAM_uint(OSSL_MAC_PARAM_C_ROUNDS, NULL), + OSSL_PARAM_uint(OSSL_MAC_PARAM_D_ROUNDS, NULL), + OSSL_PARAM_END + }; + + return known_gettable_ctx_params; +} + +static int siphash_get_ctx_params(void *vmacctx, OSSL_PARAM params[]) +{ + struct siphash_data_st *ctx = vmacctx; + OSSL_PARAM *p; + + if ((p = OSSL_PARAM_locate(params, OSSL_MAC_PARAM_SIZE)) != NULL + && !OSSL_PARAM_set_size_t(p, siphash_size(vmacctx))) + return 0; + if ((p = OSSL_PARAM_locate(params, OSSL_MAC_PARAM_C_ROUNDS)) != NULL + && !OSSL_PARAM_set_uint(p, crounds(ctx))) + return 0; + if ((p = OSSL_PARAM_locate(params, OSSL_MAC_PARAM_D_ROUNDS)) != NULL + && !OSSL_PARAM_set_uint(p, drounds(ctx))) + return 0; + return 1; +} + +static const OSSL_PARAM *siphash_settable_ctx_params(ossl_unused void *ctx, + void *provctx) +{ + static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_MAC_PARAM_SIZE, NULL), + OSSL_PARAM_octet_string(OSSL_MAC_PARAM_KEY, NULL, 0), + OSSL_PARAM_uint(OSSL_MAC_PARAM_C_ROUNDS, NULL), + OSSL_PARAM_uint(OSSL_MAC_PARAM_D_ROUNDS, NULL), + OSSL_PARAM_END + }; + + return known_settable_ctx_params; +} + +static int siphash_set_params(void *vmacctx, const OSSL_PARAM *params) +{ + struct siphash_data_st *ctx = vmacctx; + const OSSL_PARAM *p = NULL; + size_t size; + + if (ossl_param_is_empty(params)) + return 1; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_SIZE)) != NULL) { + if (!OSSL_PARAM_get_size_t(p, &size) + || !SipHash_set_hash_size(&ctx->siphash, size) + || !SipHash_set_hash_size(&ctx->sipcopy, size)) + return 0; + } + if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_C_ROUNDS)) != NULL + && !OSSL_PARAM_get_uint(p, &ctx->crounds)) + return 0; + if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_D_ROUNDS)) != NULL + && !OSSL_PARAM_get_uint(p, &ctx->drounds)) + return 0; + if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_KEY)) != NULL) + if (p->data_type != OSSL_PARAM_OCTET_STRING + || !siphash_setkey(ctx, p->data, p->data_size)) + return 0; + return 1; +} + +const OSSL_DISPATCH ossl_siphash_functions[] = { + { OSSL_FUNC_MAC_NEWCTX, (void (*)(void))siphash_new }, + { OSSL_FUNC_MAC_DUPCTX, (void (*)(void))siphash_dup }, + { OSSL_FUNC_MAC_FREECTX, (void (*)(void))siphash_free }, + { OSSL_FUNC_MAC_INIT, (void (*)(void))siphash_init }, + { OSSL_FUNC_MAC_UPDATE, (void (*)(void))siphash_update }, + { OSSL_FUNC_MAC_FINAL, (void (*)(void))siphash_final }, + { OSSL_FUNC_MAC_GETTABLE_CTX_PARAMS, + (void (*)(void))siphash_gettable_ctx_params }, + { OSSL_FUNC_MAC_GET_CTX_PARAMS, (void (*)(void))siphash_get_ctx_params }, + { OSSL_FUNC_MAC_SETTABLE_CTX_PARAMS, + (void (*)(void))siphash_settable_ctx_params }, + { OSSL_FUNC_MAC_SET_CTX_PARAMS, (void (*)(void))siphash_set_params }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/rands/build.info b/crypto/openssl/providers/implementations/rands/build.info new file mode 100644 index 000000000000..98230648e354 --- /dev/null +++ b/crypto/openssl/providers/implementations/rands/build.info @@ -0,0 +1,11 @@ +SUBDIRS=seeding + +$RANDS_GOAL=../../libdefault.a ../../libfips.a + +SOURCE[$RANDS_GOAL]=drbg.c test_rng.c drbg_ctr.c drbg_hash.c drbg_hmac.c +SOURCE[../../libdefault.a]=seed_src.c seed_src_jitter.c +SOURCE[../../libfips.a]=fips_crng_test.c + +IF[{- !$disabled{'fips-jitter'} -}] + SOURCE[../../libfips.a]=seed_src_jitter.c +ENDIF diff --git a/crypto/openssl/providers/implementations/rands/drbg.c b/crypto/openssl/providers/implementations/rands/drbg.c new file mode 100644 index 000000000000..4925a3b400ed --- /dev/null +++ b/crypto/openssl/providers/implementations/rands/drbg.c @@ -0,0 +1,1030 @@ +/* + * Copyright 2011-2025 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 + */ + +#include <string.h> +#include <openssl/crypto.h> +#include <openssl/err.h> +#include <openssl/rand.h> +#include <openssl/evp.h> +#include "crypto/rand.h" +#include <openssl/proverr.h> +#include "drbg_local.h" +#include "internal/thread_once.h" +#include "crypto/cryptlib.h" +#include "prov/seeding.h" +#include "crypto/rand_pool.h" +#include "prov/provider_ctx.h" +#include "prov/providercommon.h" +#include "crypto/context.h" + +/* + * Support framework for NIST SP 800-90A DRBG + * + * See manual page PROV_DRBG(7) for a general overview. + * + * The OpenSSL model is to have new and free functions, and that new + * does all initialization. That is not the NIST model, which has + * instantiation and un-instantiate, and reuse within a new/free + * lifecycle. (No doubt this comes from the desire to support hardware + * DRBG, where allocation of resources on something like an HSM is + * a much bigger deal than just re-setting an allocated resource.) + */ + +/* NIST SP 800-90A DRBG recommends the use of a personalization string. */ +static const char ossl_pers_string[] = DRBG_DEFAULT_PERS_STRING; + +static const OSSL_DISPATCH *find_call(const OSSL_DISPATCH *dispatch, + int function); + +static int rand_drbg_restart(PROV_DRBG *drbg); + +/* + * We interpret a call to this function as a hint only and ignore it. This + * occurs when the EVP layer thinks we should do some locking. In practice + * however we manage for ourselves when we take a lock or not on the basis + * of whether drbg->lock is present or not. + */ +int ossl_drbg_lock(void *vctx) +{ + return 1; +} + +/* Interpreted as a hint only and ignored as for ossl_drbg_lock() */ +void ossl_drbg_unlock(void *vctx) +{ +} + +static int ossl_drbg_lock_parent(PROV_DRBG *drbg) +{ + void *parent = drbg->parent; + + if (parent != NULL + && drbg->parent_lock != NULL + && !drbg->parent_lock(parent)) { + ERR_raise(ERR_LIB_PROV, PROV_R_PARENT_LOCKING_NOT_ENABLED); + return 0; + } + return 1; +} + +static void ossl_drbg_unlock_parent(PROV_DRBG *drbg) +{ + void *parent = drbg->parent; + + if (parent != NULL && drbg->parent_unlock != NULL) + drbg->parent_unlock(parent); +} + +static int get_parent_strength(PROV_DRBG *drbg, unsigned int *str) +{ + OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END }; + void *parent = drbg->parent; + int res; + + if (drbg->parent_get_ctx_params == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_UNABLE_TO_GET_PARENT_STRENGTH); + return 0; + } + + *params = OSSL_PARAM_construct_uint(OSSL_RAND_PARAM_STRENGTH, str); + if (!ossl_drbg_lock_parent(drbg)) { + ERR_raise(ERR_LIB_PROV, PROV_R_UNABLE_TO_LOCK_PARENT); + return 0; + } + res = drbg->parent_get_ctx_params(parent, params); + ossl_drbg_unlock_parent(drbg); + if (!res) { + ERR_raise(ERR_LIB_PROV, PROV_R_UNABLE_TO_GET_PARENT_STRENGTH); + return 0; + } + return 1; +} + +static unsigned int get_parent_reseed_count(PROV_DRBG *drbg) +{ + OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END }; + void *parent = drbg->parent; + unsigned int r = 0; + + *params = OSSL_PARAM_construct_uint(OSSL_DRBG_PARAM_RESEED_COUNTER, &r); + if (!ossl_drbg_lock_parent(drbg)) { + ERR_raise(ERR_LIB_PROV, PROV_R_UNABLE_TO_LOCK_PARENT); + goto err; + } + if (!drbg->parent_get_ctx_params(parent, params)) + r = 0; + ossl_drbg_unlock_parent(drbg); + return r; + + err: + r = tsan_load(&drbg->reseed_counter) - 2; + if (r == 0) + r = UINT_MAX; + return r; +} + +/* + * Implements the get_entropy() callback + * + * If the DRBG has a parent, then the required amount of entropy input + * is fetched using the parent's ossl_prov_drbg_generate(). + * + * Otherwise, the entropy is polled from the system entropy sources + * using ossl_pool_acquire_entropy(). + * + * If a random pool has been added to the DRBG using RAND_add(), then + * its entropy will be used up first. + */ +size_t ossl_drbg_get_seed(void *vdrbg, unsigned char **pout, + int entropy, size_t min_len, + size_t max_len, int prediction_resistance, + const unsigned char *adin, size_t adin_len) +{ + PROV_DRBG *drbg = (PROV_DRBG *)vdrbg; + size_t bytes_needed; + unsigned char *buffer; + + /* Figure out how many bytes we need */ + bytes_needed = entropy >= 0 ? (entropy + 7) / 8 : 0; + if (bytes_needed < min_len) + bytes_needed = min_len; + if (bytes_needed > max_len) + bytes_needed = max_len; + + /* Allocate storage */ + buffer = OPENSSL_secure_malloc(bytes_needed); + if (buffer == NULL) + return 0; + + /* + * Get random data. Include our DRBG address as + * additional input, in order to provide a distinction between + * different DRBG child instances. + * + * Note: using the sizeof() operator on a pointer triggers + * a warning in some static code analyzers, but it's + * intentional and correct here. + */ + if (!ossl_prov_drbg_generate(drbg, buffer, bytes_needed, + drbg->strength, prediction_resistance, + (unsigned char *)&drbg, sizeof(drbg))) { + OPENSSL_secure_clear_free(buffer, bytes_needed); + ERR_raise(ERR_LIB_PROV, PROV_R_GENERATE_ERROR); + return 0; + } + *pout = buffer; + return bytes_needed; +} + +/* Implements the cleanup_entropy() callback */ +void ossl_drbg_clear_seed(ossl_unused void *vdrbg, + unsigned char *out, size_t outlen) +{ + OPENSSL_secure_clear_free(out, outlen); +} + +static size_t get_entropy(PROV_DRBG *drbg, unsigned char **pout, int entropy, + size_t min_len, size_t max_len, + int prediction_resistance) +{ + size_t bytes; + unsigned int p_str; + + if (drbg->parent == NULL) + /* + * In normal use (i.e. OpenSSL's own uses), this is never called. + * This remains purely for legacy reasons. + */ + return ossl_prov_get_entropy(drbg->provctx, pout, entropy, min_len, + max_len); + + if (drbg->parent_get_seed == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_PARENT_CANNOT_SUPPLY_ENTROPY_SEED); + return 0; + } + if (!get_parent_strength(drbg, &p_str)) + return 0; + if (drbg->strength > p_str) { + /* + * We currently don't support the algorithm from NIST SP 800-90C + * 10.1.2 to use a weaker DRBG as source + */ + ERR_raise(ERR_LIB_PROV, PROV_R_PARENT_STRENGTH_TOO_WEAK); + return 0; + } + + /* + * Our lock is already held, but we need to lock our parent before + * generating bits from it. Note: taking the lock will be a no-op + * if locking is not required (while drbg->parent->lock == NULL). + */ + if (!ossl_drbg_lock_parent(drbg)) + return 0; + /* + * Get random data from parent. Include our DRBG address as + * additional input, in order to provide a distinction between + * different DRBG child instances. + * + * Note: using the sizeof() operator on a pointer triggers + * a warning in some static code analyzers, but it's + * intentional and correct here. + */ + bytes = drbg->parent_get_seed(drbg->parent, pout, + entropy > 0 ? entropy : (int) drbg->strength, + min_len, max_len, prediction_resistance, + (unsigned char *)&drbg, sizeof(drbg)); + ossl_drbg_unlock_parent(drbg); + return bytes; +} + +static void cleanup_entropy(PROV_DRBG *drbg, unsigned char *out, size_t outlen) +{ + if (drbg->parent == NULL) { + ossl_prov_cleanup_entropy(drbg->provctx, out, outlen); + } else if (drbg->parent_clear_seed != NULL) { + if (!ossl_drbg_lock_parent(drbg)) + return; + drbg->parent_clear_seed(drbg->parent, out, outlen); + ossl_drbg_unlock_parent(drbg); + } +} + +#ifndef PROV_RAND_GET_RANDOM_NONCE +typedef struct prov_drbg_nonce_global_st { + CRYPTO_RWLOCK *rand_nonce_lock; + int rand_nonce_count; +} PROV_DRBG_NONCE_GLOBAL; + +/* + * drbg_ossl_ctx_new() calls drgb_setup() which calls rand_drbg_get_nonce() + * which needs to get the rand_nonce_lock out of the OSSL_LIB_CTX...but since + * drbg_ossl_ctx_new() hasn't finished running yet we need the rand_nonce_lock + * to be in a different global data object. Otherwise we will go into an + * infinite recursion loop. + */ +void *ossl_prov_drbg_nonce_ctx_new(OSSL_LIB_CTX *libctx) +{ + PROV_DRBG_NONCE_GLOBAL *dngbl = OPENSSL_zalloc(sizeof(*dngbl)); + + if (dngbl == NULL) + return NULL; + + dngbl->rand_nonce_lock = CRYPTO_THREAD_lock_new(); + if (dngbl->rand_nonce_lock == NULL) { + OPENSSL_free(dngbl); + return NULL; + } + + return dngbl; +} + +void ossl_prov_drbg_nonce_ctx_free(void *vdngbl) +{ + PROV_DRBG_NONCE_GLOBAL *dngbl = vdngbl; + + if (dngbl == NULL) + return; + + CRYPTO_THREAD_lock_free(dngbl->rand_nonce_lock); + + OPENSSL_free(dngbl); +} + +/* Get a nonce from the operating system */ +static size_t prov_drbg_get_nonce(PROV_DRBG *drbg, unsigned char **pout, + size_t min_len, size_t max_len) +{ + size_t ret = 0, n; + unsigned char *buf = NULL; + OSSL_LIB_CTX *libctx = ossl_prov_ctx_get0_libctx(drbg->provctx); + PROV_DRBG_NONCE_GLOBAL *dngbl + = ossl_lib_ctx_get_data(libctx, OSSL_LIB_CTX_DRBG_NONCE_INDEX); + struct { + void *drbg; + int count; + } data; + + if (dngbl == NULL) + return 0; + + if (drbg->parent != NULL && drbg->parent_nonce != NULL) { + n = drbg->parent_nonce(drbg->parent, NULL, 0, drbg->min_noncelen, + drbg->max_noncelen); + if (n > 0 && (buf = OPENSSL_malloc(n)) != NULL) { + ret = drbg->parent_nonce(drbg->parent, buf, 0, + drbg->min_noncelen, drbg->max_noncelen); + if (ret == n) { + *pout = buf; + return ret; + } + OPENSSL_free(buf); + } + } + + /* Use the built in nonce source plus some of our specifics */ + memset(&data, 0, sizeof(data)); + data.drbg = drbg; + if (!CRYPTO_atomic_add(&dngbl->rand_nonce_count, 1, &data.count, + dngbl->rand_nonce_lock)) + return 0; + return ossl_prov_get_nonce(drbg->provctx, pout, min_len, max_len, + &data, sizeof(data)); +} +#endif /* PROV_RAND_GET_RANDOM_NONCE */ + +/* + * Instantiate |drbg|, after it has been initialized. Use |pers| and + * |perslen| as prediction-resistance input. + * + * Requires that drbg->lock is already locked for write, if non-null. + * + * Returns 1 on success, 0 on failure. + */ +int ossl_prov_drbg_instantiate(PROV_DRBG *drbg, unsigned int strength, + int prediction_resistance, + const unsigned char *pers, size_t perslen) +{ + unsigned char *nonce = NULL, *entropy = NULL; + size_t noncelen = 0, entropylen = 0; + size_t min_entropy, min_entropylen, max_entropylen; + + if (strength > drbg->strength) { + ERR_raise(ERR_LIB_PROV, PROV_R_INSUFFICIENT_DRBG_STRENGTH); + goto end; + } + min_entropy = drbg->strength; + min_entropylen = drbg->min_entropylen; + max_entropylen = drbg->max_entropylen; + + if (pers == NULL) { + pers = (const unsigned char *)ossl_pers_string; + perslen = sizeof(ossl_pers_string); + } + if (perslen > drbg->max_perslen) { + ERR_raise(ERR_LIB_PROV, PROV_R_PERSONALISATION_STRING_TOO_LONG); + goto end; + } + + if (drbg->state != EVP_RAND_STATE_UNINITIALISED) { + if (drbg->state == EVP_RAND_STATE_ERROR) + ERR_raise(ERR_LIB_PROV, PROV_R_IN_ERROR_STATE); + else + ERR_raise(ERR_LIB_PROV, PROV_R_ALREADY_INSTANTIATED); + goto end; + } + + drbg->state = EVP_RAND_STATE_ERROR; + + if (drbg->min_noncelen > 0) { + if (drbg->parent_nonce != NULL) { + noncelen = drbg->parent_nonce(drbg->parent, NULL, drbg->strength, + drbg->min_noncelen, + drbg->max_noncelen); + if (noncelen == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_ERROR_RETRIEVING_NONCE); + goto end; + } + nonce = OPENSSL_malloc(noncelen); + if (nonce == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_ERROR_RETRIEVING_NONCE); + goto end; + } + if (noncelen != drbg->parent_nonce(drbg->parent, nonce, + drbg->strength, + drbg->min_noncelen, + drbg->max_noncelen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_ERROR_RETRIEVING_NONCE); + goto end; + } +#ifndef PROV_RAND_GET_RANDOM_NONCE + } else if (drbg->parent != NULL) { +#endif + /* + * NIST SP800-90Ar1 section 9.1 says you can combine getting + * the entropy and nonce in 1 call by increasing the entropy + * with 50% and increasing the minimum length to accommodate + * the length of the nonce. We do this in case a nonce is + * required and there is no parental nonce capability. + */ + min_entropy += drbg->strength / 2; + min_entropylen += drbg->min_noncelen; + max_entropylen += drbg->max_noncelen; + } +#ifndef PROV_RAND_GET_RANDOM_NONCE + else { /* parent == NULL */ + noncelen = prov_drbg_get_nonce(drbg, &nonce, drbg->min_noncelen, + drbg->max_noncelen); + if (noncelen < drbg->min_noncelen + || noncelen > drbg->max_noncelen) { + ERR_raise(ERR_LIB_PROV, PROV_R_ERROR_RETRIEVING_NONCE); + goto end; + } + } +#endif + } + + drbg->reseed_next_counter = tsan_load(&drbg->reseed_counter); + if (drbg->reseed_next_counter) { + drbg->reseed_next_counter++; + if (!drbg->reseed_next_counter) + drbg->reseed_next_counter = 1; + } + + entropylen = get_entropy(drbg, &entropy, min_entropy, + min_entropylen, max_entropylen, + prediction_resistance); + if (entropylen < min_entropylen + || entropylen > max_entropylen) { + ERR_raise(ERR_LIB_PROV, PROV_R_ERROR_RETRIEVING_ENTROPY); + goto end; + } + + if (!drbg->instantiate(drbg, entropy, entropylen, nonce, noncelen, + pers, perslen)) { + cleanup_entropy(drbg, entropy, entropylen); + ERR_raise(ERR_LIB_PROV, PROV_R_ERROR_INSTANTIATING_DRBG); + goto end; + } + cleanup_entropy(drbg, entropy, entropylen); + + drbg->state = EVP_RAND_STATE_READY; + drbg->generate_counter = 1; + drbg->reseed_time = time(NULL); + tsan_store(&drbg->reseed_counter, drbg->reseed_next_counter); + + end: + if (nonce != NULL) + ossl_prov_cleanup_nonce(drbg->provctx, nonce, noncelen); + if (drbg->state == EVP_RAND_STATE_READY) + return 1; + return 0; +} + +/* + * Uninstantiate |drbg|. Must be instantiated before it can be used. + * + * Requires that drbg->lock is already locked for write, if non-null. + * + * Returns 1 on success, 0 on failure. + */ +int ossl_prov_drbg_uninstantiate(PROV_DRBG *drbg) +{ + drbg->state = EVP_RAND_STATE_UNINITIALISED; + return 1; +} + +static int ossl_prov_drbg_reseed_unlocked(PROV_DRBG *drbg, + int prediction_resistance, + const unsigned char *ent, + size_t ent_len, + const unsigned char *adin, + size_t adinlen) +{ + unsigned char *entropy = NULL; + size_t entropylen = 0; + + if (!ossl_prov_is_running()) + return 0; + + if (drbg->state != EVP_RAND_STATE_READY) { + /* try to recover from previous errors */ + rand_drbg_restart(drbg); + + if (drbg->state == EVP_RAND_STATE_ERROR) { + ERR_raise(ERR_LIB_PROV, PROV_R_IN_ERROR_STATE); + return 0; + } + if (drbg->state == EVP_RAND_STATE_UNINITIALISED) { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_INSTANTIATED); + return 0; + } + } + + if (ent != NULL) { + if (ent_len < drbg->min_entropylen) { + ERR_raise(ERR_LIB_RAND, RAND_R_ENTROPY_OUT_OF_RANGE); + drbg->state = EVP_RAND_STATE_ERROR; + return 0; + } + if (ent_len > drbg->max_entropylen) { + ERR_raise(ERR_LIB_RAND, RAND_R_ENTROPY_INPUT_TOO_LONG); + drbg->state = EVP_RAND_STATE_ERROR; + return 0; + } + } + + if (adin == NULL) { + adinlen = 0; + } else if (adinlen > drbg->max_adinlen) { + ERR_raise(ERR_LIB_PROV, PROV_R_ADDITIONAL_INPUT_TOO_LONG); + return 0; + } + + drbg->state = EVP_RAND_STATE_ERROR; + + drbg->reseed_next_counter = tsan_load(&drbg->reseed_counter); + if (drbg->reseed_next_counter) { + drbg->reseed_next_counter++; + if (!drbg->reseed_next_counter) + drbg->reseed_next_counter = 1; + } + + if (ent != NULL) { +#ifdef FIPS_MODULE + /* + * NIST SP-800-90A mandates that entropy *shall not* be provided + * by the consuming application. Instead the data is added as additional + * input. + * + * (NIST SP-800-90Ar1, Sections 9.1 and 9.2) + */ + if (!drbg->reseed(drbg, NULL, 0, ent, ent_len)) { + ERR_raise(ERR_LIB_PROV, PROV_R_UNABLE_TO_RESEED); + return 0; + } +#else + if (!drbg->reseed(drbg, ent, ent_len, adin, adinlen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_UNABLE_TO_RESEED); + return 0; + } + /* There isn't much point adding the same additional input twice */ + adin = NULL; + adinlen = 0; +#endif + } + + /* Reseed using our sources in addition */ + entropylen = get_entropy(drbg, &entropy, drbg->strength, + drbg->min_entropylen, drbg->max_entropylen, + prediction_resistance); + if (entropylen < drbg->min_entropylen + || entropylen > drbg->max_entropylen) { + ERR_raise(ERR_LIB_PROV, PROV_R_ERROR_RETRIEVING_ENTROPY); + goto end; + } + + if (!drbg->reseed(drbg, entropy, entropylen, adin, adinlen)) + goto end; + + drbg->state = EVP_RAND_STATE_READY; + drbg->generate_counter = 1; + drbg->reseed_time = time(NULL); + tsan_store(&drbg->reseed_counter, drbg->reseed_next_counter); + if (drbg->parent != NULL) + drbg->parent_reseed_counter = get_parent_reseed_count(drbg); + + end: + cleanup_entropy(drbg, entropy, entropylen); + if (drbg->state == EVP_RAND_STATE_READY) + return 1; + return 0; +} + +/* + * Reseed |drbg|, mixing in the specified data + * + * Acquires the drbg->lock for writing, if non-null. + * + * Returns 1 on success, 0 on failure. + */ +int ossl_prov_drbg_reseed(PROV_DRBG *drbg, int prediction_resistance, + const unsigned char *ent, size_t ent_len, + const unsigned char *adin, size_t adinlen) +{ + int ret; + + if (drbg->lock != NULL && !CRYPTO_THREAD_write_lock(drbg->lock)) + return 0; + + ret = ossl_prov_drbg_reseed_unlocked(drbg, prediction_resistance, ent, + ent_len, adin, adinlen); + + if (drbg->lock != NULL) + CRYPTO_THREAD_unlock(drbg->lock); + + return ret; +} + +/* + * Generate |outlen| bytes into the buffer at |out|. Reseed if we need + * to or if |prediction_resistance| is set. Additional input can be + * sent in |adin| and |adinlen|. + * + * Acquires the drbg->lock for writing if available + * + * Returns 1 on success, 0 on failure. + * + */ +int ossl_prov_drbg_generate(PROV_DRBG *drbg, unsigned char *out, size_t outlen, + unsigned int strength, int prediction_resistance, + const unsigned char *adin, size_t adinlen) +{ + int fork_id; + int reseed_required = 0; + int ret = 0; + + if (!ossl_prov_is_running()) + return 0; + + if (drbg->lock != NULL && !CRYPTO_THREAD_write_lock(drbg->lock)) + return 0; + + if (drbg->state != EVP_RAND_STATE_READY) { + /* try to recover from previous errors */ + rand_drbg_restart(drbg); + + if (drbg->state == EVP_RAND_STATE_ERROR) { + ERR_raise(ERR_LIB_PROV, PROV_R_IN_ERROR_STATE); + goto err; + } + if (drbg->state == EVP_RAND_STATE_UNINITIALISED) { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_INSTANTIATED); + goto err; + } + } + if (strength > drbg->strength) { + ERR_raise(ERR_LIB_PROV, PROV_R_INSUFFICIENT_DRBG_STRENGTH); + goto err; + } + + if (outlen > drbg->max_request) { + ERR_raise(ERR_LIB_PROV, PROV_R_REQUEST_TOO_LARGE_FOR_DRBG); + goto err; + } + if (adinlen > drbg->max_adinlen) { + ERR_raise(ERR_LIB_PROV, PROV_R_ADDITIONAL_INPUT_TOO_LONG); + goto err; + } + + fork_id = openssl_get_fork_id(); + + if (drbg->fork_id != fork_id) { + drbg->fork_id = fork_id; + reseed_required = 1; + } + + if (drbg->reseed_interval > 0) { + if (drbg->generate_counter >= drbg->reseed_interval) + reseed_required = 1; + } + if (drbg->reseed_time_interval > 0) { + time_t now = time(NULL); + if (now < drbg->reseed_time + || now - drbg->reseed_time >= drbg->reseed_time_interval) + reseed_required = 1; + } + if (drbg->parent != NULL + && get_parent_reseed_count(drbg) != drbg->parent_reseed_counter) + reseed_required = 1; + + if (reseed_required || prediction_resistance) { + if (!ossl_prov_drbg_reseed_unlocked(drbg, prediction_resistance, NULL, + 0, adin, adinlen)) { + ERR_raise(ERR_LIB_PROV, PROV_R_RESEED_ERROR); + goto err; + } + adin = NULL; + adinlen = 0; + } + + if (!drbg->generate(drbg, out, outlen, adin, adinlen)) { + drbg->state = EVP_RAND_STATE_ERROR; + ERR_raise(ERR_LIB_PROV, PROV_R_GENERATE_ERROR); + goto err; + } + + drbg->generate_counter++; + + ret = 1; + err: + if (drbg->lock != NULL) + CRYPTO_THREAD_unlock(drbg->lock); + + return ret; +} + +/* + * Restart |drbg|, using the specified entropy or additional input + * + * Tries its best to get the drbg instantiated by all means, + * regardless of its current state. + * + * Optionally, a |buffer| of |len| random bytes can be passed, + * which is assumed to contain at least |entropy| bits of entropy. + * + * If |entropy| > 0, the buffer content is used as entropy input. + * + * If |entropy| == 0, the buffer content is used as additional input + * + * Returns 1 on success, 0 on failure. + * + * This function is used internally only. + */ +static int rand_drbg_restart(PROV_DRBG *drbg) +{ + /* repair error state */ + if (drbg->state == EVP_RAND_STATE_ERROR) + drbg->uninstantiate(drbg); + + /* repair uninitialized state */ + if (drbg->state == EVP_RAND_STATE_UNINITIALISED) + /* reinstantiate drbg */ + ossl_prov_drbg_instantiate(drbg, drbg->strength, 0, NULL, 0); + + return drbg->state == EVP_RAND_STATE_READY; +} + +/* Provider support from here down */ +static const OSSL_DISPATCH *find_call(const OSSL_DISPATCH *dispatch, + int function) +{ + if (dispatch != NULL) + while (dispatch->function_id != 0) { + if (dispatch->function_id == function) + return dispatch; + dispatch++; + } + return NULL; +} + +int ossl_drbg_enable_locking(void *vctx) +{ + PROV_DRBG *drbg = vctx; + + if (drbg != NULL && drbg->lock == NULL) { + if (drbg->parent_enable_locking != NULL) + if (!drbg->parent_enable_locking(drbg->parent)) { + ERR_raise(ERR_LIB_PROV, PROV_R_PARENT_LOCKING_NOT_ENABLED); + return 0; + } + drbg->lock = CRYPTO_THREAD_lock_new(); + if (drbg->lock == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_CREATE_LOCK); + return 0; + } + } + return 1; +} + +/* + * Allocate memory and initialize a new DRBG. The DRBG is allocated on + * the secure heap if |secure| is nonzero and the secure heap is enabled. + * The |parent|, if not NULL, will be used as random source for reseeding. + * This also requires the parent's provider context and the parent's lock. + * + * Returns a pointer to the new DRBG instance on success, NULL on failure. + */ +PROV_DRBG *ossl_rand_drbg_new + (void *provctx, void *parent, const OSSL_DISPATCH *p_dispatch, + int (*dnew)(PROV_DRBG *ctx), + void (*dfree)(void *vctx), + int (*instantiate)(PROV_DRBG *drbg, + const unsigned char *entropy, size_t entropylen, + const unsigned char *nonce, size_t noncelen, + const unsigned char *pers, size_t perslen), + int (*uninstantiate)(PROV_DRBG *ctx), + int (*reseed)(PROV_DRBG *drbg, const unsigned char *ent, size_t ent_len, + const unsigned char *adin, size_t adin_len), + int (*generate)(PROV_DRBG *, unsigned char *out, size_t outlen, + const unsigned char *adin, size_t adin_len)) +{ + PROV_DRBG *drbg; + unsigned int p_str; + const OSSL_DISPATCH *pfunc; + + if (!ossl_prov_is_running()) + return NULL; + + drbg = OPENSSL_zalloc(sizeof(*drbg)); + if (drbg == NULL) + return NULL; + + drbg->provctx = provctx; + drbg->instantiate = instantiate; + drbg->uninstantiate = uninstantiate; + drbg->reseed = reseed; + drbg->generate = generate; + drbg->fork_id = openssl_get_fork_id(); + + /* Extract parent's functions */ + drbg->parent = parent; + if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_ENABLE_LOCKING)) != NULL) + drbg->parent_enable_locking = OSSL_FUNC_rand_enable_locking(pfunc); + if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_LOCK)) != NULL) + drbg->parent_lock = OSSL_FUNC_rand_lock(pfunc); + if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_UNLOCK)) != NULL) + drbg->parent_unlock = OSSL_FUNC_rand_unlock(pfunc); + if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_GET_CTX_PARAMS)) != NULL) + drbg->parent_get_ctx_params = OSSL_FUNC_rand_get_ctx_params(pfunc); + if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_NONCE)) != NULL) + drbg->parent_nonce = OSSL_FUNC_rand_nonce(pfunc); + if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_GET_SEED)) != NULL) + drbg->parent_get_seed = OSSL_FUNC_rand_get_seed(pfunc); + if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_CLEAR_SEED)) != NULL) + drbg->parent_clear_seed = OSSL_FUNC_rand_clear_seed(pfunc); + + /* Set some default maximums up */ + drbg->max_entropylen = DRBG_MAX_LENGTH; + drbg->max_noncelen = DRBG_MAX_LENGTH; + drbg->max_perslen = DRBG_MAX_LENGTH; + drbg->max_adinlen = DRBG_MAX_LENGTH; + drbg->generate_counter = 1; + drbg->reseed_counter = 1; + drbg->reseed_interval = RESEED_INTERVAL; + drbg->reseed_time_interval = TIME_INTERVAL; + + if (!dnew(drbg)) + goto err; + + if (parent != NULL) { + if (!get_parent_strength(drbg, &p_str)) + goto err; + if (drbg->strength > p_str) { + /* + * We currently don't support the algorithm from NIST SP 800-90C + * 10.1.2 to use a weaker DRBG as source + */ + ERR_raise(ERR_LIB_PROV, PROV_R_PARENT_STRENGTH_TOO_WEAK); + goto err; + } + } +#ifdef TSAN_REQUIRES_LOCKING + if (!ossl_drbg_enable_locking(drbg)) + goto err; +#endif + return drbg; + + err: + dfree(drbg); + return NULL; +} + +void ossl_rand_drbg_free(PROV_DRBG *drbg) +{ + if (drbg == NULL) + return; + + CRYPTO_THREAD_lock_free(drbg->lock); + OPENSSL_free(drbg); +} + +/* + * Helper function called by internal DRBG implementations. Assumes that at + * least a read lock has been taken on drbg->lock + */ +int ossl_drbg_get_ctx_params(PROV_DRBG *drbg, OSSL_PARAM params[]) +{ + OSSL_PARAM *p; + + p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_STATE); + if (p != NULL && !OSSL_PARAM_set_int(p, drbg->state)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_STRENGTH); + if (p != NULL && !OSSL_PARAM_set_int(p, drbg->strength)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_MIN_ENTROPYLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, drbg->min_entropylen)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_MAX_ENTROPYLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, drbg->max_entropylen)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_MIN_NONCELEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, drbg->min_noncelen)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_MAX_NONCELEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, drbg->max_noncelen)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_MAX_PERSLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, drbg->max_perslen)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_MAX_ADINLEN); + if (p != NULL && !OSSL_PARAM_set_size_t(p, drbg->max_adinlen)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_RESEED_REQUESTS); + if (p != NULL && !OSSL_PARAM_set_uint(p, drbg->reseed_interval)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_RESEED_TIME); + if (p != NULL && !OSSL_PARAM_set_time_t(p, drbg->reseed_time)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_RESEED_TIME_INTERVAL); + if (p != NULL && !OSSL_PARAM_set_time_t(p, drbg->reseed_time_interval)) + return 0; + if (!OSSL_FIPS_IND_GET_CTX_PARAM(drbg, params)) + return 0; + return 1; +} + +/* + * Helper function to get certain params that require no lock to obtain. Sets + * *complete to 1 if all the params were processed, or 0 otherwise + */ +int ossl_drbg_get_ctx_params_no_lock(PROV_DRBG *drbg, OSSL_PARAM params[], + int *complete) +{ + size_t cnt = 0; + OSSL_PARAM *p; + + /* This value never changes once set */ + p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_MAX_REQUEST); + if (p != NULL) { + if (!OSSL_PARAM_set_size_t(p, drbg->max_request)) + return 0; + cnt++; + } + + /* + * Can be changed by multiple threads, but we tolerate inaccuracies in this + * value. + */ + p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_RESEED_COUNTER); + if (p != NULL) { + if (!OSSL_PARAM_set_uint(p, tsan_load(&drbg->reseed_counter))) + return 0; + cnt++; + } + + if (params[cnt].key == NULL) + *complete = 1; + else + *complete = 0; + + return 1; +} + +int ossl_drbg_set_ctx_params(PROV_DRBG *drbg, const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p; + + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_DRBG_PARAM_RESEED_REQUESTS); + if (p != NULL && !OSSL_PARAM_get_uint(p, &drbg->reseed_interval)) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_DRBG_PARAM_RESEED_TIME_INTERVAL); + if (p != NULL && !OSSL_PARAM_get_time_t(p, &drbg->reseed_time_interval)) + return 0; + + return 1; +} + +#ifdef FIPS_MODULE +static int digest_allowed(const EVP_MD *md) +{ + /* FIPS 140-3 IG D.R limited DRBG digests to a specific set */ + static const char *const allowed_digests[] = { + "SHA1", /* SHA 1 allowed */ + "SHA2-256", "SHA2-512", /* non-truncated SHA2 allowed */ + "SHA3-256", "SHA3-512", /* non-truncated SHA3 allowed */ + }; + size_t i; + + for (i = 0; i < OSSL_NELEM(allowed_digests); i++) { + if (EVP_MD_is_a(md, allowed_digests[i])) + return 1; + } + return 0; +} +#endif + +/* Confirm digest is allowed to be used with a DRBG */ +int ossl_drbg_verify_digest(PROV_DRBG *drbg, OSSL_LIB_CTX *libctx, + const EVP_MD *md) +{ +#ifdef FIPS_MODULE + int approved = digest_allowed(md); + + if (!approved) { + if (!OSSL_FIPS_IND_ON_UNAPPROVED(drbg, OSSL_FIPS_IND_SETTABLE0, + libctx, "DRBG", "Digest", + ossl_fips_config_restricted_drbg_digests)) { + ERR_raise(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED); + return 0; + } + } +#else /* FIPS_MODULE */ + /* Outside of FIPS, any digests that are not XOF are allowed */ + if (EVP_MD_xof(md)) { + ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED); + return 0; + } +#endif /* FIPS_MODULE */ + return 1; +} diff --git a/crypto/openssl/providers/implementations/rands/drbg_ctr.c b/crypto/openssl/providers/implementations/rands/drbg_ctr.c new file mode 100644 index 000000000000..a5c929a2cadc --- /dev/null +++ b/crypto/openssl/providers/implementations/rands/drbg_ctr.c @@ -0,0 +1,857 @@ +/* + * Copyright 2011-2025 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 + */ + +#include <stdlib.h> +#include <string.h> +#include <openssl/crypto.h> +#include <openssl/err.h> +#include <openssl/rand.h> +#include <openssl/aes.h> +#include <openssl/proverr.h> +#include "crypto/modes.h" +#include "internal/thread_once.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" +#include "prov/provider_ctx.h" +#include "drbg_local.h" +#include "crypto/evp.h" +#include "crypto/evp/evp_local.h" +#include "internal/provider.h" +#include "internal/common.h" + +static OSSL_FUNC_rand_newctx_fn drbg_ctr_new_wrapper; +static OSSL_FUNC_rand_freectx_fn drbg_ctr_free; +static OSSL_FUNC_rand_instantiate_fn drbg_ctr_instantiate_wrapper; +static OSSL_FUNC_rand_uninstantiate_fn drbg_ctr_uninstantiate_wrapper; +static OSSL_FUNC_rand_generate_fn drbg_ctr_generate_wrapper; +static OSSL_FUNC_rand_reseed_fn drbg_ctr_reseed_wrapper; +static OSSL_FUNC_rand_settable_ctx_params_fn drbg_ctr_settable_ctx_params; +static OSSL_FUNC_rand_set_ctx_params_fn drbg_ctr_set_ctx_params; +static OSSL_FUNC_rand_gettable_ctx_params_fn drbg_ctr_gettable_ctx_params; +static OSSL_FUNC_rand_get_ctx_params_fn drbg_ctr_get_ctx_params; +static OSSL_FUNC_rand_verify_zeroization_fn drbg_ctr_verify_zeroization; + +static int drbg_ctr_set_ctx_params_locked(void *vctx, const OSSL_PARAM params[]); + +/* + * The state of a DRBG AES-CTR. + */ +typedef struct rand_drbg_ctr_st { + EVP_CIPHER_CTX *ctx_ecb; + EVP_CIPHER_CTX *ctx_ctr; + EVP_CIPHER_CTX *ctx_df; + EVP_CIPHER *cipher_ecb; + EVP_CIPHER *cipher_ctr; + size_t keylen; + int use_df; + unsigned char K[32]; + unsigned char V[16]; + /* Temporary block storage used by ctr_df */ + unsigned char bltmp[16]; + size_t bltmp_pos; + unsigned char KX[48]; +} PROV_DRBG_CTR; + +/* + * Implementation of NIST SP 800-90A CTR DRBG. + */ +static void inc_128(PROV_DRBG_CTR *ctr) +{ + unsigned char *p = &ctr->V[0]; + u32 n = 16, c = 1; + + do { + --n; + c += p[n]; + p[n] = (u8)c; + c >>= 8; + } while (n); +} + +static void ctr_XOR(PROV_DRBG_CTR *ctr, const unsigned char *in, size_t inlen) +{ + size_t i, n; + + if (in == NULL || inlen == 0) + return; + + /* + * Any zero padding will have no effect on the result as we + * are XORing. So just process however much input we have. + */ + n = inlen < ctr->keylen ? inlen : ctr->keylen; + if (!ossl_assert(n <= sizeof(ctr->K))) + return; + for (i = 0; i < n; i++) + ctr->K[i] ^= in[i]; + if (inlen <= ctr->keylen) + return; + + n = inlen - ctr->keylen; + if (n > 16) { + /* Should never happen */ + n = 16; + } + for (i = 0; i < n; i++) + ctr->V[i] ^= in[i + ctr->keylen]; +} + +/* + * Process a complete block using BCC algorithm of SP 800-90A 10.3.3 + */ +__owur static int ctr_BCC_block(PROV_DRBG_CTR *ctr, unsigned char *out, + const unsigned char *in, int len) +{ + int i, outlen = AES_BLOCK_SIZE; + + for (i = 0; i < len; i++) + out[i] ^= in[i]; + + if (!EVP_CipherUpdate(ctr->ctx_df, out, &outlen, out, len) + || outlen != len) + return 0; + return 1; +} + + +/* + * Handle several BCC operations for as much data as we need for K and X + */ +__owur static int ctr_BCC_blocks(PROV_DRBG_CTR *ctr, const unsigned char *in) +{ + unsigned char in_tmp[48]; + unsigned char num_of_blk = 2; + + memcpy(in_tmp, in, 16); + memcpy(in_tmp + 16, in, 16); + if (ctr->keylen != 16) { + memcpy(in_tmp + 32, in, 16); + num_of_blk = 3; + } + return ctr_BCC_block(ctr, ctr->KX, in_tmp, AES_BLOCK_SIZE * num_of_blk); +} + +/* + * Initialise BCC blocks: these have the value 0,1,2 in leftmost positions: + * see 10.3.1 stage 7. + */ +__owur static int ctr_BCC_init(PROV_DRBG_CTR *ctr) +{ + unsigned char bltmp[48] = {0}; + unsigned char num_of_blk; + + memset(ctr->KX, 0, 48); + num_of_blk = ctr->keylen == 16 ? 2 : 3; + bltmp[(AES_BLOCK_SIZE * 1) + 3] = 1; + bltmp[(AES_BLOCK_SIZE * 2) + 3] = 2; + return ctr_BCC_block(ctr, ctr->KX, bltmp, num_of_blk * AES_BLOCK_SIZE); +} + +/* + * Process several blocks into BCC algorithm, some possibly partial + */ +__owur static int ctr_BCC_update(PROV_DRBG_CTR *ctr, + const unsigned char *in, size_t inlen) +{ + if (in == NULL || inlen == 0) + return 1; + + /* If we have partial block handle it first */ + if (ctr->bltmp_pos) { + size_t left = 16 - ctr->bltmp_pos; + + /* If we now have a complete block process it */ + if (inlen >= left) { + memcpy(ctr->bltmp + ctr->bltmp_pos, in, left); + if (!ctr_BCC_blocks(ctr, ctr->bltmp)) + return 0; + ctr->bltmp_pos = 0; + inlen -= left; + in += left; + } + } + + /* Process zero or more complete blocks */ + for (; inlen >= 16; in += 16, inlen -= 16) { + if (!ctr_BCC_blocks(ctr, in)) + return 0; + } + + /* Copy any remaining partial block to the temporary buffer */ + if (inlen > 0) { + memcpy(ctr->bltmp + ctr->bltmp_pos, in, inlen); + ctr->bltmp_pos += inlen; + } + return 1; +} + +__owur static int ctr_BCC_final(PROV_DRBG_CTR *ctr) +{ + if (ctr->bltmp_pos) { + memset(ctr->bltmp + ctr->bltmp_pos, 0, 16 - ctr->bltmp_pos); + if (!ctr_BCC_blocks(ctr, ctr->bltmp)) + return 0; + } + return 1; +} + +__owur static int ctr_df(PROV_DRBG_CTR *ctr, + const unsigned char *in1, size_t in1len, + const unsigned char *in2, size_t in2len, + const unsigned char *in3, size_t in3len) +{ + static unsigned char c80 = 0x80; + size_t inlen; + unsigned char *p = ctr->bltmp; + int outlen = AES_BLOCK_SIZE; + + if (!ctr_BCC_init(ctr)) + return 0; + if (in1 == NULL) + in1len = 0; + if (in2 == NULL) + in2len = 0; + if (in3 == NULL) + in3len = 0; + inlen = in1len + in2len + in3len; + /* Initialise L||N in temporary block */ + *p++ = (inlen >> 24) & 0xff; + *p++ = (inlen >> 16) & 0xff; + *p++ = (inlen >> 8) & 0xff; + *p++ = inlen & 0xff; + + /* NB keylen is at most 32 bytes */ + *p++ = 0; + *p++ = 0; + *p++ = 0; + *p = (unsigned char)((ctr->keylen + 16) & 0xff); + ctr->bltmp_pos = 8; + if (!ctr_BCC_update(ctr, in1, in1len) + || !ctr_BCC_update(ctr, in2, in2len) + || !ctr_BCC_update(ctr, in3, in3len) + || !ctr_BCC_update(ctr, &c80, 1) + || !ctr_BCC_final(ctr)) + return 0; + /* Set up key K */ + if (!EVP_CipherInit_ex(ctr->ctx_ecb, NULL, NULL, ctr->KX, NULL, -1)) + return 0; + /* X follows key K */ + if (!EVP_CipherUpdate(ctr->ctx_ecb, ctr->KX, &outlen, ctr->KX + ctr->keylen, + AES_BLOCK_SIZE) + || outlen != AES_BLOCK_SIZE) + return 0; + if (!EVP_CipherUpdate(ctr->ctx_ecb, ctr->KX + 16, &outlen, ctr->KX, + AES_BLOCK_SIZE) + || outlen != AES_BLOCK_SIZE) + return 0; + if (ctr->keylen != 16) + if (!EVP_CipherUpdate(ctr->ctx_ecb, ctr->KX + 32, &outlen, + ctr->KX + 16, AES_BLOCK_SIZE) + || outlen != AES_BLOCK_SIZE) + return 0; + return 1; +} + +/* + * NB the no-df Update in SP800-90A specifies a constant input length + * of seedlen, however other uses of this algorithm pad the input with + * zeroes if necessary and have up to two parameters XORed together, + * so we handle both cases in this function instead. + */ +__owur static int ctr_update(PROV_DRBG *drbg, + const unsigned char *in1, size_t in1len, + const unsigned char *in2, size_t in2len, + const unsigned char *nonce, size_t noncelen) +{ + PROV_DRBG_CTR *ctr = (PROV_DRBG_CTR *)drbg->data; + int outlen = AES_BLOCK_SIZE; + unsigned char V_tmp[48], out[48]; + unsigned char len; + + /* correct key is already set up. */ + memcpy(V_tmp, ctr->V, 16); + inc_128(ctr); + memcpy(V_tmp + 16, ctr->V, 16); + if (ctr->keylen == 16) { + len = 32; + } else { + inc_128(ctr); + memcpy(V_tmp + 32, ctr->V, 16); + len = 48; + } + if (!EVP_CipherUpdate(ctr->ctx_ecb, out, &outlen, V_tmp, len) + || outlen != len) + return 0; + memcpy(ctr->K, out, ctr->keylen); + memcpy(ctr->V, out + ctr->keylen, 16); + + if (ctr->use_df) { + /* If no input reuse existing derived value */ + if (in1 != NULL || nonce != NULL || in2 != NULL) + if (!ctr_df(ctr, in1, in1len, nonce, noncelen, in2, in2len)) + return 0; + /* If this a reuse input in1len != 0 */ + if (in1len) + ctr_XOR(ctr, ctr->KX, drbg->seedlen); + } else { + ctr_XOR(ctr, in1, in1len); + ctr_XOR(ctr, in2, in2len); + } + + if (!EVP_CipherInit_ex(ctr->ctx_ecb, NULL, NULL, ctr->K, NULL, -1) + || !EVP_CipherInit_ex(ctr->ctx_ctr, NULL, NULL, ctr->K, NULL, -1)) + return 0; + return 1; +} + +static int drbg_ctr_instantiate(PROV_DRBG *drbg, + const unsigned char *entropy, size_t entropylen, + const unsigned char *nonce, size_t noncelen, + const unsigned char *pers, size_t perslen) +{ + PROV_DRBG_CTR *ctr = (PROV_DRBG_CTR *)drbg->data; + + if (entropy == NULL) + return 0; + + memset(ctr->K, 0, sizeof(ctr->K)); + memset(ctr->V, 0, sizeof(ctr->V)); + if (!EVP_CipherInit_ex(ctr->ctx_ecb, NULL, NULL, ctr->K, NULL, -1)) + return 0; + + inc_128(ctr); + if (!ctr_update(drbg, entropy, entropylen, pers, perslen, nonce, noncelen)) + return 0; + return 1; +} + +static int drbg_ctr_instantiate_wrapper(void *vdrbg, unsigned int strength, + int prediction_resistance, + const unsigned char *pstr, + size_t pstr_len, + const OSSL_PARAM params[]) +{ + PROV_DRBG *drbg = (PROV_DRBG *)vdrbg; + int ret = 0; + + if (drbg->lock != NULL && !CRYPTO_THREAD_write_lock(drbg->lock)) + return 0; + + if (!ossl_prov_is_running() + || !drbg_ctr_set_ctx_params_locked(drbg, params)) + goto err; + ret = ossl_prov_drbg_instantiate(drbg, strength, prediction_resistance, + pstr, pstr_len); + err: + if (drbg->lock != NULL) + CRYPTO_THREAD_unlock(drbg->lock); + return ret; +} + +static int drbg_ctr_reseed(PROV_DRBG *drbg, + const unsigned char *entropy, size_t entropylen, + const unsigned char *adin, size_t adinlen) +{ + PROV_DRBG_CTR *ctr = (PROV_DRBG_CTR *)drbg->data; + + if (entropy == NULL) + return 0; + + inc_128(ctr); + if (!ctr_update(drbg, entropy, entropylen, adin, adinlen, NULL, 0)) + return 0; + return 1; +} + +static int drbg_ctr_reseed_wrapper(void *vdrbg, int prediction_resistance, + const unsigned char *ent, size_t ent_len, + const unsigned char *adin, size_t adin_len) +{ + PROV_DRBG *drbg = (PROV_DRBG *)vdrbg; + + return ossl_prov_drbg_reseed(drbg, prediction_resistance, ent, ent_len, + adin, adin_len); +} + +static void ctr96_inc(unsigned char *counter) +{ + u32 n = 12, c = 1; + + do { + --n; + c += counter[n]; + counter[n] = (u8)c; + c >>= 8; + } while (n); +} + +static int drbg_ctr_generate(PROV_DRBG *drbg, + unsigned char *out, size_t outlen, + const unsigned char *adin, size_t adinlen) +{ + PROV_DRBG_CTR *ctr = (PROV_DRBG_CTR *)drbg->data; + unsigned int ctr32, blocks; + int outl, buflen; + + if (adin != NULL && adinlen != 0) { + inc_128(ctr); + + if (!ctr_update(drbg, adin, adinlen, NULL, 0, NULL, 0)) + return 0; + /* This means we reuse derived value */ + if (ctr->use_df) { + adin = NULL; + adinlen = 1; + } + } else { + adinlen = 0; + } + + inc_128(ctr); + + if (outlen == 0) { + inc_128(ctr); + + if (!ctr_update(drbg, adin, adinlen, NULL, 0, NULL, 0)) + return 0; + return 1; + } + + memset(out, 0, outlen); + + do { + if (!EVP_CipherInit_ex(ctr->ctx_ctr, + NULL, NULL, NULL, ctr->V, -1)) + return 0; + + /*- + * outlen has type size_t while EVP_CipherUpdate takes an + * int argument and thus cannot be guaranteed to process more + * than 2^31-1 bytes at a time. We process such huge generate + * requests in 2^30 byte chunks, which is the greatest multiple + * of AES block size lower than or equal to 2^31-1. + */ + buflen = outlen > (1U << 30) ? (1U << 30) : outlen; + blocks = (buflen + 15) / 16; + + ctr32 = GETU32(ctr->V + 12) + blocks; + if (ctr32 < blocks) { + /* 32-bit counter overflow into V. */ + if (ctr32 != 0) { + blocks -= ctr32; + buflen = blocks * 16; + ctr32 = 0; + } + ctr96_inc(ctr->V); + } + PUTU32(ctr->V + 12, ctr32); + + if (!EVP_CipherUpdate(ctr->ctx_ctr, out, &outl, out, buflen) + || outl != buflen) + return 0; + + out += buflen; + outlen -= buflen; + } while (outlen); + + if (!ctr_update(drbg, adin, adinlen, NULL, 0, NULL, 0)) + return 0; + return 1; +} + +static int drbg_ctr_generate_wrapper + (void *vdrbg, unsigned char *out, size_t outlen, + unsigned int strength, int prediction_resistance, + const unsigned char *adin, size_t adin_len) +{ + PROV_DRBG *drbg = (PROV_DRBG *)vdrbg; + + return ossl_prov_drbg_generate(drbg, out, outlen, strength, + prediction_resistance, adin, adin_len); +} + +static int drbg_ctr_uninstantiate(PROV_DRBG *drbg) +{ + PROV_DRBG_CTR *ctr = (PROV_DRBG_CTR *)drbg->data; + + OPENSSL_cleanse(ctr->K, sizeof(ctr->K)); + OPENSSL_cleanse(ctr->V, sizeof(ctr->V)); + OPENSSL_cleanse(ctr->bltmp, sizeof(ctr->bltmp)); + OPENSSL_cleanse(ctr->KX, sizeof(ctr->KX)); + ctr->bltmp_pos = 0; + return ossl_prov_drbg_uninstantiate(drbg); +} + +static int drbg_ctr_uninstantiate_wrapper(void *vdrbg) +{ + PROV_DRBG *drbg = (PROV_DRBG *)vdrbg; + int ret; + + if (drbg->lock != NULL && !CRYPTO_THREAD_write_lock(drbg->lock)) + return 0; + + ret = drbg_ctr_uninstantiate(drbg); + + if (drbg->lock != NULL) + CRYPTO_THREAD_unlock(drbg->lock); + + return ret; +} + +static int drbg_ctr_verify_zeroization(void *vdrbg) +{ + PROV_DRBG *drbg = (PROV_DRBG *)vdrbg; + PROV_DRBG_CTR *ctr = (PROV_DRBG_CTR *)drbg->data; + int ret = 0; + + if (drbg->lock != NULL && !CRYPTO_THREAD_read_lock(drbg->lock)) + return 0; + + PROV_DRBG_VERIFY_ZEROIZATION(ctr->K); + PROV_DRBG_VERIFY_ZEROIZATION(ctr->V); + PROV_DRBG_VERIFY_ZEROIZATION(ctr->bltmp); + PROV_DRBG_VERIFY_ZEROIZATION(ctr->KX); + if (ctr->bltmp_pos != 0) + goto err; + + ret = 1; + err: + if (drbg->lock != NULL) + CRYPTO_THREAD_unlock(drbg->lock); + return ret; +} + +static int drbg_ctr_init_lengths(PROV_DRBG *drbg) +{ + PROV_DRBG_CTR *ctr = (PROV_DRBG_CTR *)drbg->data; + int res = 1; + + /* Maximum number of bits per request = 2^19 = 2^16 bytes */ + drbg->max_request = 1 << 16; + if (ctr->use_df) { + drbg->min_entropylen = 0; + drbg->max_entropylen = DRBG_MAX_LENGTH; + drbg->min_noncelen = 0; + drbg->max_noncelen = DRBG_MAX_LENGTH; + drbg->max_perslen = DRBG_MAX_LENGTH; + drbg->max_adinlen = DRBG_MAX_LENGTH; + + if (ctr->keylen > 0) { + drbg->min_entropylen = ctr->keylen; + drbg->min_noncelen = drbg->min_entropylen / 2; + } + } else { + const size_t len = ctr->keylen > 0 ? drbg->seedlen : DRBG_MAX_LENGTH; + + drbg->min_entropylen = len; + drbg->max_entropylen = len; + /* Nonce not used */ + drbg->min_noncelen = 0; + drbg->max_noncelen = 0; + drbg->max_perslen = len; + drbg->max_adinlen = len; + } + return res; +} + +static int drbg_ctr_init(PROV_DRBG *drbg) +{ + PROV_DRBG_CTR *ctr = (PROV_DRBG_CTR *)drbg->data; + size_t keylen; + + if (ctr->cipher_ctr == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_CIPHER); + return 0; + } + ctr->keylen = keylen = EVP_CIPHER_get_key_length(ctr->cipher_ctr); + if (ctr->ctx_ecb == NULL) + ctr->ctx_ecb = EVP_CIPHER_CTX_new(); + if (ctr->ctx_ctr == NULL) + ctr->ctx_ctr = EVP_CIPHER_CTX_new(); + if (ctr->ctx_ecb == NULL || ctr->ctx_ctr == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_EVP_LIB); + goto err; + } + + if (!EVP_CipherInit_ex(ctr->ctx_ecb, + ctr->cipher_ecb, NULL, NULL, NULL, 1) + || !EVP_CipherInit_ex(ctr->ctx_ctr, + ctr->cipher_ctr, NULL, NULL, NULL, 1)) { + ERR_raise(ERR_LIB_PROV, PROV_R_UNABLE_TO_INITIALISE_CIPHERS); + goto err; + } + + drbg->strength = keylen * 8; + drbg->seedlen = keylen + 16; + + if (ctr->use_df) { + /* df initialisation */ + static const unsigned char df_key[32] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f + }; + + if (ctr->ctx_df == NULL) + ctr->ctx_df = EVP_CIPHER_CTX_new(); + if (ctr->ctx_df == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_EVP_LIB); + goto err; + } + /* Set key schedule for df_key */ + if (!EVP_CipherInit_ex(ctr->ctx_df, + ctr->cipher_ecb, NULL, df_key, NULL, 1)) { + ERR_raise(ERR_LIB_PROV, PROV_R_DERIVATION_FUNCTION_INIT_FAILED); + goto err; + } + } + return drbg_ctr_init_lengths(drbg); + +err: + EVP_CIPHER_CTX_free(ctr->ctx_ecb); + EVP_CIPHER_CTX_free(ctr->ctx_ctr); + ctr->ctx_ecb = ctr->ctx_ctr = NULL; + return 0; +} + +static int drbg_ctr_new(PROV_DRBG *drbg) +{ + PROV_DRBG_CTR *ctr; + + ctr = OPENSSL_secure_zalloc(sizeof(*ctr)); + if (ctr == NULL) + return 0; + + ctr->use_df = 1; + drbg->data = ctr; + OSSL_FIPS_IND_INIT(drbg) + return drbg_ctr_init_lengths(drbg); +} + +static void *drbg_ctr_new_wrapper(void *provctx, void *parent, + const OSSL_DISPATCH *parent_dispatch) +{ + return ossl_rand_drbg_new(provctx, parent, parent_dispatch, + &drbg_ctr_new, &drbg_ctr_free, + &drbg_ctr_instantiate, &drbg_ctr_uninstantiate, + &drbg_ctr_reseed, &drbg_ctr_generate); +} + +static void drbg_ctr_free(void *vdrbg) +{ + PROV_DRBG *drbg = (PROV_DRBG *)vdrbg; + PROV_DRBG_CTR *ctr; + + if (drbg != NULL && (ctr = (PROV_DRBG_CTR *)drbg->data) != NULL) { + EVP_CIPHER_CTX_free(ctr->ctx_ecb); + EVP_CIPHER_CTX_free(ctr->ctx_ctr); + EVP_CIPHER_CTX_free(ctr->ctx_df); + EVP_CIPHER_free(ctr->cipher_ecb); + EVP_CIPHER_free(ctr->cipher_ctr); + + OPENSSL_secure_clear_free(ctr, sizeof(*ctr)); + } + ossl_rand_drbg_free(drbg); +} + +static int drbg_ctr_get_ctx_params(void *vdrbg, OSSL_PARAM params[]) +{ + PROV_DRBG *drbg = (PROV_DRBG *)vdrbg; + PROV_DRBG_CTR *ctr = (PROV_DRBG_CTR *)drbg->data; + OSSL_PARAM *p; + int ret = 0, complete = 0; + + if (!ossl_drbg_get_ctx_params_no_lock(drbg, params, &complete)) + return 0; + + if (complete) + return 1; + + if (drbg->lock != NULL && !CRYPTO_THREAD_read_lock(drbg->lock)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_USE_DF); + if (p != NULL && !OSSL_PARAM_set_int(p, ctr->use_df)) + goto err; + + p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_CIPHER); + if (p != NULL) { + if (ctr->cipher_ctr == NULL + || !OSSL_PARAM_set_utf8_string(p, + EVP_CIPHER_get0_name(ctr->cipher_ctr))) + goto err; + } + + ret = ossl_drbg_get_ctx_params(drbg, params); + err: + if (drbg->lock != NULL) + CRYPTO_THREAD_unlock(drbg->lock); + + return ret; +} + +static const OSSL_PARAM *drbg_ctr_gettable_ctx_params(ossl_unused void *vctx, + ossl_unused void *provctx) +{ + static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_DRBG_PARAM_CIPHER, NULL, 0), + OSSL_PARAM_int(OSSL_DRBG_PARAM_USE_DF, NULL), + OSSL_PARAM_DRBG_GETTABLE_CTX_COMMON, + OSSL_FIPS_IND_GETTABLE_CTX_PARAM() + OSSL_PARAM_END + }; + return known_gettable_ctx_params; +} + +static int drbg_ctr_set_ctx_params_locked(void *vctx, const OSSL_PARAM params[]) +{ + PROV_DRBG *ctx = (PROV_DRBG *)vctx; + PROV_DRBG_CTR *ctr = (PROV_DRBG_CTR *)ctx->data; + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + OSSL_PROVIDER *prov = NULL; + const OSSL_PARAM *p; + char *ecb; + const char *propquery = NULL; + int i, cipher_init = 0; + + if ((p = OSSL_PARAM_locate_const(params, OSSL_DRBG_PARAM_USE_DF)) != NULL + && OSSL_PARAM_get_int(p, &i)) { + /* FIPS errors out in the drbg_ctr_init() call later */ + ctr->use_df = i != 0; + cipher_init = 1; + } + + if ((p = OSSL_PARAM_locate_const(params, + OSSL_DRBG_PARAM_PROPERTIES)) != NULL) { + if (p->data_type != OSSL_PARAM_UTF8_STRING) + return 0; + propquery = (const char *)p->data; + } + + if ((p = OSSL_PARAM_locate_const(params, + OSSL_PROV_PARAM_CORE_PROV_NAME)) != NULL) { + if (p->data_type != OSSL_PARAM_UTF8_STRING) + return 0; + if ((prov = ossl_provider_find(libctx, + (const char *)p->data, 1)) == NULL) + return 0; + } + + if ((p = OSSL_PARAM_locate_const(params, OSSL_DRBG_PARAM_CIPHER)) != NULL) { + const char *base = (const char *)p->data; + size_t ctr_str_len = sizeof("CTR") - 1; + size_t ecb_str_len = sizeof("ECB") - 1; + + if (p->data_type != OSSL_PARAM_UTF8_STRING + || p->data_size < ctr_str_len) { + ossl_provider_free(prov); + return 0; + } + if (OPENSSL_strcasecmp("CTR", base + p->data_size - ctr_str_len) != 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_REQUIRE_CTR_MODE_CIPHER); + ossl_provider_free(prov); + return 0; + } + if ((ecb = OPENSSL_strndup(base, p->data_size)) == NULL) { + ossl_provider_free(prov); + return 0; + } + strcpy(ecb + p->data_size - ecb_str_len, "ECB"); + EVP_CIPHER_free(ctr->cipher_ecb); + EVP_CIPHER_free(ctr->cipher_ctr); + /* + * Try to fetch algorithms from our own provider code, fallback + * to generic fetch only if that fails + */ + (void)ERR_set_mark(); + ctr->cipher_ctr = evp_cipher_fetch_from_prov(prov, base, NULL); + if (ctr->cipher_ctr == NULL) { + (void)ERR_pop_to_mark(); + ctr->cipher_ctr = EVP_CIPHER_fetch(libctx, base, propquery); + } else { + (void)ERR_clear_last_mark(); + } + (void)ERR_set_mark(); + ctr->cipher_ecb = evp_cipher_fetch_from_prov(prov, ecb, NULL); + if (ctr->cipher_ecb == NULL) { + (void)ERR_pop_to_mark(); + ctr->cipher_ecb = EVP_CIPHER_fetch(libctx, ecb, propquery); + } else { + (void)ERR_clear_last_mark(); + } + OPENSSL_free(ecb); + if (ctr->cipher_ctr == NULL || ctr->cipher_ecb == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_UNABLE_TO_FIND_CIPHERS); + ossl_provider_free(prov); + return 0; + } + cipher_init = 1; + } + ossl_provider_free(prov); + + if (cipher_init && !drbg_ctr_init(ctx)) + return 0; + + return ossl_drbg_set_ctx_params(ctx, params); +} + +static int drbg_ctr_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_DRBG *drbg = (PROV_DRBG *)vctx; + int ret; + + if (drbg->lock != NULL && !CRYPTO_THREAD_write_lock(drbg->lock)) + return 0; + + ret = drbg_ctr_set_ctx_params_locked(vctx, params); + + if (drbg->lock != NULL) + CRYPTO_THREAD_unlock(drbg->lock); + + return ret; +} + +static const OSSL_PARAM *drbg_ctr_settable_ctx_params(ossl_unused void *vctx, + ossl_unused void *provctx) +{ + static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_DRBG_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_DRBG_PARAM_CIPHER, NULL, 0), + OSSL_PARAM_int(OSSL_DRBG_PARAM_USE_DF, NULL), + OSSL_PARAM_DRBG_SETTABLE_CTX_COMMON, + OSSL_PARAM_END + }; + return known_settable_ctx_params; +} + +const OSSL_DISPATCH ossl_drbg_ctr_functions[] = { + { OSSL_FUNC_RAND_NEWCTX, (void(*)(void))drbg_ctr_new_wrapper }, + { OSSL_FUNC_RAND_FREECTX, (void(*)(void))drbg_ctr_free }, + { OSSL_FUNC_RAND_INSTANTIATE, + (void(*)(void))drbg_ctr_instantiate_wrapper }, + { OSSL_FUNC_RAND_UNINSTANTIATE, + (void(*)(void))drbg_ctr_uninstantiate_wrapper }, + { OSSL_FUNC_RAND_GENERATE, (void(*)(void))drbg_ctr_generate_wrapper }, + { OSSL_FUNC_RAND_RESEED, (void(*)(void))drbg_ctr_reseed_wrapper }, + { OSSL_FUNC_RAND_ENABLE_LOCKING, (void(*)(void))ossl_drbg_enable_locking }, + { OSSL_FUNC_RAND_LOCK, (void(*)(void))ossl_drbg_lock }, + { OSSL_FUNC_RAND_UNLOCK, (void(*)(void))ossl_drbg_unlock }, + { OSSL_FUNC_RAND_SETTABLE_CTX_PARAMS, + (void(*)(void))drbg_ctr_settable_ctx_params }, + { OSSL_FUNC_RAND_SET_CTX_PARAMS, (void(*)(void))drbg_ctr_set_ctx_params }, + { OSSL_FUNC_RAND_GETTABLE_CTX_PARAMS, + (void(*)(void))drbg_ctr_gettable_ctx_params }, + { OSSL_FUNC_RAND_GET_CTX_PARAMS, (void(*)(void))drbg_ctr_get_ctx_params }, + { OSSL_FUNC_RAND_VERIFY_ZEROIZATION, + (void(*)(void))drbg_ctr_verify_zeroization }, + { OSSL_FUNC_RAND_GET_SEED, (void(*)(void))ossl_drbg_get_seed }, + { OSSL_FUNC_RAND_CLEAR_SEED, (void(*)(void))ossl_drbg_clear_seed }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/rands/drbg_hash.c b/crypto/openssl/providers/implementations/rands/drbg_hash.c new file mode 100644 index 000000000000..8bb831ae3572 --- /dev/null +++ b/crypto/openssl/providers/implementations/rands/drbg_hash.c @@ -0,0 +1,655 @@ +/* + * Copyright 2011-2025 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 + */ + +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <openssl/sha.h> +#include <openssl/crypto.h> +#include <openssl/err.h> +#include <openssl/rand.h> +#include <openssl/core_dispatch.h> +#include <openssl/proverr.h> +#include "internal/thread_once.h" +#include "prov/providercommon.h" +#include "prov/provider_ctx.h" +#include "prov/provider_util.h" +#include "prov/implementations.h" +#include "drbg_local.h" +#include "crypto/evp.h" +#include "crypto/evp/evp_local.h" +#include "internal/provider.h" + +static OSSL_FUNC_rand_newctx_fn drbg_hash_new_wrapper; +static OSSL_FUNC_rand_freectx_fn drbg_hash_free; +static OSSL_FUNC_rand_instantiate_fn drbg_hash_instantiate_wrapper; +static OSSL_FUNC_rand_uninstantiate_fn drbg_hash_uninstantiate_wrapper; +static OSSL_FUNC_rand_generate_fn drbg_hash_generate_wrapper; +static OSSL_FUNC_rand_reseed_fn drbg_hash_reseed_wrapper; +static OSSL_FUNC_rand_settable_ctx_params_fn drbg_hash_settable_ctx_params; +static OSSL_FUNC_rand_set_ctx_params_fn drbg_hash_set_ctx_params; +static OSSL_FUNC_rand_gettable_ctx_params_fn drbg_hash_gettable_ctx_params; +static OSSL_FUNC_rand_get_ctx_params_fn drbg_hash_get_ctx_params; +static OSSL_FUNC_rand_verify_zeroization_fn drbg_hash_verify_zeroization; + +static int drbg_hash_set_ctx_params_locked(void *vctx, const OSSL_PARAM params[]); + +/* 888 bits from SP800-90Ar1 10.1 table 2 */ +#define HASH_PRNG_MAX_SEEDLEN (888/8) + +/* 440 bits from SP800-90Ar1 10.1 table 2 */ +#define HASH_PRNG_SMALL_SEEDLEN (440/8) + +/* Determine what seedlen to use based on the block length */ +#define MAX_BLOCKLEN_USING_SMALL_SEEDLEN (256/8) +#define INBYTE_IGNORE ((unsigned char)0xFF) + +typedef struct rand_drbg_hash_st { + PROV_DIGEST digest; + EVP_MD_CTX *ctx; + size_t blocklen; + unsigned char V[HASH_PRNG_MAX_SEEDLEN]; + unsigned char C[HASH_PRNG_MAX_SEEDLEN]; + /* Temporary value storage: should always exceed max digest length */ + unsigned char vtmp[HASH_PRNG_MAX_SEEDLEN]; +} PROV_DRBG_HASH; + +/* + * SP800-90Ar1 10.3.1 Derivation function using a Hash Function (Hash_df). + * The input string used is composed of: + * inbyte - An optional leading byte (ignore if equal to INBYTE_IGNORE) + * in - input string 1 (A Non NULL value). + * in2 - optional input string (Can be NULL). + * in3 - optional input string (Can be NULL). + * These are concatenated as part of the DigestUpdate process. + */ +static int hash_df(PROV_DRBG *drbg, unsigned char *out, + const unsigned char inbyte, + const unsigned char *in, size_t inlen, + const unsigned char *in2, size_t in2len, + const unsigned char *in3, size_t in3len) +{ + PROV_DRBG_HASH *hash = (PROV_DRBG_HASH *)drbg->data; + EVP_MD_CTX *ctx = hash->ctx; + unsigned char *vtmp = hash->vtmp; + /* tmp = counter || num_bits_returned || [inbyte] */ + unsigned char tmp[1 + 4 + 1]; + int tmp_sz = 0; + size_t outlen = drbg->seedlen; + size_t num_bits_returned = outlen * 8; + /* + * No need to check outlen size here, as the standard only ever needs + * seedlen bytes which is always less than the maximum permitted. + */ + + /* (Step 3) counter = 1 (tmp[0] is the 8 bit counter) */ + tmp[tmp_sz++] = 1; + /* tmp[1..4] is the fixed 32 bit no_of_bits_to_return */ + tmp[tmp_sz++] = (unsigned char)((num_bits_returned >> 24) & 0xff); + tmp[tmp_sz++] = (unsigned char)((num_bits_returned >> 16) & 0xff); + tmp[tmp_sz++] = (unsigned char)((num_bits_returned >> 8) & 0xff); + tmp[tmp_sz++] = (unsigned char)(num_bits_returned & 0xff); + /* Tack the additional input byte onto the end of tmp if it exists */ + if (inbyte != INBYTE_IGNORE) + tmp[tmp_sz++] = inbyte; + + /* (Step 4) */ + for (;;) { + /* + * (Step 4.1) out = out || Hash(tmp || in || [in2] || [in3]) + * (where tmp = counter || num_bits_returned || [inbyte]) + */ + if (!(EVP_DigestInit_ex(ctx, ossl_prov_digest_md(&hash->digest), NULL) + && EVP_DigestUpdate(ctx, tmp, tmp_sz) + && EVP_DigestUpdate(ctx, in, inlen) + && (in2 == NULL || EVP_DigestUpdate(ctx, in2, in2len)) + && (in3 == NULL || EVP_DigestUpdate(ctx, in3, in3len)))) + return 0; + + if (outlen < hash->blocklen) { + if (!EVP_DigestFinal(ctx, vtmp, NULL)) + return 0; + memcpy(out, vtmp, outlen); + OPENSSL_cleanse(vtmp, hash->blocklen); + break; + } else if (!EVP_DigestFinal(ctx, out, NULL)) { + return 0; + } + + outlen -= hash->blocklen; + if (outlen == 0) + break; + /* (Step 4.2) counter++ */ + tmp[0]++; + out += hash->blocklen; + } + return 1; +} + +/* Helper function that just passes 2 input parameters to hash_df() */ +static int hash_df1(PROV_DRBG *drbg, unsigned char *out, + const unsigned char in_byte, + const unsigned char *in1, size_t in1len) +{ + return hash_df(drbg, out, in_byte, in1, in1len, NULL, 0, NULL, 0); +} + +/* + * Add 2 byte buffers together. The first elements in each buffer are the top + * most bytes. The result is stored in the dst buffer. + * The final carry is ignored i.e: dst = (dst + in) mod (2^seedlen_bits). + * where dst size is drbg->seedlen, and inlen <= drbg->seedlen. + */ +static int add_bytes(PROV_DRBG *drbg, unsigned char *dst, + unsigned char *in, size_t inlen) +{ + size_t i; + int result; + const unsigned char *add; + unsigned char carry = 0, *d; + + assert(drbg->seedlen >= 1 && inlen >= 1 && inlen <= drbg->seedlen); + + d = &dst[drbg->seedlen - 1]; + add = &in[inlen - 1]; + + for (i = inlen; i > 0; i--, d--, add--) { + result = *d + *add + carry; + carry = (unsigned char)(result >> 8); + *d = (unsigned char)(result & 0xff); + } + + if (carry != 0) { + /* Add the carry to the top of the dst if inlen is not the same size */ + for (i = drbg->seedlen - inlen; i > 0; --i, d--) { + *d += 1; /* Carry can only be 1 */ + if (*d != 0) /* exit if carry doesn't propagate to the next byte */ + break; + } + } + return 1; +} + +/* V = (V + Hash(inbyte || V || [additional_input]) mod (2^seedlen) */ +static int add_hash_to_v(PROV_DRBG *drbg, unsigned char inbyte, + const unsigned char *adin, size_t adinlen) +{ + PROV_DRBG_HASH *hash = (PROV_DRBG_HASH *)drbg->data; + EVP_MD_CTX *ctx = hash->ctx; + + return EVP_DigestInit_ex(ctx, ossl_prov_digest_md(&hash->digest), NULL) + && EVP_DigestUpdate(ctx, &inbyte, 1) + && EVP_DigestUpdate(ctx, hash->V, drbg->seedlen) + && (adin == NULL || EVP_DigestUpdate(ctx, adin, adinlen)) + && EVP_DigestFinal(ctx, hash->vtmp, NULL) + && add_bytes(drbg, hash->V, hash->vtmp, hash->blocklen); +} + +/* + * The Hashgen() as listed in SP800-90Ar1 10.1.1.4 Hash_DRBG_Generate_Process. + * + * drbg contains the current value of V. + * outlen is the requested number of bytes. + * out is a buffer to return the generated bits. + * + * The algorithm to generate the bits is: + * data = V + * w = NULL + * for (i = 1 to m) { + * W = W || Hash(data) + * data = (data + 1) mod (2^seedlen) + * } + * out = Leftmost(W, outlen) + * + * Returns zero if an error occurs otherwise it returns 1. + */ +static int hash_gen(PROV_DRBG *drbg, unsigned char *out, size_t outlen) +{ + PROV_DRBG_HASH *hash = (PROV_DRBG_HASH *)drbg->data; + unsigned char one = 1; + + if (outlen == 0) + return 1; + memcpy(hash->vtmp, hash->V, drbg->seedlen); + for (;;) { + if (!EVP_DigestInit_ex(hash->ctx, ossl_prov_digest_md(&hash->digest), + NULL) + || !EVP_DigestUpdate(hash->ctx, hash->vtmp, drbg->seedlen)) + return 0; + + if (outlen < hash->blocklen) { + if (!EVP_DigestFinal(hash->ctx, hash->vtmp, NULL)) + return 0; + memcpy(out, hash->vtmp, outlen); + return 1; + } else { + if (!EVP_DigestFinal(hash->ctx, out, NULL)) + return 0; + outlen -= hash->blocklen; + if (outlen == 0) + break; + out += hash->blocklen; + } + add_bytes(drbg, hash->vtmp, &one, 1); + } + return 1; +} + +/* + * SP800-90Ar1 10.1.1.2 Hash_DRBG_Instantiate_Process: + * + * ent is entropy input obtained from a randomness source of length ent_len. + * nonce is a string of bytes of length nonce_len. + * pstr is a personalization string received from an application. May be NULL. + * + * Returns zero if an error occurs otherwise it returns 1. + */ +static int drbg_hash_instantiate(PROV_DRBG *drbg, + const unsigned char *ent, size_t ent_len, + const unsigned char *nonce, size_t nonce_len, + const unsigned char *pstr, size_t pstr_len) +{ + PROV_DRBG_HASH *hash = (PROV_DRBG_HASH *)drbg->data; + + EVP_MD_CTX_free(hash->ctx); + hash->ctx = EVP_MD_CTX_new(); + + /* (Step 1-3) V = Hash_df(entropy||nonce||pers, seedlen) */ + return hash->ctx != NULL + && hash_df(drbg, hash->V, INBYTE_IGNORE, + ent, ent_len, nonce, nonce_len, pstr, pstr_len) + /* (Step 4) C = Hash_df(0x00||V, seedlen) */ + && hash_df1(drbg, hash->C, 0x00, hash->V, drbg->seedlen); +} + +static int drbg_hash_instantiate_wrapper(void *vdrbg, unsigned int strength, + int prediction_resistance, + const unsigned char *pstr, + size_t pstr_len, + const OSSL_PARAM params[]) +{ + PROV_DRBG *drbg = (PROV_DRBG *)vdrbg; + int ret = 0; + + if (drbg->lock != NULL && !CRYPTO_THREAD_write_lock(drbg->lock)) + return 0; + + if (!ossl_prov_is_running() + || !drbg_hash_set_ctx_params_locked(drbg, params)) + goto err; + ret = ossl_prov_drbg_instantiate(drbg, strength, prediction_resistance, + pstr, pstr_len); + err: + if (drbg->lock != NULL) + CRYPTO_THREAD_unlock(drbg->lock); + return ret; +} + +/* + * SP800-90Ar1 10.1.1.3 Hash_DRBG_Reseed_Process: + * + * ent is entropy input bytes obtained from a randomness source. + * addin is additional input received from an application. May be NULL. + * + * Returns zero if an error occurs otherwise it returns 1. + */ +static int drbg_hash_reseed(PROV_DRBG *drbg, + const unsigned char *ent, size_t ent_len, + const unsigned char *adin, size_t adin_len) +{ + PROV_DRBG_HASH *hash = (PROV_DRBG_HASH *)drbg->data; + + /* (Step 1-2) V = Hash_df(0x01 || V || entropy_input || additional_input) */ + /* V about to be updated so use C as output instead */ + if (!hash_df(drbg, hash->C, 0x01, hash->V, drbg->seedlen, ent, ent_len, + adin, adin_len)) + return 0; + memcpy(hash->V, hash->C, drbg->seedlen); + /* (Step 4) C = Hash_df(0x00||V, seedlen) */ + return hash_df1(drbg, hash->C, 0x00, hash->V, drbg->seedlen); +} + +static int drbg_hash_reseed_wrapper(void *vdrbg, int prediction_resistance, + const unsigned char *ent, size_t ent_len, + const unsigned char *adin, size_t adin_len) +{ + PROV_DRBG *drbg = (PROV_DRBG *)vdrbg; + + return ossl_prov_drbg_reseed(drbg, prediction_resistance, ent, ent_len, + adin, adin_len); +} + +/* + * SP800-90Ar1 10.1.1.4 Hash_DRBG_Generate_Process: + * + * Generates pseudo random bytes using the drbg. + * out is a buffer to fill with outlen bytes of pseudo random data. + * addin is additional input received from an application. May be NULL. + * + * Returns zero if an error occurs otherwise it returns 1. + */ +static int drbg_hash_generate(PROV_DRBG *drbg, + unsigned char *out, size_t outlen, + const unsigned char *adin, size_t adin_len) +{ + PROV_DRBG_HASH *hash = (PROV_DRBG_HASH *)drbg->data; + unsigned char counter[4]; + int reseed_counter = drbg->generate_counter; + + counter[0] = (unsigned char)((reseed_counter >> 24) & 0xff); + counter[1] = (unsigned char)((reseed_counter >> 16) & 0xff); + counter[2] = (unsigned char)((reseed_counter >> 8) & 0xff); + counter[3] = (unsigned char)(reseed_counter & 0xff); + + return hash->ctx != NULL + && (adin == NULL + /* (Step 2) if adin != NULL then V = V + Hash(0x02||V||adin) */ + || adin_len == 0 + || add_hash_to_v(drbg, 0x02, adin, adin_len)) + /* (Step 3) Hashgen(outlen, V) */ + && hash_gen(drbg, out, outlen) + /* (Step 4/5) H = V = (V + Hash(0x03||V) mod (2^seedlen_bits) */ + && add_hash_to_v(drbg, 0x03, NULL, 0) + /* (Step 5) V = (V + H + C + reseed_counter) mod (2^seedlen_bits) */ + /* V = (V + C) mod (2^seedlen_bits) */ + && add_bytes(drbg, hash->V, hash->C, drbg->seedlen) + /* V = (V + reseed_counter) mod (2^seedlen_bits) */ + && add_bytes(drbg, hash->V, counter, 4); +} + +static int drbg_hash_generate_wrapper + (void *vdrbg, unsigned char *out, size_t outlen, unsigned int strength, + int prediction_resistance, const unsigned char *adin, size_t adin_len) +{ + PROV_DRBG *drbg = (PROV_DRBG *)vdrbg; + + return ossl_prov_drbg_generate(drbg, out, outlen, strength, + prediction_resistance, adin, adin_len); +} + +static int drbg_hash_uninstantiate(PROV_DRBG *drbg) +{ + PROV_DRBG_HASH *hash = (PROV_DRBG_HASH *)drbg->data; + + OPENSSL_cleanse(hash->V, sizeof(hash->V)); + OPENSSL_cleanse(hash->C, sizeof(hash->C)); + OPENSSL_cleanse(hash->vtmp, sizeof(hash->vtmp)); + return ossl_prov_drbg_uninstantiate(drbg); +} + +static int drbg_hash_uninstantiate_wrapper(void *vdrbg) +{ + PROV_DRBG *drbg = (PROV_DRBG *)vdrbg; + int ret; + + if (drbg->lock != NULL && !CRYPTO_THREAD_write_lock(drbg->lock)) + return 0; + + ret = drbg_hash_uninstantiate(drbg); + + if (drbg->lock != NULL) + CRYPTO_THREAD_unlock(drbg->lock); + + return ret; +} + +static int drbg_hash_verify_zeroization(void *vdrbg) +{ + PROV_DRBG *drbg = (PROV_DRBG *)vdrbg; + PROV_DRBG_HASH *hash = (PROV_DRBG_HASH *)drbg->data; + int ret = 0; + + if (drbg->lock != NULL && !CRYPTO_THREAD_read_lock(drbg->lock)) + return 0; + + PROV_DRBG_VERIFY_ZEROIZATION(hash->V); + PROV_DRBG_VERIFY_ZEROIZATION(hash->C); + PROV_DRBG_VERIFY_ZEROIZATION(hash->vtmp); + + ret = 1; + err: + if (drbg->lock != NULL) + CRYPTO_THREAD_unlock(drbg->lock); + return ret; +} + +static int drbg_hash_new(PROV_DRBG *ctx) +{ + PROV_DRBG_HASH *hash; + + hash = OPENSSL_secure_zalloc(sizeof(*hash)); + if (hash == NULL) + return 0; + + OSSL_FIPS_IND_INIT(ctx) + + ctx->data = hash; + ctx->seedlen = HASH_PRNG_MAX_SEEDLEN; + ctx->max_entropylen = DRBG_MAX_LENGTH; + ctx->max_noncelen = DRBG_MAX_LENGTH; + ctx->max_perslen = DRBG_MAX_LENGTH; + ctx->max_adinlen = DRBG_MAX_LENGTH; + + /* Maximum number of bits per request = 2^19 = 2^16 bytes */ + ctx->max_request = 1 << 16; + return 1; +} + +static void *drbg_hash_new_wrapper(void *provctx, void *parent, + const OSSL_DISPATCH *parent_dispatch) +{ + return ossl_rand_drbg_new(provctx, parent, parent_dispatch, + &drbg_hash_new, &drbg_hash_free, + &drbg_hash_instantiate, &drbg_hash_uninstantiate, + &drbg_hash_reseed, &drbg_hash_generate); +} + +static void drbg_hash_free(void *vdrbg) +{ + PROV_DRBG *drbg = (PROV_DRBG *)vdrbg; + PROV_DRBG_HASH *hash; + + if (drbg != NULL && (hash = (PROV_DRBG_HASH *)drbg->data) != NULL) { + EVP_MD_CTX_free(hash->ctx); + ossl_prov_digest_reset(&hash->digest); + OPENSSL_secure_clear_free(hash, sizeof(*hash)); + } + ossl_rand_drbg_free(drbg); +} + +static int drbg_hash_get_ctx_params(void *vdrbg, OSSL_PARAM params[]) +{ + PROV_DRBG *drbg = (PROV_DRBG *)vdrbg; + PROV_DRBG_HASH *hash = (PROV_DRBG_HASH *)drbg->data; + const EVP_MD *md; + OSSL_PARAM *p; + int ret = 0, complete = 0; + + if (!ossl_drbg_get_ctx_params_no_lock(drbg, params, &complete)) + return 0; + + if (complete) + return 1; + + if (drbg->lock != NULL && !CRYPTO_THREAD_read_lock(drbg->lock)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_DIGEST); + if (p != NULL) { + md = ossl_prov_digest_md(&hash->digest); + if (md == NULL || !OSSL_PARAM_set_utf8_string(p, EVP_MD_get0_name(md))) + goto err; + } + + ret = ossl_drbg_get_ctx_params(drbg, params); + err: + if (drbg->lock != NULL) + CRYPTO_THREAD_unlock(drbg->lock); + + return ret; +} + +static const OSSL_PARAM *drbg_hash_gettable_ctx_params(ossl_unused void *vctx, + ossl_unused void *p_ctx) +{ + static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_DRBG_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_DRBG_GETTABLE_CTX_COMMON, + OSSL_FIPS_IND_GETTABLE_CTX_PARAM() + OSSL_PARAM_END + }; + return known_gettable_ctx_params; +} + +static int drbg_fetch_digest_from_prov(const OSSL_PARAM params[], + OSSL_LIB_CTX *libctx, + EVP_MD **digest) +{ + OSSL_PROVIDER *prov = NULL; + const OSSL_PARAM *p; + EVP_MD *md = NULL; + int ret = 0; + + if (digest == NULL) + return 0; + + if ((p = OSSL_PARAM_locate_const(params, + OSSL_PROV_PARAM_CORE_PROV_NAME)) == NULL) + return 0; + if (p->data_type != OSSL_PARAM_UTF8_STRING) + return 0; + if ((prov = ossl_provider_find(libctx, (const char *)p->data, 1)) == NULL) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_DIGEST); + if (p == NULL) { + ret = 1; + goto done; + } + + if (p->data_type != OSSL_PARAM_UTF8_STRING) + goto done; + + md = evp_digest_fetch_from_prov(prov, (const char *)p->data, NULL); + if (md) { + EVP_MD_free(*digest); + *digest = md; + ret = 1; + } + +done: + ossl_provider_free(prov); + return ret; +} + +static int drbg_hash_set_ctx_params_locked(void *vctx, const OSSL_PARAM params[]) +{ + PROV_DRBG *ctx = (PROV_DRBG *)vctx; + PROV_DRBG_HASH *hash = (PROV_DRBG_HASH *)ctx->data; + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + EVP_MD *prov_md = NULL; + const EVP_MD *md; + int md_size; + + if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE0, params, + OSSL_DRBG_PARAM_FIPS_DIGEST_CHECK)) + return 0; + + /* try to fetch digest from provider */ + (void)ERR_set_mark(); + if (!drbg_fetch_digest_from_prov(params, libctx, &prov_md)) { + (void)ERR_pop_to_mark(); + /* fall back to full implementation search */ + if (!ossl_prov_digest_load_from_params(&hash->digest, params, libctx)) + return 0; + } else { + (void)ERR_clear_last_mark(); + if (prov_md) + ossl_prov_digest_set_md(&hash->digest, prov_md); + } + + md = ossl_prov_digest_md(&hash->digest); + if (md != NULL) { + if (!ossl_drbg_verify_digest(ctx, libctx, md)) + return 0; /* Error already raised for us */ + + /* These are taken from SP 800-90 10.1 Table 2 */ + md_size = EVP_MD_get_size(md); + if (md_size <= 0) + return 0; + hash->blocklen = md_size; + /* See SP800-57 Part1 Rev4 5.6.1 Table 3 */ + ctx->strength = 64 * (hash->blocklen >> 3); + if (ctx->strength > 256) + ctx->strength = 256; + if (hash->blocklen > MAX_BLOCKLEN_USING_SMALL_SEEDLEN) + ctx->seedlen = HASH_PRNG_MAX_SEEDLEN; + else + ctx->seedlen = HASH_PRNG_SMALL_SEEDLEN; + + ctx->min_entropylen = ctx->strength / 8; + ctx->min_noncelen = ctx->min_entropylen / 2; + } + + return ossl_drbg_set_ctx_params(ctx, params); +} + +static int drbg_hash_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_DRBG *drbg = (PROV_DRBG *)vctx; + int ret; + + if (drbg->lock != NULL && !CRYPTO_THREAD_write_lock(drbg->lock)) + return 0; + + ret = drbg_hash_set_ctx_params_locked(vctx, params); + + if (drbg->lock != NULL) + CRYPTO_THREAD_unlock(drbg->lock); + + return ret; +} + +static const OSSL_PARAM *drbg_hash_settable_ctx_params(ossl_unused void *vctx, + ossl_unused void *p_ctx) +{ + static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_DRBG_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_DRBG_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_DRBG_SETTABLE_CTX_COMMON, + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_DRBG_PARAM_FIPS_DIGEST_CHECK) + OSSL_PARAM_END + }; + return known_settable_ctx_params; +} + +const OSSL_DISPATCH ossl_drbg_hash_functions[] = { + { OSSL_FUNC_RAND_NEWCTX, (void(*)(void))drbg_hash_new_wrapper }, + { OSSL_FUNC_RAND_FREECTX, (void(*)(void))drbg_hash_free }, + { OSSL_FUNC_RAND_INSTANTIATE, + (void(*)(void))drbg_hash_instantiate_wrapper }, + { OSSL_FUNC_RAND_UNINSTANTIATE, + (void(*)(void))drbg_hash_uninstantiate_wrapper }, + { OSSL_FUNC_RAND_GENERATE, (void(*)(void))drbg_hash_generate_wrapper }, + { OSSL_FUNC_RAND_RESEED, (void(*)(void))drbg_hash_reseed_wrapper }, + { OSSL_FUNC_RAND_ENABLE_LOCKING, (void(*)(void))ossl_drbg_enable_locking }, + { OSSL_FUNC_RAND_LOCK, (void(*)(void))ossl_drbg_lock }, + { OSSL_FUNC_RAND_UNLOCK, (void(*)(void))ossl_drbg_unlock }, + { OSSL_FUNC_RAND_SETTABLE_CTX_PARAMS, + (void(*)(void))drbg_hash_settable_ctx_params }, + { OSSL_FUNC_RAND_SET_CTX_PARAMS, (void(*)(void))drbg_hash_set_ctx_params }, + { OSSL_FUNC_RAND_GETTABLE_CTX_PARAMS, + (void(*)(void))drbg_hash_gettable_ctx_params }, + { OSSL_FUNC_RAND_GET_CTX_PARAMS, (void(*)(void))drbg_hash_get_ctx_params }, + { OSSL_FUNC_RAND_VERIFY_ZEROIZATION, + (void(*)(void))drbg_hash_verify_zeroization }, + { OSSL_FUNC_RAND_GET_SEED, (void(*)(void))ossl_drbg_get_seed }, + { OSSL_FUNC_RAND_CLEAR_SEED, (void(*)(void))ossl_drbg_clear_seed }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/rands/drbg_hmac.c b/crypto/openssl/providers/implementations/rands/drbg_hmac.c new file mode 100644 index 000000000000..43b3f8766e3d --- /dev/null +++ b/crypto/openssl/providers/implementations/rands/drbg_hmac.c @@ -0,0 +1,579 @@ +/* + * Copyright 2011-2025 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 + */ + +#include <stdlib.h> +#include <string.h> +#include <openssl/crypto.h> +#include <openssl/err.h> +#include <openssl/rand.h> +#include <openssl/proverr.h> +#include "internal/thread_once.h" +#include "prov/providercommon.h" +#include "prov/implementations.h" +#include "prov/provider_ctx.h" +#include "prov/hmac_drbg.h" +#include "drbg_local.h" +#include "crypto/evp.h" +#include "crypto/evp/evp_local.h" +#include "internal/provider.h" + +static OSSL_FUNC_rand_newctx_fn drbg_hmac_new_wrapper; +static OSSL_FUNC_rand_freectx_fn drbg_hmac_free; +static OSSL_FUNC_rand_instantiate_fn drbg_hmac_instantiate_wrapper; +static OSSL_FUNC_rand_uninstantiate_fn drbg_hmac_uninstantiate_wrapper; +static OSSL_FUNC_rand_generate_fn drbg_hmac_generate_wrapper; +static OSSL_FUNC_rand_reseed_fn drbg_hmac_reseed_wrapper; +static OSSL_FUNC_rand_settable_ctx_params_fn drbg_hmac_settable_ctx_params; +static OSSL_FUNC_rand_set_ctx_params_fn drbg_hmac_set_ctx_params; +static OSSL_FUNC_rand_gettable_ctx_params_fn drbg_hmac_gettable_ctx_params; +static OSSL_FUNC_rand_get_ctx_params_fn drbg_hmac_get_ctx_params; +static OSSL_FUNC_rand_verify_zeroization_fn drbg_hmac_verify_zeroization; + +static int drbg_hmac_set_ctx_params_locked(void *vctx, const OSSL_PARAM params[]); + +/* + * Called twice by SP800-90Ar1 10.1.2.2 HMAC_DRBG_Update_Process. + * + * hmac is an object that holds the input/output Key and Value (K and V). + * inbyte is 0x00 on the first call and 0x01 on the second call. + * in1, in2, in3 are optional inputs that can be NULL. + * in1len, in2len, in3len are the lengths of the input buffers. + * + * The returned K,V is: + * hmac->K = HMAC(hmac->K, hmac->V || inbyte || [in1] || [in2] || [in3]) + * hmac->V = HMAC(hmac->K, hmac->V) + * + * Returns zero if an error occurs otherwise it returns 1. + */ +static int do_hmac(PROV_DRBG_HMAC *hmac, unsigned char inbyte, + const unsigned char *in1, size_t in1len, + const unsigned char *in2, size_t in2len, + const unsigned char *in3, size_t in3len) +{ + EVP_MAC_CTX *ctx = hmac->ctx; + + if (!EVP_MAC_init(ctx, hmac->K, hmac->blocklen, NULL) + /* K = HMAC(K, V || inbyte || [in1] || [in2] || [in3]) */ + || !EVP_MAC_update(ctx, hmac->V, hmac->blocklen) + || !EVP_MAC_update(ctx, &inbyte, 1) + || !(in1 == NULL || in1len == 0 || EVP_MAC_update(ctx, in1, in1len)) + || !(in2 == NULL || in2len == 0 || EVP_MAC_update(ctx, in2, in2len)) + || !(in3 == NULL || in3len == 0 || EVP_MAC_update(ctx, in3, in3len)) + || !EVP_MAC_final(ctx, hmac->K, NULL, sizeof(hmac->K))) + return 0; + + /* V = HMAC(K, V) */ + return EVP_MAC_init(ctx, hmac->K, hmac->blocklen, NULL) + && EVP_MAC_update(ctx, hmac->V, hmac->blocklen) + && EVP_MAC_final(ctx, hmac->V, NULL, sizeof(hmac->V)); +} + +/* + * SP800-90Ar1 10.1.2.2 HMAC_DRBG_Update_Process + * + * + * Updates the drbg objects Key(K) and Value(V) using the following algorithm: + * K,V = do_hmac(hmac, 0, in1, in2, in3) + * if (any input is not NULL) + * K,V = do_hmac(hmac, 1, in1, in2, in3) + * + * where in1, in2, in3 are optional input buffers that can be NULL. + * in1len, in2len, in3len are the lengths of the input buffers. + * + * Returns zero if an error occurs otherwise it returns 1. + */ +static int drbg_hmac_update(PROV_DRBG_HMAC *hmac, + const unsigned char *in1, size_t in1len, + const unsigned char *in2, size_t in2len, + const unsigned char *in3, size_t in3len) +{ + /* (Steps 1-2) K = HMAC(K, V||0x00||provided_data). V = HMAC(K,V) */ + if (!do_hmac(hmac, 0x00, in1, in1len, in2, in2len, in3, in3len)) + return 0; + /* (Step 3) If provided_data == NULL then return (K,V) */ + if (in1len == 0 && in2len == 0 && in3len == 0) + return 1; + /* (Steps 4-5) K = HMAC(K, V||0x01||provided_data). V = HMAC(K,V) */ + return do_hmac(hmac, 0x01, in1, in1len, in2, in2len, in3, in3len); +} + +/* + * SP800-90Ar1 10.1.2.3 HMAC_DRBG_Instantiate_Process: + * + * This sets the drbg Key (K) to all zeros, and Value (V) to all 1's. + * and then calls (K,V) = drbg_hmac_update() with input parameters: + * ent = entropy data (Can be NULL) of length ent_len. + * nonce = nonce data (Can be NULL) of length nonce_len. + * pstr = personalization data (Can be NULL) of length pstr_len. + * + * Returns zero if an error occurs otherwise it returns 1. + */ +int ossl_drbg_hmac_init(PROV_DRBG_HMAC *hmac, + const unsigned char *ent, size_t ent_len, + const unsigned char *nonce, size_t nonce_len, + const unsigned char *pstr, size_t pstr_len) +{ + if (hmac->ctx == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MAC); + return 0; + } + + /* (Step 2) Key = 0x00 00...00 */ + memset(hmac->K, 0x00, hmac->blocklen); + /* (Step 3) V = 0x01 01...01 */ + memset(hmac->V, 0x01, hmac->blocklen); + /* (Step 4) (K,V) = HMAC_DRBG_Update(entropy||nonce||pers string, K, V) */ + return drbg_hmac_update(hmac, ent, ent_len, nonce, nonce_len, pstr, + pstr_len); +} +static int drbg_hmac_instantiate(PROV_DRBG *drbg, + const unsigned char *ent, size_t ent_len, + const unsigned char *nonce, size_t nonce_len, + const unsigned char *pstr, size_t pstr_len) +{ + return ossl_drbg_hmac_init((PROV_DRBG_HMAC *)drbg->data, ent, ent_len, + nonce, nonce_len, pstr, pstr_len); +} + +static int drbg_hmac_instantiate_wrapper(void *vdrbg, unsigned int strength, + int prediction_resistance, + const unsigned char *pstr, + size_t pstr_len, + const OSSL_PARAM params[]) +{ + PROV_DRBG *drbg = (PROV_DRBG *)vdrbg; + int ret = 0; + + if (drbg->lock != NULL && !CRYPTO_THREAD_write_lock(drbg->lock)) + return 0; + + if (!ossl_prov_is_running() + || !drbg_hmac_set_ctx_params_locked(drbg, params)) + goto err; + ret = ossl_prov_drbg_instantiate(drbg, strength, prediction_resistance, + pstr, pstr_len); + err: + if (drbg->lock != NULL) + CRYPTO_THREAD_unlock(drbg->lock); + return ret; +} + + +/* + * SP800-90Ar1 10.1.2.4 HMAC_DRBG_Reseed_Process: + * + * Reseeds the drbg's Key (K) and Value (V) by calling + * (K,V) = drbg_hmac_update() with the following input parameters: + * ent = entropy input data (Can be NULL) of length ent_len. + * adin = additional input data (Can be NULL) of length adin_len. + * + * Returns zero if an error occurs otherwise it returns 1. + */ +static int drbg_hmac_reseed(PROV_DRBG *drbg, + const unsigned char *ent, size_t ent_len, + const unsigned char *adin, size_t adin_len) +{ + PROV_DRBG_HMAC *hmac = (PROV_DRBG_HMAC *)drbg->data; + + /* (Step 2) (K,V) = HMAC_DRBG_Update(entropy||additional_input, K, V) */ + return drbg_hmac_update(hmac, ent, ent_len, adin, adin_len, NULL, 0); +} + +static int drbg_hmac_reseed_wrapper(void *vdrbg, int prediction_resistance, + const unsigned char *ent, size_t ent_len, + const unsigned char *adin, size_t adin_len) +{ + PROV_DRBG *drbg = (PROV_DRBG *)vdrbg; + + return ossl_prov_drbg_reseed(drbg, prediction_resistance, ent, ent_len, + adin, adin_len); +} + +/* + * SP800-90Ar1 10.1.2.5 HMAC_DRBG_Generate_Process: + * + * Generates pseudo random bytes and updates the internal K,V for the drbg. + * out is a buffer to fill with outlen bytes of pseudo random data. + * adin is an additional_input string of size adin_len that may be NULL. + * + * Returns zero if an error occurs otherwise it returns 1. + */ +int ossl_drbg_hmac_generate(PROV_DRBG_HMAC *hmac, + unsigned char *out, size_t outlen, + const unsigned char *adin, size_t adin_len) +{ + EVP_MAC_CTX *ctx = hmac->ctx; + const unsigned char *temp = hmac->V; + + /* (Step 2) if adin != NULL then (K,V) = HMAC_DRBG_Update(adin, K, V) */ + if (adin != NULL + && adin_len > 0 + && !drbg_hmac_update(hmac, adin, adin_len, NULL, 0, NULL, 0)) + return 0; + + /* + * (Steps 3-5) temp = NULL + * while (len(temp) < outlen) { + * V = HMAC(K, V) + * temp = temp || V + * } + */ + for (;;) { + if (!EVP_MAC_init(ctx, hmac->K, hmac->blocklen, NULL) + || !EVP_MAC_update(ctx, temp, hmac->blocklen)) + return 0; + + if (outlen > hmac->blocklen) { + if (!EVP_MAC_final(ctx, out, NULL, outlen)) + return 0; + temp = out; + } else { + if (!EVP_MAC_final(ctx, hmac->V, NULL, sizeof(hmac->V))) + return 0; + memcpy(out, hmac->V, outlen); + break; + } + out += hmac->blocklen; + outlen -= hmac->blocklen; + } + /* (Step 6) (K,V) = HMAC_DRBG_Update(adin, K, V) */ + if (!drbg_hmac_update(hmac, adin, adin_len, NULL, 0, NULL, 0)) + return 0; + + return 1; +} + +static int drbg_hmac_generate(PROV_DRBG *drbg, + unsigned char *out, size_t outlen, + const unsigned char *adin, size_t adin_len) +{ + return ossl_drbg_hmac_generate((PROV_DRBG_HMAC *)drbg->data, out, outlen, + adin, adin_len); +} + +static int drbg_hmac_generate_wrapper(void *vdrbg, + unsigned char *out, size_t outlen, unsigned int strength, + int prediction_resistance, const unsigned char *adin, size_t adin_len) +{ + PROV_DRBG *drbg = (PROV_DRBG *)vdrbg; + + return ossl_prov_drbg_generate(drbg, out, outlen, strength, + prediction_resistance, adin, adin_len); +} + +static int drbg_hmac_uninstantiate(PROV_DRBG *drbg) +{ + PROV_DRBG_HMAC *hmac = (PROV_DRBG_HMAC *)drbg->data; + + OPENSSL_cleanse(hmac->K, sizeof(hmac->K)); + OPENSSL_cleanse(hmac->V, sizeof(hmac->V)); + return ossl_prov_drbg_uninstantiate(drbg); +} + +static int drbg_hmac_uninstantiate_wrapper(void *vdrbg) +{ + PROV_DRBG *drbg = (PROV_DRBG *)vdrbg; + int ret; + + if (drbg->lock != NULL && !CRYPTO_THREAD_write_lock(drbg->lock)) + return 0; + + ret = drbg_hmac_uninstantiate(drbg); + + if (drbg->lock != NULL) + CRYPTO_THREAD_unlock(drbg->lock); + + return ret; +} + +static int drbg_hmac_verify_zeroization(void *vdrbg) +{ + PROV_DRBG *drbg = (PROV_DRBG *)vdrbg; + PROV_DRBG_HMAC *hmac = (PROV_DRBG_HMAC *)drbg->data; + int ret = 0; + + if (drbg->lock != NULL && !CRYPTO_THREAD_read_lock(drbg->lock)) + return 0; + + PROV_DRBG_VERIFY_ZEROIZATION(hmac->K); + PROV_DRBG_VERIFY_ZEROIZATION(hmac->V); + + ret = 1; + err: + if (drbg->lock != NULL) + CRYPTO_THREAD_unlock(drbg->lock); + return ret; +} + +static int drbg_hmac_new(PROV_DRBG *drbg) +{ + PROV_DRBG_HMAC *hmac; + + hmac = OPENSSL_secure_zalloc(sizeof(*hmac)); + if (hmac == NULL) + return 0; + + OSSL_FIPS_IND_INIT(drbg) + + drbg->data = hmac; + /* See SP800-57 Part1 Rev4 5.6.1 Table 3 */ + drbg->max_entropylen = DRBG_MAX_LENGTH; + drbg->max_noncelen = DRBG_MAX_LENGTH; + drbg->max_perslen = DRBG_MAX_LENGTH; + drbg->max_adinlen = DRBG_MAX_LENGTH; + + /* Maximum number of bits per request = 2^19 = 2^16 bytes */ + drbg->max_request = 1 << 16; + return 1; +} + +static void *drbg_hmac_new_wrapper(void *provctx, void *parent, + const OSSL_DISPATCH *parent_dispatch) +{ + return ossl_rand_drbg_new(provctx, parent, parent_dispatch, + &drbg_hmac_new, &drbg_hmac_free, + &drbg_hmac_instantiate, &drbg_hmac_uninstantiate, + &drbg_hmac_reseed, &drbg_hmac_generate); +} + +static void drbg_hmac_free(void *vdrbg) +{ + PROV_DRBG *drbg = (PROV_DRBG *)vdrbg; + PROV_DRBG_HMAC *hmac; + + if (drbg != NULL && (hmac = (PROV_DRBG_HMAC *)drbg->data) != NULL) { + EVP_MAC_CTX_free(hmac->ctx); + ossl_prov_digest_reset(&hmac->digest); + OPENSSL_secure_clear_free(hmac, sizeof(*hmac)); + } + ossl_rand_drbg_free(drbg); +} + +static int drbg_hmac_get_ctx_params(void *vdrbg, OSSL_PARAM params[]) +{ + PROV_DRBG *drbg = (PROV_DRBG *)vdrbg; + PROV_DRBG_HMAC *hmac = (PROV_DRBG_HMAC *)drbg->data; + const char *name; + const EVP_MD *md; + OSSL_PARAM *p; + int ret = 0, complete = 0; + + if (!ossl_drbg_get_ctx_params_no_lock(drbg, params, &complete)) + return 0; + + if (complete) + return 1; + + if (drbg->lock != NULL && !CRYPTO_THREAD_read_lock(drbg->lock)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_MAC); + if (p != NULL) { + if (hmac->ctx == NULL) + goto err; + name = EVP_MAC_get0_name(EVP_MAC_CTX_get0_mac(hmac->ctx)); + if (!OSSL_PARAM_set_utf8_string(p, name)) + goto err; + } + + p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_DIGEST); + if (p != NULL) { + md = ossl_prov_digest_md(&hmac->digest); + if (md == NULL || !OSSL_PARAM_set_utf8_string(p, EVP_MD_get0_name(md))) + goto err; + } + + ret = ossl_drbg_get_ctx_params(drbg, params); + err: + if (drbg->lock != NULL) + CRYPTO_THREAD_unlock(drbg->lock); + + return ret; +} + +static const OSSL_PARAM *drbg_hmac_gettable_ctx_params(ossl_unused void *vctx, + ossl_unused void *p_ctx) +{ + static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_DRBG_PARAM_MAC, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_DRBG_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_DRBG_GETTABLE_CTX_COMMON, + OSSL_FIPS_IND_GETTABLE_CTX_PARAM() + OSSL_PARAM_END + }; + return known_gettable_ctx_params; +} + +static int drbg_fetch_algs_from_prov(const OSSL_PARAM params[], + OSSL_LIB_CTX *libctx, + EVP_MAC_CTX **macctx, + EVP_MD **digest) +{ + OSSL_PROVIDER *prov = NULL; + const OSSL_PARAM *p; + EVP_MD *md = NULL; + EVP_MAC *mac = NULL; + int ret = 0; + + if (macctx == NULL || digest == NULL) + return 0; + + if ((p = OSSL_PARAM_locate_const(params, + OSSL_PROV_PARAM_CORE_PROV_NAME)) == NULL) + return 0; + if (p->data_type != OSSL_PARAM_UTF8_STRING) + return 0; + if ((prov = ossl_provider_find(libctx, (const char *)p->data, 1)) == NULL) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_DIGEST); + if (p) { + if (p->data_type != OSSL_PARAM_UTF8_STRING) + goto done; + + md = evp_digest_fetch_from_prov(prov, (const char *)p->data, NULL); + if (md) { + EVP_MD_free(*digest); + *digest = md; + } else { + goto done; + } + } + + p = OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_MAC); + if (p == NULL) { + ret = 1; + goto done; + } + + if (p->data_type != OSSL_PARAM_UTF8_STRING) + goto done; + + EVP_MAC_CTX_free(*macctx); + *macctx = NULL; + + mac = evp_mac_fetch_from_prov(prov, (const char *)p->data, NULL); + if (mac) { + *macctx = EVP_MAC_CTX_new(mac); + /* The context holds on to the MAC */ + EVP_MAC_free(mac); + ret = 1; + } + +done: + ossl_provider_free(prov); + return ret; +} + +static int drbg_hmac_set_ctx_params_locked(void *vctx, const OSSL_PARAM params[]) +{ + PROV_DRBG *ctx = (PROV_DRBG *)vctx; + PROV_DRBG_HMAC *hmac = (PROV_DRBG_HMAC *)ctx->data; + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx); + EVP_MD *prov_md = NULL; + const EVP_MD *md; + int md_size; + + if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE0, params, + OSSL_DRBG_PARAM_FIPS_DIGEST_CHECK)) + return 0; + + /* try to fetch mac and digest from provider */ + (void)ERR_set_mark(); + if (!drbg_fetch_algs_from_prov(params, libctx, &hmac->ctx, &prov_md)) { + (void)ERR_pop_to_mark(); + /* fall back to full implementation search */ + if (!ossl_prov_digest_load_from_params(&hmac->digest, params, libctx)) + return 0; + + if (!ossl_prov_macctx_load_from_params(&hmac->ctx, params, + NULL, NULL, NULL, libctx)) + return 0; + } else { + (void)ERR_clear_last_mark(); + if (prov_md) + ossl_prov_digest_set_md(&hmac->digest, prov_md); + } + + md = ossl_prov_digest_md(&hmac->digest); + if (md != NULL && !ossl_drbg_verify_digest(ctx, libctx, md)) + return 0; /* Error already raised for us */ + + if (md != NULL && hmac->ctx != NULL) { + /* These are taken from SP 800-90 10.1 Table 2 */ + md_size = EVP_MD_get_size(md); + if (md_size <= 0) + return 0; + hmac->blocklen = (size_t)md_size; + /* See SP800-57 Part1 Rev4 5.6.1 Table 3 */ + ctx->strength = 64 * (int)(hmac->blocklen >> 3); + if (ctx->strength > 256) + ctx->strength = 256; + ctx->seedlen = hmac->blocklen; + ctx->min_entropylen = ctx->strength / 8; + ctx->min_noncelen = ctx->min_entropylen / 2; + } + + return ossl_drbg_set_ctx_params(ctx, params); +} + +static int drbg_hmac_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_DRBG *drbg = (PROV_DRBG *)vctx; + int ret; + + if (drbg->lock != NULL && !CRYPTO_THREAD_write_lock(drbg->lock)) + return 0; + + ret = drbg_hmac_set_ctx_params_locked(vctx, params); + + if (drbg->lock != NULL) + CRYPTO_THREAD_unlock(drbg->lock); + + return ret; +} + +static const OSSL_PARAM *drbg_hmac_settable_ctx_params(ossl_unused void *vctx, + ossl_unused void *p_ctx) +{ + static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_DRBG_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_DRBG_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_DRBG_PARAM_MAC, NULL, 0), + OSSL_PARAM_DRBG_SETTABLE_CTX_COMMON, + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_DRBG_PARAM_FIPS_DIGEST_CHECK) + OSSL_PARAM_END + }; + return known_settable_ctx_params; +} + +const OSSL_DISPATCH ossl_drbg_ossl_hmac_functions[] = { + { OSSL_FUNC_RAND_NEWCTX, (void(*)(void))drbg_hmac_new_wrapper }, + { OSSL_FUNC_RAND_FREECTX, (void(*)(void))drbg_hmac_free }, + { OSSL_FUNC_RAND_INSTANTIATE, + (void(*)(void))drbg_hmac_instantiate_wrapper }, + { OSSL_FUNC_RAND_UNINSTANTIATE, + (void(*)(void))drbg_hmac_uninstantiate_wrapper }, + { OSSL_FUNC_RAND_GENERATE, (void(*)(void))drbg_hmac_generate_wrapper }, + { OSSL_FUNC_RAND_RESEED, (void(*)(void))drbg_hmac_reseed_wrapper }, + { OSSL_FUNC_RAND_ENABLE_LOCKING, (void(*)(void))ossl_drbg_enable_locking }, + { OSSL_FUNC_RAND_LOCK, (void(*)(void))ossl_drbg_lock }, + { OSSL_FUNC_RAND_UNLOCK, (void(*)(void))ossl_drbg_unlock }, + { OSSL_FUNC_RAND_SETTABLE_CTX_PARAMS, + (void(*)(void))drbg_hmac_settable_ctx_params }, + { OSSL_FUNC_RAND_SET_CTX_PARAMS, (void(*)(void))drbg_hmac_set_ctx_params }, + { OSSL_FUNC_RAND_GETTABLE_CTX_PARAMS, + (void(*)(void))drbg_hmac_gettable_ctx_params }, + { OSSL_FUNC_RAND_GET_CTX_PARAMS, (void(*)(void))drbg_hmac_get_ctx_params }, + { OSSL_FUNC_RAND_VERIFY_ZEROIZATION, + (void(*)(void))drbg_hmac_verify_zeroization }, + { OSSL_FUNC_RAND_GET_SEED, (void(*)(void))ossl_drbg_get_seed }, + { OSSL_FUNC_RAND_CLEAR_SEED, (void(*)(void))ossl_drbg_clear_seed }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/rands/drbg_local.h b/crypto/openssl/providers/implementations/rands/drbg_local.h new file mode 100644 index 000000000000..6cfd43db2d1d --- /dev/null +++ b/crypto/openssl/providers/implementations/rands/drbg_local.h @@ -0,0 +1,243 @@ +/* + * Copyright 1995-2025 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 + */ + +#ifndef OSSL_CRYPTO_PROV_LOCAL_H +# define OSSL_CRYPTO_PROV_LOCAL_H + +# include <openssl/evp.h> +# include <openssl/core_dispatch.h> +# include <openssl/core_names.h> +# include <openssl/params.h> +# include "internal/tsan_assist.h" +# include "internal/nelem.h" +# include "internal/numbers.h" +# include "prov/provider_ctx.h" +# include "prov/securitycheck.h" + +/* How many times to read the TSC as a randomness source. */ +# define TSC_READ_COUNT 4 + +/* Maximum reseed intervals */ +# define MAX_RESEED_INTERVAL (1 << 24) +# define MAX_RESEED_TIME_INTERVAL (1 << 20) /* approx. 12 days */ + +/* Default reseed intervals */ +# define RESEED_INTERVAL (1 << 8) +# define TIME_INTERVAL (60*60) /* 1 hour */ + +/* + * Maximum input size for the DRBG (entropy, nonce, personalization string) + * + * NIST SP800 90Ar1 allows a maximum of (1 << 35) bits i.e., (1 << 32) bytes. + * + * We lower it to 'only' INT32_MAX bytes, which is equivalent to 2 gigabytes. + */ +# define DRBG_MAX_LENGTH INT32_MAX + +/* The default nonce */ +/* ASCII: "OpenSSL NIST SP 800-90A DRBG", in hex for EBCDIC compatibility */ +#define DRBG_DEFAULT_PERS_STRING "\x4f\x70\x65\x6e\x53\x53\x4c\x20\x4e\x49\x53\x54\x20\x53\x50\x20\x38\x30\x30\x2d\x39\x30\x41\x20\x44\x52\x42\x47" + +typedef struct prov_drbg_st PROV_DRBG; + +/* DRBG status values */ +typedef enum drbg_status_e { + DRBG_UNINITIALISED, + DRBG_READY, + DRBG_ERROR +} DRBG_STATUS; + +/* + * The state of all types of DRBGs. + */ +struct prov_drbg_st { + CRYPTO_RWLOCK *lock; + PROV_CTX *provctx; + + /* Virtual functions are cached here */ + int (*instantiate)(PROV_DRBG *drbg, + const unsigned char *entropy, size_t entropylen, + const unsigned char *nonce, size_t noncelen, + const unsigned char *pers, size_t perslen); + int (*uninstantiate)(PROV_DRBG *ctx); + int (*reseed)(PROV_DRBG *drbg, const unsigned char *ent, size_t ent_len, + const unsigned char *adin, size_t adin_len); + int (*generate)(PROV_DRBG *, unsigned char *out, size_t outlen, + const unsigned char *adin, size_t adin_len); + + /* Parent PROV_RAND and its dispatch table functions */ + void *parent; + OSSL_FUNC_rand_enable_locking_fn *parent_enable_locking; + OSSL_FUNC_rand_lock_fn *parent_lock; + OSSL_FUNC_rand_unlock_fn *parent_unlock; + OSSL_FUNC_rand_get_ctx_params_fn *parent_get_ctx_params; + OSSL_FUNC_rand_nonce_fn *parent_nonce; + OSSL_FUNC_rand_get_seed_fn *parent_get_seed; + OSSL_FUNC_rand_clear_seed_fn *parent_clear_seed; + + /* + * Stores the return value of openssl_get_fork_id() as of when we last + * reseeded. The DRBG reseeds automatically whenever drbg->fork_id != + * openssl_get_fork_id(). Used to provide fork-safety and reseed this + * DRBG in the child process. + */ + int fork_id; + unsigned short flags; /* various external flags */ + + /* + * The following parameters are setup by the per-type "init" function. + * + * The supported types and their init functions are: + * (1) CTR_DRBG: drbg_ctr_init(). + * (2) HMAC_DRBG: drbg_hmac_init(). + * (3) HASH_DRBG: drbg_hash_init(). + * + * The parameters are closely related to the ones described in + * section '10.2.1 CTR_DRBG' of [NIST SP 800-90Ar1], with one + * crucial difference: In the NIST standard, all counts are given + * in bits, whereas in OpenSSL entropy counts are given in bits + * and buffer lengths are given in bytes. + * + * Since this difference has lead to some confusion in the past, + * (see [GitHub Issue #2443], formerly [rt.openssl.org #4055]) + * the 'len' suffix has been added to all buffer sizes for + * clarification. + */ + + unsigned int strength; + size_t max_request; + size_t min_entropylen, max_entropylen; + size_t min_noncelen, max_noncelen; + size_t max_perslen, max_adinlen; + + /* + * Counts the number of generate requests since the last reseed + * (Starts at 1). This value is the reseed_counter as defined in + * NIST SP 800-90Ar1 + */ + unsigned int generate_counter; + /* + * Maximum number of generate requests until a reseed is required. + * This value is ignored if it is zero. + */ + unsigned int reseed_interval; + /* Stores the time when the last reseeding occurred */ + time_t reseed_time; + /* + * Specifies the maximum time interval (in seconds) between reseeds. + * This value is ignored if it is zero. + */ + time_t reseed_time_interval; + /* + * Counts the number of reseeds since instantiation. + * This value is ignored if it is zero. + * + * This counter is used only for seed propagation from the <master> DRBG + * to its two children, the <public> and <private> DRBG. This feature is + * very special and its sole purpose is to ensure that any randomness which + * is added by PROV_add() or PROV_seed() will have an immediate effect on + * the output of PROV_bytes() resp. PROV_priv_bytes(). + */ + TSAN_QUALIFIER unsigned int reseed_counter; + unsigned int reseed_next_counter; + unsigned int parent_reseed_counter; + + size_t seedlen; + DRBG_STATUS state; + + /* DRBG specific data */ + void *data; + + /* Entropy and nonce gathering callbacks */ + void *callback_arg; + OSSL_INOUT_CALLBACK *get_entropy_fn; + OSSL_CALLBACK *cleanup_entropy_fn; + OSSL_INOUT_CALLBACK *get_nonce_fn; + OSSL_CALLBACK *cleanup_nonce_fn; + + OSSL_FIPS_IND_DECLARE +}; + +PROV_DRBG *ossl_rand_drbg_new + (void *provctx, void *parent, const OSSL_DISPATCH *parent_dispatch, + int (*dnew)(PROV_DRBG *ctx), + void (*dfree)(void *vctx), + int (*instantiate)(PROV_DRBG *drbg, + const unsigned char *entropy, size_t entropylen, + const unsigned char *nonce, size_t noncelen, + const unsigned char *pers, size_t perslen), + int (*uninstantiate)(PROV_DRBG *ctx), + int (*reseed)(PROV_DRBG *drbg, const unsigned char *ent, size_t ent_len, + const unsigned char *adin, size_t adin_len), + int (*generate)(PROV_DRBG *, unsigned char *out, size_t outlen, + const unsigned char *adin, size_t adin_len)); +void ossl_rand_drbg_free(PROV_DRBG *drbg); + +int ossl_prov_drbg_instantiate(PROV_DRBG *drbg, unsigned int strength, + int prediction_resistance, + const unsigned char *pers, size_t perslen); + +int ossl_prov_drbg_uninstantiate(PROV_DRBG *drbg); + +int ossl_prov_drbg_reseed(PROV_DRBG *drbg, int prediction_resistance, + const unsigned char *ent, size_t ent_len, + const unsigned char *adin, size_t adinlen); + +int ossl_prov_drbg_generate(PROV_DRBG *drbg, unsigned char *out, size_t outlen, + unsigned int strength, int prediction_resistance, + const unsigned char *adin, size_t adinlen); + +/* Seeding api */ +OSSL_FUNC_rand_get_seed_fn ossl_drbg_get_seed; +OSSL_FUNC_rand_clear_seed_fn ossl_drbg_clear_seed; + +/* Verify that an array of numeric values is all zero */ +#define PROV_DRBG_VERIFY_ZEROIZATION(v) \ + { \ + size_t i; \ + \ + for (i = 0; i < OSSL_NELEM(v); i++) \ + if ((v)[i] != 0) \ + goto err; \ + } + +/* locking api */ +OSSL_FUNC_rand_enable_locking_fn ossl_drbg_enable_locking; +OSSL_FUNC_rand_lock_fn ossl_drbg_lock; +OSSL_FUNC_rand_unlock_fn ossl_drbg_unlock; + +/* Common parameters for all of our DRBGs */ +int ossl_drbg_get_ctx_params(PROV_DRBG *drbg, OSSL_PARAM params[]); +int ossl_drbg_get_ctx_params_no_lock(PROV_DRBG *drbg, OSSL_PARAM params[], + int *complete); +int ossl_drbg_set_ctx_params(PROV_DRBG *drbg, const OSSL_PARAM params[]); + +#define OSSL_PARAM_DRBG_SETTABLE_CTX_COMMON \ + OSSL_PARAM_uint(OSSL_DRBG_PARAM_RESEED_REQUESTS, NULL), \ + OSSL_PARAM_uint64(OSSL_DRBG_PARAM_RESEED_TIME_INTERVAL, NULL) + +#define OSSL_PARAM_DRBG_GETTABLE_CTX_COMMON \ + OSSL_PARAM_int(OSSL_RAND_PARAM_STATE, NULL), \ + OSSL_PARAM_uint(OSSL_RAND_PARAM_STRENGTH, NULL), \ + OSSL_PARAM_size_t(OSSL_RAND_PARAM_MAX_REQUEST, NULL), \ + OSSL_PARAM_size_t(OSSL_DRBG_PARAM_MIN_ENTROPYLEN, NULL), \ + OSSL_PARAM_size_t(OSSL_DRBG_PARAM_MAX_ENTROPYLEN, NULL), \ + OSSL_PARAM_size_t(OSSL_DRBG_PARAM_MIN_NONCELEN, NULL), \ + OSSL_PARAM_size_t(OSSL_DRBG_PARAM_MAX_NONCELEN, NULL), \ + OSSL_PARAM_size_t(OSSL_DRBG_PARAM_MAX_PERSLEN, NULL), \ + OSSL_PARAM_size_t(OSSL_DRBG_PARAM_MAX_ADINLEN, NULL), \ + OSSL_PARAM_uint(OSSL_DRBG_PARAM_RESEED_COUNTER, NULL), \ + OSSL_PARAM_time_t(OSSL_DRBG_PARAM_RESEED_TIME, NULL), \ + OSSL_PARAM_uint(OSSL_DRBG_PARAM_RESEED_REQUESTS, NULL), \ + OSSL_PARAM_uint64(OSSL_DRBG_PARAM_RESEED_TIME_INTERVAL, NULL) + +/* Confirm digest is allowed to be used with a DRBG */ +int ossl_drbg_verify_digest(PROV_DRBG *drbg, OSSL_LIB_CTX *libctx, const EVP_MD *md); + +#endif diff --git a/crypto/openssl/providers/implementations/rands/fips_crng_test.c b/crypto/openssl/providers/implementations/rands/fips_crng_test.c new file mode 100644 index 000000000000..209a1adb2785 --- /dev/null +++ b/crypto/openssl/providers/implementations/rands/fips_crng_test.c @@ -0,0 +1,428 @@ +/* + * Copyright 2024 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 + */ + +/* + * Implementation of SP 800-90B section 4.4 Approved Continuous Health Tests. + */ + +#include <string.h> +#include <openssl/evp.h> +#include <openssl/core_dispatch.h> +#include <openssl/params.h> +#include <openssl/self_test.h> +#include <openssl/proverr.h> +#include "prov/providercommon.h" +#include "prov/provider_ctx.h" +#include "prov/implementations.h" +#include "internal/cryptlib.h" +#include "crypto/rand_pool.h" +#include "drbg_local.h" +#include "prov/seeding.h" +#include "crypto/context.h" + +static OSSL_FUNC_rand_newctx_fn crng_test_new; +static OSSL_FUNC_rand_freectx_fn crng_test_free; +static OSSL_FUNC_rand_instantiate_fn crng_test_instantiate; +static OSSL_FUNC_rand_uninstantiate_fn crng_test_uninstantiate; +static OSSL_FUNC_rand_generate_fn crng_test_generate; +static OSSL_FUNC_rand_reseed_fn crng_test_reseed; +static OSSL_FUNC_rand_gettable_ctx_params_fn crng_test_gettable_ctx_params; +static OSSL_FUNC_rand_get_ctx_params_fn crng_test_get_ctx_params; +static OSSL_FUNC_rand_verify_zeroization_fn crng_test_verify_zeroization; +static OSSL_FUNC_rand_enable_locking_fn crng_test_enable_locking; +static OSSL_FUNC_rand_lock_fn crng_test_lock; +static OSSL_FUNC_rand_unlock_fn crng_test_unlock; +static OSSL_FUNC_rand_get_seed_fn crng_test_get_seed; +static OSSL_FUNC_rand_clear_seed_fn crng_test_clear_seed; + +#ifndef ENTROPY_H +# define ENTROPY_H 6 /* default to six bits per byte of entropy */ +#endif +#ifndef ENTROPY_APT_W +# define ENTROPY_APT_W 512 +#endif + +typedef struct crng_testal_st { + void *provctx; + CRYPTO_RWLOCK *lock; + int state; + + /* State for SP 800-90B 4.4.1 Repetition Count Test */ + struct { + unsigned int b; + uint8_t a; + } rct; + + /* State for SP 800-90B 4.4.2 Adaptive Proportion Test */ + struct { + unsigned int b; + unsigned int i; + uint8_t a; + } apt; + + /* Parent PROV_RAND and its dispatch table functions */ + void *parent; + OSSL_FUNC_rand_enable_locking_fn *parent_enable_locking; + OSSL_FUNC_rand_lock_fn *parent_lock; + OSSL_FUNC_rand_unlock_fn *parent_unlock; + OSSL_FUNC_rand_get_ctx_params_fn *parent_get_ctx_params; + OSSL_FUNC_rand_gettable_ctx_params_fn *parent_gettable_ctx_params; + OSSL_FUNC_rand_get_seed_fn *parent_get_seed; + OSSL_FUNC_rand_clear_seed_fn *parent_clear_seed; +} CRNG_TEST; + +/* + * Some helper functions + */ +static int lock_parent(CRNG_TEST *crngt) +{ + void *parent = crngt->parent; + + if (parent != NULL + && crngt->parent_lock != NULL + && !crngt->parent_lock(parent)) { + ERR_raise(ERR_LIB_PROV, PROV_R_PARENT_LOCKING_NOT_ENABLED); + return 0; + } + return 1; +} + +static void unlock_parent(CRNG_TEST *crngt) +{ + void *parent = crngt->parent; + + if (parent != NULL && crngt->parent_unlock != NULL) + crngt->parent_unlock(parent); +} + +/* + * Implementation of SP 800-90B section 4.4.1: Repetition Count Test + */ +static int RCT_test(CRNG_TEST *crngt, uint8_t next) +{ + /* + * Critical values for this test are computed using: + * + * C = 1 + \left\lceil\frac{-log_2 \alpha}H\right\rceil + * + * where alpha = 2^-20 and H is the expected entropy per sample. + */ + static const unsigned int rct_c[9] = { + 41, /* H = 0.5 */ + 21, 11, 8, 6, 5, 5, 4, 4 /* H = 1, ..., 8 */ + }; + + if (ossl_likely(crngt->rct.b != 0) + && ossl_unlikely(next == crngt->rct.a)) + return ossl_likely(++crngt->rct.b < rct_c[ENTROPY_H]); + crngt->rct.a = next; + crngt->rct.b = 1; + return 1; +} + +/* + * Implementation of SP 800-90B section 4.4.2: Adaptive Proportion Test + */ +static int APT_test(CRNG_TEST *crngt, uint8_t next) +{ + /* + * Critical values for this test are drawn from a binomial + * distribution with n = 512, p = 2^-H at a critical threshold of + * 2^-20. H being the expected entropy per sample. Refer SP 800-90B + * section 4.4.2, table 2. + */ + static const unsigned int apt_c[9] = { + 410, /* H = 0.5 */ + 311, 177, 103, 62, 39, 25, 18, 13 /* H = 1, ..., 8 */ + }; + + if (ossl_likely(crngt->apt.b != 0)) { + if (ossl_unlikely(crngt->apt.a == next) + && ossl_unlikely(++crngt->apt.b >= apt_c[ENTROPY_H])) { + crngt->apt.b = 0; + return 0; + } + if (ossl_unlikely(++crngt->apt.i >= ENTROPY_APT_W)) + crngt->apt.b = 0; + return 1; + } + crngt->apt.a = next; + crngt->apt.b = 1; + crngt->apt.i = 1; + return 1; +} + +static int crng_test(CRNG_TEST *crngt, const unsigned char *buf, size_t n) +{ + size_t i; + + for (i = 0; i < n; i++) + if (!RCT_test(crngt, buf[i]) || !APT_test(crngt, buf[i])) { + crngt->state = EVP_RAND_STATE_ERROR; + ERR_raise(ERR_LIB_PROV, + PROV_R_ENTROPY_SOURCE_FAILED_CONTINUOUS_TESTS); + return 0; + } + return 1; +} + +static const OSSL_DISPATCH *find_call(const OSSL_DISPATCH *dispatch, + int function) +{ + if (dispatch != NULL) + while (dispatch->function_id != 0) { + if (dispatch->function_id == function) + return dispatch; + dispatch++; + } + return NULL; +} + +static void *crng_test_new(void *provctx, void *parent, + const OSSL_DISPATCH *p_dispatch) +{ + CRNG_TEST *crngt = OPENSSL_zalloc(sizeof(*crngt)); + const OSSL_DISPATCH *pfunc; + + if (crngt == NULL) + return NULL; + + crngt->provctx = provctx; + crngt->state = EVP_RAND_STATE_UNINITIALISED; + + /* Extract parent's functions */ + if (parent != NULL) { + crngt->parent = parent; + if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_ENABLE_LOCKING)) != NULL) + crngt->parent_enable_locking = OSSL_FUNC_rand_enable_locking(pfunc); + if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_LOCK)) != NULL) + crngt->parent_lock = OSSL_FUNC_rand_lock(pfunc); + if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_UNLOCK)) != NULL) + crngt->parent_unlock = OSSL_FUNC_rand_unlock(pfunc); + if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_GETTABLE_CTX_PARAMS)) != NULL) + crngt->parent_gettable_ctx_params = OSSL_FUNC_rand_gettable_ctx_params(pfunc); + if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_GET_CTX_PARAMS)) != NULL) + crngt->parent_get_ctx_params = OSSL_FUNC_rand_get_ctx_params(pfunc); + if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_GET_SEED)) != NULL) + crngt->parent_get_seed = OSSL_FUNC_rand_get_seed(pfunc); + if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_CLEAR_SEED)) != NULL) + crngt->parent_clear_seed = OSSL_FUNC_rand_clear_seed(pfunc); + } + + return crngt; +} + +static void crng_test_free(void *vcrngt) +{ + CRNG_TEST *crngt = (CRNG_TEST *)vcrngt; + + if (crngt != NULL) { + CRYPTO_THREAD_lock_free(crngt->lock); + OPENSSL_free(crngt); + } +} + +static int crng_test_instantiate(void *vcrngt, unsigned int strength, + int prediction_resistance, + const unsigned char *pstr, + size_t pstr_len, + ossl_unused const OSSL_PARAM params[]) +{ + CRNG_TEST *crngt = (CRNG_TEST *)vcrngt; + + /* Start up health tests should go here */ + crngt->state = EVP_RAND_STATE_READY; + return 1; +} + +static int crng_test_uninstantiate(void *vcrngt) +{ + CRNG_TEST *crngt = (CRNG_TEST *)vcrngt; + + crngt->state = EVP_RAND_STATE_UNINITIALISED; + return 1; +} + +static int crng_test_generate(void *vcrngt, unsigned char *out, size_t outlen, + unsigned int strength, int prediction_resistance, + const unsigned char *adin, size_t adin_len) +{ + unsigned char *p; + CRNG_TEST *crngt = (CRNG_TEST *)vcrngt; + + if (!crng_test_get_seed(crngt, &p, 0, outlen, outlen, prediction_resistance, + adin, adin_len)) + return 0; + memcpy(out, p, outlen); + crng_test_clear_seed(crngt, p, outlen); + return 1; +} + +static int crng_test_reseed(ossl_unused void *vcrngt, + ossl_unused int prediction_resistance, + ossl_unused const unsigned char *ent, + ossl_unused size_t ent_len, + ossl_unused const unsigned char *adin, + ossl_unused size_t adin_len) +{ + return 1; +} + +static int crng_test_verify_zeroization(ossl_unused void *vcrngt) +{ + return 1; +} + +static size_t crng_test_get_seed(void *vcrngt, unsigned char **pout, + int entropy, size_t min_len, + size_t max_len, + int prediction_resistance, + const unsigned char *adin, + size_t adin_len) +{ + CRNG_TEST *crngt = (CRNG_TEST *)vcrngt; + size_t n; + int r = 0; + + /* Without a parent, we rely on the up calls */ + if (crngt->parent == NULL + || crngt->parent_get_seed == NULL) { + n = ossl_prov_get_entropy(crngt->provctx, pout, entropy, + min_len, max_len); + if (n == 0) + return 0; + r = crng_test(crngt, *pout, n); + return r > 0 ? n : 0; + } + + /* Grab seed from our parent */ + if (!lock_parent(crngt)) + return 0; + + n = crngt->parent_get_seed(crngt->parent, pout, entropy, + min_len, max_len, prediction_resistance, + adin, adin_len); + if (n > 0 && crng_test(crngt, *pout, n) > 0) + r = n; + else if (crngt->parent_clear_seed != NULL) + crngt->parent_clear_seed(crngt->parent, *pout, n); + unlock_parent(crngt); + return r; +} + +static void crng_test_clear_seed(void *vcrngt, + unsigned char *out, size_t outlen) +{ + CRNG_TEST *crngt = (CRNG_TEST *)vcrngt; + + if (crngt->parent == NULL || crngt->parent_get_seed == NULL) + ossl_prov_cleanup_entropy(crngt->provctx, out, outlen); + else if (crngt->parent_clear_seed != NULL) + crngt->parent_clear_seed(crngt->parent, out, outlen); +} + +static int crng_test_enable_locking(void *vcrngt) +{ + CRNG_TEST *crngt = (CRNG_TEST *)vcrngt; + + if (crngt != NULL && crngt->lock == NULL) { + if (crngt->parent_enable_locking != NULL) + if (!crngt->parent_enable_locking(crngt->parent)) { + ERR_raise(ERR_LIB_PROV, PROV_R_PARENT_LOCKING_NOT_ENABLED); + return 0; + } + crngt->lock = CRYPTO_THREAD_lock_new(); + if (crngt->lock == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_CREATE_LOCK); + return 0; + } + } + return 1; +} + +static int crng_test_lock(ossl_unused void *vcrngt) +{ + CRNG_TEST *crngt = (CRNG_TEST *)vcrngt; + + return crngt->lock == NULL || CRYPTO_THREAD_write_lock(crngt->lock); +} + +static void crng_test_unlock(ossl_unused void *vcrngt) +{ + CRNG_TEST *crngt = (CRNG_TEST *)vcrngt; + + if (crngt->lock != NULL) + CRYPTO_THREAD_unlock(crngt->lock); +} + +static int crng_test_get_ctx_params(void *vcrngt, OSSL_PARAM params[]) +{ + CRNG_TEST *crngt = (CRNG_TEST *)vcrngt; + OSSL_PARAM *p; + + if (crngt->parent != NULL && crngt->parent_get_ctx_params != NULL) + return crngt->parent_get_ctx_params(crngt->parent, params); + + /* No parent means we are using call backs for entropy */ + p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_STATE); + if (p != NULL && !OSSL_PARAM_set_int(p, crngt->state)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_STRENGTH); + if (p != NULL && !OSSL_PARAM_set_int(p, 1024)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_MAX_REQUEST); + if (p != NULL && !OSSL_PARAM_set_size_t(p, 128)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_FIPS_APPROVED_INDICATOR); + if (p != NULL && !OSSL_PARAM_set_int(p, 0)) + return 0; + return 1; +} + +static const OSSL_PARAM *crng_test_gettable_ctx_params(void *vcrngt, + void *provctx) +{ + CRNG_TEST *crngt = (CRNG_TEST *)vcrngt; + static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_PARAM_int(OSSL_RAND_PARAM_STATE, NULL), + OSSL_PARAM_uint(OSSL_RAND_PARAM_STRENGTH, NULL), + OSSL_PARAM_size_t(OSSL_RAND_PARAM_MAX_REQUEST, NULL), + OSSL_PARAM_int(OSSL_RAND_PARAM_FIPS_APPROVED_INDICATOR, NULL), + OSSL_PARAM_END + }; + + if (crngt->parent != NULL && crngt->parent_gettable_ctx_params != NULL) + return crngt->parent_gettable_ctx_params(crngt->parent, provctx); + return known_gettable_ctx_params; +} + +const OSSL_DISPATCH ossl_crng_test_functions[] = { + { OSSL_FUNC_RAND_NEWCTX, (void(*)(void))crng_test_new }, + { OSSL_FUNC_RAND_FREECTX, (void(*)(void))crng_test_free }, + { OSSL_FUNC_RAND_INSTANTIATE, + (void(*)(void))crng_test_instantiate }, + { OSSL_FUNC_RAND_UNINSTANTIATE, + (void(*)(void))crng_test_uninstantiate }, + { OSSL_FUNC_RAND_GENERATE, (void(*)(void))crng_test_generate }, + { OSSL_FUNC_RAND_RESEED, (void(*)(void))crng_test_reseed }, + { OSSL_FUNC_RAND_ENABLE_LOCKING, (void(*)(void))crng_test_enable_locking }, + { OSSL_FUNC_RAND_LOCK, (void(*)(void))crng_test_lock }, + { OSSL_FUNC_RAND_UNLOCK, (void(*)(void))crng_test_unlock }, + { OSSL_FUNC_RAND_GETTABLE_CTX_PARAMS, + (void(*)(void))crng_test_gettable_ctx_params }, + { OSSL_FUNC_RAND_GET_CTX_PARAMS, (void(*)(void))crng_test_get_ctx_params }, + { OSSL_FUNC_RAND_VERIFY_ZEROIZATION, + (void(*)(void))crng_test_verify_zeroization }, + { OSSL_FUNC_RAND_GET_SEED, (void(*)(void))crng_test_get_seed }, + { OSSL_FUNC_RAND_CLEAR_SEED, (void(*)(void))crng_test_clear_seed }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/rands/seed_src.c b/crypto/openssl/providers/implementations/rands/seed_src.c new file mode 100644 index 000000000000..1faab39138d2 --- /dev/null +++ b/crypto/openssl/providers/implementations/rands/seed_src.c @@ -0,0 +1,249 @@ +/* + * Copyright 2020-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 + */ + +#include <string.h> +#include <openssl/rand.h> +#include <openssl/core_dispatch.h> +#include <openssl/e_os2.h> +#include <openssl/params.h> +#include <openssl/core_names.h> +#include <openssl/evp.h> +#include <openssl/err.h> +#include <openssl/randerr.h> +#include <openssl/proverr.h> +#include "prov/implementations.h" +#include "prov/provider_ctx.h" +#include "crypto/rand.h" +#include "crypto/rand_pool.h" + +static OSSL_FUNC_rand_newctx_fn seed_src_new; +static OSSL_FUNC_rand_freectx_fn seed_src_free; +static OSSL_FUNC_rand_instantiate_fn seed_src_instantiate; +static OSSL_FUNC_rand_uninstantiate_fn seed_src_uninstantiate; +static OSSL_FUNC_rand_generate_fn seed_src_generate; +static OSSL_FUNC_rand_reseed_fn seed_src_reseed; +static OSSL_FUNC_rand_gettable_ctx_params_fn seed_src_gettable_ctx_params; +static OSSL_FUNC_rand_get_ctx_params_fn seed_src_get_ctx_params; +static OSSL_FUNC_rand_verify_zeroization_fn seed_src_verify_zeroization; +static OSSL_FUNC_rand_enable_locking_fn seed_src_enable_locking; +static OSSL_FUNC_rand_lock_fn seed_src_lock; +static OSSL_FUNC_rand_unlock_fn seed_src_unlock; +static OSSL_FUNC_rand_get_seed_fn seed_get_seed; +static OSSL_FUNC_rand_clear_seed_fn seed_clear_seed; + +typedef struct { + void *provctx; + int state; +} PROV_SEED_SRC; + +static void *seed_src_new(void *provctx, void *parent, + const OSSL_DISPATCH *parent_dispatch) +{ + PROV_SEED_SRC *s; + + if (parent != NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_SEED_SOURCES_MUST_NOT_HAVE_A_PARENT); + return NULL; + } + + s = OPENSSL_zalloc(sizeof(*s)); + if (s == NULL) + return NULL; + + s->provctx = provctx; + s->state = EVP_RAND_STATE_UNINITIALISED; + return s; +} + +static void seed_src_free(void *vseed) +{ + OPENSSL_free(vseed); +} + +static int seed_src_instantiate(void *vseed, unsigned int strength, + int prediction_resistance, + const unsigned char *pstr, size_t pstr_len, + ossl_unused const OSSL_PARAM params[]) +{ + PROV_SEED_SRC *s = (PROV_SEED_SRC *)vseed; + + s->state = EVP_RAND_STATE_READY; + return 1; +} + +static int seed_src_uninstantiate(void *vseed) +{ + PROV_SEED_SRC *s = (PROV_SEED_SRC *)vseed; + + s->state = EVP_RAND_STATE_UNINITIALISED; + return 1; +} + +static int seed_src_generate(void *vseed, unsigned char *out, size_t outlen, + unsigned int strength, + ossl_unused int prediction_resistance, + const unsigned char *adin, + size_t adin_len) +{ + PROV_SEED_SRC *s = (PROV_SEED_SRC *)vseed; + size_t entropy_available; + RAND_POOL *pool; + + if (s->state != EVP_RAND_STATE_READY) { + ERR_raise(ERR_LIB_PROV, + s->state == EVP_RAND_STATE_ERROR ? PROV_R_IN_ERROR_STATE + : PROV_R_NOT_INSTANTIATED); + return 0; + } + + pool = ossl_rand_pool_new(strength, 1, outlen, outlen); + if (pool == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_RAND_LIB); + return 0; + } + + /* Get entropy by polling system entropy sources. */ + entropy_available = ossl_pool_acquire_entropy(pool); + + if (entropy_available > 0) { + if (!ossl_rand_pool_adin_mix_in(pool, adin, adin_len)) { + ossl_rand_pool_free(pool); + return 0; + } + memcpy(out, ossl_rand_pool_buffer(pool), ossl_rand_pool_length(pool)); + } + + ossl_rand_pool_free(pool); + return entropy_available > 0; +} + +static int seed_src_reseed(void *vseed, + ossl_unused int prediction_resistance, + ossl_unused const unsigned char *ent, + ossl_unused size_t ent_len, + ossl_unused const unsigned char *adin, + ossl_unused size_t adin_len) +{ + PROV_SEED_SRC *s = (PROV_SEED_SRC *)vseed; + + if (s->state != EVP_RAND_STATE_READY) { + ERR_raise(ERR_LIB_PROV, + s->state == EVP_RAND_STATE_ERROR ? PROV_R_IN_ERROR_STATE + : PROV_R_NOT_INSTANTIATED); + return 0; + } + return 1; +} + +static int seed_src_get_ctx_params(void *vseed, OSSL_PARAM params[]) +{ + PROV_SEED_SRC *s = (PROV_SEED_SRC *)vseed; + OSSL_PARAM *p; + + p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_STATE); + if (p != NULL && !OSSL_PARAM_set_int(p, s->state)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_STRENGTH); + if (p != NULL && !OSSL_PARAM_set_int(p, 1024)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_MAX_REQUEST); + if (p != NULL && !OSSL_PARAM_set_size_t(p, 128)) + return 0; + return 1; +} + +static const OSSL_PARAM *seed_src_gettable_ctx_params(ossl_unused void *vseed, + ossl_unused void *provctx) +{ + static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_PARAM_int(OSSL_RAND_PARAM_STATE, NULL), + OSSL_PARAM_uint(OSSL_RAND_PARAM_STRENGTH, NULL), + OSSL_PARAM_size_t(OSSL_RAND_PARAM_MAX_REQUEST, NULL), + OSSL_PARAM_END + }; + return known_gettable_ctx_params; +} + +static int seed_src_verify_zeroization(ossl_unused void *vseed) +{ + return 1; +} + +static size_t seed_get_seed(void *vseed, unsigned char **pout, + int entropy, size_t min_len, size_t max_len, + int prediction_resistance, + const unsigned char *adin, size_t adin_len) +{ + size_t ret = 0; + size_t entropy_available = 0; + RAND_POOL *pool; + + pool = ossl_rand_pool_new(entropy, 1, min_len, max_len); + if (pool == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_RAND_LIB); + return 0; + } + + /* Get entropy by polling system entropy sources. */ + entropy_available = ossl_pool_acquire_entropy(pool); + + if (entropy_available > 0 + && ossl_rand_pool_adin_mix_in(pool, adin, adin_len)) { + ret = ossl_rand_pool_length(pool); + *pout = ossl_rand_pool_detach(pool); + } else { + ERR_raise(ERR_LIB_PROV, PROV_R_ENTROPY_SOURCE_STRENGTH_TOO_WEAK); + } + ossl_rand_pool_free(pool); + return ret; +} + +static void seed_clear_seed(ossl_unused void *vdrbg, + unsigned char *out, size_t outlen) +{ + OPENSSL_secure_clear_free(out, outlen); +} + +static int seed_src_enable_locking(ossl_unused void *vseed) +{ + return 1; +} + +int seed_src_lock(ossl_unused void *vctx) +{ + return 1; +} + +void seed_src_unlock(ossl_unused void *vctx) +{ +} + +const OSSL_DISPATCH ossl_seed_src_functions[] = { + { OSSL_FUNC_RAND_NEWCTX, (void(*)(void))seed_src_new }, + { OSSL_FUNC_RAND_FREECTX, (void(*)(void))seed_src_free }, + { OSSL_FUNC_RAND_INSTANTIATE, + (void(*)(void))seed_src_instantiate }, + { OSSL_FUNC_RAND_UNINSTANTIATE, + (void(*)(void))seed_src_uninstantiate }, + { OSSL_FUNC_RAND_GENERATE, (void(*)(void))seed_src_generate }, + { OSSL_FUNC_RAND_RESEED, (void(*)(void))seed_src_reseed }, + { OSSL_FUNC_RAND_ENABLE_LOCKING, (void(*)(void))seed_src_enable_locking }, + { OSSL_FUNC_RAND_LOCK, (void(*)(void))seed_src_lock }, + { OSSL_FUNC_RAND_UNLOCK, (void(*)(void))seed_src_unlock }, + { OSSL_FUNC_RAND_GETTABLE_CTX_PARAMS, + (void(*)(void))seed_src_gettable_ctx_params }, + { OSSL_FUNC_RAND_GET_CTX_PARAMS, (void(*)(void))seed_src_get_ctx_params }, + { OSSL_FUNC_RAND_VERIFY_ZEROIZATION, + (void(*)(void))seed_src_verify_zeroization }, + { OSSL_FUNC_RAND_GET_SEED, (void(*)(void))seed_get_seed }, + { OSSL_FUNC_RAND_CLEAR_SEED, (void(*)(void))seed_clear_seed }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/rands/seed_src_jitter.c b/crypto/openssl/providers/implementations/rands/seed_src_jitter.c new file mode 100644 index 000000000000..ac57f1c14bff --- /dev/null +++ b/crypto/openssl/providers/implementations/rands/seed_src_jitter.c @@ -0,0 +1,359 @@ +/* + * Copyright 2024-2025 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 + */ + +#include <string.h> +#include <openssl/rand.h> +#include <openssl/core_dispatch.h> +#include <openssl/e_os2.h> +#include <openssl/params.h> +#include <openssl/core_names.h> +#include <openssl/evp.h> +#include <openssl/err.h> +#include <openssl/randerr.h> +#include <openssl/proverr.h> +#include <openssl/self_test.h> +#include "prov/implementations.h" +#include "prov/provider_ctx.h" +#include "prov/providercommon.h" +#include "crypto/rand.h" +#include "crypto/rand_pool.h" + +#ifndef OPENSSL_NO_JITTER +# include <jitterentropy.h> + +# define JITTER_MAX_NUM_TRIES 3 + +static OSSL_FUNC_rand_newctx_fn jitter_new; +static OSSL_FUNC_rand_freectx_fn jitter_free; +static OSSL_FUNC_rand_instantiate_fn jitter_instantiate; +static OSSL_FUNC_rand_uninstantiate_fn jitter_uninstantiate; +static OSSL_FUNC_rand_generate_fn jitter_generate; +static OSSL_FUNC_rand_reseed_fn jitter_reseed; +static OSSL_FUNC_rand_gettable_ctx_params_fn jitter_gettable_ctx_params; +static OSSL_FUNC_rand_get_ctx_params_fn jitter_get_ctx_params; +static OSSL_FUNC_rand_verify_zeroization_fn jitter_verify_zeroization; +static OSSL_FUNC_rand_enable_locking_fn jitter_enable_locking; +static OSSL_FUNC_rand_lock_fn jitter_lock; +static OSSL_FUNC_rand_unlock_fn jitter_unlock; +static OSSL_FUNC_rand_get_seed_fn jitter_get_seed; +static OSSL_FUNC_rand_clear_seed_fn jitter_clear_seed; + +typedef struct { + void *provctx; + int state; +} PROV_JITTER; + +static size_t get_jitter_random_value(PROV_JITTER *s, unsigned char *buf, size_t len); + +/* + * Acquire entropy from jitterentropy library + * + * Returns the total entropy count, if it exceeds the requested + * entropy count. Otherwise, returns an entropy count of 0. + */ +static size_t ossl_prov_acquire_entropy_from_jitter(PROV_JITTER *s, + RAND_POOL *pool) +{ + size_t bytes_needed; + unsigned char *buffer; + + bytes_needed = ossl_rand_pool_bytes_needed(pool, 1 /* entropy_factor */); + if (bytes_needed > 0) { + buffer = ossl_rand_pool_add_begin(pool, bytes_needed); + + if (buffer != NULL) { + if (get_jitter_random_value(s, buffer, bytes_needed) == bytes_needed) { + ossl_rand_pool_add_end(pool, bytes_needed, 8 * bytes_needed); + } else { + ossl_rand_pool_add_end(pool, 0, 0); + } + } + } + + return ossl_rand_pool_entropy_available(pool); +} + +/* Obtain random bytes from the jitter library */ +static size_t get_jitter_random_value(PROV_JITTER *s, + unsigned char *buf, size_t len) +{ + struct rand_data *jitter_ec = NULL; + ssize_t result = 0; + size_t num_tries; + + /* Retry intermittent failures, then give up */ + for (num_tries = 0; num_tries < JITTER_MAX_NUM_TRIES; num_tries++) { + /* Allocate a fresh collector */ + jitter_ec = jent_entropy_collector_alloc(0, JENT_FORCE_FIPS); + if (jitter_ec == NULL) + continue; + + /* Do not use _safe API as per typical security policies */ + result = jent_read_entropy(jitter_ec, (char *) buf, len); + jent_entropy_collector_free(jitter_ec); + + /* + * Permanent Failure + * https://github.com/smuellerDD/jitterentropy-library/blob/master/doc/jitterentropy.3#L234 + */ + if (result < -5) { + ossl_set_error_state(OSSL_SELF_TEST_TYPE_CRNG); + break; + } + + /* Success */ + if (result >= 0 && (size_t)result == len) + return len; + } + + /* Permanent failure or too many intermittent failures */ + s->state = EVP_RAND_STATE_ERROR; + ERR_raise_data(ERR_LIB_RAND, RAND_R_ERROR_RETRIEVING_ENTROPY, + "jent_read_entropy (%d)", result); + return 0; +} + +static void *jitter_new(void *provctx, void *parent, + const OSSL_DISPATCH *parent_dispatch) +{ + PROV_JITTER *s; + + if (parent != NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_SEED_SOURCES_MUST_NOT_HAVE_A_PARENT); + return NULL; + } + + s = OPENSSL_zalloc(sizeof(*s)); + if (s == NULL) + return NULL; + + s->provctx = provctx; + s->state = EVP_RAND_STATE_UNINITIALISED; + return s; +} + +static void jitter_free(void *vseed) +{ + OPENSSL_free(vseed); +} + +static int jitter_instantiate(void *vseed, unsigned int strength, + int prediction_resistance, + const unsigned char *pstr, + size_t pstr_len, + ossl_unused const OSSL_PARAM params[]) +{ + PROV_JITTER *s = (PROV_JITTER *)vseed; + int ret; + + if ((ret = jent_entropy_init_ex(0, JENT_FORCE_FIPS)) != 0) { + ERR_raise_data(ERR_LIB_RAND, RAND_R_ERROR_RETRIEVING_ENTROPY, + "jent_entropy_init_ex (%d)", ret); + s->state = EVP_RAND_STATE_ERROR; + return 0; + } + + s->state = EVP_RAND_STATE_READY; + return 1; +} + +static int jitter_uninstantiate(void *vseed) +{ + PROV_JITTER *s = (PROV_JITTER *)vseed; + + s->state = EVP_RAND_STATE_UNINITIALISED; + return 1; +} + +static int jitter_generate(void *vseed, unsigned char *out, size_t outlen, + unsigned int strength, + ossl_unused int prediction_resistance, + ossl_unused const unsigned char *adin, + ossl_unused size_t adin_len) +{ + PROV_JITTER *s = (PROV_JITTER *)vseed; + size_t entropy_available; + RAND_POOL *pool; + + if (s->state != EVP_RAND_STATE_READY) { + ERR_raise(ERR_LIB_PROV, + s->state == EVP_RAND_STATE_ERROR ? PROV_R_IN_ERROR_STATE + : PROV_R_NOT_INSTANTIATED); + return 0; + } + + pool = ossl_rand_pool_new(strength, 1, outlen, outlen); + if (pool == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_RAND_LIB); + return 0; + } + + /* Get entropy from jitter entropy library. */ + entropy_available = ossl_prov_acquire_entropy_from_jitter(s, pool); + + if (entropy_available > 0) { + if (!ossl_rand_pool_adin_mix_in(pool, adin, adin_len)) { + ossl_rand_pool_free(pool); + return 0; + } + memcpy(out, ossl_rand_pool_buffer(pool), ossl_rand_pool_length(pool)); + } + + ossl_rand_pool_free(pool); + return entropy_available > 0; +} + +static int jitter_reseed(void *vseed, + ossl_unused int prediction_resistance, + ossl_unused const unsigned char *ent, + ossl_unused size_t ent_len, + ossl_unused const unsigned char *adin, + ossl_unused size_t adin_len) +{ + PROV_JITTER *s = (PROV_JITTER *)vseed; + + if (s->state != EVP_RAND_STATE_READY) { + ERR_raise(ERR_LIB_PROV, + s->state == EVP_RAND_STATE_ERROR ? PROV_R_IN_ERROR_STATE + : PROV_R_NOT_INSTANTIATED); + return 0; + } + return 1; +} + +static int jitter_get_ctx_params(void *vseed, OSSL_PARAM params[]) +{ + PROV_JITTER *s = (PROV_JITTER *)vseed; + OSSL_PARAM *p; + + p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_STATE); + if (p != NULL && !OSSL_PARAM_set_int(p, s->state)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_STRENGTH); + if (p != NULL && !OSSL_PARAM_set_int(p, 1024)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_MAX_REQUEST); + if (p != NULL && !OSSL_PARAM_set_size_t(p, 128)) + return 0; + return 1; +} + +static const OSSL_PARAM *jitter_gettable_ctx_params(ossl_unused void *vseed, + ossl_unused void *provctx) +{ + static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_PARAM_int(OSSL_RAND_PARAM_STATE, NULL), + OSSL_PARAM_uint(OSSL_RAND_PARAM_STRENGTH, NULL), + OSSL_PARAM_size_t(OSSL_RAND_PARAM_MAX_REQUEST, NULL), + OSSL_PARAM_END + }; + return known_gettable_ctx_params; +} + +static int jitter_verify_zeroization(ossl_unused void *vseed) +{ + return 1; +} + +static size_t jitter_get_seed(void *vseed, unsigned char **pout, + int entropy, size_t min_len, + size_t max_len, + int prediction_resistance, + const unsigned char *adin, + size_t adin_len) +{ + size_t ret = 0; + size_t entropy_available = 0; + RAND_POOL *pool; + PROV_JITTER *s = (PROV_JITTER *)vseed; + + pool = ossl_rand_pool_new(entropy, 1, min_len, max_len); + if (pool == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_RAND_LIB); + return 0; + } + + /* Get entropy from jitter entropy library. */ + entropy_available = ossl_prov_acquire_entropy_from_jitter(s, pool); + + if (entropy_available > 0 + && ossl_rand_pool_adin_mix_in(pool, adin, adin_len)) { + ret = ossl_rand_pool_length(pool); + *pout = ossl_rand_pool_detach(pool); + } else { + ERR_raise(ERR_LIB_PROV, PROV_R_ENTROPY_SOURCE_STRENGTH_TOO_WEAK); + } + ossl_rand_pool_free(pool); + return ret; +} + +# ifndef OPENSSL_NO_FIPS_JITTER +size_t ossl_rand_jitter_get_seed(unsigned char **pout, int entropy, size_t min_len, size_t max_len) +{ + size_t ret = 0; + OSSL_PARAM params[1] = { OSSL_PARAM_END }; + PROV_JITTER *s = jitter_new(NULL, NULL, NULL); + + if (s == NULL) + return ret; + if (!jitter_instantiate(s, 0, 0, NULL, 0, params)) + goto end; + ret = jitter_get_seed(s, pout, entropy, min_len, max_len, 0, NULL, 0); + end: + jitter_free(s); + return ret; +} +# endif + +static void jitter_clear_seed(ossl_unused void *vdrbg, + unsigned char *out, size_t outlen) +{ + OPENSSL_secure_clear_free(out, outlen); +} + +static int jitter_enable_locking(ossl_unused void *vseed) +{ + return 1; +} + +int jitter_lock(ossl_unused void *vctx) +{ + return 1; +} + +void jitter_unlock(ossl_unused void *vctx) +{ +} + +const OSSL_DISPATCH ossl_jitter_functions[] = { + { OSSL_FUNC_RAND_NEWCTX, (void(*)(void))jitter_new }, + { OSSL_FUNC_RAND_FREECTX, (void(*)(void))jitter_free }, + { OSSL_FUNC_RAND_INSTANTIATE, + (void(*)(void))jitter_instantiate }, + { OSSL_FUNC_RAND_UNINSTANTIATE, + (void(*)(void))jitter_uninstantiate }, + { OSSL_FUNC_RAND_GENERATE, (void(*)(void))jitter_generate }, + { OSSL_FUNC_RAND_RESEED, (void(*)(void))jitter_reseed }, + { OSSL_FUNC_RAND_ENABLE_LOCKING, (void(*)(void))jitter_enable_locking }, + { OSSL_FUNC_RAND_LOCK, (void(*)(void))jitter_lock }, + { OSSL_FUNC_RAND_UNLOCK, (void(*)(void))jitter_unlock }, + { OSSL_FUNC_RAND_GETTABLE_CTX_PARAMS, + (void(*)(void))jitter_gettable_ctx_params }, + { OSSL_FUNC_RAND_GET_CTX_PARAMS, (void(*)(void))jitter_get_ctx_params }, + { OSSL_FUNC_RAND_VERIFY_ZEROIZATION, + (void(*)(void))jitter_verify_zeroization }, + { OSSL_FUNC_RAND_GET_SEED, (void(*)(void))jitter_get_seed }, + { OSSL_FUNC_RAND_CLEAR_SEED, (void(*)(void))jitter_clear_seed }, + OSSL_DISPATCH_END +}; +#else +NON_EMPTY_TRANSLATION_UNIT +#endif diff --git a/crypto/openssl/providers/implementations/rands/seeding/build.info b/crypto/openssl/providers/implementations/rands/seeding/build.info new file mode 100644 index 000000000000..9c5eefee2d0c --- /dev/null +++ b/crypto/openssl/providers/implementations/rands/seeding/build.info @@ -0,0 +1,15 @@ +$COMMON=rand_unix.c rand_win.c rand_tsc.c +IF[{- $config{target} =~ /vxworks/i -}] + $COMMON=$COMMON rand_vxworks.c +ENDIF +IF[{- $config{target} =~ /vms/i -}] + $COMMON=$COMMON rand_vms.c +ENDIF +IF[{- !$disabled{asm} && $config{target} =~ '.*aarch64' -}] + $COMMON=$COMMON rand_cpu_arm64.c +ELSE + $COMMON=$COMMON rand_cpu_x86.c +ENDIF + +SOURCE[../../../libdefault.a]=$COMMON + diff --git a/crypto/openssl/providers/implementations/rands/seeding/rand_cpu_arm64.c b/crypto/openssl/providers/implementations/rands/seeding/rand_cpu_arm64.c new file mode 100644 index 000000000000..a8530e02b576 --- /dev/null +++ b/crypto/openssl/providers/implementations/rands/seeding/rand_cpu_arm64.c @@ -0,0 +1,67 @@ +/* + * Copyright 2021 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 + */ + +#include "internal/cryptlib.h" +#include <openssl/opensslconf.h> +#include "crypto/rand_pool.h" +#include "prov/seeding.h" + + +#ifdef OPENSSL_RAND_SEED_RDCPU +#include "crypto/arm_arch.h" + +size_t OPENSSL_rndrrs_bytes(unsigned char *buf, size_t len); + +static size_t get_hardware_random_value(unsigned char *buf, size_t len); + +/* + * Acquire entropy using Arm-specific cpu instructions + * + * Uses the RNDRRS instruction. RNDR is never needed since + * RNDRRS will always be available if RNDR is an available + * instruction. + * + * Returns the total entropy count, if it exceeds the requested + * entropy count. Otherwise, returns an entropy count of 0. + */ +size_t ossl_prov_acquire_entropy_from_cpu(RAND_POOL *pool) +{ + size_t bytes_needed; + unsigned char *buffer; + + bytes_needed = ossl_rand_pool_bytes_needed(pool, 1 /*entropy_factor*/); + if (bytes_needed > 0) { + buffer = ossl_rand_pool_add_begin(pool, bytes_needed); + + if (buffer != NULL) { + if (get_hardware_random_value(buffer, bytes_needed) == bytes_needed) + ossl_rand_pool_add_end(pool, bytes_needed, 8 * bytes_needed); + else + ossl_rand_pool_add_end(pool, 0, 0); + } + } + + return ossl_rand_pool_entropy_available(pool); +} + +static size_t get_hardware_random_value(unsigned char *buf, size_t len) +{ + /* Always use RNDRRS or nothing */ + if (OPENSSL_armcap_P & ARMV8_RNG) { + if (OPENSSL_rndrrs_bytes(buf, len) != len) + return 0; + } else { + return 0; + } + return len; +} + +#else +NON_EMPTY_TRANSLATION_UNIT +#endif /* OPENSSL_RAND_SEED_RDCPU */ diff --git a/crypto/openssl/providers/implementations/rands/seeding/rand_cpu_x86.c b/crypto/openssl/providers/implementations/rands/seeding/rand_cpu_x86.c new file mode 100644 index 000000000000..0e062fa45aa2 --- /dev/null +++ b/crypto/openssl/providers/implementations/rands/seeding/rand_cpu_x86.c @@ -0,0 +1,107 @@ +/* + * Copyright 1995-2021 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 + */ + +#include "internal/cryptlib.h" +#include <openssl/opensslconf.h> +#include "crypto/rand_pool.h" +#include "prov/seeding.h" + +#ifdef OPENSSL_RAND_SEED_RDCPU +# if defined(OPENSSL_SYS_TANDEM) && defined(_TNS_X_TARGET) +# include <builtin.h> /* _rdrand64 */ +# include <string.h> /* memcpy */ +# else +size_t OPENSSL_ia32_rdseed_bytes(unsigned char *buf, size_t len); +size_t OPENSSL_ia32_rdrand_bytes(unsigned char *buf, size_t len); +# endif + +static size_t get_hardware_random_value(unsigned char *buf, size_t len); + +/* + * Acquire entropy using Intel-specific cpu instructions + * + * Uses the RDSEED instruction if available, otherwise uses + * RDRAND if available. + * + * For the differences between RDSEED and RDRAND, and why RDSEED + * is the preferred choice, see https://goo.gl/oK3KcN + * + * Returns the total entropy count, if it exceeds the requested + * entropy count. Otherwise, returns an entropy count of 0. + */ +size_t ossl_prov_acquire_entropy_from_cpu(RAND_POOL *pool) +{ + size_t bytes_needed; + unsigned char *buffer; + + bytes_needed = ossl_rand_pool_bytes_needed(pool, 1 /*entropy_factor*/); + if (bytes_needed > 0) { + buffer = ossl_rand_pool_add_begin(pool, bytes_needed); + + if (buffer != NULL) { + if (get_hardware_random_value(buffer, bytes_needed) == bytes_needed) { + ossl_rand_pool_add_end(pool, bytes_needed, 8 * bytes_needed); + } else { + ossl_rand_pool_add_end(pool, 0, 0); + } + } + } + + return ossl_rand_pool_entropy_available(pool); +} + +#if defined(OPENSSL_SYS_TANDEM) && defined(_TNS_X_TARGET) +/* Obtain random bytes from the x86 hardware random function in 64 bit chunks */ +static size_t get_hardware_random_value(unsigned char *buf, size_t len) +{ + size_t bytes_remaining = len; + + while (bytes_remaining > 0) { + /* Always use 64 bit fetch, then use the lower bytes as needed. */ + /* The platform is big-endian. */ + uint64_t random_value = 0; + + if (_rdrand64(&random_value) != 0) { + unsigned char *random_buffer = (unsigned char *)&random_value; + + if (bytes_remaining >= sizeof(random_value)) { + memcpy(buf, random_buffer, sizeof(random_value)); + bytes_remaining -= sizeof(random_value); + buf += sizeof(random_value); + } else { + memcpy(buf, + random_buffer + (sizeof(random_value) - bytes_remaining), + bytes_remaining); + bytes_remaining = 0; /* This will terminate the loop */ + } + } else + break; + } + if (bytes_remaining == 0) + return len; + return 0; +} +#else +static size_t get_hardware_random_value(unsigned char *buf, size_t len) { + /* Whichever comes first, use RDSEED, RDRAND or nothing */ + if ((OPENSSL_ia32cap_P[2] & (1 << 18)) != 0) { + if (OPENSSL_ia32_rdseed_bytes(buf, len) != len) + return 0; + } else if ((OPENSSL_ia32cap_P[1] & (1 << (62 - 32))) != 0) { + if (OPENSSL_ia32_rdrand_bytes(buf, len) != len) + return 0; + } else + return 0; + return len; +} +#endif + +#else +NON_EMPTY_TRANSLATION_UNIT +#endif diff --git a/crypto/openssl/providers/implementations/rands/seeding/rand_tsc.c b/crypto/openssl/providers/implementations/rands/seeding/rand_tsc.c new file mode 100644 index 000000000000..98dd836b24d9 --- /dev/null +++ b/crypto/openssl/providers/implementations/rands/seeding/rand_tsc.c @@ -0,0 +1,48 @@ +/* + * Copyright 1995-2021 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 + */ + +#include "internal/cryptlib.h" +#include <openssl/opensslconf.h> +#include "crypto/rand_pool.h" +#include "prov/seeding.h" + +#ifdef OPENSSL_RAND_SEED_RDTSC +/* + * IMPORTANT NOTE: It is not currently possible to use this code + * because we are not sure about the amount of randomness it provides. + * Some SP800-90B tests have been run, but there is internal skepticism. + * So for now this code is not used. + */ +# error "RDTSC enabled? Should not be possible!" + +/* + * Acquire entropy from high-speed clock + * + * Since we get some randomness from the low-order bits of the + * high-speed clock, it can help. + * + * Returns the total entropy count, if it exceeds the requested + * entropy count. Otherwise, returns an entropy count of 0. + */ +size_t ossl_prov_acquire_entropy_from_tsc(RAND_POOL *pool) +{ + unsigned char c; + int i; + + if ((OPENSSL_ia32cap_P[0] & (1 << 4)) != 0) { + for (i = 0; i < TSC_READ_COUNT; i++) { + c = (unsigned char)(OPENSSL_rdtsc() & 0xFF); + ossl_rand_pool_add(pool, &c, 1, 4); + } + } + return ossl_rand_pool_entropy_available(pool); +} +#else +NON_EMPTY_TRANSLATION_UNIT +#endif diff --git a/crypto/openssl/providers/implementations/rands/seeding/rand_unix.c b/crypto/openssl/providers/implementations/rands/seeding/rand_unix.c new file mode 100644 index 000000000000..c3a5d8b3bf24 --- /dev/null +++ b/crypto/openssl/providers/implementations/rands/seeding/rand_unix.c @@ -0,0 +1,802 @@ +/* + * Copyright 1995-2024 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 + */ + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif +#include "internal/e_os.h" +#include <stdio.h> +#include "internal/cryptlib.h" +#include <openssl/rand.h> +#include <openssl/crypto.h> +#include "crypto/rand_pool.h" +#include "crypto/rand.h" +#include "internal/dso.h" +#include "internal/nelem.h" +#include "prov/seeding.h" + +#ifndef OPENSSL_SYS_UEFI +# ifdef __linux +# include <sys/syscall.h> +# ifdef DEVRANDOM_WAIT +# include <sys/shm.h> +# include <sys/utsname.h> +# endif +# endif +# if defined(__FreeBSD__) || defined(__NetBSD__) +# include <sys/types.h> +# include <sys/sysctl.h> +# include <sys/param.h> +# endif +# if defined(__FreeBSD__) && __FreeBSD_version >= 1200061 +# include <sys/random.h> +# endif +# if defined(__OpenBSD__) +# include <sys/param.h> +# endif +# if defined(__DragonFly__) +# include <sys/param.h> +# include <sys/random.h> +# endif +#endif + +#if (defined(OPENSSL_SYS_UNIX) && !defined(OPENSSL_SYS_VXWORKS)) \ + || defined(__DJGPP__) +# include <sys/types.h> +# include <sys/stat.h> +# include <fcntl.h> +# include <unistd.h> +# include <sys/time.h> + +static uint64_t get_time_stamp(void); + +/* Macro to convert two thirty two bit values into a sixty four bit one */ +# define TWO32TO64(a, b) ((((uint64_t)(a)) << 32) + (b)) + +/* + * Check for the existence and support of POSIX timers. The standard + * says that the _POSIX_TIMERS macro will have a positive value if they + * are available. + * + * However, we want an additional constraint: that the timer support does + * not require an extra library dependency. Early versions of glibc + * require -lrt to be specified on the link line to access the timers, + * so this needs to be checked for. + * + * It is worse because some libraries define __GLIBC__ but don't + * support the version testing macro (e.g. uClibc). This means + * an extra check is needed. + * + * The final condition is: + * "have posix timers and either not glibc or glibc without -lrt" + * + * The nested #if sequences are required to avoid using a parameterised + * macro that might be undefined. + */ +# undef OSSL_POSIX_TIMER_OKAY +/* On some systems, _POSIX_TIMERS is defined but empty. + * Subtracting by 0 when comparing avoids an error in this case. */ +# if defined(_POSIX_TIMERS) && _POSIX_TIMERS -0 > 0 +# if defined(__GLIBC__) +# if defined(__GLIBC_PREREQ) +# if __GLIBC_PREREQ(2, 17) +# define OSSL_POSIX_TIMER_OKAY +# endif +# endif +# else +# define OSSL_POSIX_TIMER_OKAY +# endif +# endif +#endif /* (defined(OPENSSL_SYS_UNIX) && !defined(OPENSSL_SYS_VXWORKS)) + || defined(__DJGPP__) */ + +#if defined(OPENSSL_RAND_SEED_NONE) +/* none means none. this simplifies the following logic */ +# undef OPENSSL_RAND_SEED_OS +# undef OPENSSL_RAND_SEED_GETRANDOM +# undef OPENSSL_RAND_SEED_DEVRANDOM +# undef OPENSSL_RAND_SEED_RDTSC +# undef OPENSSL_RAND_SEED_RDCPU +# undef OPENSSL_RAND_SEED_EGD +#endif + +#if defined(OPENSSL_SYS_UEFI) && !defined(OPENSSL_RAND_SEED_NONE) +# error "UEFI only supports seeding NONE" +#endif + +#if !(defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) \ + || defined(OPENSSL_SYS_VMS) || defined(OPENSSL_SYS_VXWORKS) \ + || defined(OPENSSL_SYS_UEFI)) + +# if defined(OPENSSL_SYS_VOS) + +# ifndef OPENSSL_RAND_SEED_OS +# error "Unsupported seeding method configured; must be os" +# endif + +# if defined(OPENSSL_SYS_VOS_HPPA) && defined(OPENSSL_SYS_VOS_IA32) +# error "Unsupported HP-PA and IA32 at the same time." +# endif +# if !defined(OPENSSL_SYS_VOS_HPPA) && !defined(OPENSSL_SYS_VOS_IA32) +# error "Must have one of HP-PA or IA32" +# endif + +/* + * The following algorithm repeatedly samples the real-time clock (RTC) to + * generate a sequence of unpredictable data. The algorithm relies upon the + * uneven execution speed of the code (due to factors such as cache misses, + * interrupts, bus activity, and scheduling) and upon the rather large + * relative difference between the speed of the clock and the rate at which + * it can be read. If it is ported to an environment where execution speed + * is more constant or where the RTC ticks at a much slower rate, or the + * clock can be read with fewer instructions, it is likely that the results + * would be far more predictable. This should only be used for legacy + * platforms. + * + * As a precaution, we assume only 2 bits of entropy per byte. + */ +size_t ossl_pool_acquire_entropy(RAND_POOL *pool) +{ + short int code; + int i, k; + size_t bytes_needed; + struct timespec ts; + unsigned char v; +# ifdef OPENSSL_SYS_VOS_HPPA + long duration; + extern void s$sleep(long *_duration, short int *_code); +# else + long long duration; + extern void s$sleep2(long long *_duration, short int *_code); +# endif + + bytes_needed = ossl_rand_pool_bytes_needed(pool, 4 /*entropy_factor*/); + + for (i = 0; i < bytes_needed; i++) { + /* + * burn some cpu; hope for interrupts, cache collisions, bus + * interference, etc. + */ + for (k = 0; k < 99; k++) + ts.tv_nsec = random(); + +# ifdef OPENSSL_SYS_VOS_HPPA + /* sleep for 1/1024 of a second (976 us). */ + duration = 1; + s$sleep(&duration, &code); +# else + /* sleep for 1/65536 of a second (15 us). */ + duration = 1; + s$sleep2(&duration, &code); +# endif + + /* Get wall clock time, take 8 bits. */ + clock_gettime(CLOCK_REALTIME, &ts); + v = (unsigned char)(ts.tv_nsec & 0xFF); + ossl_rand_pool_add(pool, arg, &v, sizeof(v), 2); + } + return ossl_rand_pool_entropy_available(pool); +} + +void ossl_rand_pool_cleanup(void) +{ +} + +void ossl_rand_pool_keep_random_devices_open(int keep) +{ +} + +# else + +# if defined(OPENSSL_RAND_SEED_EGD) && \ + (defined(OPENSSL_NO_EGD) || !defined(DEVRANDOM_EGD)) +# error "Seeding uses EGD but EGD is turned off or no device given" +# endif + +# if defined(OPENSSL_RAND_SEED_DEVRANDOM) && !defined(DEVRANDOM) +# error "Seeding uses urandom but DEVRANDOM is not configured" +# endif + +# if defined(OPENSSL_RAND_SEED_OS) +# if !defined(DEVRANDOM) +# error "OS seeding requires DEVRANDOM to be configured" +# endif +# define OPENSSL_RAND_SEED_GETRANDOM +# define OPENSSL_RAND_SEED_DEVRANDOM +# endif + +# if (defined(__FreeBSD__) || defined(__NetBSD__)) && defined(KERN_ARND) +/* + * sysctl_random(): Use sysctl() to read a random number from the kernel + * Returns the number of bytes returned in buf on success, -1 on failure. + */ +static ssize_t sysctl_random(char *buf, size_t buflen) +{ + int mib[2]; + size_t done = 0; + size_t len; + + /* + * Note: sign conversion between size_t and ssize_t is safe even + * without a range check, see comment in syscall_random() + */ + + /* + * On FreeBSD old implementations returned longs, newer versions support + * variable sizes up to 256 byte. The code below would not work properly + * when the sysctl returns long and we want to request something not a + * multiple of longs, which should never be the case. + */ +#if defined(__FreeBSD__) + if (!ossl_assert(buflen % sizeof(long) == 0)) { + errno = EINVAL; + return -1; + } +#endif + + /* + * On NetBSD before 4.0 KERN_ARND was an alias for KERN_URND, and only + * filled in an int, leaving the rest uninitialized. Since NetBSD 4.0 + * it returns a variable number of bytes with the current version supporting + * up to 256 bytes. + * Just return an error on older NetBSD versions. + */ +#if defined(__NetBSD__) && __NetBSD_Version__ < 400000000 + errno = ENOSYS; + return -1; +#endif + + mib[0] = CTL_KERN; + mib[1] = KERN_ARND; + + do { + len = buflen > 256 ? 256 : buflen; + if (sysctl(mib, 2, buf, &len, NULL, 0) == -1) + return done > 0 ? done : -1; + done += len; + buf += len; + buflen -= len; + } while (buflen > 0); + + return done; +} +# endif + +# if defined(OPENSSL_RAND_SEED_GETRANDOM) + +# if defined(__linux) && !defined(__NR_getrandom) +# if defined(__arm__) +# define __NR_getrandom (__NR_SYSCALL_BASE+384) +# elif defined(__i386__) +# define __NR_getrandom 355 +# elif defined(__x86_64__) +# if defined(__ILP32__) +# define __NR_getrandom (__X32_SYSCALL_BIT + 318) +# else +# define __NR_getrandom 318 +# endif +# elif defined(__xtensa__) +# define __NR_getrandom 338 +# elif defined(__s390__) || defined(__s390x__) +# define __NR_getrandom 349 +# elif defined(__bfin__) +# define __NR_getrandom 389 +# elif defined(__powerpc__) +# define __NR_getrandom 359 +# elif defined(__mips__) || defined(__mips64) +# if _MIPS_SIM == _MIPS_SIM_ABI32 +# define __NR_getrandom (__NR_Linux + 353) +# elif _MIPS_SIM == _MIPS_SIM_ABI64 +# define __NR_getrandom (__NR_Linux + 313) +# elif _MIPS_SIM == _MIPS_SIM_NABI32 +# define __NR_getrandom (__NR_Linux + 317) +# endif +# elif defined(__hppa__) +# define __NR_getrandom (__NR_Linux + 339) +# elif defined(__sparc__) +# define __NR_getrandom 347 +# elif defined(__ia64__) +# define __NR_getrandom 1339 +# elif defined(__alpha__) +# define __NR_getrandom 511 +# elif defined(__sh__) +# if defined(__SH5__) +# define __NR_getrandom 373 +# else +# define __NR_getrandom 384 +# endif +# elif defined(__avr32__) +# define __NR_getrandom 317 +# elif defined(__microblaze__) +# define __NR_getrandom 385 +# elif defined(__m68k__) +# define __NR_getrandom 352 +# elif defined(__cris__) +# define __NR_getrandom 356 +# else /* generic (f.e. aarch64, loongarch, loongarch64) */ +# define __NR_getrandom 278 +# endif +# endif + +/* + * syscall_random(): Try to get random data using a system call + * returns the number of bytes returned in buf, or < 0 on error. + */ +static ssize_t syscall_random(void *buf, size_t buflen) +{ + /* + * Note: 'buflen' equals the size of the buffer which is used by the + * get_entropy() callback of the RAND_DRBG. It is roughly bounded by + * + * 2 * RAND_POOL_FACTOR * (RAND_DRBG_STRENGTH / 8) = 2^14 + * + * which is way below the OSSL_SSIZE_MAX limit. Therefore sign conversion + * between size_t and ssize_t is safe even without a range check. + */ + + /* + * Do runtime detection to find getentropy(). + * + * Known OSs that should support this: + * - Darwin since 16 (OSX 10.12, IOS 10.0). + * - Solaris since 11.3 + * - OpenBSD since 5.6 + * - Linux since 3.17 with glibc 2.25 + * + * Note: Sometimes getentropy() can be provided but not implemented + * internally. So we need to check errno for ENOSYS + */ +# if !defined(__DragonFly__) && !defined(__NetBSD__) && !defined(__FreeBSD__) +# if defined(__GNUC__) && __GNUC__>=2 && defined(__ELF__) && !defined(__hpux) + extern int getentropy(void *buffer, size_t length) __attribute__((weak)); + + if (getentropy != NULL) { + if (getentropy(buf, buflen) == 0) + return (ssize_t)buflen; + if (errno != ENOSYS) + return -1; + } +# elif defined(OPENSSL_APPLE_CRYPTO_RANDOM) + + if (CCRandomGenerateBytes(buf, buflen) == kCCSuccess) + return (ssize_t)buflen; + + return -1; +# else + union { + void *p; + int (*f)(void *buffer, size_t length); + } p_getentropy; + + /* + * We could cache the result of the lookup, but we normally don't + * call this function often. + */ + ERR_set_mark(); + p_getentropy.p = DSO_global_lookup("getentropy"); + ERR_pop_to_mark(); + if (p_getentropy.p != NULL) + return p_getentropy.f(buf, buflen) == 0 ? (ssize_t)buflen : -1; +# endif +# endif /* !__DragonFly__ && !__NetBSD__ && !__FreeBSD__ */ + + /* Linux supports this since version 3.17 */ +# if defined(__linux) && defined(__NR_getrandom) + return syscall(__NR_getrandom, buf, buflen, 0); +# elif (defined(__DragonFly__) && __DragonFly_version >= 500700) \ + || (defined(__NetBSD__) && __NetBSD_Version >= 1000000000) \ + || (defined(__FreeBSD__) && __FreeBSD_version >= 1200061) + return getrandom(buf, buflen, 0); +# elif (defined(__FreeBSD__) || defined(__NetBSD__)) && defined(KERN_ARND) + return sysctl_random(buf, buflen); +# elif defined(__wasi__) + if (getentropy(buf, buflen) == 0) + return (ssize_t)buflen; + return -1; +# else + errno = ENOSYS; + return -1; +# endif +} +# endif /* defined(OPENSSL_RAND_SEED_GETRANDOM) */ + +# if defined(OPENSSL_RAND_SEED_DEVRANDOM) +static const char *random_device_paths[] = { DEVRANDOM }; +static struct random_device { + int fd; + dev_t dev; + ino_t ino; + mode_t mode; + dev_t rdev; +} random_devices[OSSL_NELEM(random_device_paths)]; +static int keep_random_devices_open = 1; + +# if defined(__linux) && defined(DEVRANDOM_WAIT) \ + && defined(OPENSSL_RAND_SEED_GETRANDOM) +static void *shm_addr; + +static void cleanup_shm(void) +{ + shmdt(shm_addr); +} + +/* + * Ensure that the system randomness source has been adequately seeded. + * This is done by having the first start of libcrypto, wait until the device + * /dev/random becomes able to supply a byte of entropy. Subsequent starts + * of the library and later reseedings do not need to do this. + */ +static int wait_random_seeded(void) +{ + static int seeded = OPENSSL_RAND_SEED_DEVRANDOM_SHM_ID < 0; + static const int kernel_version[] = { DEVRANDOM_SAFE_KERNEL }; + int kernel[2]; + int shm_id, fd, r; + char c, *p; + struct utsname un; + fd_set fds; + + if (!seeded) { + /* See if anything has created the global seeded indication */ + if ((shm_id = shmget(OPENSSL_RAND_SEED_DEVRANDOM_SHM_ID, 1, 0)) == -1) { + /* + * Check the kernel's version and fail if it is too recent. + * + * Linux kernels from 4.8 onwards do not guarantee that + * /dev/urandom is properly seeded when /dev/random becomes + * readable. However, such kernels support the getentropy(2) + * system call and this should always succeed which renders + * this alternative but essentially identical source moot. + */ + if (uname(&un) == 0) { + kernel[0] = atoi(un.release); + p = strchr(un.release, '.'); + kernel[1] = p == NULL ? 0 : atoi(p + 1); + if (kernel[0] > kernel_version[0] + || (kernel[0] == kernel_version[0] + && kernel[1] >= kernel_version[1])) { + return 0; + } + } + /* Open /dev/random and wait for it to be readable */ + if ((fd = open(DEVRANDOM_WAIT, O_RDONLY)) != -1) { + if (DEVRANDM_WAIT_USE_SELECT && fd < FD_SETSIZE) { + FD_ZERO(&fds); + FD_SET(fd, &fds); + while ((r = select(fd + 1, &fds, NULL, NULL, NULL)) < 0 + && errno == EINTR); + } else { + while ((r = read(fd, &c, 1)) < 0 && errno == EINTR); + } + close(fd); + if (r == 1) { + seeded = 1; + /* Create the shared memory indicator */ + shm_id = shmget(OPENSSL_RAND_SEED_DEVRANDOM_SHM_ID, 1, + IPC_CREAT | S_IRUSR | S_IRGRP | S_IROTH); + } + } + } + if (shm_id != -1) { + seeded = 1; + /* + * Map the shared memory to prevent its premature destruction. + * If this call fails, it isn't a big problem. + */ + shm_addr = shmat(shm_id, NULL, SHM_RDONLY); + if (shm_addr != (void *)-1) + OPENSSL_atexit(&cleanup_shm); + } + } + return seeded; +} +# else /* defined __linux && DEVRANDOM_WAIT && OPENSSL_RAND_SEED_GETRANDOM */ +static int wait_random_seeded(void) +{ + return 1; +} +# endif + +/* + * Verify that the file descriptor associated with the random source is + * still valid. The rationale for doing this is the fact that it is not + * uncommon for daemons to close all open file handles when daemonizing. + * So the handle might have been closed or even reused for opening + * another file. + */ +static int check_random_device(struct random_device *rd) +{ + struct stat st; + + return rd->fd != -1 + && fstat(rd->fd, &st) != -1 + && rd->dev == st.st_dev + && rd->ino == st.st_ino + && ((rd->mode ^ st.st_mode) & ~(S_IRWXU | S_IRWXG | S_IRWXO)) == 0 + && rd->rdev == st.st_rdev; +} + +/* + * Open a random device if required and return its file descriptor or -1 on error + */ +static int get_random_device(size_t n) +{ + struct stat st; + struct random_device *rd = &random_devices[n]; + + /* reuse existing file descriptor if it is (still) valid */ + if (check_random_device(rd)) + return rd->fd; + + /* open the random device ... */ + if ((rd->fd = open(random_device_paths[n], O_RDONLY)) == -1) + return rd->fd; + + /* ... and cache its relevant stat(2) data */ + if (fstat(rd->fd, &st) != -1) { + rd->dev = st.st_dev; + rd->ino = st.st_ino; + rd->mode = st.st_mode; + rd->rdev = st.st_rdev; + } else { + close(rd->fd); + rd->fd = -1; + } + + return rd->fd; +} + +/* + * Close a random device making sure it is a random device + */ +static void close_random_device(size_t n) +{ + struct random_device *rd = &random_devices[n]; + + if (check_random_device(rd)) + close(rd->fd); + rd->fd = -1; +} + +int ossl_rand_pool_init(void) +{ + size_t i; + + for (i = 0; i < OSSL_NELEM(random_devices); i++) + random_devices[i].fd = -1; + + return 1; +} + +void ossl_rand_pool_cleanup(void) +{ + size_t i; + + for (i = 0; i < OSSL_NELEM(random_devices); i++) + close_random_device(i); +} + +void ossl_rand_pool_keep_random_devices_open(int keep) +{ + if (!keep) + ossl_rand_pool_cleanup(); + + keep_random_devices_open = keep; +} + +# else /* !defined(OPENSSL_RAND_SEED_DEVRANDOM) */ + +int ossl_rand_pool_init(void) +{ + return 1; +} + +void ossl_rand_pool_cleanup(void) +{ +} + +void ossl_rand_pool_keep_random_devices_open(int keep) +{ +} + +# endif /* defined(OPENSSL_RAND_SEED_DEVRANDOM) */ + +/* + * Try the various seeding methods in turn, exit when successful. + * + * If more than one entropy source is available, is it + * preferable to stop as soon as enough entropy has been collected + * (as favored by @rsalz) or should one rather be defensive and add + * more entropy than requested and/or from different sources? + * + * Currently, the user can select multiple entropy sources in the + * configure step, yet in practice only the first available source + * will be used. A more flexible solution has been requested, but + * currently it is not clear how this can be achieved without + * overengineering the problem. There are many parameters which + * could be taken into account when selecting the order and amount + * of input from the different entropy sources (trust, quality, + * possibility of blocking). + */ +size_t ossl_pool_acquire_entropy(RAND_POOL *pool) +{ +# if defined(OPENSSL_RAND_SEED_NONE) + return ossl_rand_pool_entropy_available(pool); +# else + size_t entropy_available = 0; + + (void)entropy_available; /* avoid compiler warning */ + +# if defined(OPENSSL_RAND_SEED_GETRANDOM) + { + size_t bytes_needed; + unsigned char *buffer; + ssize_t bytes; + /* Maximum allowed number of consecutive unsuccessful attempts */ + int attempts = 3; + + bytes_needed = ossl_rand_pool_bytes_needed(pool, 1 /*entropy_factor*/); + while (bytes_needed != 0 && attempts-- > 0) { + buffer = ossl_rand_pool_add_begin(pool, bytes_needed); + bytes = syscall_random(buffer, bytes_needed); + if (bytes > 0) { + ossl_rand_pool_add_end(pool, bytes, 8 * bytes); + bytes_needed -= bytes; + attempts = 3; /* reset counter after successful attempt */ + } else if (bytes < 0 && errno != EINTR) { + break; + } + } + } + entropy_available = ossl_rand_pool_entropy_available(pool); + if (entropy_available > 0) + return entropy_available; +# endif + +# if defined(OPENSSL_RAND_SEED_DEVRANDOM) + if (wait_random_seeded()) { + size_t bytes_needed; + unsigned char *buffer; + size_t i; + + bytes_needed = ossl_rand_pool_bytes_needed(pool, 1 /*entropy_factor*/); + for (i = 0; bytes_needed > 0 && i < OSSL_NELEM(random_device_paths); + i++) { + ssize_t bytes = 0; + /* Maximum number of consecutive unsuccessful attempts */ + int attempts = 3; + const int fd = get_random_device(i); + + if (fd == -1) + continue; + + while (bytes_needed != 0 && attempts-- > 0) { + buffer = ossl_rand_pool_add_begin(pool, bytes_needed); + bytes = read(fd, buffer, bytes_needed); + + if (bytes > 0) { + ossl_rand_pool_add_end(pool, bytes, 8 * bytes); + bytes_needed -= bytes; + attempts = 3; /* reset counter on successful attempt */ + } else if (bytes < 0 && errno != EINTR) { + break; + } + } + if (bytes < 0 || !keep_random_devices_open) + close_random_device(i); + + bytes_needed = ossl_rand_pool_bytes_needed(pool, 1); + } + entropy_available = ossl_rand_pool_entropy_available(pool); + if (entropy_available > 0) + return entropy_available; + } +# endif + +# if defined(OPENSSL_RAND_SEED_RDTSC) + entropy_available = ossl_prov_acquire_entropy_from_tsc(pool); + if (entropy_available > 0) + return entropy_available; +# endif + +# if defined(OPENSSL_RAND_SEED_RDCPU) + entropy_available = ossl_prov_acquire_entropy_from_cpu(pool); + if (entropy_available > 0) + return entropy_available; +# endif + +# if defined(OPENSSL_RAND_SEED_EGD) + { + static const char *paths[] = { DEVRANDOM_EGD, NULL }; + size_t bytes_needed; + unsigned char *buffer; + int i; + + bytes_needed = ossl_rand_pool_bytes_needed(pool, 1 /*entropy_factor*/); + for (i = 0; bytes_needed > 0 && paths[i] != NULL; i++) { + size_t bytes = 0; + int num; + + buffer = ossl_rand_pool_add_begin(pool, bytes_needed); + num = RAND_query_egd_bytes(paths[i], + buffer, (int)bytes_needed); + if (num == (int)bytes_needed) + bytes = bytes_needed; + + ossl_rand_pool_add_end(pool, bytes, 8 * bytes); + bytes_needed = ossl_rand_pool_bytes_needed(pool, 1); + } + entropy_available = ossl_rand_pool_entropy_available(pool); + if (entropy_available > 0) + return entropy_available; + } +# endif + + return ossl_rand_pool_entropy_available(pool); +# endif +} +# endif +#endif + +#if (defined(OPENSSL_SYS_UNIX) && !defined(OPENSSL_SYS_VXWORKS)) \ + || defined(__DJGPP__) +int ossl_pool_add_nonce_data(RAND_POOL *pool) +{ + struct { + pid_t pid; + CRYPTO_THREAD_ID tid; + uint64_t time; + } data; + + /* Erase the entire structure including any padding */ + memset(&data, 0, sizeof(data)); + + /* + * Add process id, thread id, and a high resolution timestamp to + * ensure that the nonce is unique with high probability for + * different process instances. + */ + data.pid = getpid(); + data.tid = CRYPTO_THREAD_get_current_id(); + data.time = get_time_stamp(); + + return ossl_rand_pool_add(pool, (unsigned char *)&data, sizeof(data), 0); +} + +/* + * Get the current time with the highest possible resolution + * + * The time stamp is added to the nonce, so it is optimized for not repeating. + * The current time is ideal for this purpose, provided the computer's clock + * is synchronized. + */ +static uint64_t get_time_stamp(void) +{ +# if defined(OSSL_POSIX_TIMER_OKAY) + { + struct timespec ts; + + if (clock_gettime(CLOCK_REALTIME, &ts) == 0) + return TWO32TO64(ts.tv_sec, ts.tv_nsec); + } +# endif +# if defined(__unix__) \ + || (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) + { + struct timeval tv; + + if (gettimeofday(&tv, NULL) == 0) + return TWO32TO64(tv.tv_sec, tv.tv_usec); + } +# endif + return time(NULL); +} + +#endif /* (defined(OPENSSL_SYS_UNIX) && !defined(OPENSSL_SYS_VXWORKS)) + || defined(__DJGPP__) */ diff --git a/crypto/openssl/providers/implementations/rands/seeding/rand_vms.c b/crypto/openssl/providers/implementations/rands/seeding/rand_vms.c new file mode 100644 index 000000000000..4ff879491a6b --- /dev/null +++ b/crypto/openssl/providers/implementations/rands/seeding/rand_vms.c @@ -0,0 +1,616 @@ +/* + * Copyright 2001-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 + */ + +#include "internal/e_os.h" + +#define __NEW_STARLET 1 /* New starlet definitions since VMS 7.0 */ +#include <unistd.h> +#include "internal/cryptlib.h" +#include "internal/nelem.h" +#include <openssl/rand.h> +#include "crypto/rand.h" +#include "crypto/rand_pool.h" +#include "prov/seeding.h" +#include <descrip.h> +#include <dvidef.h> +#include <jpidef.h> +#include <rmidef.h> +#include <syidef.h> +#include <ssdef.h> +#include <starlet.h> +#include <efndef.h> +#include <gen64def.h> +#include <iosbdef.h> +#include <iledef.h> +#include <lib$routines.h> +#ifdef __DECC +# pragma message disable DOLLARID +#endif + +#include <dlfcn.h> /* SYS$GET_ENTROPY presence */ + +#ifndef OPENSSL_RAND_SEED_OS +# error "Unsupported seeding method configured; must be os" +#endif + +/* + * DATA COLLECTION METHOD + * ====================== + * + * This is a method to get low quality entropy. + * It works by collecting all kinds of statistical data that + * VMS offers and using them as random seed. + */ + +/* We need to make sure we have the right size pointer in some cases */ +#if __INITIAL_POINTER_SIZE == 64 +# pragma pointer_size save +# pragma pointer_size 32 +#endif +typedef uint32_t *uint32_t__ptr32; +#if __INITIAL_POINTER_SIZE == 64 +# pragma pointer_size restore +#endif + +struct item_st { + short length, code; /* length is number of bytes */ +}; + +static const struct item_st DVI_item_data[] = { + {4, DVI$_ERRCNT}, + {4, DVI$_REFCNT}, +}; + +static const struct item_st JPI_item_data[] = { + {4, JPI$_BUFIO}, + {4, JPI$_CPUTIM}, + {4, JPI$_DIRIO}, + {4, JPI$_IMAGECOUNT}, + {4, JPI$_PAGEFLTS}, + {4, JPI$_PID}, + {4, JPI$_PPGCNT}, + {4, JPI$_WSPEAK}, + /* + * Note: the direct result is just a 32-bit address. However, it points + * to a list of 4 32-bit words, so we make extra space for them so we can + * do in-place replacement of values + */ + {16, JPI$_FINALEXC}, +}; + +static const struct item_st JPI_item_data_64bit[] = { + {8, JPI$_LAST_LOGIN_I}, + {8, JPI$_LOGINTIM}, +}; + +static const struct item_st RMI_item_data[] = { + {4, RMI$_COLPG}, + {4, RMI$_MWAIT}, + {4, RMI$_CEF}, + {4, RMI$_PFW}, + {4, RMI$_LEF}, + {4, RMI$_LEFO}, + {4, RMI$_HIB}, + {4, RMI$_HIBO}, + {4, RMI$_SUSP}, + {4, RMI$_SUSPO}, + {4, RMI$_FPG}, + {4, RMI$_COM}, + {4, RMI$_COMO}, + {4, RMI$_CUR}, +#if defined __alpha + {4, RMI$_FRLIST}, + {4, RMI$_MODLIST}, +#endif + {4, RMI$_FAULTS}, + {4, RMI$_PREADS}, + {4, RMI$_PWRITES}, + {4, RMI$_PWRITIO}, + {4, RMI$_PREADIO}, + {4, RMI$_GVALFLTS}, + {4, RMI$_WRTINPROG}, + {4, RMI$_FREFLTS}, + {4, RMI$_DZROFLTS}, + {4, RMI$_SYSFAULTS}, + {4, RMI$_ISWPCNT}, + {4, RMI$_DIRIO}, + {4, RMI$_BUFIO}, + {4, RMI$_MBREADS}, + {4, RMI$_MBWRITES}, + {4, RMI$_LOGNAM}, + {4, RMI$_FCPCALLS}, + {4, RMI$_FCPREAD}, + {4, RMI$_FCPWRITE}, + {4, RMI$_FCPCACHE}, + {4, RMI$_FCPCPU}, + {4, RMI$_FCPHIT}, + {4, RMI$_FCPSPLIT}, + {4, RMI$_FCPFAULT}, + {4, RMI$_ENQNEW}, + {4, RMI$_ENQCVT}, + {4, RMI$_DEQ}, + {4, RMI$_BLKAST}, + {4, RMI$_ENQWAIT}, + {4, RMI$_ENQNOTQD}, + {4, RMI$_DLCKSRCH}, + {4, RMI$_DLCKFND}, + {4, RMI$_NUMLOCKS}, + {4, RMI$_NUMRES}, + {4, RMI$_ARRLOCPK}, + {4, RMI$_DEPLOCPK}, + {4, RMI$_ARRTRAPK}, + {4, RMI$_TRCNGLOS}, + {4, RMI$_RCVBUFFL}, + {4, RMI$_ENQNEWLOC}, + {4, RMI$_ENQNEWIN}, + {4, RMI$_ENQNEWOUT}, + {4, RMI$_ENQCVTLOC}, + {4, RMI$_ENQCVTIN}, + {4, RMI$_ENQCVTOUT}, + {4, RMI$_DEQLOC}, + {4, RMI$_DEQIN}, + {4, RMI$_DEQOUT}, + {4, RMI$_BLKLOC}, + {4, RMI$_BLKIN}, + {4, RMI$_BLKOUT}, + {4, RMI$_DIRIN}, + {4, RMI$_DIROUT}, + /* We currently get a fault when trying these */ +#if 0 + {140, RMI$_MSCP_EVERYTHING}, /* 35 32-bit words */ + {152, RMI$_DDTM_ALL}, /* 38 32-bit words */ + {80, RMI$_TMSCP_EVERYTHING} /* 20 32-bit words */ +#endif + {4, RMI$_LPZ_PAGCNT}, + {4, RMI$_LPZ_HITS}, + {4, RMI$_LPZ_MISSES}, + {4, RMI$_LPZ_EXPCNT}, + {4, RMI$_LPZ_ALLOCF}, + {4, RMI$_LPZ_ALLOC2}, + {4, RMI$_ACCESS}, + {4, RMI$_ALLOC}, + {4, RMI$_FCPCREATE}, + {4, RMI$_VOLWAIT}, + {4, RMI$_FCPTURN}, + {4, RMI$_FCPERASE}, + {4, RMI$_OPENS}, + {4, RMI$_FIDHIT}, + {4, RMI$_FIDMISS}, + {4, RMI$_FILHDR_HIT}, + {4, RMI$_DIRFCB_HIT}, + {4, RMI$_DIRFCB_MISS}, + {4, RMI$_DIRDATA_HIT}, + {4, RMI$_EXTHIT}, + {4, RMI$_EXTMISS}, + {4, RMI$_QUOHIT}, + {4, RMI$_QUOMISS}, + {4, RMI$_STORAGMAP_HIT}, + {4, RMI$_VOLLCK}, + {4, RMI$_SYNCHLCK}, + {4, RMI$_SYNCHWAIT}, + {4, RMI$_ACCLCK}, + {4, RMI$_XQPCACHEWAIT}, + {4, RMI$_DIRDATA_MISS}, + {4, RMI$_FILHDR_MISS}, + {4, RMI$_STORAGMAP_MISS}, + {4, RMI$_PROCCNTMAX}, + {4, RMI$_PROCBATCNT}, + {4, RMI$_PROCINTCNT}, + {4, RMI$_PROCNETCNT}, + {4, RMI$_PROCSWITCHCNT}, + {4, RMI$_PROCBALSETCNT}, + {4, RMI$_PROCLOADCNT}, + {4, RMI$_BADFLTS}, + {4, RMI$_EXEFAULTS}, + {4, RMI$_HDRINSWAPS}, + {4, RMI$_HDROUTSWAPS}, + {4, RMI$_IOPAGCNT}, + {4, RMI$_ISWPCNTPG}, + {4, RMI$_OSWPCNT}, + {4, RMI$_OSWPCNTPG}, + {4, RMI$_RDFAULTS}, + {4, RMI$_TRANSFLTS}, + {4, RMI$_WRTFAULTS}, +#if defined __alpha + {4, RMI$_USERPAGES}, +#endif + {4, RMI$_VMSPAGES}, + {4, RMI$_TTWRITES}, + {4, RMI$_BUFOBJPAG}, + {4, RMI$_BUFOBJPAGPEAK}, + {4, RMI$_BUFOBJPAGS01}, + {4, RMI$_BUFOBJPAGS2}, + {4, RMI$_BUFOBJPAGMAXS01}, + {4, RMI$_BUFOBJPAGMAXS2}, + {4, RMI$_BUFOBJPAGPEAKS01}, + {4, RMI$_BUFOBJPAGPEAKS2}, + {4, RMI$_BUFOBJPGLTMAXS01}, + {4, RMI$_BUFOBJPGLTMAXS2}, + {4, RMI$_DLCK_INCMPLT}, + {4, RMI$_DLCKMSGS_IN}, + {4, RMI$_DLCKMSGS_OUT}, + {4, RMI$_MCHKERRS}, + {4, RMI$_MEMERRS}, +}; + +static const struct item_st RMI_item_data_64bit[] = { +#if defined __ia64 + {8, RMI$_FRLIST}, + {8, RMI$_MODLIST}, +#endif + {8, RMI$_LCKMGR_REQCNT}, + {8, RMI$_LCKMGR_REQTIME}, + {8, RMI$_LCKMGR_SPINCNT}, + {8, RMI$_LCKMGR_SPINTIME}, + {8, RMI$_CPUINTSTK}, + {8, RMI$_CPUMPSYNCH}, + {8, RMI$_CPUKERNEL}, + {8, RMI$_CPUEXEC}, + {8, RMI$_CPUSUPER}, + {8, RMI$_CPUUSER}, +#if defined __ia64 + {8, RMI$_USERPAGES}, +#endif + {8, RMI$_TQETOTAL}, + {8, RMI$_TQESYSUB}, + {8, RMI$_TQEUSRTIMR}, + {8, RMI$_TQEUSRWAKE}, +}; + +static const struct item_st SYI_item_data[] = { + {4, SYI$_PAGEFILE_FREE}, +}; + +/* + * Input: + * items_data - an array of lengths and codes + * items_data_num - number of elements in that array + * + * Output: + * items - pre-allocated ILE3 array to be filled. + * It's assumed to have items_data_num elements plus + * one extra for the terminating NULL element + * databuffer - pre-allocated 32-bit word array. + * + * Returns the number of elements used in databuffer + */ +static size_t prepare_item_list(const struct item_st *items_input, + size_t items_input_num, + ILE3 *items, + uint32_t__ptr32 databuffer) +{ + size_t data_sz = 0; + + for (; items_input_num-- > 0; items_input++, items++) { + + items->ile3$w_code = items_input->code; + /* Special treatment of JPI$_FINALEXC */ + if (items->ile3$w_code == JPI$_FINALEXC) + items->ile3$w_length = 4; + else + items->ile3$w_length = items_input->length; + + items->ile3$ps_bufaddr = databuffer; + items->ile3$ps_retlen_addr = 0; + + databuffer += items_input->length / sizeof(databuffer[0]); + data_sz += items_input->length; + } + /* Terminating NULL entry */ + items->ile3$w_length = items->ile3$w_code = 0; + items->ile3$ps_bufaddr = items->ile3$ps_retlen_addr = NULL; + + return data_sz / sizeof(databuffer[0]); +} + +static void massage_JPI(ILE3 *items) +{ + /* + * Special treatment of JPI$_FINALEXC + * The result of that item's data buffer is a 32-bit address to a list of + * 4 32-bit words. + */ + for (; items->ile3$w_length != 0; items++) { + if (items->ile3$w_code == JPI$_FINALEXC) { + uint32_t *data = items->ile3$ps_bufaddr; + uint32_t *ptr = (uint32_t *)*data; + size_t j; + + /* + * We know we made space for 4 32-bit words, so we can do in-place + * replacement. + */ + for (j = 0; j < 4; j++) + data[j] = ptr[j]; + + break; + } + } +} + +/* + * This number expresses how many bits of data contain 1 bit of entropy. + * + * For the moment, we assume about 0.05 entropy bits per data bit, or 1 + * bit of entropy per 20 data bits. + */ +#define ENTROPY_FACTOR 20 + +size_t data_collect_method(RAND_POOL *pool) +{ + ILE3 JPI_items_64bit[OSSL_NELEM(JPI_item_data_64bit) + 1]; + ILE3 RMI_items_64bit[OSSL_NELEM(RMI_item_data_64bit) + 1]; + ILE3 DVI_items[OSSL_NELEM(DVI_item_data) + 1]; + ILE3 JPI_items[OSSL_NELEM(JPI_item_data) + 1]; + ILE3 RMI_items[OSSL_NELEM(RMI_item_data) + 1]; + ILE3 SYI_items[OSSL_NELEM(SYI_item_data) + 1]; + union { + /* This ensures buffer starts at 64 bit boundary */ + uint64_t dummy; + uint32_t buffer[OSSL_NELEM(JPI_item_data_64bit) * 2 + + OSSL_NELEM(RMI_item_data_64bit) * 2 + + OSSL_NELEM(DVI_item_data) + + OSSL_NELEM(JPI_item_data) + + OSSL_NELEM(RMI_item_data) + + OSSL_NELEM(SYI_item_data) + + 4 /* For JPI$_FINALEXC */]; + } data; + size_t total_elems = 0; + size_t total_length = 0; + size_t bytes_needed = ossl_rand_pool_bytes_needed(pool, ENTROPY_FACTOR); + size_t bytes_remaining = ossl_rand_pool_bytes_remaining(pool); + + /* Take all the 64-bit items first, to ensure proper alignment of data */ + total_elems += + prepare_item_list(JPI_item_data_64bit, OSSL_NELEM(JPI_item_data_64bit), + JPI_items_64bit, &data.buffer[total_elems]); + total_elems += + prepare_item_list(RMI_item_data_64bit, OSSL_NELEM(RMI_item_data_64bit), + RMI_items_64bit, &data.buffer[total_elems]); + /* Now the 32-bit items */ + total_elems += prepare_item_list(DVI_item_data, OSSL_NELEM(DVI_item_data), + DVI_items, &data.buffer[total_elems]); + total_elems += prepare_item_list(JPI_item_data, OSSL_NELEM(JPI_item_data), + JPI_items, &data.buffer[total_elems]); + total_elems += prepare_item_list(RMI_item_data, OSSL_NELEM(RMI_item_data), + RMI_items, &data.buffer[total_elems]); + total_elems += prepare_item_list(SYI_item_data, OSSL_NELEM(SYI_item_data), + SYI_items, &data.buffer[total_elems]); + total_length = total_elems * sizeof(data.buffer[0]); + + /* Fill data.buffer with various info bits from this process */ + { + uint32_t status; + uint32_t efn; + IOSB iosb; + $DESCRIPTOR(SYSDEVICE, "SYS$SYSDEVICE:"); + + if ((status = sys$getdviw(EFN$C_ENF, 0, &SYSDEVICE, DVI_items, + 0, 0, 0, 0, 0)) != SS$_NORMAL) { + lib$signal(status); + return 0; + } + if ((status = sys$getjpiw(EFN$C_ENF, 0, 0, JPI_items_64bit, 0, 0, 0)) + != SS$_NORMAL) { + lib$signal(status); + return 0; + } + if ((status = sys$getjpiw(EFN$C_ENF, 0, 0, JPI_items, 0, 0, 0)) + != SS$_NORMAL) { + lib$signal(status); + return 0; + } + if ((status = sys$getsyiw(EFN$C_ENF, 0, 0, SYI_items, 0, 0, 0)) + != SS$_NORMAL) { + lib$signal(status); + return 0; + } + /* + * The RMI service is a bit special, as there is no synchronous + * variant, so we MUST create an event flag to synchronise on. + */ + if ((status = lib$get_ef(&efn)) != SS$_NORMAL) { + lib$signal(status); + return 0; + } + if ((status = sys$getrmi(efn, 0, 0, RMI_items_64bit, &iosb, 0, 0)) + != SS$_NORMAL) { + lib$signal(status); + return 0; + } + if ((status = sys$synch(efn, &iosb)) != SS$_NORMAL) { + lib$signal(status); + return 0; + } + if (iosb.iosb$l_getxxi_status != SS$_NORMAL) { + lib$signal(iosb.iosb$l_getxxi_status); + return 0; + } + if ((status = sys$getrmi(efn, 0, 0, RMI_items, &iosb, 0, 0)) + != SS$_NORMAL) { + lib$signal(status); + return 0; + } + if ((status = sys$synch(efn, &iosb)) != SS$_NORMAL) { + lib$signal(status); + return 0; + } + if (iosb.iosb$l_getxxi_status != SS$_NORMAL) { + lib$signal(iosb.iosb$l_getxxi_status); + return 0; + } + if ((status = lib$free_ef(&efn)) != SS$_NORMAL) { + lib$signal(status); + return 0; + } + } + + massage_JPI(JPI_items); + + /* + * If we can't feed the requirements from the caller, we're in deep trouble. + */ + if (!ossl_assert(total_length >= bytes_needed)) { + ERR_raise_data(ERR_LIB_RAND, RAND_R_RANDOM_POOL_UNDERFLOW, + "Needed: %zu, Available: %zu", + bytes_needed, total_length); + return 0; + } + + /* + * Try not to overfeed the pool + */ + if (total_length > bytes_remaining) + total_length = bytes_remaining; + + /* We give the pessimistic value for the amount of entropy */ + ossl_rand_pool_add(pool, (unsigned char *)data.buffer, total_length, + 8 * total_length / ENTROPY_FACTOR); + return ossl_rand_pool_entropy_available(pool); +} + +/* + * SYS$GET_ENTROPY METHOD + * ====================== + * + * This is a high entropy method based on a new system service that is + * based on getentropy() from FreeBSD 12. It's only used if available, + * and its availability is detected at run-time. + * + * We assume that this function provides full entropy random output. + */ +#define PUBLIC_VECTORS "SYS$LIBRARY:SYS$PUBLIC_VECTORS.EXE" +#define GET_ENTROPY "SYS$GET_ENTROPY" + +static int get_entropy_address_flag = 0; +static int (*get_entropy_address)(void *buffer, size_t buffer_size) = NULL; +static int init_get_entropy_address(void) +{ + if (get_entropy_address_flag == 0) + get_entropy_address = dlsym(dlopen(PUBLIC_VECTORS, 0), GET_ENTROPY); + get_entropy_address_flag = 1; + return get_entropy_address != NULL; +} + +size_t get_entropy_method(RAND_POOL *pool) +{ + /* + * The documentation says that SYS$GET_ENTROPY will give a maximum of + * 256 bytes of data. + */ + unsigned char buffer[256]; + size_t bytes_needed; + size_t bytes_to_get = 0; + uint32_t status; + + for (bytes_needed = ossl_rand_pool_bytes_needed(pool, 1); + bytes_needed > 0; + bytes_needed -= bytes_to_get) { + bytes_to_get = + bytes_needed > sizeof(buffer) ? sizeof(buffer) : bytes_needed; + + status = get_entropy_address(buffer, bytes_to_get); + if (status == SS$_RETRY) { + /* Set to zero so the loop doesn't diminish |bytes_needed| */ + bytes_to_get = 0; + /* Should sleep some amount of time */ + continue; + } + + if (status != SS$_NORMAL) { + lib$signal(status); + return 0; + } + + ossl_rand_pool_add(pool, buffer, bytes_to_get, 8 * bytes_to_get); + } + + return ossl_rand_pool_entropy_available(pool); +} + +/* + * MAIN ENTROPY ACQUISITION FUNCTIONS + * ================================== + * + * These functions are called by the RAND / DRBG functions + */ + +size_t ossl_pool_acquire_entropy(RAND_POOL *pool) +{ + if (init_get_entropy_address()) + return get_entropy_method(pool); + return data_collect_method(pool); +} + +int ossl_pool_add_nonce_data(RAND_POOL *pool) +{ + /* + * Two variables to ensure that two nonces won't ever be the same + */ + static unsigned __int64 last_time = 0; + static unsigned __int32 last_seq = 0; + + struct { + pid_t pid; + CRYPTO_THREAD_ID tid; + unsigned __int64 time; + unsigned __int32 seq; + } data; + + /* Erase the entire structure including any padding */ + memset(&data, 0, sizeof(data)); + + /* + * Add process id, thread id, a timestamp, and a sequence number in case + * the same time stamp is repeated, to ensure that the nonce is unique + * with high probability for different process instances. + * + * The normal OpenVMS time is specified to be high granularity (100ns), + * but the time update granularity given by sys$gettim() may be lower. + * + * OpenVMS version 8.4 (which is the latest for Alpha and Itanium) and + * on have sys$gettim_prec() as well, which is supposedly having a better + * time update granularity, but tests on Itanium (and even Alpha) have + * shown that compared with sys$gettim(), the difference is marginal, + * so of very little significance in terms of entropy. + * Given that, and that it's a high ask to expect everyone to have + * upgraded to OpenVMS version 8.4, only sys$gettim() is used, and a + * sequence number is added as well, in case sys$gettim() returns the + * same time value more than once. + * + * This function is assumed to be called under thread lock, and does + * therefore not take concurrency into account. + */ + data.pid = getpid(); + data.tid = CRYPTO_THREAD_get_current_id(); + data.seq = 0; + sys$gettim((void*)&data.time); + + if (data.time == last_time) { + data.seq = ++last_seq; + } else { + last_time = data.time; + last_seq = 0; + } + + return ossl_rand_pool_add(pool, (unsigned char *)&data, sizeof(data), 0); +} + +int ossl_rand_pool_init(void) +{ + return 1; +} + +void ossl_rand_pool_cleanup(void) +{ +} + +void ossl_rand_pool_keep_random_devices_open(int keep) +{ +} diff --git a/crypto/openssl/providers/implementations/rands/seeding/rand_vxworks.c b/crypto/openssl/providers/implementations/rands/seeding/rand_vxworks.c new file mode 100644 index 000000000000..64acf6903f9c --- /dev/null +++ b/crypto/openssl/providers/implementations/rands/seeding/rand_vxworks.c @@ -0,0 +1,146 @@ +/* + * Copyright 2019-2024 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 + */ + +#include <openssl/opensslconf.h> + +#include <openssl/rand.h> +#include "crypto/rand_pool.h" +#include "crypto/rand.h" +#include "internal/cryptlib.h" +#include "prov/seeding.h" +#include <version.h> +#include <taskLib.h> + +#if defined(OPENSSL_RAND_SEED_NONE) +/* none means none */ +# undef OPENSSL_RAND_SEED_OS +#endif + +#if defined(OPENSSL_RAND_SEED_OS) +# if _WRS_VXWORKS_MAJOR >= 7 +# define RAND_SEED_VXRANDLIB +# else +# error "VxWorks <7 only support RAND_SEED_NONE" +# endif +#endif + +#if defined(RAND_SEED_VXRANDLIB) +# include <randomNumGen.h> +#endif + +/* Macro to convert two thirty two bit values into a sixty four bit one */ +#define TWO32TO64(a, b) ((((uint64_t)(a)) << 32) + (b)) + +static uint64_t get_time_stamp(void) +{ + struct timespec ts; + + if (clock_gettime(CLOCK_REALTIME, &ts) == 0) + return TWO32TO64(ts.tv_sec, ts.tv_nsec); + return time(NULL); +} + +static uint64_t get_timer_bits(void) +{ + uint64_t res = OPENSSL_rdtsc(); + struct timespec ts; + + if (res != 0) + return res; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + return TWO32TO64(ts.tv_sec, ts.tv_nsec); + return time(NULL); +} + +/* + * empty implementation + * vxworks does not need to init/cleanup or keep open the random lib + */ +int ossl_rand_pool_init(void) +{ + return 1; +} + +void ossl_rand_pool_cleanup(void) +{ +} + +void ossl_rand_pool_keep_random_devices_open(int keep) +{ +} + +int ossl_pool_add_nonce_data(RAND_POOL *pool) +{ + struct { + pid_t pid; + CRYPTO_THREAD_ID tid; + uint64_t time; + } data; + + memset(&data, 0, sizeof(data)); + + /* + * Add process id, thread id, and a high resolution timestamp to + * ensure that the nonce is unique with high probability for + * different process instances. + */ + data.pid = getpid(); + data.tid = CRYPTO_THREAD_get_current_id(); + data.time = get_time_stamp(); + + return ossl_rand_pool_add(pool, (unsigned char *)&data, sizeof(data), 0); +} + +size_t ossl_pool_acquire_entropy(RAND_POOL *pool) +{ +#if defined(RAND_SEED_VXRANDLIB) + /* vxRandLib based entropy method */ + size_t bytes_needed; + + bytes_needed = ossl_rand_pool_bytes_needed(pool, 1 /*entropy_factor*/); + if (bytes_needed > 0) { + int retryCount = 0; + STATUS result = ERROR; + unsigned char *buffer; + + buffer = ossl_rand_pool_add_begin(pool, bytes_needed); + while ((result != OK) && (retryCount < 10)) { + RANDOM_NUM_GEN_STATUS status = randStatus(); + + if ((status == RANDOM_NUM_GEN_ENOUGH_ENTROPY) + || (status == RANDOM_NUM_GEN_MAX_ENTROPY)) { + result = randBytes(buffer, bytes_needed); + if (result == OK) + ossl_rand_pool_add_end(pool, bytes_needed, 8 * bytes_needed); + /* + * no else here: randStatus said ok, if randBytes failed + * it will result in another loop or no entropy + */ + } else { + /* + * give a minimum delay here to allow OS to collect more + * entropy. taskDelay duration will depend on the system tick, + * this is by design as the sw-random lib uses interrupts + * which will at least happen during ticks + */ + taskDelay(5); + } + retryCount++; + } + } + return ossl_rand_pool_entropy_available(pool); +#else + /* + * SEED_NONE means none, without randlib we dont have entropy and + * rely on it being added externally + */ + return ossl_rand_pool_entropy_available(pool); +#endif /* defined(RAND_SEED_VXRANDLIB) */ +} diff --git a/crypto/openssl/providers/implementations/rands/seeding/rand_win.c b/crypto/openssl/providers/implementations/rands/seeding/rand_win.c new file mode 100644 index 000000000000..ee2d3e4d7f61 --- /dev/null +++ b/crypto/openssl/providers/implementations/rands/seeding/rand_win.c @@ -0,0 +1,163 @@ +/* + * Copyright 1995-2021 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 + */ + +#include "internal/cryptlib.h" +#include <openssl/rand.h> +#include "crypto/rand_pool.h" +#include "crypto/rand.h" +#include "prov/seeding.h" + +#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) + +# ifndef OPENSSL_RAND_SEED_OS +# error "Unsupported seeding method configured; must be os" +# endif + +# include <windows.h> +/* On Windows Vista or higher use BCrypt instead of the legacy CryptoAPI */ +# if defined(_MSC_VER) && _MSC_VER > 1500 /* 1500 = Visual Studio 2008 */ \ + && defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600 +# define USE_BCRYPTGENRANDOM +# endif + +# ifdef USE_BCRYPTGENRANDOM +# include <bcrypt.h> +# ifdef _MSC_VER +# pragma comment(lib, "bcrypt.lib") +# endif +# ifndef STATUS_SUCCESS +# define STATUS_SUCCESS ((NTSTATUS)0x00000000L) +# endif +# else +# include <wincrypt.h> +/* + * Intel hardware RNG CSP -- available from + * http://developer.intel.com/design/security/rng/redist_license.htm + */ +# define PROV_INTEL_SEC 22 +# define INTEL_DEF_PROV L"Intel Hardware Cryptographic Service Provider" +# endif + +size_t ossl_pool_acquire_entropy(RAND_POOL *pool) +{ +# ifndef USE_BCRYPTGENRANDOM + HCRYPTPROV hProvider; +# endif + unsigned char *buffer; + size_t bytes_needed; + size_t entropy_available = 0; + + +# ifdef OPENSSL_RAND_SEED_RDTSC + entropy_available = ossl_prov_acquire_entropy_from_tsc(pool); + if (entropy_available > 0) + return entropy_available; +# endif + +# ifdef OPENSSL_RAND_SEED_RDCPU + entropy_available = ossl_prov_acquire_entropy_from_cpu(pool); + if (entropy_available > 0) + return entropy_available; +# endif + +# ifdef USE_BCRYPTGENRANDOM + bytes_needed = ossl_rand_pool_bytes_needed(pool, 1 /*entropy_factor*/); + buffer = ossl_rand_pool_add_begin(pool, bytes_needed); + if (buffer != NULL) { + size_t bytes = 0; + if (BCryptGenRandom(NULL, buffer, bytes_needed, + BCRYPT_USE_SYSTEM_PREFERRED_RNG) == STATUS_SUCCESS) + bytes = bytes_needed; + + ossl_rand_pool_add_end(pool, bytes, 8 * bytes); + entropy_available = ossl_rand_pool_entropy_available(pool); + } + if (entropy_available > 0) + return entropy_available; +# else + bytes_needed = ossl_rand_pool_bytes_needed(pool, 1 /*entropy_factor*/); + buffer = ossl_rand_pool_add_begin(pool, bytes_needed); + if (buffer != NULL) { + size_t bytes = 0; + /* poll the CryptoAPI PRNG */ + if (CryptAcquireContextW(&hProvider, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT | CRYPT_SILENT) != 0) { + if (CryptGenRandom(hProvider, bytes_needed, buffer) != 0) + bytes = bytes_needed; + + CryptReleaseContext(hProvider, 0); + } + + ossl_rand_pool_add_end(pool, bytes, 8 * bytes); + entropy_available = ossl_rand_pool_entropy_available(pool); + } + if (entropy_available > 0) + return entropy_available; + + bytes_needed = ossl_rand_pool_bytes_needed(pool, 1 /*entropy_factor*/); + buffer = ossl_rand_pool_add_begin(pool, bytes_needed); + if (buffer != NULL) { + size_t bytes = 0; + /* poll the Pentium PRG with CryptoAPI */ + if (CryptAcquireContextW(&hProvider, NULL, + INTEL_DEF_PROV, PROV_INTEL_SEC, + CRYPT_VERIFYCONTEXT | CRYPT_SILENT) != 0) { + if (CryptGenRandom(hProvider, bytes_needed, buffer) != 0) + bytes = bytes_needed; + + CryptReleaseContext(hProvider, 0); + } + ossl_rand_pool_add_end(pool, bytes, 8 * bytes); + entropy_available = ossl_rand_pool_entropy_available(pool); + } + if (entropy_available > 0) + return entropy_available; +# endif + + return ossl_rand_pool_entropy_available(pool); +} + + +int ossl_pool_add_nonce_data(RAND_POOL *pool) +{ + struct { + DWORD pid; + DWORD tid; + FILETIME time; + } data; + + /* Erase the entire structure including any padding */ + memset(&data, 0, sizeof(data)); + + /* + * Add process id, thread id, and a high resolution timestamp to + * ensure that the nonce is unique with high probability for + * different process instances. + */ + data.pid = GetCurrentProcessId(); + data.tid = GetCurrentThreadId(); + GetSystemTimeAsFileTime(&data.time); + + return ossl_rand_pool_add(pool, (unsigned char *)&data, sizeof(data), 0); +} + +int ossl_rand_pool_init(void) +{ + return 1; +} + +void ossl_rand_pool_cleanup(void) +{ +} + +void ossl_rand_pool_keep_random_devices_open(int keep) +{ +} + +#endif diff --git a/crypto/openssl/providers/implementations/rands/test_rng.c b/crypto/openssl/providers/implementations/rands/test_rng.c new file mode 100644 index 000000000000..e81c82d23fa8 --- /dev/null +++ b/crypto/openssl/providers/implementations/rands/test_rng.c @@ -0,0 +1,355 @@ +/* + * Copyright 2020-2025 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 + */ + +#include <string.h> +#include <stdlib.h> +#include <openssl/core_dispatch.h> +#include <openssl/e_os2.h> +#include <openssl/params.h> +#include <openssl/core_names.h> +#include <openssl/evp.h> +#include <openssl/err.h> +#include <openssl/randerr.h> +#include "prov/securitycheck.h" +#include "prov/providercommon.h" +#include "prov/provider_ctx.h" +#include "prov/provider_util.h" +#include "prov/implementations.h" + +static OSSL_FUNC_rand_newctx_fn test_rng_new; +static OSSL_FUNC_rand_freectx_fn test_rng_free; +static OSSL_FUNC_rand_instantiate_fn test_rng_instantiate; +static OSSL_FUNC_rand_uninstantiate_fn test_rng_uninstantiate; +static OSSL_FUNC_rand_generate_fn test_rng_generate; +static OSSL_FUNC_rand_reseed_fn test_rng_reseed; +static OSSL_FUNC_rand_nonce_fn test_rng_nonce; +static OSSL_FUNC_rand_settable_ctx_params_fn test_rng_settable_ctx_params; +static OSSL_FUNC_rand_set_ctx_params_fn test_rng_set_ctx_params; +static OSSL_FUNC_rand_gettable_ctx_params_fn test_rng_gettable_ctx_params; +static OSSL_FUNC_rand_get_ctx_params_fn test_rng_get_ctx_params; +static OSSL_FUNC_rand_verify_zeroization_fn test_rng_verify_zeroization; +static OSSL_FUNC_rand_enable_locking_fn test_rng_enable_locking; +static OSSL_FUNC_rand_lock_fn test_rng_lock; +static OSSL_FUNC_rand_unlock_fn test_rng_unlock; +static OSSL_FUNC_rand_get_seed_fn test_rng_get_seed; + +typedef struct { + void *provctx; + unsigned int generate; + int state; + unsigned int strength; + size_t max_request; + unsigned char *entropy, *nonce; + size_t entropy_len, entropy_pos, nonce_len; + CRYPTO_RWLOCK *lock; + uint32_t seed; +} PROV_TEST_RNG; + +static void *test_rng_new(void *provctx, void *parent, + const OSSL_DISPATCH *parent_dispatch) +{ + PROV_TEST_RNG *t; + + t = OPENSSL_zalloc(sizeof(*t)); + if (t == NULL) + return NULL; + + t->max_request = INT_MAX; + t->provctx = provctx; + t->state = EVP_RAND_STATE_UNINITIALISED; + return t; +} + +static void test_rng_free(void *vtest) +{ + PROV_TEST_RNG *t = (PROV_TEST_RNG *)vtest; + + if (t == NULL) + return; + OPENSSL_free(t->entropy); + OPENSSL_free(t->nonce); + CRYPTO_THREAD_lock_free(t->lock); + OPENSSL_free(t); +} + +static int test_rng_instantiate(void *vtest, unsigned int strength, + int prediction_resistance, + const unsigned char *pstr, size_t pstr_len, + const OSSL_PARAM params[]) +{ + PROV_TEST_RNG *t = (PROV_TEST_RNG *)vtest; + + if (!test_rng_set_ctx_params(t, params) || strength > t->strength) + return 0; + + t->state = EVP_RAND_STATE_READY; + t->entropy_pos = 0; + t->seed = 221953166; /* Value doesn't matter, so long as it isn't zero */ + + return 1; +} + +static int test_rng_uninstantiate(void *vtest) +{ + PROV_TEST_RNG *t = (PROV_TEST_RNG *)vtest; + + t->entropy_pos = 0; + t->state = EVP_RAND_STATE_UNINITIALISED; + return 1; +} + +static unsigned char gen_byte(PROV_TEST_RNG *t) +{ + uint32_t n; + + /* + * Implement the 32 bit xorshift as suggested by George Marsaglia in: + * https://doi.org/10.18637/jss.v008.i14 + * + * This is a very fast PRNG so there is no need to extract bytes one at a + * time and use the entire value each time. + */ + n = t->seed; + n ^= n << 13; + n ^= n >> 17; + n ^= n << 5; + t->seed = n; + + return n & 0xff; +} + +static int test_rng_generate(void *vtest, unsigned char *out, size_t outlen, + unsigned int strength, int prediction_resistance, + const unsigned char *adin, size_t adin_len) +{ + PROV_TEST_RNG *t = (PROV_TEST_RNG *)vtest; + size_t i; + + if (strength > t->strength) + return 0; + if (t->generate) { + for (i = 0; i < outlen; i++) + out[i] = gen_byte(t); + } else { + if (t->entropy_len - t->entropy_pos < outlen) + return 0; + + memcpy(out, t->entropy + t->entropy_pos, outlen); + t->entropy_pos += outlen; + } + return 1; +} + +static int test_rng_reseed(ossl_unused void *vtest, + ossl_unused int prediction_resistance, + ossl_unused const unsigned char *ent, + ossl_unused size_t ent_len, + ossl_unused const unsigned char *adin, + ossl_unused size_t adin_len) +{ + return 1; +} + +static size_t test_rng_nonce(void *vtest, unsigned char *out, + unsigned int strength, size_t min_noncelen, + size_t max_noncelen) +{ + PROV_TEST_RNG *t = (PROV_TEST_RNG *)vtest; + size_t i; + + if (strength > t->strength) + return 0; + + if (t->generate) { + for (i = 0; i < min_noncelen; i++) + out[i] = gen_byte(t); + return min_noncelen; + } + + if (t->nonce == NULL) + return 0; + i = t->nonce_len > max_noncelen ? max_noncelen : t->nonce_len; + if (out != NULL) + memcpy(out, t->nonce, i); + return i; +} + +static int test_rng_get_ctx_params(void *vtest, OSSL_PARAM params[]) +{ + PROV_TEST_RNG *t = (PROV_TEST_RNG *)vtest; + OSSL_PARAM *p; + + p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_STATE); + if (p != NULL && !OSSL_PARAM_set_int(p, t->state)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_STRENGTH); + if (p != NULL && !OSSL_PARAM_set_int(p, t->strength)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_MAX_REQUEST); + if (p != NULL && !OSSL_PARAM_set_size_t(p, t->max_request)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_GENERATE); + if (p != NULL && !OSSL_PARAM_set_uint(p, t->generate)) + return 0; + +#ifdef FIPS_MODULE + p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_FIPS_APPROVED_INDICATOR); + if (p != NULL && !OSSL_PARAM_set_int(p, 0)) + return 0; +#endif /* FIPS_MODULE */ + return 1; +} + +static const OSSL_PARAM *test_rng_gettable_ctx_params(ossl_unused void *vtest, + ossl_unused void *provctx) +{ + static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_PARAM_int(OSSL_RAND_PARAM_STATE, NULL), + OSSL_PARAM_uint(OSSL_RAND_PARAM_STRENGTH, NULL), + OSSL_PARAM_size_t(OSSL_RAND_PARAM_MAX_REQUEST, NULL), + OSSL_PARAM_uint(OSSL_RAND_PARAM_GENERATE, NULL), + OSSL_FIPS_IND_GETTABLE_CTX_PARAM() + OSSL_PARAM_END + }; + return known_gettable_ctx_params; +} + +static int test_rng_set_ctx_params(void *vtest, const OSSL_PARAM params[]) +{ + PROV_TEST_RNG *t = (PROV_TEST_RNG *)vtest; + const OSSL_PARAM *p; + void *ptr = NULL; + size_t size = 0; + + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_RAND_PARAM_STRENGTH); + if (p != NULL && !OSSL_PARAM_get_uint(p, &t->strength)) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_RAND_PARAM_TEST_ENTROPY); + if (p != NULL) { + if (!OSSL_PARAM_get_octet_string(p, &ptr, 0, &size)) + return 0; + OPENSSL_free(t->entropy); + t->entropy = ptr; + t->entropy_len = size; + t->entropy_pos = 0; + ptr = NULL; + } + + p = OSSL_PARAM_locate_const(params, OSSL_RAND_PARAM_TEST_NONCE); + if (p != NULL) { + if (!OSSL_PARAM_get_octet_string(p, &ptr, 0, &size)) + return 0; + OPENSSL_free(t->nonce); + t->nonce = ptr; + t->nonce_len = size; + } + + p = OSSL_PARAM_locate_const(params, OSSL_RAND_PARAM_MAX_REQUEST); + if (p != NULL && !OSSL_PARAM_get_size_t(p, &t->max_request)) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_RAND_PARAM_GENERATE); + if (p != NULL && !OSSL_PARAM_get_uint(p, &t->generate)) + return 0; + return 1; +} + +static const OSSL_PARAM *test_rng_settable_ctx_params(ossl_unused void *vtest, + ossl_unused void *provctx) +{ + static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_octet_string(OSSL_RAND_PARAM_TEST_ENTROPY, NULL, 0), + OSSL_PARAM_octet_string(OSSL_RAND_PARAM_TEST_NONCE, NULL, 0), + OSSL_PARAM_uint(OSSL_RAND_PARAM_STRENGTH, NULL), + OSSL_PARAM_size_t(OSSL_RAND_PARAM_MAX_REQUEST, NULL), + OSSL_PARAM_uint(OSSL_RAND_PARAM_GENERATE, NULL), + OSSL_PARAM_END + }; + return known_settable_ctx_params; +} + +static int test_rng_verify_zeroization(ossl_unused void *vtest) +{ + return 1; +} + +static size_t test_rng_get_seed(void *vtest, unsigned char **pout, + int entropy, size_t min_len, size_t max_len, + ossl_unused int prediction_resistance, + ossl_unused const unsigned char *adin, + ossl_unused size_t adin_len) +{ + PROV_TEST_RNG *t = (PROV_TEST_RNG *)vtest; + + *pout = t->entropy; + return t->entropy_len > max_len ? max_len : t->entropy_len; +} + +static int test_rng_enable_locking(void *vtest) +{ + PROV_TEST_RNG *t = (PROV_TEST_RNG *)vtest; + + if (t != NULL && t->lock == NULL) { + t->lock = CRYPTO_THREAD_lock_new(); + if (t->lock == NULL) { + ERR_raise(ERR_LIB_PROV, RAND_R_FAILED_TO_CREATE_LOCK); + return 0; + } + } + return 1; +} + +static int test_rng_lock(void *vtest) +{ + PROV_TEST_RNG *t = (PROV_TEST_RNG *)vtest; + + if (t == NULL || t->lock == NULL) + return 1; + return CRYPTO_THREAD_write_lock(t->lock); +} + +static void test_rng_unlock(void *vtest) +{ + PROV_TEST_RNG *t = (PROV_TEST_RNG *)vtest; + + if (t != NULL && t->lock != NULL) + CRYPTO_THREAD_unlock(t->lock); +} + +const OSSL_DISPATCH ossl_test_rng_functions[] = { + { OSSL_FUNC_RAND_NEWCTX, (void(*)(void))test_rng_new }, + { OSSL_FUNC_RAND_FREECTX, (void(*)(void))test_rng_free }, + { OSSL_FUNC_RAND_INSTANTIATE, + (void(*)(void))test_rng_instantiate }, + { OSSL_FUNC_RAND_UNINSTANTIATE, + (void(*)(void))test_rng_uninstantiate }, + { OSSL_FUNC_RAND_GENERATE, (void(*)(void))test_rng_generate }, + { OSSL_FUNC_RAND_RESEED, (void(*)(void))test_rng_reseed }, + { OSSL_FUNC_RAND_NONCE, (void(*)(void))test_rng_nonce }, + { OSSL_FUNC_RAND_ENABLE_LOCKING, (void(*)(void))test_rng_enable_locking }, + { OSSL_FUNC_RAND_LOCK, (void(*)(void))test_rng_lock }, + { OSSL_FUNC_RAND_UNLOCK, (void(*)(void))test_rng_unlock }, + { OSSL_FUNC_RAND_SETTABLE_CTX_PARAMS, + (void(*)(void))test_rng_settable_ctx_params }, + { OSSL_FUNC_RAND_SET_CTX_PARAMS, (void(*)(void))test_rng_set_ctx_params }, + { OSSL_FUNC_RAND_GETTABLE_CTX_PARAMS, + (void(*)(void))test_rng_gettable_ctx_params }, + { OSSL_FUNC_RAND_GET_CTX_PARAMS, (void(*)(void))test_rng_get_ctx_params }, + { OSSL_FUNC_RAND_VERIFY_ZEROIZATION, + (void(*)(void))test_rng_verify_zeroization }, + { OSSL_FUNC_RAND_GET_SEED, (void(*)(void))test_rng_get_seed }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/signature/build.info b/crypto/openssl/providers/implementations/signature/build.info new file mode 100644 index 000000000000..0fd39841f340 --- /dev/null +++ b/crypto/openssl/providers/implementations/signature/build.info @@ -0,0 +1,45 @@ +# We make separate GOAL variables for each algorithm, to make it easy to +# switch each to the Legacy provider when needed. + +$DSA_GOAL=../../libdefault.a ../../libfips.a +$EC_GOAL=../../libdefault.a ../../libfips.a +$MAC_GOAL=../../libdefault.a ../../libfips.a +$RSA_GOAL=../../libdefault.a ../../libfips.a +$SM2_GOAL=../../libdefault.a +$ML_DSA_GOAL=../../libdefault.a ../../libfips.a +$SLH_DSA_GOAL=../../libdefault.a ../../libfips.a + +IF[{- !$disabled{dsa} -}] + SOURCE[$DSA_GOAL]=dsa_sig.c +ENDIF + +IF[{- !$disabled{ec} -}] + SOURCE[$EC_GOAL]=ecdsa_sig.c + IF[{- !$disabled{ecx} -}] + SOURCE[$EC_GOAL]=eddsa_sig.c + ENDIF +ENDIF + +IF[{- !$disabled{sm2} -}] + SOURCE[$SM2_GOAL]=sm2_sig.c +ENDIF + +SOURCE[$RSA_GOAL]=rsa_sig.c + +DEPEND[ml_dsa_sig.o]=../../common/include/prov/der_ml_dsa.h +DEPEND[slh_dsa_sig.o]=../../common/include/prov/der_slh_dsa.h +DEPEND[rsa_sig.o]=../../common/include/prov/der_rsa.h +DEPEND[dsa_sig.o]=../../common/include/prov/der_dsa.h +DEPEND[ecdsa_sig.o]=../../common/include/prov/der_ec.h +DEPEND[eddsa_sig.o]=../../common/include/prov/der_ecx.h +DEPEND[sm2_sig.o]=../../common/include/prov/der_sm2.h + +SOURCE[$MAC_GOAL]=mac_legacy_sig.c + +IF[{- !$disabled{'ml-dsa'} -}] + SOURCE[$ML_DSA_GOAL]=ml_dsa_sig.c +ENDIF + +IF[{- !$disabled{'slh-dsa'} -}] + SOURCE[$DSA_GOAL]=slh_dsa_sig.c +ENDIF diff --git a/crypto/openssl/providers/implementations/signature/dsa_sig.c b/crypto/openssl/providers/implementations/signature/dsa_sig.c new file mode 100644 index 000000000000..887f6cbb9018 --- /dev/null +++ b/crypto/openssl/providers/implementations/signature/dsa_sig.c @@ -0,0 +1,1085 @@ +/* + * Copyright 2019-2025 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 + */ + +/* + * DSA low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include <string.h> + +#include <openssl/crypto.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/err.h> +#include <openssl/dsa.h> +#include <openssl/params.h> +#include <openssl/evp.h> +#include <openssl/proverr.h> +#include "internal/nelem.h" +#include "internal/sizes.h" +#include "internal/cryptlib.h" +#include "prov/providercommon.h" +#include "prov/implementations.h" +#include "prov/provider_ctx.h" +#include "prov/securitycheck.h" +#include "prov/der_dsa.h" +#include "crypto/dsa.h" + +static OSSL_FUNC_signature_newctx_fn dsa_newctx; +static OSSL_FUNC_signature_sign_init_fn dsa_sign_init; +static OSSL_FUNC_signature_verify_init_fn dsa_verify_init; +static OSSL_FUNC_signature_sign_fn dsa_sign; +static OSSL_FUNC_signature_sign_message_update_fn dsa_signverify_message_update; +static OSSL_FUNC_signature_sign_message_final_fn dsa_sign_message_final; +static OSSL_FUNC_signature_verify_fn dsa_verify; +static OSSL_FUNC_signature_verify_message_update_fn dsa_signverify_message_update; +static OSSL_FUNC_signature_verify_message_final_fn dsa_verify_message_final; +static OSSL_FUNC_signature_digest_sign_init_fn dsa_digest_sign_init; +static OSSL_FUNC_signature_digest_sign_update_fn dsa_digest_signverify_update; +static OSSL_FUNC_signature_digest_sign_final_fn dsa_digest_sign_final; +static OSSL_FUNC_signature_digest_verify_init_fn dsa_digest_verify_init; +static OSSL_FUNC_signature_digest_verify_update_fn dsa_digest_signverify_update; +static OSSL_FUNC_signature_digest_verify_final_fn dsa_digest_verify_final; +static OSSL_FUNC_signature_freectx_fn dsa_freectx; +static OSSL_FUNC_signature_dupctx_fn dsa_dupctx; +static OSSL_FUNC_signature_query_key_types_fn dsa_sigalg_query_key_types; +static OSSL_FUNC_signature_get_ctx_params_fn dsa_get_ctx_params; +static OSSL_FUNC_signature_gettable_ctx_params_fn dsa_gettable_ctx_params; +static OSSL_FUNC_signature_set_ctx_params_fn dsa_set_ctx_params; +static OSSL_FUNC_signature_settable_ctx_params_fn dsa_settable_ctx_params; +static OSSL_FUNC_signature_get_ctx_md_params_fn dsa_get_ctx_md_params; +static OSSL_FUNC_signature_gettable_ctx_md_params_fn dsa_gettable_ctx_md_params; +static OSSL_FUNC_signature_set_ctx_md_params_fn dsa_set_ctx_md_params; +static OSSL_FUNC_signature_settable_ctx_md_params_fn dsa_settable_ctx_md_params; +static OSSL_FUNC_signature_set_ctx_params_fn dsa_sigalg_set_ctx_params; +static OSSL_FUNC_signature_settable_ctx_params_fn dsa_sigalg_settable_ctx_params; + +/* + * What's passed as an actual key is defined by the KEYMGMT interface. + * We happen to know that our KEYMGMT simply passes DSA structures, so + * we use that here too. + */ + +typedef struct { + OSSL_LIB_CTX *libctx; + char *propq; + DSA *dsa; + /* |operation| reuses EVP's operation bitfield */ + int operation; + + /* + * Flag to determine if a full sigalg is run (1) or if a composable + * signature algorithm is run (0). + * + * When a full sigalg is run (1), this currently affects the following + * other flags, which are to remain untouched after their initialization: + * + * - flag_allow_md (initialized to 0) + */ + unsigned int flag_sigalg : 1; + /* + * Flag to determine if the hash function can be changed (1) or not (0) + * Because it's dangerous to change during a DigestSign or DigestVerify + * operation, this flag is cleared by their Init function, and set again + * by their Final function. + */ + unsigned int flag_allow_md : 1; + + /* If this is set to 1 then the generated k is not random */ + unsigned int nonce_type; + + /* The Algorithm Identifier of the combined signature algorithm */ + unsigned char aid_buf[OSSL_MAX_ALGORITHM_ID_SIZE]; + size_t aid_len; + + /* main digest */ + char mdname[OSSL_MAX_NAME_SIZE]; + EVP_MD *md; + EVP_MD_CTX *mdctx; + + /* Signature, for verification */ + unsigned char *sig; + size_t siglen; + + OSSL_FIPS_IND_DECLARE +} PROV_DSA_CTX; + +static size_t dsa_get_md_size(const PROV_DSA_CTX *pdsactx) +{ + int md_size; + + if (pdsactx->md != NULL) { + md_size = EVP_MD_get_size(pdsactx->md); + if (md_size <= 0) + return 0; + return (size_t)md_size; + } + return 0; +} + +static void *dsa_newctx(void *provctx, const char *propq) +{ + PROV_DSA_CTX *pdsactx; + + if (!ossl_prov_is_running()) + return NULL; + + pdsactx = OPENSSL_zalloc(sizeof(PROV_DSA_CTX)); + if (pdsactx == NULL) + return NULL; + + pdsactx->libctx = PROV_LIBCTX_OF(provctx); + pdsactx->flag_allow_md = 1; + OSSL_FIPS_IND_INIT(pdsactx) + if (propq != NULL && (pdsactx->propq = OPENSSL_strdup(propq)) == NULL) { + OPENSSL_free(pdsactx); + pdsactx = NULL; + } + return pdsactx; +} + +static int dsa_setup_md(PROV_DSA_CTX *ctx, + const char *mdname, const char *mdprops, + const char *desc) +{ + EVP_MD *md = NULL; + + if (mdprops == NULL) + mdprops = ctx->propq; + + if (mdname != NULL) { + WPACKET pkt; + int md_nid; + size_t mdname_len = strlen(mdname); + unsigned char *aid = NULL; + + md = EVP_MD_fetch(ctx->libctx, mdname, mdprops); + md_nid = ossl_digest_get_approved_nid(md); + + if (md == NULL) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST, + "%s could not be fetched", mdname); + goto err; + } + if (md_nid == NID_undef) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED, + "digest=%s", mdname); + goto err; + } + if (mdname_len >= sizeof(ctx->mdname)) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST, + "%s exceeds name buffer length", mdname); + goto err; + } + /* XOF digests don't work */ + if (EVP_MD_xof(md)) { + ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED); + goto err; + } +#ifdef FIPS_MODULE + { + int sha1_allowed + = ((ctx->operation + & (EVP_PKEY_OP_SIGN | EVP_PKEY_OP_SIGNMSG)) == 0); + + if (!ossl_fips_ind_digest_sign_check(OSSL_FIPS_IND_GET(ctx), + OSSL_FIPS_IND_SETTABLE1, + ctx->libctx, + md_nid, sha1_allowed, 0, desc, + ossl_fips_config_signature_digest_check)) + goto err; + } +#endif + + if (!ctx->flag_allow_md) { + if (ctx->mdname[0] != '\0' + && !EVP_MD_is_a(md, ctx->mdname)) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED, + "digest %s != %s", mdname, ctx->mdname); + goto err; + } + EVP_MD_free(md); + return 1; + } + + EVP_MD_CTX_free(ctx->mdctx); + EVP_MD_free(ctx->md); + + /* + * We do not care about DER writing errors. + * All it really means is that for some reason, there's no + * AlgorithmIdentifier to be had, but the operation itself is + * still valid, just as long as it's not used to construct + * anything that needs an AlgorithmIdentifier. + */ + ctx->aid_len = 0; + if (WPACKET_init_der(&pkt, ctx->aid_buf, sizeof(ctx->aid_buf)) + && ossl_DER_w_algorithmIdentifier_DSA_with_MD(&pkt, -1, ctx->dsa, + md_nid) + && WPACKET_finish(&pkt)) { + WPACKET_get_total_written(&pkt, &ctx->aid_len); + aid = WPACKET_get_curr(&pkt); + } + WPACKET_cleanup(&pkt); + if (aid != NULL && ctx->aid_len != 0) + memmove(ctx->aid_buf, aid, ctx->aid_len); + + ctx->mdctx = NULL; + ctx->md = md; + OPENSSL_strlcpy(ctx->mdname, mdname, sizeof(ctx->mdname)); + } + + return 1; + err: + EVP_MD_free(md); + return 0; +} + +#ifdef FIPS_MODULE + +static int dsa_sign_check_approved(PROV_DSA_CTX *ctx, int signing, + const char *desc) +{ + /* DSA Signing is not approved in FIPS 140-3 */ + if (signing + && !OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE2, + ctx->libctx, desc, "DSA", + ossl_fips_config_dsa_sign_disallowed)) + return 0; + return 1; +} + +static int dsa_check_key(PROV_DSA_CTX *ctx, int sign, const char *desc) +{ + int approved = ossl_dsa_check_key(ctx->dsa, sign); + + if (!approved) { + if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE0, + ctx->libctx, desc, "DSA Key", + ossl_fips_config_signature_digest_check)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + } + return 1; +} +#endif + +static int +dsa_signverify_init(void *vpdsactx, void *vdsa, + OSSL_FUNC_signature_set_ctx_params_fn *set_ctx_params, + const OSSL_PARAM params[], int operation, + const char *desc) +{ + PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx; + + if (!ossl_prov_is_running() + || pdsactx == NULL) + return 0; + + if (vdsa == NULL && pdsactx->dsa == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET); + return 0; + } + + if (vdsa != NULL) { + if (!DSA_up_ref(vdsa)) + return 0; + DSA_free(pdsactx->dsa); + pdsactx->dsa = vdsa; + } + + pdsactx->operation = operation; + + OSSL_FIPS_IND_SET_APPROVED(pdsactx) + if (!set_ctx_params(pdsactx, params)) + return 0; +#ifdef FIPS_MODULE + { + int operation_is_sign + = (operation & (EVP_PKEY_OP_SIGN | EVP_PKEY_OP_SIGNMSG)) != 0; + + if (!dsa_sign_check_approved(pdsactx, operation_is_sign, desc)) + return 0; + if (!dsa_check_key(pdsactx, operation_is_sign, desc)) + return 0; + } +#endif + return 1; +} + +static int dsa_sign_init(void *vpdsactx, void *vdsa, const OSSL_PARAM params[]) +{ + return dsa_signverify_init(vpdsactx, vdsa, dsa_set_ctx_params, params, + EVP_PKEY_OP_SIGN, "DSA Sign Init"); +} + +/* + * Sign tbs without digesting it first. This is suitable for "primitive" + * signing and signing the digest of a message, i.e. should be used with + * implementations of the keytype related algorithms. + */ +static int dsa_sign_directly(void *vpdsactx, + unsigned char *sig, size_t *siglen, size_t sigsize, + const unsigned char *tbs, size_t tbslen) +{ + PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx; + int ret; + unsigned int sltmp; + size_t dsasize = DSA_size(pdsactx->dsa); + size_t mdsize = dsa_get_md_size(pdsactx); + + if (!ossl_prov_is_running()) + return 0; + +#ifdef FIPS_MODULE + if (!dsa_sign_check_approved(pdsactx, 1, "Sign")) + return 0; +#endif + + if (sig == NULL) { + *siglen = dsasize; + return 1; + } + + if (sigsize < dsasize) + return 0; + + if (mdsize != 0 && tbslen != mdsize) + return 0; + + ret = ossl_dsa_sign_int(0, tbs, tbslen, sig, &sltmp, pdsactx->dsa, + pdsactx->nonce_type, pdsactx->mdname, + pdsactx->libctx, pdsactx->propq); + if (ret <= 0) + return 0; + + *siglen = sltmp; + return 1; +} + +static int dsa_signverify_message_update(void *vpdsactx, + const unsigned char *data, + size_t datalen) +{ + PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx; + + if (pdsactx == NULL) + return 0; + + return EVP_DigestUpdate(pdsactx->mdctx, data, datalen); +} + +static int dsa_sign_message_final(void *vpdsactx, unsigned char *sig, + size_t *siglen, size_t sigsize) +{ + PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx; + unsigned char digest[EVP_MAX_MD_SIZE]; + unsigned int dlen = 0; + + if (!ossl_prov_is_running() || pdsactx == NULL || pdsactx->mdctx == NULL) + return 0; + /* + * If sig is NULL then we're just finding out the sig size. Other fields + * are ignored. Defer to dsa_sign. + */ + if (sig != NULL) { + /* + * When this function is used through dsa_digest_sign_final(), + * there is the possibility that some externally provided digests + * exceed EVP_MAX_MD_SIZE. We should probably handle that + * somehow but that problem is much larger than just in DSA. + */ + if (!EVP_DigestFinal_ex(pdsactx->mdctx, digest, &dlen)) + return 0; + } + + return dsa_sign_directly(vpdsactx, sig, siglen, sigsize, digest, dlen); +} + +/* + * If signing a message, digest tbs and sign the result. + * Otherwise, sign tbs directly. + */ +static int dsa_sign(void *vpdsactx, unsigned char *sig, size_t *siglen, + size_t sigsize, const unsigned char *tbs, size_t tbslen) +{ + PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx; + + if (pdsactx->operation == EVP_PKEY_OP_SIGNMSG) { + /* + * If |sig| is NULL, the caller is only looking for the sig length. + * DO NOT update the input in this case. + */ + if (sig == NULL) + return dsa_sign_message_final(pdsactx, sig, siglen, sigsize); + + if (dsa_signverify_message_update(pdsactx, tbs, tbslen) <= 0) + return 0; + return dsa_sign_message_final(pdsactx, sig, siglen, sigsize); + } + return dsa_sign_directly(pdsactx, sig, siglen, sigsize, tbs, tbslen); +} + +static int dsa_verify_init(void *vpdsactx, void *vdsa, + const OSSL_PARAM params[]) +{ + return dsa_signverify_init(vpdsactx, vdsa, dsa_set_ctx_params, params, + EVP_PKEY_OP_VERIFY, "DSA Verify Init"); +} + +static int dsa_verify_directly(void *vpdsactx, + const unsigned char *sig, size_t siglen, + const unsigned char *tbs, size_t tbslen) +{ + PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx; + size_t mdsize = dsa_get_md_size(pdsactx); + + if (!ossl_prov_is_running() || (mdsize != 0 && tbslen != mdsize)) + return 0; + + return DSA_verify(0, tbs, tbslen, sig, siglen, pdsactx->dsa); +} + +static int dsa_verify_set_sig(void *vpdsactx, + const unsigned char *sig, size_t siglen) +{ + PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx; + OSSL_PARAM params[2]; + + params[0] = + OSSL_PARAM_construct_octet_string(OSSL_SIGNATURE_PARAM_SIGNATURE, + (unsigned char *)sig, siglen); + params[1] = OSSL_PARAM_construct_end(); + return dsa_sigalg_set_ctx_params(pdsactx, params); +} + +static int dsa_verify_message_final(void *vpdsactx) +{ + PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx; + unsigned char digest[EVP_MAX_MD_SIZE]; + unsigned int dlen = 0; + + if (!ossl_prov_is_running()) + return 0; + + if (pdsactx == NULL || pdsactx->mdctx == NULL) + return 0; + + /* + * The digests used here are all known (see dsa_get_md_nid()), so they + * should not exceed the internal buffer size of EVP_MAX_MD_SIZE. + */ + if (!EVP_DigestFinal_ex(pdsactx->mdctx, digest, &dlen)) + return 0; + + return dsa_verify_directly(vpdsactx, pdsactx->sig, pdsactx->siglen, + digest, dlen); +} + +/* + * If verifying a message, digest tbs and verify the result. + * Otherwise, verify tbs directly. + */ +static int dsa_verify(void *vpdsactx, + const unsigned char *sig, size_t siglen, + const unsigned char *tbs, size_t tbslen) +{ + PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx; + + if (pdsactx->operation == EVP_PKEY_OP_VERIFYMSG) { + if (dsa_verify_set_sig(pdsactx, sig, siglen) <= 0) + return 0; + if (dsa_signverify_message_update(pdsactx, tbs, tbslen) <= 0) + return 0; + return dsa_verify_message_final(pdsactx); + } + return dsa_verify_directly(pdsactx, sig, siglen, tbs, tbslen); +} + +/* DigestSign/DigestVerify wrappers */ + +static int dsa_digest_signverify_init(void *vpdsactx, const char *mdname, + void *vdsa, const OSSL_PARAM params[], + int operation, const char *desc) +{ + PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx; + + if (!ossl_prov_is_running()) + return 0; + + if (!dsa_signverify_init(vpdsactx, vdsa, dsa_set_ctx_params, params, + operation, desc)) + return 0; + + if (mdname != NULL + /* was dsa_setup_md already called in dsa_signverify_init()? */ + && (mdname[0] == '\0' || OPENSSL_strcasecmp(pdsactx->mdname, mdname) != 0) + && !dsa_setup_md(pdsactx, mdname, NULL, desc)) + return 0; + + pdsactx->flag_allow_md = 0; + + if (pdsactx->mdctx == NULL) { + pdsactx->mdctx = EVP_MD_CTX_new(); + if (pdsactx->mdctx == NULL) + goto error; + } + + if (!EVP_DigestInit_ex2(pdsactx->mdctx, pdsactx->md, params)) + goto error; + + return 1; + + error: + EVP_MD_CTX_free(pdsactx->mdctx); + pdsactx->mdctx = NULL; + return 0; +} + +static int dsa_digest_sign_init(void *vpdsactx, const char *mdname, + void *vdsa, const OSSL_PARAM params[]) +{ + return dsa_digest_signverify_init(vpdsactx, mdname, vdsa, params, + EVP_PKEY_OP_SIGNMSG, + "DSA Digest Sign Init"); +} + +static int dsa_digest_signverify_update(void *vpdsactx, const unsigned char *data, + size_t datalen) +{ + PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx; + + if (pdsactx == NULL) + return 0; + /* Sigalg implementations shouldn't do digest_sign */ + if (pdsactx->flag_sigalg) + return 0; + + return dsa_signverify_message_update(vpdsactx, data, datalen); +} + +static int dsa_digest_sign_final(void *vpdsactx, unsigned char *sig, + size_t *siglen, size_t sigsize) +{ + PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx; + int ok = 0; + + if (pdsactx == NULL) + return 0; + /* Sigalg implementations shouldn't do digest_sign */ + if (pdsactx->flag_sigalg) + return 0; + + ok = dsa_sign_message_final(pdsactx, sig, siglen, sigsize); + + pdsactx->flag_allow_md = 1; + + return ok; +} + +static int dsa_digest_verify_init(void *vpdsactx, const char *mdname, + void *vdsa, const OSSL_PARAM params[]) +{ + return dsa_digest_signverify_init(vpdsactx, mdname, vdsa, params, + EVP_PKEY_OP_VERIFYMSG, + "DSA Digest Verify Init"); +} + +int dsa_digest_verify_final(void *vpdsactx, const unsigned char *sig, + size_t siglen) +{ + PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx; + int ok = 0; + + if (pdsactx == NULL) + return 0; + /* Sigalg implementations shouldn't do digest_verify */ + if (pdsactx->flag_sigalg) + return 0; + + if (dsa_verify_set_sig(pdsactx, sig, siglen)) + ok = dsa_verify_message_final(vpdsactx); + + pdsactx->flag_allow_md = 1; + + return ok; +} + +static void dsa_freectx(void *vpdsactx) +{ + PROV_DSA_CTX *ctx = (PROV_DSA_CTX *)vpdsactx; + + EVP_MD_CTX_free(ctx->mdctx); + EVP_MD_free(ctx->md); + OPENSSL_free(ctx->sig); + OPENSSL_free(ctx->propq); + DSA_free(ctx->dsa); + OPENSSL_free(ctx); +} + +static void *dsa_dupctx(void *vpdsactx) +{ + PROV_DSA_CTX *srcctx = (PROV_DSA_CTX *)vpdsactx; + PROV_DSA_CTX *dstctx; + + if (!ossl_prov_is_running()) + return NULL; + + dstctx = OPENSSL_zalloc(sizeof(*srcctx)); + if (dstctx == NULL) + return NULL; + + *dstctx = *srcctx; + dstctx->dsa = NULL; + dstctx->propq = NULL; + + if (srcctx->dsa != NULL && !DSA_up_ref(srcctx->dsa)) + goto err; + dstctx->dsa = srcctx->dsa; + + if (srcctx->md != NULL && !EVP_MD_up_ref(srcctx->md)) + goto err; + dstctx->md = srcctx->md; + + if (srcctx->mdctx != NULL) { + dstctx->mdctx = EVP_MD_CTX_new(); + if (dstctx->mdctx == NULL + || !EVP_MD_CTX_copy_ex(dstctx->mdctx, srcctx->mdctx)) + goto err; + } + + if (srcctx->propq != NULL) { + dstctx->propq = OPENSSL_strdup(srcctx->propq); + if (dstctx->propq == NULL) + goto err; + } + + return dstctx; + err: + dsa_freectx(dstctx); + return NULL; +} + +static int dsa_get_ctx_params(void *vpdsactx, OSSL_PARAM *params) +{ + PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx; + OSSL_PARAM *p; + + if (pdsactx == NULL) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_ALGORITHM_ID); + if (p != NULL + && !OSSL_PARAM_set_octet_string(p, + pdsactx->aid_len == 0 ? NULL : pdsactx->aid_buf, + pdsactx->aid_len)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_DIGEST); + if (p != NULL && !OSSL_PARAM_set_utf8_string(p, pdsactx->mdname)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_NONCE_TYPE); + if (p != NULL && !OSSL_PARAM_set_uint(p, pdsactx->nonce_type)) + return 0; + if (!OSSL_FIPS_IND_GET_CTX_PARAM(pdsactx, params)) + return 0; + + return 1; +} + +static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_ALGORITHM_ID, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_uint(OSSL_SIGNATURE_PARAM_NONCE_TYPE, NULL), + OSSL_FIPS_IND_GETTABLE_CTX_PARAM() + OSSL_PARAM_END +}; + +static const OSSL_PARAM *dsa_gettable_ctx_params(ossl_unused void *ctx, + ossl_unused void *provctx) +{ + return known_gettable_ctx_params; +} + +/** + * @brief Setup common params for dsa_set_ctx_params and dsa_sigalg_set_ctx_params + * The caller is responsible for checking |vpdsactx| is not NULL and |params| + * is not empty. + */ +static int dsa_common_set_ctx_params(void *vpdsactx, const OSSL_PARAM params[]) +{ + PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx; + const OSSL_PARAM *p; + + if (!OSSL_FIPS_IND_SET_CTX_PARAM(pdsactx, OSSL_FIPS_IND_SETTABLE0, params, + OSSL_SIGNATURE_PARAM_FIPS_KEY_CHECK)) + return 0; + if (!OSSL_FIPS_IND_SET_CTX_PARAM(pdsactx, OSSL_FIPS_IND_SETTABLE1, params, + OSSL_SIGNATURE_PARAM_FIPS_DIGEST_CHECK)) + return 0; + if (!OSSL_FIPS_IND_SET_CTX_PARAM(pdsactx, OSSL_FIPS_IND_SETTABLE2, params, + OSSL_SIGNATURE_PARAM_FIPS_SIGN_CHECK)) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_NONCE_TYPE); + if (p != NULL + && !OSSL_PARAM_get_uint(p, &pdsactx->nonce_type)) + return 0; + return 1; +} + +#define DSA_COMMON_SETTABLE_CTX_PARAMS \ + OSSL_PARAM_uint(OSSL_SIGNATURE_PARAM_NONCE_TYPE, NULL), \ + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_SIGNATURE_PARAM_FIPS_KEY_CHECK) \ + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_SIGNATURE_PARAM_FIPS_DIGEST_CHECK) \ + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_SIGNATURE_PARAM_FIPS_SIGN_CHECK) \ + OSSL_PARAM_END + +static int dsa_set_ctx_params(void *vpdsactx, const OSSL_PARAM params[]) +{ + PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx; + const OSSL_PARAM *p; + int ret; + + if (pdsactx == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 1; + + if ((ret = dsa_common_set_ctx_params(pdsactx, params)) <= 0) + return ret; + + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_DIGEST); + if (p != NULL) { + char mdname[OSSL_MAX_NAME_SIZE] = "", *pmdname = mdname; + char mdprops[OSSL_MAX_PROPQUERY_SIZE] = "", *pmdprops = mdprops; + const OSSL_PARAM *propsp = + OSSL_PARAM_locate_const(params, + OSSL_SIGNATURE_PARAM_PROPERTIES); + + if (!OSSL_PARAM_get_utf8_string(p, &pmdname, sizeof(mdname))) + return 0; + if (propsp != NULL + && !OSSL_PARAM_get_utf8_string(propsp, &pmdprops, sizeof(mdprops))) + return 0; + if (!dsa_setup_md(pdsactx, mdname, mdprops, "DSA Set Ctx")) + return 0; + } + return 1; +} + +static const OSSL_PARAM settable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PROPERTIES, NULL, 0), + DSA_COMMON_SETTABLE_CTX_PARAMS +}; + +static const OSSL_PARAM settable_ctx_params_no_digest[] = { + OSSL_PARAM_END +}; + +static const OSSL_PARAM *dsa_settable_ctx_params(void *vpdsactx, + ossl_unused void *provctx) +{ + PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx; + + if (pdsactx != NULL && !pdsactx->flag_allow_md) + return settable_ctx_params_no_digest; + return settable_ctx_params; +} + +static int dsa_get_ctx_md_params(void *vpdsactx, OSSL_PARAM *params) +{ + PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx; + + if (pdsactx->mdctx == NULL) + return 0; + + return EVP_MD_CTX_get_params(pdsactx->mdctx, params); +} + +static const OSSL_PARAM *dsa_gettable_ctx_md_params(void *vpdsactx) +{ + PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx; + + if (pdsactx->md == NULL) + return 0; + + return EVP_MD_gettable_ctx_params(pdsactx->md); +} + +static int dsa_set_ctx_md_params(void *vpdsactx, const OSSL_PARAM params[]) +{ + PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx; + + if (pdsactx->mdctx == NULL) + return 0; + + return EVP_MD_CTX_set_params(pdsactx->mdctx, params); +} + +static const OSSL_PARAM *dsa_settable_ctx_md_params(void *vpdsactx) +{ + PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx; + + if (pdsactx->md == NULL) + return 0; + + return EVP_MD_settable_ctx_params(pdsactx->md); +} + +const OSSL_DISPATCH ossl_dsa_signature_functions[] = { + { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))dsa_newctx }, + { OSSL_FUNC_SIGNATURE_SIGN_INIT, (void (*)(void))dsa_sign_init }, + { OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))dsa_sign }, + { OSSL_FUNC_SIGNATURE_VERIFY_INIT, (void (*)(void))dsa_verify_init }, + { OSSL_FUNC_SIGNATURE_VERIFY, (void (*)(void))dsa_verify }, + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT, + (void (*)(void))dsa_digest_sign_init }, + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_UPDATE, + (void (*)(void))dsa_digest_signverify_update }, + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_FINAL, + (void (*)(void))dsa_digest_sign_final }, + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT, + (void (*)(void))dsa_digest_verify_init }, + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_UPDATE, + (void (*)(void))dsa_digest_signverify_update }, + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_FINAL, + (void (*)(void))dsa_digest_verify_final }, + { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))dsa_freectx }, + { OSSL_FUNC_SIGNATURE_DUPCTX, (void (*)(void))dsa_dupctx }, + { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, (void (*)(void))dsa_get_ctx_params }, + { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, + (void (*)(void))dsa_gettable_ctx_params }, + { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, (void (*)(void))dsa_set_ctx_params }, + { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, + (void (*)(void))dsa_settable_ctx_params }, + { OSSL_FUNC_SIGNATURE_GET_CTX_MD_PARAMS, + (void (*)(void))dsa_get_ctx_md_params }, + { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_MD_PARAMS, + (void (*)(void))dsa_gettable_ctx_md_params }, + { OSSL_FUNC_SIGNATURE_SET_CTX_MD_PARAMS, + (void (*)(void))dsa_set_ctx_md_params }, + { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_MD_PARAMS, + (void (*)(void))dsa_settable_ctx_md_params }, + OSSL_DISPATCH_END +}; + +/* ------------------------------------------------------------------ */ + +/* + * So called sigalgs (composite DSA+hash) implemented below. They + * are pretty much hard coded. + */ + +static OSSL_FUNC_signature_query_key_types_fn dsa_sigalg_query_key_types; +static OSSL_FUNC_signature_settable_ctx_params_fn dsa_sigalg_settable_ctx_params; +static OSSL_FUNC_signature_set_ctx_params_fn dsa_sigalg_set_ctx_params; + +/* + * dsa_sigalg_signverify_init() is almost like dsa_digest_signverify_init(), + * just doesn't allow fetching an MD from whatever the user chooses. + */ +static int dsa_sigalg_signverify_init(void *vpdsactx, void *vdsa, + OSSL_FUNC_signature_set_ctx_params_fn *set_ctx_params, + const OSSL_PARAM params[], + const char *mdname, + int operation, const char *desc) +{ + PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx; + + if (!ossl_prov_is_running()) + return 0; + + if (!dsa_signverify_init(vpdsactx, vdsa, set_ctx_params, params, operation, + desc)) + return 0; + + if (!dsa_setup_md(pdsactx, mdname, NULL, desc)) + return 0; + + pdsactx->flag_sigalg = 1; + pdsactx->flag_allow_md = 0; + + if (pdsactx->mdctx == NULL) { + pdsactx->mdctx = EVP_MD_CTX_new(); + if (pdsactx->mdctx == NULL) + goto error; + } + + if (!EVP_DigestInit_ex2(pdsactx->mdctx, pdsactx->md, params)) + goto error; + + return 1; + + error: + EVP_MD_CTX_free(pdsactx->mdctx); + pdsactx->mdctx = NULL; + return 0; +} + +static const char **dsa_sigalg_query_key_types(void) +{ + static const char *keytypes[] = { "DSA", NULL }; + + return keytypes; +} + +static const OSSL_PARAM settable_sigalg_ctx_params[] = { + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_SIGNATURE, NULL, 0), + DSA_COMMON_SETTABLE_CTX_PARAMS +}; + +static const OSSL_PARAM *dsa_sigalg_settable_ctx_params(void *vpdsactx, + ossl_unused void *provctx) +{ + PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx; + + if (pdsactx != NULL && pdsactx->operation == EVP_PKEY_OP_VERIFYMSG) + return settable_sigalg_ctx_params; + return NULL; +} + +static int dsa_sigalg_set_ctx_params(void *vpdsactx, const OSSL_PARAM params[]) +{ + PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx; + const OSSL_PARAM *p; + int ret; + + if (pdsactx == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 1; + + if ((ret = dsa_common_set_ctx_params(pdsactx, params)) <= 0) + return ret; + + if (pdsactx->operation == EVP_PKEY_OP_VERIFYMSG) { + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_SIGNATURE); + if (p != NULL) { + OPENSSL_free(pdsactx->sig); + pdsactx->sig = NULL; + pdsactx->siglen = 0; + if (!OSSL_PARAM_get_octet_string(p, (void **)&pdsactx->sig, + 0, &pdsactx->siglen)) + return 0; + } + } + return 1; +} + +#define IMPL_DSA_SIGALG(md, MD) \ + static OSSL_FUNC_signature_sign_init_fn dsa_##md##_sign_init; \ + static OSSL_FUNC_signature_sign_message_init_fn \ + dsa_##md##_sign_message_init; \ + static OSSL_FUNC_signature_verify_init_fn dsa_##md##_verify_init; \ + static OSSL_FUNC_signature_verify_message_init_fn \ + dsa_##md##_verify_message_init; \ + \ + static int \ + dsa_##md##_sign_init(void *vpdsactx, void *vdsa, \ + const OSSL_PARAM params[]) \ + { \ + static const char desc[] = "DSA-" #MD " Sign Init"; \ + \ + return dsa_sigalg_signverify_init(vpdsactx, vdsa, \ + dsa_sigalg_set_ctx_params, \ + params, #MD, \ + EVP_PKEY_OP_SIGN, \ + desc); \ + } \ + \ + static int \ + dsa_##md##_sign_message_init(void *vpdsactx, void *vdsa, \ + const OSSL_PARAM params[]) \ + { \ + static const char desc[] = "DSA-" #MD " Sign Message Init"; \ + \ + return dsa_sigalg_signverify_init(vpdsactx, vdsa, \ + dsa_sigalg_set_ctx_params, \ + params, #MD, \ + EVP_PKEY_OP_SIGNMSG, \ + desc); \ + } \ + \ + static int \ + dsa_##md##_verify_init(void *vpdsactx, void *vdsa, \ + const OSSL_PARAM params[]) \ + { \ + static const char desc[] = "DSA-" #MD " Verify Init"; \ + \ + return dsa_sigalg_signverify_init(vpdsactx, vdsa, \ + dsa_sigalg_set_ctx_params, \ + params, #MD, \ + EVP_PKEY_OP_VERIFY, \ + desc); \ + } \ + \ + static int \ + dsa_##md##_verify_message_init(void *vpdsactx, void *vdsa, \ + const OSSL_PARAM params[]) \ + { \ + static const char desc[] = "DSA-" #MD " Verify Message Init"; \ + \ + return dsa_sigalg_signverify_init(vpdsactx, vdsa, \ + dsa_sigalg_set_ctx_params, \ + params, #MD, \ + EVP_PKEY_OP_VERIFYMSG, \ + desc); \ + } \ + \ + const OSSL_DISPATCH ossl_dsa_##md##_signature_functions[] = { \ + { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))dsa_newctx }, \ + { OSSL_FUNC_SIGNATURE_SIGN_INIT, \ + (void (*)(void))dsa_##md##_sign_init }, \ + { OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))dsa_sign }, \ + { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_INIT, \ + (void (*)(void))dsa_##md##_sign_message_init }, \ + { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_UPDATE, \ + (void (*)(void))dsa_signverify_message_update }, \ + { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_FINAL, \ + (void (*)(void))dsa_sign_message_final }, \ + { OSSL_FUNC_SIGNATURE_VERIFY_INIT, \ + (void (*)(void))dsa_##md##_verify_init }, \ + { OSSL_FUNC_SIGNATURE_VERIFY, \ + (void (*)(void))dsa_verify }, \ + { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_INIT, \ + (void (*)(void))dsa_##md##_verify_message_init }, \ + { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_UPDATE, \ + (void (*)(void))dsa_signverify_message_update }, \ + { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_FINAL, \ + (void (*)(void))dsa_verify_message_final }, \ + { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))dsa_freectx }, \ + { OSSL_FUNC_SIGNATURE_DUPCTX, (void (*)(void))dsa_dupctx }, \ + { OSSL_FUNC_SIGNATURE_QUERY_KEY_TYPES, \ + (void (*)(void))dsa_sigalg_query_key_types }, \ + { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, \ + (void (*)(void))dsa_get_ctx_params }, \ + { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, \ + (void (*)(void))dsa_gettable_ctx_params }, \ + { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, \ + (void (*)(void))dsa_sigalg_set_ctx_params }, \ + { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, \ + (void (*)(void))dsa_sigalg_settable_ctx_params }, \ + OSSL_DISPATCH_END \ + } + +IMPL_DSA_SIGALG(sha1, SHA1); +IMPL_DSA_SIGALG(sha224, SHA2-224); +IMPL_DSA_SIGALG(sha256, SHA2-256); +IMPL_DSA_SIGALG(sha384, SHA2-384); +IMPL_DSA_SIGALG(sha512, SHA2-512); +IMPL_DSA_SIGALG(sha3_224, SHA3-224); +IMPL_DSA_SIGALG(sha3_256, SHA3-256); +IMPL_DSA_SIGALG(sha3_384, SHA3-384); +IMPL_DSA_SIGALG(sha3_512, SHA3-512); diff --git a/crypto/openssl/providers/implementations/signature/ecdsa_sig.c b/crypto/openssl/providers/implementations/signature/ecdsa_sig.c new file mode 100644 index 000000000000..73bfbf4aa9c1 --- /dev/null +++ b/crypto/openssl/providers/implementations/signature/ecdsa_sig.c @@ -0,0 +1,1108 @@ +/* + * Copyright 2020-2025 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 + */ + +/* + * ECDSA low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include <string.h> /* memcpy */ +#include <openssl/crypto.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/dsa.h> +#include <openssl/params.h> +#include <openssl/evp.h> +#include <openssl/err.h> +#include <openssl/proverr.h> +#include "internal/nelem.h" +#include "internal/sizes.h" +#include "internal/cryptlib.h" +#include "internal/deterministic_nonce.h" +#include "prov/providercommon.h" +#include "prov/implementations.h" +#include "prov/provider_ctx.h" +#include "prov/securitycheck.h" +#include "prov/der_ec.h" +#include "crypto/ec.h" + +static OSSL_FUNC_signature_newctx_fn ecdsa_newctx; +static OSSL_FUNC_signature_sign_init_fn ecdsa_sign_init; +static OSSL_FUNC_signature_verify_init_fn ecdsa_verify_init; +static OSSL_FUNC_signature_sign_fn ecdsa_sign; +static OSSL_FUNC_signature_sign_message_update_fn ecdsa_signverify_message_update; +static OSSL_FUNC_signature_sign_message_final_fn ecdsa_sign_message_final; +static OSSL_FUNC_signature_verify_fn ecdsa_verify; +static OSSL_FUNC_signature_verify_message_update_fn ecdsa_signverify_message_update; +static OSSL_FUNC_signature_verify_message_final_fn ecdsa_verify_message_final; +static OSSL_FUNC_signature_digest_sign_init_fn ecdsa_digest_sign_init; +static OSSL_FUNC_signature_digest_sign_update_fn ecdsa_digest_signverify_update; +static OSSL_FUNC_signature_digest_sign_final_fn ecdsa_digest_sign_final; +static OSSL_FUNC_signature_digest_verify_init_fn ecdsa_digest_verify_init; +static OSSL_FUNC_signature_digest_verify_update_fn ecdsa_digest_signverify_update; +static OSSL_FUNC_signature_digest_verify_final_fn ecdsa_digest_verify_final; +static OSSL_FUNC_signature_freectx_fn ecdsa_freectx; +static OSSL_FUNC_signature_dupctx_fn ecdsa_dupctx; +static OSSL_FUNC_signature_query_key_types_fn ecdsa_sigalg_query_key_types; +static OSSL_FUNC_signature_get_ctx_params_fn ecdsa_get_ctx_params; +static OSSL_FUNC_signature_gettable_ctx_params_fn ecdsa_gettable_ctx_params; +static OSSL_FUNC_signature_set_ctx_params_fn ecdsa_set_ctx_params; +static OSSL_FUNC_signature_settable_ctx_params_fn ecdsa_settable_ctx_params; +static OSSL_FUNC_signature_get_ctx_md_params_fn ecdsa_get_ctx_md_params; +static OSSL_FUNC_signature_gettable_ctx_md_params_fn ecdsa_gettable_ctx_md_params; +static OSSL_FUNC_signature_set_ctx_md_params_fn ecdsa_set_ctx_md_params; +static OSSL_FUNC_signature_settable_ctx_md_params_fn ecdsa_settable_ctx_md_params; +static OSSL_FUNC_signature_set_ctx_params_fn ecdsa_sigalg_set_ctx_params; +static OSSL_FUNC_signature_settable_ctx_params_fn ecdsa_sigalg_settable_ctx_params; + +/* + * What's passed as an actual key is defined by the KEYMGMT interface. + * We happen to know that our KEYMGMT simply passes DSA structures, so + * we use that here too. + */ + +typedef struct { + OSSL_LIB_CTX *libctx; + char *propq; + EC_KEY *ec; + /* |operation| reuses EVP's operation bitfield */ + int operation; + + /* + * Flag to determine if a full sigalg is run (1) or if a composable + * signature algorithm is run (0). + * + * When a full sigalg is run (1), this currently affects the following + * other flags, which are to remain untouched after their initialization: + * + * - flag_allow_md (initialized to 0) + */ + unsigned int flag_sigalg : 1; + /* + * Flag to determine if the hash function can be changed (1) or not (0) + * Because it's dangerous to change during a DigestSign or DigestVerify + * operation, this flag is cleared by their Init function, and set again + * by their Final function. + */ + unsigned int flag_allow_md : 1; + + /* The Algorithm Identifier of the combined signature algorithm */ + unsigned char aid_buf[OSSL_MAX_ALGORITHM_ID_SIZE]; + size_t aid_len; + + /* main digest */ + char mdname[OSSL_MAX_NAME_SIZE]; + EVP_MD *md; + EVP_MD_CTX *mdctx; + size_t mdsize; + + /* Signature, for verification */ + unsigned char *sig; + size_t siglen; + + /* + * Internally used to cache the results of calling the EC group + * sign_setup() methods which are then passed to the sign operation. + * This is used by CAVS failure tests to terminate a loop if the signature + * is not valid. + * This could of also been done with a simple flag. + */ + BIGNUM *kinv; + BIGNUM *r; +#if !defined(OPENSSL_NO_ACVP_TESTS) + /* + * This indicates that KAT (CAVS) test is running. Externally an app will + * override the random callback such that the generated private key and k + * are known. + * Normal operation will loop to choose a new k if the signature is not + * valid - but for this mode of operation it forces a failure instead. + */ + unsigned int kattest; +#endif +#ifdef FIPS_MODULE + /* + * FIPS 140-3 IG 2.4.B mandates that verification based on a digest of a + * message is not permitted. However, signing based on a digest is still + * permitted. + */ + int verify_message; +#endif + /* If this is set then the generated k is not random */ + unsigned int nonce_type; + OSSL_FIPS_IND_DECLARE +} PROV_ECDSA_CTX; + +static void *ecdsa_newctx(void *provctx, const char *propq) +{ + PROV_ECDSA_CTX *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + ctx = OPENSSL_zalloc(sizeof(PROV_ECDSA_CTX)); + if (ctx == NULL) + return NULL; + + OSSL_FIPS_IND_INIT(ctx) + ctx->flag_allow_md = 1; +#ifdef FIPS_MODULE + ctx->verify_message = 1; +#endif + ctx->libctx = PROV_LIBCTX_OF(provctx); + if (propq != NULL && (ctx->propq = OPENSSL_strdup(propq)) == NULL) { + OPENSSL_free(ctx); + ctx = NULL; + } + return ctx; +} + +static int ecdsa_setup_md(PROV_ECDSA_CTX *ctx, + const char *mdname, const char *mdprops, + const char *desc) +{ + EVP_MD *md = NULL; + size_t mdname_len; + int md_nid, md_size; + WPACKET pkt; + unsigned char *aid = NULL; + + if (mdname == NULL) + return 1; + + mdname_len = strlen(mdname); + if (mdname_len >= sizeof(ctx->mdname)) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST, + "%s exceeds name buffer length", mdname); + return 0; + } + if (mdprops == NULL) + mdprops = ctx->propq; + md = EVP_MD_fetch(ctx->libctx, mdname, mdprops); + if (md == NULL) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST, + "%s could not be fetched", mdname); + return 0; + } + md_size = EVP_MD_get_size(md); + if (md_size <= 0) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST, + "%s has invalid md size %d", mdname, md_size); + goto err; + } + md_nid = ossl_digest_get_approved_nid(md); +#ifdef FIPS_MODULE + if (md_nid == NID_undef) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED, + "digest=%s", mdname); + goto err; + } +#endif + /* XOF digests don't work */ + if (EVP_MD_xof(md)) { + ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED); + goto err; + } + +#ifdef FIPS_MODULE + { + int sha1_allowed + = ((ctx->operation + & (EVP_PKEY_OP_SIGN | EVP_PKEY_OP_SIGNMSG)) == 0); + + if (!ossl_fips_ind_digest_sign_check(OSSL_FIPS_IND_GET(ctx), + OSSL_FIPS_IND_SETTABLE1, + ctx->libctx, + md_nid, sha1_allowed, 0, desc, + ossl_fips_config_signature_digest_check)) + goto err; + } +#endif + + if (!ctx->flag_allow_md) { + if (ctx->mdname[0] != '\0' && !EVP_MD_is_a(md, ctx->mdname)) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED, + "digest %s != %s", mdname, ctx->mdname); + goto err; + } + EVP_MD_free(md); + return 1; + } + + EVP_MD_CTX_free(ctx->mdctx); + EVP_MD_free(ctx->md); + + ctx->aid_len = 0; +#ifndef FIPS_MODULE + if (md_nid != NID_undef) { +#else + { +#endif + if (WPACKET_init_der(&pkt, ctx->aid_buf, sizeof(ctx->aid_buf)) + && ossl_DER_w_algorithmIdentifier_ECDSA_with_MD(&pkt, -1, ctx->ec, + md_nid) + && WPACKET_finish(&pkt)) { + WPACKET_get_total_written(&pkt, &ctx->aid_len); + aid = WPACKET_get_curr(&pkt); + } + WPACKET_cleanup(&pkt); + if (aid != NULL && ctx->aid_len != 0) + memmove(ctx->aid_buf, aid, ctx->aid_len); + } + + ctx->mdctx = NULL; + ctx->md = md; + ctx->mdsize = (size_t)md_size; + OPENSSL_strlcpy(ctx->mdname, mdname, sizeof(ctx->mdname)); + + return 1; + err: + EVP_MD_free(md); + return 0; +} + +static int +ecdsa_signverify_init(PROV_ECDSA_CTX *ctx, void *ec, + OSSL_FUNC_signature_set_ctx_params_fn *set_ctx_params, + const OSSL_PARAM params[], int operation, + const char *desc) +{ + if (!ossl_prov_is_running() + || ctx == NULL) + return 0; + + if (ec == NULL && ctx->ec == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET); + return 0; + } + + if (ec != NULL) { + if (!EC_KEY_up_ref(ec)) + return 0; + EC_KEY_free(ctx->ec); + ctx->ec = ec; + } + + ctx->operation = operation; + + OSSL_FIPS_IND_SET_APPROVED(ctx) + if (!set_ctx_params(ctx, params)) + return 0; +#ifdef FIPS_MODULE + if (!ossl_fips_ind_ec_key_check(OSSL_FIPS_IND_GET(ctx), + OSSL_FIPS_IND_SETTABLE0, ctx->libctx, + EC_KEY_get0_group(ctx->ec), desc, + (operation & (EVP_PKEY_OP_SIGN + | EVP_PKEY_OP_SIGNMSG)) != 0)) + return 0; +#endif + return 1; +} + +static int ecdsa_sign_init(void *vctx, void *ec, const OSSL_PARAM params[]) +{ + PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx; + +#ifdef FIPS_MODULE + ctx->verify_message = 1; +#endif + return ecdsa_signverify_init(ctx, ec, ecdsa_set_ctx_params, params, + EVP_PKEY_OP_SIGN, "ECDSA Sign Init"); +} + +/* + * Sign tbs without digesting it first. This is suitable for "primitive" + * signing and signing the digest of a message. + */ +static int ecdsa_sign_directly(void *vctx, + unsigned char *sig, size_t *siglen, size_t sigsize, + const unsigned char *tbs, size_t tbslen) +{ + PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx; + int ret; + unsigned int sltmp; + size_t ecsize = ECDSA_size(ctx->ec); + + if (!ossl_prov_is_running()) + return 0; + + if (sig == NULL) { + *siglen = ecsize; + return 1; + } + +#if !defined(OPENSSL_NO_ACVP_TESTS) + if (ctx->kattest && !ECDSA_sign_setup(ctx->ec, NULL, &ctx->kinv, &ctx->r)) + return 0; +#endif + + if (sigsize < (size_t)ecsize) + return 0; + + if (ctx->mdsize != 0 && tbslen != ctx->mdsize) + return 0; + + if (ctx->nonce_type != 0) { + const char *mdname = NULL; + + if (ctx->mdname[0] != '\0') + mdname = ctx->mdname; + ret = ossl_ecdsa_deterministic_sign(tbs, tbslen, sig, &sltmp, + ctx->ec, ctx->nonce_type, + mdname, + ctx->libctx, ctx->propq); + } else { + ret = ECDSA_sign_ex(0, tbs, tbslen, sig, &sltmp, ctx->kinv, ctx->r, + ctx->ec); + } + if (ret <= 0) + return 0; + + *siglen = sltmp; + return 1; +} + +static int ecdsa_signverify_message_update(void *vctx, + const unsigned char *data, + size_t datalen) +{ + PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx; + + if (ctx == NULL) + return 0; + + return EVP_DigestUpdate(ctx->mdctx, data, datalen); +} + +static int ecdsa_sign_message_final(void *vctx, unsigned char *sig, + size_t *siglen, size_t sigsize) +{ + PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx; + unsigned char digest[EVP_MAX_MD_SIZE]; + unsigned int dlen = 0; + + if (!ossl_prov_is_running() || ctx == NULL) + return 0; + if (ctx->mdctx == NULL) + return 0; + /* + * If sig is NULL then we're just finding out the sig size. Other fields + * are ignored. Defer to ecdsa_sign. + */ + if (sig != NULL + && !EVP_DigestFinal_ex(ctx->mdctx, digest, &dlen)) + return 0; + return ecdsa_sign_directly(vctx, sig, siglen, sigsize, digest, dlen); +} + +/* + * If signing a message, digest tbs and sign the result. + * Otherwise, sign tbs directly. + */ +static int ecdsa_sign(void *vctx, unsigned char *sig, size_t *siglen, + size_t sigsize, const unsigned char *tbs, size_t tbslen) +{ + PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx; + + if (ctx->operation == EVP_PKEY_OP_SIGNMSG) { + /* + * If |sig| is NULL, the caller is only looking for the sig length. + * DO NOT update the input in this case. + */ + if (sig == NULL) + return ecdsa_sign_message_final(ctx, sig, siglen, sigsize); + + if (ecdsa_signverify_message_update(ctx, tbs, tbslen) <= 0) + return 0; + return ecdsa_sign_message_final(ctx, sig, siglen, sigsize); + } + return ecdsa_sign_directly(ctx, sig, siglen, sigsize, tbs, tbslen); +} + +static int ecdsa_verify_init(void *vctx, void *ec, const OSSL_PARAM params[]) +{ + PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx; + +#ifdef FIPS_MODULE + ctx->verify_message = 0; +#endif + return ecdsa_signverify_init(ctx, ec, ecdsa_set_ctx_params, params, + EVP_PKEY_OP_VERIFY, "ECDSA Verify Init"); +} + +static int ecdsa_verify_directly(void *vctx, + const unsigned char *sig, size_t siglen, + const unsigned char *tbs, size_t tbslen) +{ + PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx; + + if (!ossl_prov_is_running() || (ctx->mdsize != 0 && tbslen != ctx->mdsize)) + return 0; + + return ECDSA_verify(0, tbs, tbslen, sig, siglen, ctx->ec); +} + +static int ecdsa_verify_set_sig(void *vctx, + const unsigned char *sig, size_t siglen) +{ + PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx; + OSSL_PARAM params[2]; + + params[0] = + OSSL_PARAM_construct_octet_string(OSSL_SIGNATURE_PARAM_SIGNATURE, + (unsigned char *)sig, siglen); + params[1] = OSSL_PARAM_construct_end(); + return ecdsa_sigalg_set_ctx_params(ctx, params); +} + +static int ecdsa_verify_message_final(void *vctx) +{ + PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx; + unsigned char digest[EVP_MAX_MD_SIZE]; + unsigned int dlen = 0; + + if (!ossl_prov_is_running() || ctx == NULL || ctx->mdctx == NULL) + return 0; + + /* + * The digests used here are all known (see ecdsa_get_md_nid()), so they + * should not exceed the internal buffer size of EVP_MAX_MD_SIZE. + */ + if (!EVP_DigestFinal_ex(ctx->mdctx, digest, &dlen)) + return 0; + + return ecdsa_verify_directly(vctx, ctx->sig, ctx->siglen, + digest, dlen); +} + +/* + * If verifying a message, digest tbs and verify the result. + * Otherwise, verify tbs directly. + */ +static int ecdsa_verify(void *vctx, + const unsigned char *sig, size_t siglen, + const unsigned char *tbs, size_t tbslen) +{ + PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx; + + if (ctx->operation == EVP_PKEY_OP_VERIFYMSG) { + if (ecdsa_verify_set_sig(ctx, sig, siglen) <= 0) + return 0; + if (ecdsa_signverify_message_update(ctx, tbs, tbslen) <= 0) + return 0; + return ecdsa_verify_message_final(ctx); + } + return ecdsa_verify_directly(ctx, sig, siglen, tbs, tbslen); +} + +/* DigestSign/DigestVerify wrappers */ + +static int ecdsa_digest_signverify_init(void *vctx, const char *mdname, + void *ec, const OSSL_PARAM params[], + int operation, const char *desc) +{ + PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx; + + if (!ossl_prov_is_running()) + return 0; + +#ifdef FIPS_MODULE + ctx->verify_message = 1; +#endif + if (!ecdsa_signverify_init(vctx, ec, ecdsa_set_ctx_params, params, + operation, desc)) + return 0; + + if (mdname != NULL + /* was ecdsa_setup_md already called in ecdsa_signverify_init()? */ + && (mdname[0] == '\0' || OPENSSL_strcasecmp(ctx->mdname, mdname) != 0) + && !ecdsa_setup_md(ctx, mdname, NULL, desc)) + return 0; + + ctx->flag_allow_md = 0; + + if (ctx->mdctx == NULL) { + ctx->mdctx = EVP_MD_CTX_new(); + if (ctx->mdctx == NULL) + goto error; + } + + if (!EVP_DigestInit_ex2(ctx->mdctx, ctx->md, params)) + goto error; + return 1; +error: + EVP_MD_CTX_free(ctx->mdctx); + ctx->mdctx = NULL; + return 0; +} + +static int ecdsa_digest_sign_init(void *vctx, const char *mdname, void *ec, + const OSSL_PARAM params[]) +{ + return ecdsa_digest_signverify_init(vctx, mdname, ec, params, + EVP_PKEY_OP_SIGNMSG, + "ECDSA Digest Sign Init"); +} + +static int ecdsa_digest_signverify_update(void *vctx, const unsigned char *data, + size_t datalen) +{ + PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx; + + if (ctx == NULL || ctx->mdctx == NULL) + return 0; + /* Sigalg implementations shouldn't do digest_sign */ + if (ctx->flag_sigalg) + return 0; + + return ecdsa_signverify_message_update(vctx, data, datalen); +} + +int ecdsa_digest_sign_final(void *vctx, unsigned char *sig, size_t *siglen, + size_t sigsize) +{ + PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx; + int ok = 0; + + if (ctx == NULL) + return 0; + /* Sigalg implementations shouldn't do digest_sign */ + if (ctx->flag_sigalg) + return 0; + + ok = ecdsa_sign_message_final(ctx, sig, siglen, sigsize); + + ctx->flag_allow_md = 1; + + return ok; +} + +static int ecdsa_digest_verify_init(void *vctx, const char *mdname, void *ec, + const OSSL_PARAM params[]) +{ + return ecdsa_digest_signverify_init(vctx, mdname, ec, params, + EVP_PKEY_OP_VERIFYMSG, + "ECDSA Digest Verify Init"); +} + +int ecdsa_digest_verify_final(void *vctx, const unsigned char *sig, + size_t siglen) +{ + PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx; + int ok = 0; + + if (!ossl_prov_is_running() || ctx == NULL || ctx->mdctx == NULL) + return 0; + + /* Sigalg implementations shouldn't do digest_verify */ + if (ctx->flag_sigalg) + return 0; + + if (ecdsa_verify_set_sig(ctx, sig, siglen)) + ok = ecdsa_verify_message_final(ctx); + + ctx->flag_allow_md = 1; + + return ok; +} + +static void ecdsa_freectx(void *vctx) +{ + PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx; + + EVP_MD_CTX_free(ctx->mdctx); + EVP_MD_free(ctx->md); + OPENSSL_free(ctx->propq); + OPENSSL_free(ctx->sig); + EC_KEY_free(ctx->ec); + BN_clear_free(ctx->kinv); + BN_clear_free(ctx->r); + OPENSSL_free(ctx); +} + +static void *ecdsa_dupctx(void *vctx) +{ + PROV_ECDSA_CTX *srcctx = (PROV_ECDSA_CTX *)vctx; + PROV_ECDSA_CTX *dstctx; + + if (!ossl_prov_is_running()) + return NULL; + + dstctx = OPENSSL_zalloc(sizeof(*srcctx)); + if (dstctx == NULL) + return NULL; + + *dstctx = *srcctx; + dstctx->ec = NULL; + dstctx->propq = NULL; + + if (srcctx->ec != NULL && !EC_KEY_up_ref(srcctx->ec)) + goto err; + /* Test KATS should not need to be supported */ + if (srcctx->kinv != NULL || srcctx->r != NULL) + goto err; + dstctx->ec = srcctx->ec; + + if (srcctx->md != NULL && !EVP_MD_up_ref(srcctx->md)) + goto err; + dstctx->md = srcctx->md; + + if (srcctx->mdctx != NULL) { + dstctx->mdctx = EVP_MD_CTX_new(); + if (dstctx->mdctx == NULL + || !EVP_MD_CTX_copy_ex(dstctx->mdctx, srcctx->mdctx)) + goto err; + } + + if (srcctx->propq != NULL) { + dstctx->propq = OPENSSL_strdup(srcctx->propq); + if (dstctx->propq == NULL) + goto err; + } + + return dstctx; + err: + ecdsa_freectx(dstctx); + return NULL; +} + +static int ecdsa_get_ctx_params(void *vctx, OSSL_PARAM *params) +{ + PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx; + OSSL_PARAM *p; + + if (ctx == NULL) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_ALGORITHM_ID); + if (p != NULL && !OSSL_PARAM_set_octet_string(p, + ctx->aid_len == 0 ? NULL : ctx->aid_buf, + ctx->aid_len)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_DIGEST_SIZE); + if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->mdsize)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_DIGEST); + if (p != NULL && !OSSL_PARAM_set_utf8_string(p, ctx->md == NULL + ? ctx->mdname + : EVP_MD_get0_name(ctx->md))) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_NONCE_TYPE); + if (p != NULL && !OSSL_PARAM_set_uint(p, ctx->nonce_type)) + return 0; + +#ifdef FIPS_MODULE + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_FIPS_VERIFY_MESSAGE); + if (p != NULL && !OSSL_PARAM_set_uint(p, ctx->verify_message)) + return 0; +#endif + + if (!OSSL_FIPS_IND_GET_CTX_PARAM(ctx, params)) + return 0; + return 1; +} + +static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_ALGORITHM_ID, NULL, 0), + OSSL_PARAM_size_t(OSSL_SIGNATURE_PARAM_DIGEST_SIZE, NULL), + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_uint(OSSL_SIGNATURE_PARAM_NONCE_TYPE, NULL), +#ifdef FIPS_MODULE + OSSL_PARAM_uint(OSSL_SIGNATURE_PARAM_FIPS_VERIFY_MESSAGE, NULL), +#endif + OSSL_FIPS_IND_GETTABLE_CTX_PARAM() + OSSL_PARAM_END +}; + +static const OSSL_PARAM *ecdsa_gettable_ctx_params(ossl_unused void *vctx, + ossl_unused void *provctx) +{ + return known_gettable_ctx_params; +} + +/** + * @brief Set up common params for ecdsa_set_ctx_params and + * ecdsa_sigalg_set_ctx_params. The caller is responsible for checking |vctx| is + * not NULL and |params| is not empty. + */ +static int ecdsa_common_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx; + const OSSL_PARAM *p; + + if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE0, params, + OSSL_SIGNATURE_PARAM_FIPS_KEY_CHECK)) + return 0; + if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE1, params, + OSSL_SIGNATURE_PARAM_FIPS_DIGEST_CHECK)) + return 0; + +#if !defined(OPENSSL_NO_ACVP_TESTS) + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_KAT); + if (p != NULL && !OSSL_PARAM_get_uint(p, &ctx->kattest)) + return 0; +#endif + + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_NONCE_TYPE); + if (p != NULL + && !OSSL_PARAM_get_uint(p, &ctx->nonce_type)) + return 0; + return 1; +} + +#define ECDSA_COMMON_SETTABLE_CTX_PARAMS \ + OSSL_PARAM_uint(OSSL_SIGNATURE_PARAM_KAT, NULL), \ + OSSL_PARAM_uint(OSSL_SIGNATURE_PARAM_NONCE_TYPE, NULL), \ + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_SIGNATURE_PARAM_FIPS_KEY_CHECK) \ + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_SIGNATURE_PARAM_FIPS_DIGEST_CHECK) \ + OSSL_PARAM_END + +static int ecdsa_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx; + const OSSL_PARAM *p; + size_t mdsize = 0; + int ret; + + if (ctx == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 1; + + if ((ret = ecdsa_common_set_ctx_params(ctx, params)) <= 0) + return ret; + + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_DIGEST); + if (p != NULL) { + char mdname[OSSL_MAX_NAME_SIZE] = "", *pmdname = mdname; + char mdprops[OSSL_MAX_PROPQUERY_SIZE] = "", *pmdprops = mdprops; + const OSSL_PARAM *propsp = + OSSL_PARAM_locate_const(params, + OSSL_SIGNATURE_PARAM_PROPERTIES); + + if (!OSSL_PARAM_get_utf8_string(p, &pmdname, sizeof(mdname))) + return 0; + if (propsp != NULL + && !OSSL_PARAM_get_utf8_string(propsp, &pmdprops, sizeof(mdprops))) + return 0; + if (!ecdsa_setup_md(ctx, mdname, mdprops, "ECDSA Set Ctx")) + return 0; + } + + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_DIGEST_SIZE); + if (p != NULL) { + if (!OSSL_PARAM_get_size_t(p, &mdsize) + || (!ctx->flag_allow_md && mdsize != ctx->mdsize)) + return 0; + ctx->mdsize = mdsize; + } + return 1; +} + +static const OSSL_PARAM settable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_size_t(OSSL_SIGNATURE_PARAM_DIGEST_SIZE, NULL), + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PROPERTIES, NULL, 0), + ECDSA_COMMON_SETTABLE_CTX_PARAMS +}; + +static const OSSL_PARAM *ecdsa_settable_ctx_params(void *vctx, + ossl_unused void *provctx) +{ + return settable_ctx_params; +} + +static int ecdsa_get_ctx_md_params(void *vctx, OSSL_PARAM *params) +{ + PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx; + + if (ctx->mdctx == NULL) + return 0; + + return EVP_MD_CTX_get_params(ctx->mdctx, params); +} + +static const OSSL_PARAM *ecdsa_gettable_ctx_md_params(void *vctx) +{ + PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx; + + if (ctx->md == NULL) + return 0; + + return EVP_MD_gettable_ctx_params(ctx->md); +} + +static int ecdsa_set_ctx_md_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx; + + if (ctx->mdctx == NULL) + return 0; + + return EVP_MD_CTX_set_params(ctx->mdctx, params); +} + +static const OSSL_PARAM *ecdsa_settable_ctx_md_params(void *vctx) +{ + PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx; + + if (ctx->md == NULL) + return 0; + + return EVP_MD_settable_ctx_params(ctx->md); +} + +const OSSL_DISPATCH ossl_ecdsa_signature_functions[] = { + { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))ecdsa_newctx }, + { OSSL_FUNC_SIGNATURE_SIGN_INIT, (void (*)(void))ecdsa_sign_init }, + { OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))ecdsa_sign }, + { OSSL_FUNC_SIGNATURE_VERIFY_INIT, (void (*)(void))ecdsa_verify_init }, + { OSSL_FUNC_SIGNATURE_VERIFY, (void (*)(void))ecdsa_verify }, + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT, + (void (*)(void))ecdsa_digest_sign_init }, + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_UPDATE, + (void (*)(void))ecdsa_digest_signverify_update }, + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_FINAL, + (void (*)(void))ecdsa_digest_sign_final }, + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT, + (void (*)(void))ecdsa_digest_verify_init }, + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_UPDATE, + (void (*)(void))ecdsa_digest_signverify_update }, + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_FINAL, + (void (*)(void))ecdsa_digest_verify_final }, + { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))ecdsa_freectx }, + { OSSL_FUNC_SIGNATURE_DUPCTX, (void (*)(void))ecdsa_dupctx }, + { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, (void (*)(void))ecdsa_get_ctx_params }, + { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, + (void (*)(void))ecdsa_gettable_ctx_params }, + { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, (void (*)(void))ecdsa_set_ctx_params }, + { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, + (void (*)(void))ecdsa_settable_ctx_params }, + { OSSL_FUNC_SIGNATURE_GET_CTX_MD_PARAMS, + (void (*)(void))ecdsa_get_ctx_md_params }, + { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_MD_PARAMS, + (void (*)(void))ecdsa_gettable_ctx_md_params }, + { OSSL_FUNC_SIGNATURE_SET_CTX_MD_PARAMS, + (void (*)(void))ecdsa_set_ctx_md_params }, + { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_MD_PARAMS, + (void (*)(void))ecdsa_settable_ctx_md_params }, + OSSL_DISPATCH_END +}; + +/* ------------------------------------------------------------------ */ + +/* + * So called sigalgs (composite ECDSA+hash) implemented below. They + * are pretty much hard coded. + */ + +static OSSL_FUNC_signature_query_key_types_fn ecdsa_sigalg_query_key_types; +static OSSL_FUNC_signature_settable_ctx_params_fn ecdsa_sigalg_settable_ctx_params; +static OSSL_FUNC_signature_set_ctx_params_fn ecdsa_sigalg_set_ctx_params; + +/* + * ecdsa_sigalg_signverify_init() is almost like ecdsa_digest_signverify_init(), + * just doesn't allow fetching an MD from whatever the user chooses. + */ +static int ecdsa_sigalg_signverify_init(void *vctx, void *vec, + OSSL_FUNC_signature_set_ctx_params_fn *set_ctx_params, + const OSSL_PARAM params[], + const char *mdname, + int operation, const char *desc) +{ + PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx; + + if (!ossl_prov_is_running()) + return 0; + + if (!ecdsa_signverify_init(vctx, vec, set_ctx_params, params, operation, + desc)) + return 0; + + if (!ecdsa_setup_md(ctx, mdname, NULL, desc)) + return 0; + + ctx->flag_sigalg = 1; + ctx->flag_allow_md = 0; + + if (ctx->mdctx == NULL) { + ctx->mdctx = EVP_MD_CTX_new(); + if (ctx->mdctx == NULL) + goto error; + } + + if (!EVP_DigestInit_ex2(ctx->mdctx, ctx->md, params)) + goto error; + + return 1; + + error: + EVP_MD_CTX_free(ctx->mdctx); + ctx->mdctx = NULL; + return 0; +} + +static const char **ecdsa_sigalg_query_key_types(void) +{ + static const char *keytypes[] = { "EC", NULL }; + + return keytypes; +} + +static const OSSL_PARAM settable_sigalg_ctx_params[] = { + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_SIGNATURE, NULL, 0), + ECDSA_COMMON_SETTABLE_CTX_PARAMS +}; + +static const OSSL_PARAM *ecdsa_sigalg_settable_ctx_params(void *vctx, + ossl_unused void *provctx) +{ + PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx; + + if (ctx != NULL && ctx->operation == EVP_PKEY_OP_VERIFYMSG) + return settable_sigalg_ctx_params; + return NULL; +} + +static int ecdsa_sigalg_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx; + const OSSL_PARAM *p; + int ret; + + if (ctx == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 1; + + if ((ret = ecdsa_common_set_ctx_params(ctx, params)) <= 0) + return ret; + + if (ctx->operation == EVP_PKEY_OP_VERIFYMSG) { + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_SIGNATURE); + if (p != NULL) { + OPENSSL_free(ctx->sig); + ctx->sig = NULL; + ctx->siglen = 0; + if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->sig, + 0, &ctx->siglen)) + return 0; + } + } + return 1; +} + +#define IMPL_ECDSA_SIGALG(md, MD) \ + static OSSL_FUNC_signature_sign_init_fn ecdsa_##md##_sign_init; \ + static OSSL_FUNC_signature_sign_message_init_fn \ + ecdsa_##md##_sign_message_init; \ + static OSSL_FUNC_signature_verify_init_fn ecdsa_##md##_verify_init; \ + static OSSL_FUNC_signature_verify_message_init_fn \ + ecdsa_##md##_verify_message_init; \ + \ + static int \ + ecdsa_##md##_sign_init(void *vctx, void *vec, \ + const OSSL_PARAM params[]) \ + { \ + static const char desc[] = "ECDSA-" #MD " Sign Init"; \ + \ + return ecdsa_sigalg_signverify_init(vctx, vec, \ + ecdsa_sigalg_set_ctx_params, \ + params, #MD, \ + EVP_PKEY_OP_SIGN, \ + desc); \ + } \ + \ + static int \ + ecdsa_##md##_sign_message_init(void *vctx, void *vec, \ + const OSSL_PARAM params[]) \ + { \ + static const char desc[] = "ECDSA-" #MD " Sign Message Init"; \ + \ + return ecdsa_sigalg_signverify_init(vctx, vec, \ + ecdsa_sigalg_set_ctx_params, \ + params, #MD, \ + EVP_PKEY_OP_SIGNMSG, \ + desc); \ + } \ + \ + static int \ + ecdsa_##md##_verify_init(void *vctx, void *vec, \ + const OSSL_PARAM params[]) \ + { \ + static const char desc[] = "ECDSA-" #MD " Verify Init"; \ + \ + return ecdsa_sigalg_signverify_init(vctx, vec, \ + ecdsa_sigalg_set_ctx_params, \ + params, #MD, \ + EVP_PKEY_OP_VERIFY, \ + desc); \ + } \ + \ + static int \ + ecdsa_##md##_verify_message_init(void *vctx, void *vec, \ + const OSSL_PARAM params[]) \ + { \ + static const char desc[] = "ECDSA-" #MD " Verify Message Init"; \ + \ + return ecdsa_sigalg_signverify_init(vctx, vec, \ + ecdsa_sigalg_set_ctx_params, \ + params, #MD, \ + EVP_PKEY_OP_VERIFYMSG, \ + desc); \ + } \ + \ + const OSSL_DISPATCH ossl_ecdsa_##md##_signature_functions[] = { \ + { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))ecdsa_newctx }, \ + { OSSL_FUNC_SIGNATURE_SIGN_INIT, \ + (void (*)(void))ecdsa_##md##_sign_init }, \ + { OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))ecdsa_sign }, \ + { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_INIT, \ + (void (*)(void))ecdsa_##md##_sign_message_init }, \ + { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_UPDATE, \ + (void (*)(void))ecdsa_signverify_message_update }, \ + { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_FINAL, \ + (void (*)(void))ecdsa_sign_message_final }, \ + { OSSL_FUNC_SIGNATURE_VERIFY_INIT, \ + (void (*)(void))ecdsa_##md##_verify_init }, \ + { OSSL_FUNC_SIGNATURE_VERIFY, \ + (void (*)(void))ecdsa_verify }, \ + { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_INIT, \ + (void (*)(void))ecdsa_##md##_verify_message_init }, \ + { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_UPDATE, \ + (void (*)(void))ecdsa_signverify_message_update }, \ + { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_FINAL, \ + (void (*)(void))ecdsa_verify_message_final }, \ + { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))ecdsa_freectx }, \ + { OSSL_FUNC_SIGNATURE_DUPCTX, (void (*)(void))ecdsa_dupctx }, \ + { OSSL_FUNC_SIGNATURE_QUERY_KEY_TYPES, \ + (void (*)(void))ecdsa_sigalg_query_key_types }, \ + { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, \ + (void (*)(void))ecdsa_get_ctx_params }, \ + { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, \ + (void (*)(void))ecdsa_gettable_ctx_params }, \ + { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, \ + (void (*)(void))ecdsa_sigalg_set_ctx_params }, \ + { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, \ + (void (*)(void))ecdsa_sigalg_settable_ctx_params }, \ + OSSL_DISPATCH_END \ + } + +IMPL_ECDSA_SIGALG(sha1, SHA1); +IMPL_ECDSA_SIGALG(sha224, SHA2-224); +IMPL_ECDSA_SIGALG(sha256, SHA2-256); +IMPL_ECDSA_SIGALG(sha384, SHA2-384); +IMPL_ECDSA_SIGALG(sha512, SHA2-512); +IMPL_ECDSA_SIGALG(sha3_224, SHA3-224); +IMPL_ECDSA_SIGALG(sha3_256, SHA3-256); +IMPL_ECDSA_SIGALG(sha3_384, SHA3-384); +IMPL_ECDSA_SIGALG(sha3_512, SHA3-512); diff --git a/crypto/openssl/providers/implementations/signature/eddsa_sig.c b/crypto/openssl/providers/implementations/signature/eddsa_sig.c new file mode 100644 index 000000000000..28b17eab93f3 --- /dev/null +++ b/crypto/openssl/providers/implementations/signature/eddsa_sig.c @@ -0,0 +1,1146 @@ +/* + * Copyright 2020-2025 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 + */ + +#include <openssl/crypto.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/err.h> +#include <openssl/params.h> +#include <openssl/evp.h> +#include <openssl/proverr.h> +#include "internal/nelem.h" +#include "internal/sizes.h" +#include "prov/providercommon.h" +#include "prov/implementations.h" +#include "prov/securitycheck.h" +#include "prov/provider_ctx.h" +#include "prov/der_ecx.h" +#include "crypto/ecx.h" + +#ifdef S390X_EC_ASM +# include "s390x_arch.h" + +# define S390X_CAN_SIGN(edtype) \ +((OPENSSL_s390xcap_P.pcc[1] & S390X_CAPBIT(S390X_SCALAR_MULTIPLY_##edtype)) \ +&& (OPENSSL_s390xcap_P.kdsa[0] & S390X_CAPBIT(S390X_EDDSA_SIGN_##edtype)) \ +&& (OPENSSL_s390xcap_P.kdsa[0] & S390X_CAPBIT(S390X_EDDSA_VERIFY_##edtype))) + +static int s390x_ed25519_digestsign(const ECX_KEY *edkey, unsigned char *sig, + const unsigned char *tbs, size_t tbslen); +static int s390x_ed448_digestsign(const ECX_KEY *edkey, unsigned char *sig, + const unsigned char *tbs, size_t tbslen); +static int s390x_ed25519_digestverify(const ECX_KEY *edkey, + const unsigned char *sig, + const unsigned char *tbs, size_t tbslen); +static int s390x_ed448_digestverify(const ECX_KEY *edkey, + const unsigned char *sig, + const unsigned char *tbs, size_t tbslen); + +#endif /* S390X_EC_ASM */ + +enum ID_EdDSA_INSTANCE { + ID_NOT_SET = 0, + ID_Ed25519, + ID_Ed25519ctx, + ID_Ed25519ph, + ID_Ed448, + ID_Ed448ph +}; + +#define SN_Ed25519 "Ed25519" +#define SN_Ed25519ph "Ed25519ph" +#define SN_Ed25519ctx "Ed25519ctx" +#define SN_Ed448 "Ed448" +#define SN_Ed448ph "Ed448ph" + +#define EDDSA_MAX_CONTEXT_STRING_LEN 255 +#define EDDSA_PREHASH_OUTPUT_LEN 64 + +static OSSL_FUNC_signature_newctx_fn eddsa_newctx; +static OSSL_FUNC_signature_sign_message_init_fn ed25519_signverify_message_init; +static OSSL_FUNC_signature_sign_message_init_fn ed25519ph_signverify_message_init; +static OSSL_FUNC_signature_sign_message_init_fn ed25519ctx_signverify_message_init; +static OSSL_FUNC_signature_sign_message_init_fn ed448_signverify_message_init; +static OSSL_FUNC_signature_sign_message_init_fn ed448ph_signverify_message_init; +static OSSL_FUNC_signature_sign_fn ed25519_sign; +static OSSL_FUNC_signature_sign_fn ed448_sign; +static OSSL_FUNC_signature_verify_fn ed25519_verify; +static OSSL_FUNC_signature_verify_fn ed448_verify; +static OSSL_FUNC_signature_digest_sign_init_fn ed25519_digest_signverify_init; +static OSSL_FUNC_signature_digest_sign_init_fn ed448_digest_signverify_init; +static OSSL_FUNC_signature_digest_sign_fn ed25519_digest_sign; +static OSSL_FUNC_signature_digest_sign_fn ed448_digest_sign; +static OSSL_FUNC_signature_digest_verify_fn ed25519_digest_verify; +static OSSL_FUNC_signature_digest_verify_fn ed448_digest_verify; +static OSSL_FUNC_signature_freectx_fn eddsa_freectx; +static OSSL_FUNC_signature_dupctx_fn eddsa_dupctx; +static OSSL_FUNC_signature_query_key_types_fn ed25519_sigalg_query_key_types; +static OSSL_FUNC_signature_query_key_types_fn ed448_sigalg_query_key_types; +static OSSL_FUNC_signature_get_ctx_params_fn eddsa_get_ctx_params; +static OSSL_FUNC_signature_gettable_ctx_params_fn eddsa_gettable_ctx_params; +static OSSL_FUNC_signature_set_ctx_params_fn eddsa_set_ctx_params; +static OSSL_FUNC_signature_settable_ctx_params_fn eddsa_settable_ctx_params; +static OSSL_FUNC_signature_settable_ctx_params_fn eddsa_settable_variant_ctx_params; + +/* there are five EdDSA instances: + + Ed25519 + Ed25519ph + Ed25519ctx + Ed448 + Ed448ph + + Quoting from RFC 8032, Section 5.1: + + For Ed25519, dom2(f,c) is the empty string. The phflag value is + irrelevant. The context (if present at all) MUST be empty. This + causes the scheme to be one and the same with the Ed25519 scheme + published earlier. + + For Ed25519ctx, phflag=0. The context input SHOULD NOT be empty. + + For Ed25519ph, phflag=1 and PH is SHA512 instead. That is, the input + is hashed using SHA-512 before signing with Ed25519. + + Quoting from RFC 8032, Section 5.2: + + Ed448ph is the same but with PH being SHAKE256(x, 64) and phflag + being 1, i.e., the input is hashed before signing with Ed448 with a + hash constant modified. + + Value of context is set by signer and verifier (maximum of 255 + octets; the default is empty string) and has to match octet by octet + for verification to be successful. + + Quoting from RFC 8032, Section 2: + + dom2(x, y) The blank octet string when signing or verifying + Ed25519. Otherwise, the octet string: "SigEd25519 no + Ed25519 collisions" || octet(x) || octet(OLEN(y)) || + y, where x is in range 0-255 and y is an octet string + of at most 255 octets. "SigEd25519 no Ed25519 + collisions" is in ASCII (32 octets). + + dom4(x, y) The octet string "SigEd448" || octet(x) || + octet(OLEN(y)) || y, where x is in range 0-255 and y + is an octet string of at most 255 octets. "SigEd448" + is in ASCII (8 octets). + + Note above that x is the pre-hash flag, and y is the context string. +*/ + +typedef struct { + OSSL_LIB_CTX *libctx; + ECX_KEY *key; + + /* The Algorithm Identifier of the signature algorithm */ + unsigned char aid_buf[OSSL_MAX_ALGORITHM_ID_SIZE]; + size_t aid_len; + + /* id indicating the EdDSA instance */ + int instance_id; + /* indicates that instance_id and associated flags are preset / hardcoded */ + unsigned int instance_id_preset_flag : 1; + /* for ph instances, this indicates whether the caller is expected to prehash */ + unsigned int prehash_by_caller_flag : 1; + + unsigned int dom2_flag : 1; + unsigned int prehash_flag : 1; + + /* indicates that a non-empty context string is required, as in Ed25519ctx */ + unsigned int context_string_flag : 1; + + unsigned char context_string[EDDSA_MAX_CONTEXT_STRING_LEN]; + size_t context_string_len; + +} PROV_EDDSA_CTX; + +static void *eddsa_newctx(void *provctx, const char *propq_unused) +{ + PROV_EDDSA_CTX *peddsactx; + + if (!ossl_prov_is_running()) + return NULL; + + peddsactx = OPENSSL_zalloc(sizeof(PROV_EDDSA_CTX)); + if (peddsactx == NULL) + return NULL; + + peddsactx->libctx = PROV_LIBCTX_OF(provctx); + + return peddsactx; +} + +static int eddsa_setup_instance(void *vpeddsactx, int instance_id, + unsigned int instance_id_preset, + unsigned int prehash_by_caller) +{ + PROV_EDDSA_CTX *peddsactx = (PROV_EDDSA_CTX *)vpeddsactx; + + switch (instance_id) { + case ID_Ed25519: + if (peddsactx->key->type != ECX_KEY_TYPE_ED25519) + return 0; + peddsactx->dom2_flag = 0; + peddsactx->prehash_flag = 0; + peddsactx->context_string_flag = 0; + break; + case ID_Ed25519ctx: + if (peddsactx->key->type != ECX_KEY_TYPE_ED25519) + return 0; + peddsactx->dom2_flag = 1; + peddsactx->prehash_flag = 0; + peddsactx->context_string_flag = 1; + break; + case ID_Ed25519ph: + if (peddsactx->key->type != ECX_KEY_TYPE_ED25519) + return 0; + peddsactx->dom2_flag = 1; + peddsactx->prehash_flag = 1; + peddsactx->context_string_flag = 0; + break; + case ID_Ed448: + if (peddsactx->key->type != ECX_KEY_TYPE_ED448) + return 0; + peddsactx->prehash_flag = 0; + peddsactx->context_string_flag = 0; + break; + case ID_Ed448ph: + if (peddsactx->key->type != ECX_KEY_TYPE_ED448) + return 0; + peddsactx->prehash_flag = 1; + peddsactx->context_string_flag = 0; + break; + default: + /* we did not recognize the instance */ + return 0; + } + peddsactx->instance_id = instance_id; + peddsactx->instance_id_preset_flag = instance_id_preset; + peddsactx->prehash_by_caller_flag = prehash_by_caller; + return 1; +} + +static int eddsa_signverify_init(void *vpeddsactx, void *vedkey) +{ + PROV_EDDSA_CTX *peddsactx = (PROV_EDDSA_CTX *)vpeddsactx; + ECX_KEY *edkey = (ECX_KEY *)vedkey; + WPACKET pkt; + int ret; + unsigned char *aid = NULL; + + if (!ossl_prov_is_running()) + return 0; + + if (edkey == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET); + return 0; + } + + if (!ossl_ecx_key_up_ref(edkey)) { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + return 0; + } + + peddsactx->instance_id_preset_flag = 0; + peddsactx->dom2_flag = 0; + peddsactx->prehash_flag = 0; + peddsactx->context_string_flag = 0; + peddsactx->context_string_len = 0; + + peddsactx->key = edkey; + + /* + * We do not care about DER writing errors. + * All it really means is that for some reason, there's no + * AlgorithmIdentifier to be had, but the operation itself is + * still valid, just as long as it's not used to construct + * anything that needs an AlgorithmIdentifier. + */ + peddsactx->aid_len = 0; + ret = WPACKET_init_der(&pkt, peddsactx->aid_buf, sizeof(peddsactx->aid_buf)); + switch (edkey->type) { + case ECX_KEY_TYPE_ED25519: + ret = ret && ossl_DER_w_algorithmIdentifier_ED25519(&pkt, -1, edkey); + break; + case ECX_KEY_TYPE_ED448: + ret = ret && ossl_DER_w_algorithmIdentifier_ED448(&pkt, -1, edkey); + break; + default: + /* Should never happen */ + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + ossl_ecx_key_free(edkey); + peddsactx->key = NULL; + WPACKET_cleanup(&pkt); + return 0; + } + if (ret && WPACKET_finish(&pkt)) { + WPACKET_get_total_written(&pkt, &peddsactx->aid_len); + aid = WPACKET_get_curr(&pkt); + } + WPACKET_cleanup(&pkt); + if (aid != NULL && peddsactx->aid_len != 0) + memmove(peddsactx->aid_buf, aid, peddsactx->aid_len); + + return 1; +} + +static int ed25519_signverify_message_init(void *vpeddsactx, void *vedkey, + const OSSL_PARAM params[]) +{ + return eddsa_signverify_init(vpeddsactx, vedkey) + && eddsa_setup_instance(vpeddsactx, ID_Ed25519, 1, 0) + && eddsa_set_ctx_params(vpeddsactx, params); +} + +static int ed25519ph_signverify_message_init(void *vpeddsactx, void *vedkey, + const OSSL_PARAM params[]) +{ + return eddsa_signverify_init(vpeddsactx, vedkey) + && eddsa_setup_instance(vpeddsactx, ID_Ed25519ph, 1, 0) + && eddsa_set_ctx_params(vpeddsactx, params); +} + +static int ed25519ph_signverify_init(void *vpeddsactx, void *vedkey, + const OSSL_PARAM params[]) +{ + return eddsa_signverify_init(vpeddsactx, vedkey) + && eddsa_setup_instance(vpeddsactx, ID_Ed25519ph, 1, 1) + && eddsa_set_ctx_params(vpeddsactx, params); +} + +/* + * This supports using ED25519 with EVP_PKEY_{sign,verify}_init_ex() and + * EVP_PKEY_{sign,verify}_init_ex2(), under the condition that the caller + * explicitly sets the Ed25519ph instance (this is verified by ed25519_sign() + * and ed25519_verify()) + */ +static int ed25519_signverify_init(void *vpeddsactx, void *vedkey, + const OSSL_PARAM params[]) +{ + return eddsa_signverify_init(vpeddsactx, vedkey) + && eddsa_setup_instance(vpeddsactx, ID_Ed25519, 0, 1) + && eddsa_set_ctx_params(vpeddsactx, params); +} + +static int ed25519ctx_signverify_message_init(void *vpeddsactx, void *vedkey, + const OSSL_PARAM params[]) +{ + return eddsa_signverify_init(vpeddsactx, vedkey) + && eddsa_setup_instance(vpeddsactx, ID_Ed25519ctx, 1, 0) + && eddsa_set_ctx_params(vpeddsactx, params); +} + +static int ed448_signverify_message_init(void *vpeddsactx, void *vedkey, + const OSSL_PARAM params[]) +{ + return eddsa_signverify_init(vpeddsactx, vedkey) + && eddsa_setup_instance(vpeddsactx, ID_Ed448, 1, 0) + && eddsa_set_ctx_params(vpeddsactx, params); +} + +static int ed448ph_signverify_message_init(void *vpeddsactx, void *vedkey, + const OSSL_PARAM params[]) +{ + return eddsa_signverify_init(vpeddsactx, vedkey) + && eddsa_setup_instance(vpeddsactx, ID_Ed448ph, 1, 0) + && eddsa_set_ctx_params(vpeddsactx, params); +} + +static int ed448ph_signverify_init(void *vpeddsactx, void *vedkey, + const OSSL_PARAM params[]) +{ + return eddsa_signverify_init(vpeddsactx, vedkey) + && eddsa_setup_instance(vpeddsactx, ID_Ed448ph, 1, 1) + && eddsa_set_ctx_params(vpeddsactx, params); +} + +/* + * This supports using ED448 with EVP_PKEY_{sign,verify}_init_ex() and + * EVP_PKEY_{sign,verify}_init_ex2(), under the condition that the caller + * explicitly sets the Ed448ph instance (this is verified by ed448_sign() + * and ed448_verify()) + */ +static int ed448_signverify_init(void *vpeddsactx, void *vedkey, + const OSSL_PARAM params[]) +{ + return eddsa_signverify_init(vpeddsactx, vedkey) + && eddsa_setup_instance(vpeddsactx, ID_Ed448, 0, 1) + && eddsa_set_ctx_params(vpeddsactx, params); +} + +/* + * This is used directly for OSSL_FUNC_SIGNATURE_SIGN and indirectly + * for OSSL_FUNC_SIGNATURE_DIGEST_SIGN + */ +static int ed25519_sign(void *vpeddsactx, + unsigned char *sigret, size_t *siglen, size_t sigsize, + const unsigned char *tbs, size_t tbslen) +{ + PROV_EDDSA_CTX *peddsactx = (PROV_EDDSA_CTX *)vpeddsactx; + const ECX_KEY *edkey = peddsactx->key; + uint8_t md[EVP_MAX_MD_SIZE]; + size_t mdlen; + + if (!ossl_prov_is_running()) + return 0; + + if (sigret == NULL) { + *siglen = ED25519_SIGSIZE; + return 1; + } + if (sigsize < ED25519_SIGSIZE) { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + return 0; + } + if (edkey->privkey == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PRIVATE_KEY); + return 0; + } +#ifdef S390X_EC_ASM + /* + * s390x_ed25519_digestsign() does not yet support dom2 or context-strings. + * fall back to non-accelerated sign if those options are set, or pre-hasing + * is provided. + */ + if (S390X_CAN_SIGN(ED25519) + && !peddsactx->dom2_flag + && !peddsactx->context_string_flag + && peddsactx->context_string_len == 0 + && !peddsactx->prehash_flag + && !peddsactx->prehash_by_caller_flag) { + if (s390x_ed25519_digestsign(edkey, sigret, tbs, tbslen) == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SIGN); + return 0; + } + *siglen = ED25519_SIGSIZE; + return 1; + } +#endif /* S390X_EC_ASM */ + + if (peddsactx->prehash_flag) { + if (!peddsactx->prehash_by_caller_flag) { + if (!EVP_Q_digest(peddsactx->libctx, SN_sha512, NULL, + tbs, tbslen, md, &mdlen) + || mdlen != EDDSA_PREHASH_OUTPUT_LEN) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_PREHASHED_DIGEST_LENGTH); + return 0; + } + tbs = md; + tbslen = mdlen; + } else if (tbslen != EDDSA_PREHASH_OUTPUT_LEN) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_LENGTH); + return 0; + } + } else if (peddsactx->prehash_by_caller_flag) { + /* The caller is supposed to set up a ph instance! */ + ERR_raise(ERR_LIB_PROV, + PROV_R_INVALID_EDDSA_INSTANCE_FOR_ATTEMPTED_OPERATION); + return 0; + } + + if (ossl_ed25519_sign(sigret, tbs, tbslen, edkey->pubkey, edkey->privkey, + peddsactx->dom2_flag, peddsactx->prehash_flag, peddsactx->context_string_flag, + peddsactx->context_string, peddsactx->context_string_len, + peddsactx->libctx, NULL) == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SIGN); + return 0; + } + *siglen = ED25519_SIGSIZE; + return 1; +} + +/* EVP_Q_digest() does not allow variable output length for XOFs, + so we use this function */ +static int ed448_shake256(OSSL_LIB_CTX *libctx, + const char *propq, + const uint8_t *in, size_t inlen, + uint8_t *out, size_t outlen) +{ + int ret = 0; + EVP_MD_CTX *hash_ctx = EVP_MD_CTX_new(); + EVP_MD *shake256 = EVP_MD_fetch(libctx, SN_shake256, propq); + + if (hash_ctx == NULL || shake256 == NULL) + goto err; + + if (!EVP_DigestInit_ex(hash_ctx, shake256, NULL) + || !EVP_DigestUpdate(hash_ctx, in, inlen) + || !EVP_DigestFinalXOF(hash_ctx, out, outlen)) + goto err; + + ret = 1; + + err: + EVP_MD_CTX_free(hash_ctx); + EVP_MD_free(shake256); + return ret; +} + +/* + * This is used directly for OSSL_FUNC_SIGNATURE_SIGN and indirectly + * for OSSL_FUNC_SIGNATURE_DIGEST_SIGN + */ +static int ed448_sign(void *vpeddsactx, + unsigned char *sigret, size_t *siglen, size_t sigsize, + const unsigned char *tbs, size_t tbslen) +{ + PROV_EDDSA_CTX *peddsactx = (PROV_EDDSA_CTX *)vpeddsactx; + const ECX_KEY *edkey = peddsactx->key; + uint8_t md[EDDSA_PREHASH_OUTPUT_LEN]; + size_t mdlen = sizeof(md); + + if (!ossl_prov_is_running()) + return 0; + + if (sigret == NULL) { + *siglen = ED448_SIGSIZE; + return 1; + } + if (sigsize < ED448_SIGSIZE) { + ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL); + return 0; + } + if (edkey->privkey == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PRIVATE_KEY); + return 0; + } +#ifdef S390X_EC_ASM + /* + * s390x_ed448_digestsign() does not yet support context-strings or + * pre-hashing. Fall back to non-accelerated sign if a context-string or + * pre-hasing is provided. + */ + if (S390X_CAN_SIGN(ED448) + && peddsactx->context_string_len == 0 + && !peddsactx->prehash_flag + && !peddsactx->prehash_by_caller_flag) { + if (s390x_ed448_digestsign(edkey, sigret, tbs, tbslen) == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SIGN); + return 0; + } + *siglen = ED448_SIGSIZE; + return 1; + } +#endif /* S390X_EC_ASM */ + + if (peddsactx->prehash_flag) { + if (!peddsactx->prehash_by_caller_flag) { + if (!ed448_shake256(peddsactx->libctx, NULL, tbs, tbslen, md, mdlen)) + return 0; + tbs = md; + tbslen = mdlen; + } else if (tbslen != EDDSA_PREHASH_OUTPUT_LEN) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_LENGTH); + return 0; + } + } else if (peddsactx->prehash_by_caller_flag) { + /* The caller is supposed to set up a ph instance! */ + ERR_raise(ERR_LIB_PROV, + PROV_R_INVALID_EDDSA_INSTANCE_FOR_ATTEMPTED_OPERATION); + return 0; + } + + if (ossl_ed448_sign(peddsactx->libctx, sigret, tbs, tbslen, + edkey->pubkey, edkey->privkey, + peddsactx->context_string, peddsactx->context_string_len, + peddsactx->prehash_flag, edkey->propq) == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SIGN); + return 0; + } + *siglen = ED448_SIGSIZE; + return 1; +} + +/* + * This is used directly for OSSL_FUNC_SIGNATURE_VERIFY and indirectly + * for OSSL_FUNC_SIGNATURE_DIGEST_VERIFY + */ +static int ed25519_verify(void *vpeddsactx, + const unsigned char *sig, size_t siglen, + const unsigned char *tbs, size_t tbslen) +{ + PROV_EDDSA_CTX *peddsactx = (PROV_EDDSA_CTX *)vpeddsactx; + const ECX_KEY *edkey = peddsactx->key; + uint8_t md[EVP_MAX_MD_SIZE]; + size_t mdlen; + + if (!ossl_prov_is_running() || siglen != ED25519_SIGSIZE) + return 0; + +#ifdef S390X_EC_ASM + /* + * s390x_ed25519_digestverify() does not yet support dom2 or context-strings. + * fall back to non-accelerated verify if those options are set, or + * pre-hasing is provided. + */ + if (S390X_CAN_SIGN(ED25519) + && !peddsactx->dom2_flag + && !peddsactx->context_string_flag + && peddsactx->context_string_len == 0 + && !peddsactx->prehash_flag + && !peddsactx->prehash_by_caller_flag) + return s390x_ed25519_digestverify(edkey, sig, tbs, tbslen); +#endif /* S390X_EC_ASM */ + + if (peddsactx->prehash_flag) { + if (!peddsactx->prehash_by_caller_flag) { + if (!EVP_Q_digest(peddsactx->libctx, SN_sha512, NULL, + tbs, tbslen, md, &mdlen) + || mdlen != EDDSA_PREHASH_OUTPUT_LEN) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_PREHASHED_DIGEST_LENGTH); + return 0; + } + tbs = md; + tbslen = mdlen; + } else if (tbslen != EDDSA_PREHASH_OUTPUT_LEN) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_LENGTH); + return 0; + } + } else if (peddsactx->prehash_by_caller_flag) { + /* The caller is supposed to set up a ph instance! */ + ERR_raise(ERR_LIB_PROV, + PROV_R_INVALID_EDDSA_INSTANCE_FOR_ATTEMPTED_OPERATION); + return 0; + } + + return ossl_ed25519_verify(tbs, tbslen, sig, edkey->pubkey, + peddsactx->dom2_flag, peddsactx->prehash_flag, peddsactx->context_string_flag, + peddsactx->context_string, peddsactx->context_string_len, + peddsactx->libctx, edkey->propq); +} + +/* + * This is used directly for OSSL_FUNC_SIGNATURE_VERIFY and indirectly + * for OSSL_FUNC_SIGNATURE_DIGEST_VERIFY + */ +static int ed448_verify(void *vpeddsactx, + const unsigned char *sig, size_t siglen, + const unsigned char *tbs, size_t tbslen) +{ + PROV_EDDSA_CTX *peddsactx = (PROV_EDDSA_CTX *)vpeddsactx; + const ECX_KEY *edkey = peddsactx->key; + uint8_t md[EDDSA_PREHASH_OUTPUT_LEN]; + size_t mdlen = sizeof(md); + + if (!ossl_prov_is_running() || siglen != ED448_SIGSIZE) + return 0; + +#ifdef S390X_EC_ASM + /* + * s390x_ed448_digestverify() does not yet support context-strings or + * pre-hashing. Fall back to non-accelerated verify if a context-string or + * pre-hasing is provided. + */ + if (S390X_CAN_SIGN(ED448) + && peddsactx->context_string_len == 0 + && !peddsactx->prehash_flag + && !peddsactx->prehash_by_caller_flag) + return s390x_ed448_digestverify(edkey, sig, tbs, tbslen); +#endif /* S390X_EC_ASM */ + + if (peddsactx->prehash_flag) { + if (!peddsactx->prehash_by_caller_flag) { + if (!ed448_shake256(peddsactx->libctx, NULL, tbs, tbslen, md, mdlen)) + return 0; + tbs = md; + tbslen = mdlen; + } else if (tbslen != EDDSA_PREHASH_OUTPUT_LEN) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_LENGTH); + return 0; + } + } else if (peddsactx->prehash_by_caller_flag) { + /* The caller is supposed to set up a ph instance! */ + ERR_raise(ERR_LIB_PROV, + PROV_R_INVALID_EDDSA_INSTANCE_FOR_ATTEMPTED_OPERATION); + return 0; + } + + return ossl_ed448_verify(peddsactx->libctx, tbs, tbslen, sig, edkey->pubkey, + peddsactx->context_string, peddsactx->context_string_len, + peddsactx->prehash_flag, edkey->propq); +} + +/* All digest_{sign,verify} are simple wrappers around the functions above */ + +static int ed25519_digest_signverify_init(void *vpeddsactx, const char *mdname, + void *vedkey, + const OSSL_PARAM params[]) +{ + PROV_EDDSA_CTX *peddsactx = (PROV_EDDSA_CTX *)vpeddsactx; + + if (mdname != NULL && mdname[0] != '\0') { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST, + "Explicit digest not allowed with EdDSA operations"); + return 0; + } + + if (vedkey == NULL && peddsactx->key != NULL) + return eddsa_set_ctx_params(peddsactx, params); + + return eddsa_signverify_init(vpeddsactx, vedkey) + && eddsa_setup_instance(vpeddsactx, ID_Ed25519, 0, 0) + && eddsa_set_ctx_params(vpeddsactx, params); +} + +static int ed25519_digest_sign(void *vpeddsactx, + unsigned char *sigret, size_t *siglen, size_t sigsize, + const unsigned char *tbs, size_t tbslen) +{ + return ed25519_sign(vpeddsactx, sigret, siglen, sigsize, tbs, tbslen); +} + +static int ed25519_digest_verify(void *vpeddsactx, + const unsigned char *sigret, size_t siglen, + const unsigned char *tbs, size_t tbslen) +{ + return ed25519_verify(vpeddsactx, sigret, siglen, tbs, tbslen); +} + +static int ed448_digest_signverify_init(void *vpeddsactx, const char *mdname, + void *vedkey, + const OSSL_PARAM params[]) +{ + PROV_EDDSA_CTX *peddsactx = (PROV_EDDSA_CTX *)vpeddsactx; + + if (mdname != NULL && mdname[0] != '\0') { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST, + "Explicit digest not allowed with EdDSA operations"); + return 0; + } + + if (vedkey == NULL && peddsactx->key != NULL) + return eddsa_set_ctx_params(peddsactx, params); + + return eddsa_signverify_init(vpeddsactx, vedkey) + && eddsa_setup_instance(vpeddsactx, ID_Ed448, 0, 0) + && eddsa_set_ctx_params(vpeddsactx, params); +} + +static int ed448_digest_sign(void *vpeddsactx, + unsigned char *sigret, size_t *siglen, size_t sigsize, + const unsigned char *tbs, size_t tbslen) +{ + return ed448_sign(vpeddsactx, sigret, siglen, sigsize, tbs, tbslen); +} + +static int ed448_digest_verify(void *vpeddsactx, + const unsigned char *sigret, size_t siglen, + const unsigned char *tbs, size_t tbslen) +{ + return ed448_verify(vpeddsactx, sigret, siglen, tbs, tbslen); +} + +static void eddsa_freectx(void *vpeddsactx) +{ + PROV_EDDSA_CTX *peddsactx = (PROV_EDDSA_CTX *)vpeddsactx; + + ossl_ecx_key_free(peddsactx->key); + + OPENSSL_free(peddsactx); +} + +static void *eddsa_dupctx(void *vpeddsactx) +{ + PROV_EDDSA_CTX *srcctx = (PROV_EDDSA_CTX *)vpeddsactx; + PROV_EDDSA_CTX *dstctx; + + if (!ossl_prov_is_running()) + return NULL; + + dstctx = OPENSSL_zalloc(sizeof(*srcctx)); + if (dstctx == NULL) + return NULL; + + *dstctx = *srcctx; + dstctx->key = NULL; + + if (srcctx->key != NULL && !ossl_ecx_key_up_ref(srcctx->key)) { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + goto err; + } + dstctx->key = srcctx->key; + + return dstctx; + err: + eddsa_freectx(dstctx); + return NULL; +} + +static const char **ed25519_sigalg_query_key_types(void) +{ + static const char *keytypes[] = { "ED25519", NULL }; + + return keytypes; +} + +static const char **ed448_sigalg_query_key_types(void) +{ + static const char *keytypes[] = { "ED448", NULL }; + + return keytypes; +} + + + +static int eddsa_get_ctx_params(void *vpeddsactx, OSSL_PARAM *params) +{ + PROV_EDDSA_CTX *peddsactx = (PROV_EDDSA_CTX *)vpeddsactx; + OSSL_PARAM *p; + + if (peddsactx == NULL) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_ALGORITHM_ID); + if (p != NULL + && !OSSL_PARAM_set_octet_string(p, + peddsactx->aid_len == 0 ? NULL : peddsactx->aid_buf, + peddsactx->aid_len)) + return 0; + + return 1; +} + +static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_ALGORITHM_ID, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_INSTANCE, NULL, 0), + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, NULL, 0), + OSSL_PARAM_END +}; + +static const OSSL_PARAM *eddsa_gettable_ctx_params(ossl_unused void *vpeddsactx, + ossl_unused void *provctx) +{ + return known_gettable_ctx_params; +} + +static int eddsa_set_ctx_params(void *vpeddsactx, const OSSL_PARAM params[]) +{ + PROV_EDDSA_CTX *peddsactx = (PROV_EDDSA_CTX *)vpeddsactx; + const OSSL_PARAM *p; + + if (peddsactx == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_INSTANCE); + if (p != NULL) { + char instance_name[OSSL_MAX_NAME_SIZE] = ""; + char *pinstance_name = instance_name; + + if (peddsactx->instance_id_preset_flag) { + /* When the instance is preset, the caller must no try to set it */ + ERR_raise_data(ERR_LIB_PROV, PROV_R_NO_INSTANCE_ALLOWED, + "the EdDSA instance is preset, you may not try to specify it", + NULL); + return 0; + } + + if (!OSSL_PARAM_get_utf8_string(p, &pinstance_name, sizeof(instance_name))) + return 0; + + /* + * When setting the new instance, we're careful not to change the + * prehash_by_caller flag, as that's always preset by the init + * functions. The sign functions will determine if the instance + * matches this flag. + */ + if (OPENSSL_strcasecmp(pinstance_name, SN_Ed25519) == 0) { + eddsa_setup_instance(peddsactx, ID_Ed25519, 0, + peddsactx->prehash_by_caller_flag); + } else if (OPENSSL_strcasecmp(pinstance_name, SN_Ed25519ctx) == 0) { + eddsa_setup_instance(peddsactx, ID_Ed25519ctx, 0, + peddsactx->prehash_by_caller_flag); + } else if (OPENSSL_strcasecmp(pinstance_name, SN_Ed25519ph) == 0) { + eddsa_setup_instance(peddsactx, ID_Ed25519ph, 0, + peddsactx->prehash_by_caller_flag); + } else if (OPENSSL_strcasecmp(pinstance_name, SN_Ed448) == 0) { + eddsa_setup_instance(peddsactx, ID_Ed448, 0, + peddsactx->prehash_by_caller_flag); + } else if (OPENSSL_strcasecmp(pinstance_name, SN_Ed448ph) == 0) { + eddsa_setup_instance(peddsactx, ID_Ed448ph, 0, + peddsactx->prehash_by_caller_flag); + } else { + /* we did not recognize the instance */ + return 0; + } + + } + + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_CONTEXT_STRING); + if (p != NULL) { + void *vp_context_string = peddsactx->context_string; + + if (!OSSL_PARAM_get_octet_string(p, &vp_context_string, sizeof(peddsactx->context_string), &(peddsactx->context_string_len))) { + peddsactx->context_string_len = 0; + return 0; + } + } + + return 1; +} + +static const OSSL_PARAM settable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_INSTANCE, NULL, 0), + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, NULL, 0), + OSSL_PARAM_END +}; + +static const OSSL_PARAM *eddsa_settable_ctx_params(ossl_unused void *vpeddsactx, + ossl_unused void *provctx) +{ + return settable_ctx_params; +} + +static const OSSL_PARAM settable_variant_ctx_params[] = { + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, NULL, 0), + OSSL_PARAM_END +}; + +static const OSSL_PARAM * +eddsa_settable_variant_ctx_params(ossl_unused void *vpeddsactx, + ossl_unused void *provctx) +{ + return settable_variant_ctx_params; +} + +/* + * Ed25519 can be used with: + * - EVP_PKEY_sign_init_ex2() [ instance and prehash assumed done by caller ] + * - EVP_PKEY_verify_init_ex2() [ instance and prehash assumed done by caller ] + * - EVP_PKEY_sign_message_init() + * - EVP_PKEY_verify_message_init() + * - EVP_DigestSignInit_ex() + * - EVP_DigestVerifyInit_ex() + * Ed25519ph can be used with: + * - EVP_PKEY_sign_init_ex2() [ prehash assumed done by caller ] + * - EVP_PKEY_verify_init_ex2() [ prehash assumed done by caller ] + * - EVP_PKEY_sign_message_init() + * - EVP_PKEY_verify_message_init() + * Ed25519ctx can be used with: + * - EVP_PKEY_sign_message_init() + * - EVP_PKEY_verify_message_init() + * Ed448 can be used with: + * - EVP_PKEY_sign_init_ex2() [ instance and prehash assumed done by caller ] + * - EVP_PKEY_verify_init_ex2() [ instance and prehash assumed done by caller ] + * - EVP_PKEY_sign_message_init() + * - EVP_PKEY_verify_message_init() + * - EVP_DigestSignInit_ex() + * - EVP_DigestVerifyInit_ex() + * Ed448ph can be used with: + * - EVP_PKEY_sign_init_ex2() [ prehash assumed done by caller ] + * - EVP_PKEY_verify_init_ex2() [ prehash assumed done by caller ] + * - EVP_PKEY_sign_message_init() + * - EVP_PKEY_verify_message_init() + */ + +#define ed25519_DISPATCH_END \ + { OSSL_FUNC_SIGNATURE_SIGN_INIT, \ + (void (*)(void))ed25519_signverify_init }, \ + { OSSL_FUNC_SIGNATURE_VERIFY_INIT, \ + (void (*)(void))ed25519_signverify_init }, \ + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT, \ + (void (*)(void))ed25519_digest_signverify_init }, \ + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN, \ + (void (*)(void))ed25519_digest_sign }, \ + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT, \ + (void (*)(void))ed25519_digest_signverify_init }, \ + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY, \ + (void (*)(void))ed25519_digest_verify }, \ + { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, \ + (void (*)(void))eddsa_get_ctx_params }, \ + { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, \ + (void (*)(void))eddsa_gettable_ctx_params }, \ + { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, \ + (void (*)(void))eddsa_set_ctx_params }, \ + { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, \ + (void (*)(void))eddsa_settable_ctx_params }, \ + OSSL_DISPATCH_END + +#define eddsa_variant_DISPATCH_END(v) \ + { OSSL_FUNC_SIGNATURE_SIGN_INIT, \ + (void (*)(void))v##_signverify_message_init }, \ + { OSSL_FUNC_SIGNATURE_VERIFY_INIT, \ + (void (*)(void))v##_signverify_message_init }, \ + { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, \ + (void (*)(void))eddsa_get_ctx_params }, \ + { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, \ + (void (*)(void))eddsa_gettable_ctx_params }, \ + { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, \ + (void (*)(void))eddsa_set_ctx_params }, \ + { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, \ + (void (*)(void))eddsa_settable_variant_ctx_params }, \ + OSSL_DISPATCH_END + +#define ed25519ph_DISPATCH_END \ + { OSSL_FUNC_SIGNATURE_SIGN_INIT, \ + (void (*)(void))ed25519ph_signverify_init }, \ + { OSSL_FUNC_SIGNATURE_VERIFY_INIT, \ + (void (*)(void))ed25519ph_signverify_init }, \ + eddsa_variant_DISPATCH_END(ed25519ph) + +#define ed25519ctx_DISPATCH_END eddsa_variant_DISPATCH_END(ed25519ctx) + +#define ed448_DISPATCH_END \ + { OSSL_FUNC_SIGNATURE_SIGN_INIT, \ + (void (*)(void))ed448_signverify_init }, \ + { OSSL_FUNC_SIGNATURE_VERIFY_INIT, \ + (void (*)(void))ed448_signverify_init }, \ + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT, \ + (void (*)(void))ed448_digest_signverify_init }, \ + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN, \ + (void (*)(void))ed448_digest_sign }, \ + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT, \ + (void (*)(void))ed448_digest_signverify_init }, \ + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY, \ + (void (*)(void))ed448_digest_verify }, \ + { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, \ + (void (*)(void))eddsa_get_ctx_params }, \ + { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, \ + (void (*)(void))eddsa_gettable_ctx_params }, \ + { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, \ + (void (*)(void))eddsa_set_ctx_params }, \ + { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, \ + (void (*)(void))eddsa_settable_ctx_params }, \ + OSSL_DISPATCH_END + +#define ed448ph_DISPATCH_END \ + { OSSL_FUNC_SIGNATURE_SIGN_INIT, \ + (void (*)(void))ed448ph_signverify_init }, \ + { OSSL_FUNC_SIGNATURE_VERIFY_INIT, \ + (void (*)(void))ed448ph_signverify_init }, \ + eddsa_variant_DISPATCH_END(ed448ph) + +/* vn = variant name, bn = base name */ +#define IMPL_EDDSA_DISPATCH(vn,bn) \ + const OSSL_DISPATCH ossl_##vn##_signature_functions[] = { \ + { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))eddsa_newctx }, \ + { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_INIT, \ + (void (*)(void))vn##_signverify_message_init }, \ + { OSSL_FUNC_SIGNATURE_SIGN, \ + (void (*)(void))bn##_sign }, \ + { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_INIT, \ + (void (*)(void))vn##_signverify_message_init }, \ + { OSSL_FUNC_SIGNATURE_VERIFY, \ + (void (*)(void))bn##_verify }, \ + { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))eddsa_freectx }, \ + { OSSL_FUNC_SIGNATURE_DUPCTX, (void (*)(void))eddsa_dupctx }, \ + { OSSL_FUNC_SIGNATURE_QUERY_KEY_TYPES, \ + (void (*)(void))bn##_sigalg_query_key_types }, \ + vn##_DISPATCH_END \ + } + +IMPL_EDDSA_DISPATCH(ed25519,ed25519); +IMPL_EDDSA_DISPATCH(ed25519ph,ed25519); +IMPL_EDDSA_DISPATCH(ed25519ctx,ed25519); +IMPL_EDDSA_DISPATCH(ed448,ed448); +IMPL_EDDSA_DISPATCH(ed448ph,ed448); + +#ifdef S390X_EC_ASM + +static int s390x_ed25519_digestsign(const ECX_KEY *edkey, unsigned char *sig, + const unsigned char *tbs, size_t tbslen) +{ + int rc; + union { + struct { + unsigned char sig[64]; + unsigned char priv[32]; + } ed25519; + unsigned long long buff[512]; + } param; + + memset(¶m, 0, sizeof(param)); + memcpy(param.ed25519.priv, edkey->privkey, sizeof(param.ed25519.priv)); + + rc = s390x_kdsa(S390X_EDDSA_SIGN_ED25519, ¶m.ed25519, tbs, tbslen); + OPENSSL_cleanse(param.ed25519.priv, sizeof(param.ed25519.priv)); + if (rc != 0) + return 0; + + s390x_flip_endian32(sig, param.ed25519.sig); + s390x_flip_endian32(sig + 32, param.ed25519.sig + 32); + return 1; +} + +static int s390x_ed448_digestsign(const ECX_KEY *edkey, unsigned char *sig, + const unsigned char *tbs, size_t tbslen) +{ + int rc; + union { + struct { + unsigned char sig[128]; + unsigned char priv[64]; + } ed448; + unsigned long long buff[512]; + } param; + + memset(¶m, 0, sizeof(param)); + memcpy(param.ed448.priv + 64 - 57, edkey->privkey, 57); + + rc = s390x_kdsa(S390X_EDDSA_SIGN_ED448, ¶m.ed448, tbs, tbslen); + OPENSSL_cleanse(param.ed448.priv, sizeof(param.ed448.priv)); + if (rc != 0) + return 0; + + s390x_flip_endian64(param.ed448.sig, param.ed448.sig); + s390x_flip_endian64(param.ed448.sig + 64, param.ed448.sig + 64); + memcpy(sig, param.ed448.sig, 57); + memcpy(sig + 57, param.ed448.sig + 64, 57); + return 1; +} + +static int s390x_ed25519_digestverify(const ECX_KEY *edkey, + const unsigned char *sig, + const unsigned char *tbs, size_t tbslen) +{ + union { + struct { + unsigned char sig[64]; + unsigned char pub[32]; + } ed25519; + unsigned long long buff[512]; + } param; + + memset(¶m, 0, sizeof(param)); + s390x_flip_endian32(param.ed25519.sig, sig); + s390x_flip_endian32(param.ed25519.sig + 32, sig + 32); + s390x_flip_endian32(param.ed25519.pub, edkey->pubkey); + + return s390x_kdsa(S390X_EDDSA_VERIFY_ED25519, + ¶m.ed25519, tbs, tbslen) == 0 ? 1 : 0; +} + +static int s390x_ed448_digestverify(const ECX_KEY *edkey, + const unsigned char *sig, + const unsigned char *tbs, + size_t tbslen) +{ + union { + struct { + unsigned char sig[128]; + unsigned char pub[64]; + } ed448; + unsigned long long buff[512]; + } param; + + memset(¶m, 0, sizeof(param)); + memcpy(param.ed448.sig, sig, 57); + s390x_flip_endian64(param.ed448.sig, param.ed448.sig); + memcpy(param.ed448.sig + 64, sig + 57, 57); + s390x_flip_endian64(param.ed448.sig + 64, param.ed448.sig + 64); + memcpy(param.ed448.pub, edkey->pubkey, 57); + s390x_flip_endian64(param.ed448.pub, param.ed448.pub); + + return s390x_kdsa(S390X_EDDSA_VERIFY_ED448, + ¶m.ed448, tbs, tbslen) == 0 ? 1 : 0; +} + +#endif /* S390X_EC_ASM */ diff --git a/crypto/openssl/providers/implementations/signature/mac_legacy_sig.c b/crypto/openssl/providers/implementations/signature/mac_legacy_sig.c new file mode 100644 index 000000000000..b25a74506ab0 --- /dev/null +++ b/crypto/openssl/providers/implementations/signature/mac_legacy_sig.c @@ -0,0 +1,265 @@ +/* + * Copyright 2019-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 + */ + +/* We need to use some engine deprecated APIs */ +#define OPENSSL_SUPPRESS_DEPRECATED + +#include <openssl/crypto.h> +#include <openssl/evp.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/params.h> +#include <openssl/err.h> +#include <openssl/proverr.h> +#ifndef FIPS_MODULE +# include <openssl/engine.h> +#endif +#include "prov/implementations.h" +#include "prov/provider_ctx.h" +#include "prov/macsignature.h" +#include "prov/providercommon.h" + +static OSSL_FUNC_signature_newctx_fn mac_hmac_newctx; +static OSSL_FUNC_signature_newctx_fn mac_siphash_newctx; +static OSSL_FUNC_signature_newctx_fn mac_poly1305_newctx; +static OSSL_FUNC_signature_newctx_fn mac_cmac_newctx; +static OSSL_FUNC_signature_digest_sign_init_fn mac_digest_sign_init; +static OSSL_FUNC_signature_digest_sign_update_fn mac_digest_sign_update; +static OSSL_FUNC_signature_digest_sign_final_fn mac_digest_sign_final; +static OSSL_FUNC_signature_freectx_fn mac_freectx; +static OSSL_FUNC_signature_dupctx_fn mac_dupctx; +static OSSL_FUNC_signature_set_ctx_params_fn mac_set_ctx_params; +static OSSL_FUNC_signature_settable_ctx_params_fn mac_hmac_settable_ctx_params; +static OSSL_FUNC_signature_settable_ctx_params_fn mac_siphash_settable_ctx_params; +static OSSL_FUNC_signature_settable_ctx_params_fn mac_poly1305_settable_ctx_params; +static OSSL_FUNC_signature_settable_ctx_params_fn mac_cmac_settable_ctx_params; + +typedef struct { + OSSL_LIB_CTX *libctx; + char *propq; + MAC_KEY *key; + EVP_MAC_CTX *macctx; +} PROV_MAC_CTX; + +static void *mac_newctx(void *provctx, const char *propq, const char *macname) +{ + PROV_MAC_CTX *pmacctx; + EVP_MAC *mac = NULL; + + if (!ossl_prov_is_running()) + return NULL; + + pmacctx = OPENSSL_zalloc(sizeof(PROV_MAC_CTX)); + if (pmacctx == NULL) + return NULL; + + pmacctx->libctx = PROV_LIBCTX_OF(provctx); + if (propq != NULL && (pmacctx->propq = OPENSSL_strdup(propq)) == NULL) + goto err; + + mac = EVP_MAC_fetch(pmacctx->libctx, macname, propq); + if (mac == NULL) + goto err; + + pmacctx->macctx = EVP_MAC_CTX_new(mac); + if (pmacctx->macctx == NULL) + goto err; + + EVP_MAC_free(mac); + + return pmacctx; + + err: + OPENSSL_free(pmacctx->propq); + OPENSSL_free(pmacctx); + EVP_MAC_free(mac); + return NULL; +} + +#define MAC_NEWCTX(funcname, macname) \ + static void *mac_##funcname##_newctx(void *provctx, const char *propq) \ + { \ + return mac_newctx(provctx, propq, macname); \ + } + +MAC_NEWCTX(hmac, "HMAC") +MAC_NEWCTX(siphash, "SIPHASH") +MAC_NEWCTX(poly1305, "POLY1305") +MAC_NEWCTX(cmac, "CMAC") + +static int mac_digest_sign_init(void *vpmacctx, const char *mdname, void *vkey, + const OSSL_PARAM params[]) +{ + PROV_MAC_CTX *pmacctx = (PROV_MAC_CTX *)vpmacctx; + const char *ciphername = NULL, *engine = NULL; + + if (!ossl_prov_is_running() + || pmacctx == NULL) + return 0; + + if (pmacctx->key == NULL && vkey == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET); + return 0; + } + + if (vkey != NULL) { + if (!ossl_mac_key_up_ref(vkey)) + return 0; + ossl_mac_key_free(pmacctx->key); + pmacctx->key = vkey; + } + + if (pmacctx->key->cipher.cipher != NULL) + ciphername = (char *)EVP_CIPHER_get0_name(pmacctx->key->cipher.cipher); +#if !defined(OPENSSL_NO_ENGINE) && !defined(FIPS_MODULE) + if (pmacctx->key->cipher.engine != NULL) + engine = (char *)ENGINE_get_id(pmacctx->key->cipher.engine); +#endif + + if (!ossl_prov_set_macctx(pmacctx->macctx, NULL, + (char *)ciphername, + (char *)mdname, + (char *)engine, + pmacctx->key->properties, + NULL, 0)) + return 0; + + if (!EVP_MAC_init(pmacctx->macctx, pmacctx->key->priv_key, + pmacctx->key->priv_key_len, params)) + return 0; + + return 1; +} + +int mac_digest_sign_update(void *vpmacctx, const unsigned char *data, + size_t datalen) +{ + PROV_MAC_CTX *pmacctx = (PROV_MAC_CTX *)vpmacctx; + + if (pmacctx == NULL || pmacctx->macctx == NULL) + return 0; + + return EVP_MAC_update(pmacctx->macctx, data, datalen); +} + +int mac_digest_sign_final(void *vpmacctx, unsigned char *mac, size_t *maclen, + size_t macsize) +{ + PROV_MAC_CTX *pmacctx = (PROV_MAC_CTX *)vpmacctx; + + if (!ossl_prov_is_running() || pmacctx == NULL || pmacctx->macctx == NULL) + return 0; + + return EVP_MAC_final(pmacctx->macctx, mac, maclen, macsize); +} + +static void mac_freectx(void *vpmacctx) +{ + PROV_MAC_CTX *ctx = (PROV_MAC_CTX *)vpmacctx; + + OPENSSL_free(ctx->propq); + EVP_MAC_CTX_free(ctx->macctx); + ossl_mac_key_free(ctx->key); + OPENSSL_free(ctx); +} + +static void *mac_dupctx(void *vpmacctx) +{ + PROV_MAC_CTX *srcctx = (PROV_MAC_CTX *)vpmacctx; + PROV_MAC_CTX *dstctx; + + if (!ossl_prov_is_running()) + return NULL; + + dstctx = OPENSSL_zalloc(sizeof(*srcctx)); + if (dstctx == NULL) + return NULL; + + *dstctx = *srcctx; + dstctx->propq = NULL; + dstctx->key = NULL; + dstctx->macctx = NULL; + + if (srcctx->propq != NULL && (dstctx->propq = OPENSSL_strdup(srcctx->propq)) == NULL) + goto err; + + if (srcctx->key != NULL && !ossl_mac_key_up_ref(srcctx->key)) + goto err; + dstctx->key = srcctx->key; + + if (srcctx->macctx != NULL) { + dstctx->macctx = EVP_MAC_CTX_dup(srcctx->macctx); + if (dstctx->macctx == NULL) + goto err; + } + + return dstctx; + err: + mac_freectx(dstctx); + return NULL; +} + +static int mac_set_ctx_params(void *vpmacctx, const OSSL_PARAM params[]) +{ + PROV_MAC_CTX *ctx = (PROV_MAC_CTX *)vpmacctx; + + return EVP_MAC_CTX_set_params(ctx->macctx, params); +} + +static const OSSL_PARAM *mac_settable_ctx_params(ossl_unused void *ctx, + void *provctx, + const char *macname) +{ + EVP_MAC *mac = EVP_MAC_fetch(PROV_LIBCTX_OF(provctx), macname, + NULL); + const OSSL_PARAM *params; + + if (mac == NULL) + return NULL; + + params = EVP_MAC_settable_ctx_params(mac); + EVP_MAC_free(mac); + + return params; +} + +#define MAC_SETTABLE_CTX_PARAMS(funcname, macname) \ + static const OSSL_PARAM *mac_##funcname##_settable_ctx_params(void *ctx, \ + void *provctx) \ + { \ + return mac_settable_ctx_params(ctx, provctx, macname); \ + } + +MAC_SETTABLE_CTX_PARAMS(hmac, "HMAC") +MAC_SETTABLE_CTX_PARAMS(siphash, "SIPHASH") +MAC_SETTABLE_CTX_PARAMS(poly1305, "POLY1305") +MAC_SETTABLE_CTX_PARAMS(cmac, "CMAC") + +#define MAC_SIGNATURE_FUNCTIONS(funcname) \ + const OSSL_DISPATCH ossl_mac_legacy_##funcname##_signature_functions[] = { \ + { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))mac_##funcname##_newctx }, \ + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT, \ + (void (*)(void))mac_digest_sign_init }, \ + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_UPDATE, \ + (void (*)(void))mac_digest_sign_update }, \ + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_FINAL, \ + (void (*)(void))mac_digest_sign_final }, \ + { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))mac_freectx }, \ + { OSSL_FUNC_SIGNATURE_DUPCTX, (void (*)(void))mac_dupctx }, \ + { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, \ + (void (*)(void))mac_set_ctx_params }, \ + { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, \ + (void (*)(void))mac_##funcname##_settable_ctx_params }, \ + OSSL_DISPATCH_END \ + }; + +MAC_SIGNATURE_FUNCTIONS(hmac) +MAC_SIGNATURE_FUNCTIONS(siphash) +MAC_SIGNATURE_FUNCTIONS(poly1305) +MAC_SIGNATURE_FUNCTIONS(cmac) diff --git a/crypto/openssl/providers/implementations/signature/ml_dsa_sig.c b/crypto/openssl/providers/implementations/signature/ml_dsa_sig.c new file mode 100644 index 000000000000..e235e31752eb --- /dev/null +++ b/crypto/openssl/providers/implementations/signature/ml_dsa_sig.c @@ -0,0 +1,368 @@ +/* + * Copyright 2024-2025 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 + */ + +#include "internal/deprecated.h" + +#include <assert.h> +#include <string.h> /* memset */ +#include <openssl/core_names.h> +#include <openssl/err.h> +#include <openssl/rand.h> +#include <openssl/proverr.h> +#include "prov/implementations.h" +#include "prov/providercommon.h" +#include "prov/provider_ctx.h" +#include "prov/der_ml_dsa.h" +#include "crypto/ml_dsa.h" +#include "internal/packet.h" +#include "internal/sizes.h" + +#define ML_DSA_MESSAGE_ENCODE_RAW 0 +#define ML_DSA_MESSAGE_ENCODE_PURE 1 + +static OSSL_FUNC_signature_sign_message_init_fn ml_dsa_sign_msg_init; +static OSSL_FUNC_signature_sign_fn ml_dsa_sign; +static OSSL_FUNC_signature_verify_message_init_fn ml_dsa_verify_msg_init; +static OSSL_FUNC_signature_verify_fn ml_dsa_verify; +static OSSL_FUNC_signature_digest_sign_init_fn ml_dsa_digest_signverify_init; +static OSSL_FUNC_signature_digest_sign_fn ml_dsa_digest_sign; +static OSSL_FUNC_signature_digest_verify_fn ml_dsa_digest_verify; +static OSSL_FUNC_signature_freectx_fn ml_dsa_freectx; +static OSSL_FUNC_signature_set_ctx_params_fn ml_dsa_set_ctx_params; +static OSSL_FUNC_signature_settable_ctx_params_fn ml_dsa_settable_ctx_params; +static OSSL_FUNC_signature_get_ctx_params_fn ml_dsa_get_ctx_params; +static OSSL_FUNC_signature_gettable_ctx_params_fn ml_dsa_gettable_ctx_params; +static OSSL_FUNC_signature_dupctx_fn ml_dsa_dupctx; + +typedef struct { + ML_DSA_KEY *key; + OSSL_LIB_CTX *libctx; + uint8_t context_string[ML_DSA_MAX_CONTEXT_STRING_LEN]; + size_t context_string_len; + uint8_t test_entropy[ML_DSA_ENTROPY_LEN]; + size_t test_entropy_len; + int msg_encode; + int deterministic; + int evp_type; + /* The Algorithm Identifier of the signature algorithm */ + uint8_t aid_buf[OSSL_MAX_ALGORITHM_ID_SIZE]; + size_t aid_len; + int mu; /* Flag indicating we should begin from \mu, not the message */ +} PROV_ML_DSA_CTX; + +static void ml_dsa_freectx(void *vctx) +{ + PROV_ML_DSA_CTX *ctx = (PROV_ML_DSA_CTX *)vctx; + + OPENSSL_cleanse(ctx->test_entropy, ctx->test_entropy_len); + OPENSSL_free(ctx); +} + +static void *ml_dsa_newctx(void *provctx, int evp_type, const char *propq) +{ + PROV_ML_DSA_CTX *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + ctx = OPENSSL_zalloc(sizeof(PROV_ML_DSA_CTX)); + if (ctx == NULL) + return NULL; + + ctx->libctx = PROV_LIBCTX_OF(provctx); + ctx->msg_encode = ML_DSA_MESSAGE_ENCODE_PURE; + ctx->evp_type = evp_type; + return ctx; +} + +static void *ml_dsa_dupctx(void *vctx) +{ + PROV_ML_DSA_CTX *srcctx = (PROV_ML_DSA_CTX *)vctx; + + if (!ossl_prov_is_running()) + return NULL; + + /* + * Note that the ML_DSA_KEY is ref counted via EVP_PKEY so we can just copy + * the key here. + */ + return OPENSSL_memdup(srcctx, sizeof(*srcctx)); +} + +static int set_alg_id_buffer(PROV_ML_DSA_CTX *ctx) +{ + int ret; + WPACKET pkt; + uint8_t *aid = NULL; + + /* + * We do not care about DER writing errors. + * All it really means is that for some reason, there's no + * AlgorithmIdentifier to be had, but the operation itself is + * still valid, just as long as it's not used to construct + * anything that needs an AlgorithmIdentifier. + */ + ctx->aid_len = 0; + ret = WPACKET_init_der(&pkt, ctx->aid_buf, sizeof(ctx->aid_buf)); + ret = ret && ossl_DER_w_algorithmIdentifier_ML_DSA(&pkt, -1, ctx->key); + if (ret && WPACKET_finish(&pkt)) { + WPACKET_get_total_written(&pkt, &ctx->aid_len); + aid = WPACKET_get_curr(&pkt); + } + WPACKET_cleanup(&pkt); + if (aid != NULL && ctx->aid_len != 0) + memmove(ctx->aid_buf, aid, ctx->aid_len); + return 1; +} + +static int ml_dsa_signverify_msg_init(void *vctx, void *vkey, + const OSSL_PARAM params[], int operation, + const char *desc) +{ + PROV_ML_DSA_CTX *ctx = (PROV_ML_DSA_CTX *)vctx; + ML_DSA_KEY *key = vkey; + + if (!ossl_prov_is_running() + || ctx == NULL) + return 0; + + if (vkey == NULL && ctx->key == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET); + return 0; + } + + if (key != NULL) + ctx->key = vkey; + if (!ossl_ml_dsa_key_matches(ctx->key, ctx->evp_type)) + return 0; + + set_alg_id_buffer(ctx); + ctx->mu = 0; + + return ml_dsa_set_ctx_params(ctx, params); +} + +static int ml_dsa_sign_msg_init(void *vctx, void *vkey, const OSSL_PARAM params[]) +{ + return ml_dsa_signverify_msg_init(vctx, vkey, params, + EVP_PKEY_OP_SIGN, "ML_DSA Sign Init"); +} + +static int ml_dsa_digest_signverify_init(void *vctx, const char *mdname, + void *vkey, const OSSL_PARAM params[]) +{ + PROV_ML_DSA_CTX *ctx = (PROV_ML_DSA_CTX *)vctx; + + if (mdname != NULL && mdname[0] != '\0') { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST, + "Explicit digest not supported for ML-DSA operations"); + return 0; + } + + ctx->mu = 0; + + if (vkey == NULL && ctx->key != NULL) + return ml_dsa_set_ctx_params(ctx, params); + + return ml_dsa_signverify_msg_init(vctx, vkey, params, + EVP_PKEY_OP_SIGN, "ML_DSA Sign Init"); +} + +static int ml_dsa_sign(void *vctx, uint8_t *sig, size_t *siglen, size_t sigsize, + const uint8_t *msg, size_t msg_len) +{ + int ret = 0; + PROV_ML_DSA_CTX *ctx = (PROV_ML_DSA_CTX *)vctx; + uint8_t rand_tmp[ML_DSA_ENTROPY_LEN], *rnd = NULL; + + if (!ossl_prov_is_running()) + return 0; + + if (sig != NULL) { + if (ctx->test_entropy_len != 0) { + rnd = ctx->test_entropy; + } else { + rnd = rand_tmp; + + if (ctx->deterministic == 1) + memset(rnd, 0, sizeof(rand_tmp)); + else if (RAND_priv_bytes_ex(ctx->libctx, rnd, sizeof(rand_tmp), 0) <= 0) + return 0; + } + } + ret = ossl_ml_dsa_sign(ctx->key, ctx->mu, msg, msg_len, + ctx->context_string, ctx->context_string_len, + rnd, sizeof(rand_tmp), ctx->msg_encode, + sig, siglen, sigsize); + if (rnd != ctx->test_entropy) + OPENSSL_cleanse(rand_tmp, sizeof(rand_tmp)); + return ret; +} + +static int ml_dsa_digest_sign(void *vctx, uint8_t *sig, size_t *siglen, size_t sigsize, + const uint8_t *tbs, size_t tbslen) +{ + return ml_dsa_sign(vctx, sig, siglen, sigsize, tbs, tbslen); +} + +static int ml_dsa_verify_msg_init(void *vctx, void *vkey, const OSSL_PARAM params[]) +{ + return ml_dsa_signverify_msg_init(vctx, vkey, params, EVP_PKEY_OP_VERIFY, + "ML_DSA Verify Init"); +} + +static int ml_dsa_verify(void *vctx, const uint8_t *sig, size_t siglen, + const uint8_t *msg, size_t msg_len) +{ + PROV_ML_DSA_CTX *ctx = (PROV_ML_DSA_CTX *)vctx; + + if (!ossl_prov_is_running()) + return 0; + return ossl_ml_dsa_verify(ctx->key, ctx->mu, msg, msg_len, + ctx->context_string, ctx->context_string_len, + ctx->msg_encode, sig, siglen); +} +static int ml_dsa_digest_verify(void *vctx, + const uint8_t *sig, size_t siglen, + const uint8_t *tbs, size_t tbslen) +{ + return ml_dsa_verify(vctx, sig, siglen, tbs, tbslen); +} + +static int ml_dsa_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_ML_DSA_CTX *pctx = (PROV_ML_DSA_CTX *)vctx; + const OSSL_PARAM *p; + + if (pctx == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_CONTEXT_STRING); + if (p != NULL) { + void *vp = pctx->context_string; + + if (!OSSL_PARAM_get_octet_string(p, &vp, sizeof(pctx->context_string), + &(pctx->context_string_len))) { + pctx->context_string_len = 0; + return 0; + } + } + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_TEST_ENTROPY); + if (p != NULL) { + void *vp = pctx->test_entropy; + + pctx->test_entropy_len = 0; + if (!OSSL_PARAM_get_octet_string(p, &vp, sizeof(pctx->test_entropy), + &(pctx->test_entropy_len))) + return 0; + if (pctx->test_entropy_len != sizeof(pctx->test_entropy)) { + pctx->test_entropy_len = 0; + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SEED_LENGTH); + return 0; + } + } + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_DETERMINISTIC); + if (p != NULL && !OSSL_PARAM_get_int(p, &pctx->deterministic)) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_MESSAGE_ENCODING); + if (p != NULL && !OSSL_PARAM_get_int(p, &pctx->msg_encode)) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_MU); + if (p != NULL && !OSSL_PARAM_get_int(p, &pctx->mu)) + return 0; + + return 1; +} + +static const OSSL_PARAM *ml_dsa_settable_ctx_params(void *vctx, + ossl_unused void *provctx) +{ + static const OSSL_PARAM settable_ctx_params[] = { + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, NULL, 0), + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_TEST_ENTROPY, NULL, 0), + OSSL_PARAM_int(OSSL_SIGNATURE_PARAM_DETERMINISTIC, 0), + OSSL_PARAM_int(OSSL_SIGNATURE_PARAM_MU, 0), + OSSL_PARAM_int(OSSL_SIGNATURE_PARAM_MESSAGE_ENCODING, 0), + OSSL_PARAM_END + }; + + return settable_ctx_params; +} + +static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_ALGORITHM_ID, NULL, 0), + OSSL_PARAM_END +}; + +static const OSSL_PARAM *ml_dsa_gettable_ctx_params(ossl_unused void *vctx, + ossl_unused void *provctx) +{ + return known_gettable_ctx_params; +} + +static int ml_dsa_get_ctx_params(void *vctx, OSSL_PARAM *params) +{ + PROV_ML_DSA_CTX *ctx = (PROV_ML_DSA_CTX *)vctx; + OSSL_PARAM *p; + + if (ctx == NULL) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_ALGORITHM_ID); + if (p != NULL + && !OSSL_PARAM_set_octet_string(p, + ctx->aid_len == 0 ? NULL : ctx->aid_buf, + ctx->aid_len)) + return 0; + + return 1; +} + +#define MAKE_SIGNATURE_FUNCTIONS(alg) \ + static OSSL_FUNC_signature_newctx_fn ml_dsa_##alg##_newctx; \ + static void *ml_dsa_##alg##_newctx(void *provctx, const char *propq) \ + { \ + return ml_dsa_newctx(provctx, EVP_PKEY_ML_DSA_##alg, propq); \ + } \ + const OSSL_DISPATCH ossl_ml_dsa_##alg##_signature_functions[] = { \ + { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))ml_dsa_##alg##_newctx }, \ + { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_INIT, \ + (void (*)(void))ml_dsa_sign_msg_init }, \ + { OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))ml_dsa_sign }, \ + { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_INIT, \ + (void (*)(void))ml_dsa_verify_msg_init }, \ + { OSSL_FUNC_SIGNATURE_VERIFY, (void (*)(void))ml_dsa_verify }, \ + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT, \ + (void (*)(void))ml_dsa_digest_signverify_init }, \ + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN, \ + (void (*)(void))ml_dsa_digest_sign }, \ + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT, \ + (void (*)(void))ml_dsa_digest_signverify_init }, \ + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY, \ + (void (*)(void))ml_dsa_digest_verify }, \ + { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))ml_dsa_freectx }, \ + { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, \ + (void (*)(void))ml_dsa_set_ctx_params }, \ + { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, \ + (void (*)(void))ml_dsa_settable_ctx_params }, \ + { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, \ + (void (*)(void))ml_dsa_get_ctx_params }, \ + { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, \ + (void (*)(void))ml_dsa_gettable_ctx_params }, \ + { OSSL_FUNC_SIGNATURE_DUPCTX, (void (*)(void))ml_dsa_dupctx }, \ + OSSL_DISPATCH_END \ + } + +MAKE_SIGNATURE_FUNCTIONS(44); +MAKE_SIGNATURE_FUNCTIONS(65); +MAKE_SIGNATURE_FUNCTIONS(87); diff --git a/crypto/openssl/providers/implementations/signature/rsa_sig.c b/crypto/openssl/providers/implementations/signature/rsa_sig.c new file mode 100644 index 000000000000..d8357cfe1578 --- /dev/null +++ b/crypto/openssl/providers/implementations/signature/rsa_sig.c @@ -0,0 +1,2144 @@ +/* + * Copyright 2019-2025 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 + */ + +/* + * RSA low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include <string.h> +#include <openssl/crypto.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/err.h> +#include <openssl/obj_mac.h> +#include <openssl/rsa.h> +#include <openssl/params.h> +#include <openssl/evp.h> +#include <openssl/proverr.h> +#include "internal/cryptlib.h" +#include "internal/nelem.h" +#include "internal/sizes.h" +#include "crypto/rsa.h" +#include "prov/providercommon.h" +#include "prov/implementations.h" +#include "prov/provider_ctx.h" +#include "prov/der_rsa.h" +#include "prov/securitycheck.h" + +#define RSA_DEFAULT_DIGEST_NAME OSSL_DIGEST_NAME_SHA1 + +static OSSL_FUNC_signature_newctx_fn rsa_newctx; +static OSSL_FUNC_signature_sign_init_fn rsa_sign_init; +static OSSL_FUNC_signature_verify_init_fn rsa_verify_init; +static OSSL_FUNC_signature_verify_recover_init_fn rsa_verify_recover_init; +static OSSL_FUNC_signature_sign_fn rsa_sign; +static OSSL_FUNC_signature_sign_message_update_fn rsa_signverify_message_update; +static OSSL_FUNC_signature_sign_message_final_fn rsa_sign_message_final; +static OSSL_FUNC_signature_verify_fn rsa_verify; +static OSSL_FUNC_signature_verify_recover_fn rsa_verify_recover; +static OSSL_FUNC_signature_verify_message_update_fn rsa_signverify_message_update; +static OSSL_FUNC_signature_verify_message_final_fn rsa_verify_message_final; +static OSSL_FUNC_signature_digest_sign_init_fn rsa_digest_sign_init; +static OSSL_FUNC_signature_digest_sign_update_fn rsa_digest_sign_update; +static OSSL_FUNC_signature_digest_sign_final_fn rsa_digest_sign_final; +static OSSL_FUNC_signature_digest_verify_init_fn rsa_digest_verify_init; +static OSSL_FUNC_signature_digest_verify_update_fn rsa_digest_verify_update; +static OSSL_FUNC_signature_digest_verify_final_fn rsa_digest_verify_final; +static OSSL_FUNC_signature_freectx_fn rsa_freectx; +static OSSL_FUNC_signature_dupctx_fn rsa_dupctx; +static OSSL_FUNC_signature_query_key_types_fn rsa_sigalg_query_key_types; +static OSSL_FUNC_signature_get_ctx_params_fn rsa_get_ctx_params; +static OSSL_FUNC_signature_gettable_ctx_params_fn rsa_gettable_ctx_params; +static OSSL_FUNC_signature_set_ctx_params_fn rsa_set_ctx_params; +static OSSL_FUNC_signature_settable_ctx_params_fn rsa_settable_ctx_params; +static OSSL_FUNC_signature_get_ctx_md_params_fn rsa_get_ctx_md_params; +static OSSL_FUNC_signature_gettable_ctx_md_params_fn rsa_gettable_ctx_md_params; +static OSSL_FUNC_signature_set_ctx_md_params_fn rsa_set_ctx_md_params; +static OSSL_FUNC_signature_settable_ctx_md_params_fn rsa_settable_ctx_md_params; +static OSSL_FUNC_signature_set_ctx_params_fn rsa_sigalg_set_ctx_params; +static OSSL_FUNC_signature_settable_ctx_params_fn rsa_sigalg_settable_ctx_params; + +static OSSL_ITEM padding_item[] = { + { RSA_PKCS1_PADDING, OSSL_PKEY_RSA_PAD_MODE_PKCSV15 }, + { RSA_NO_PADDING, OSSL_PKEY_RSA_PAD_MODE_NONE }, + { RSA_X931_PADDING, OSSL_PKEY_RSA_PAD_MODE_X931 }, + { RSA_PKCS1_PSS_PADDING, OSSL_PKEY_RSA_PAD_MODE_PSS }, + { 0, NULL } +}; + +/* + * What's passed as an actual key is defined by the KEYMGMT interface. + * We happen to know that our KEYMGMT simply passes RSA structures, so + * we use that here too. + */ + +typedef struct { + OSSL_LIB_CTX *libctx; + char *propq; + RSA *rsa; + int operation; + + /* + * Flag to determine if a full sigalg is run (1) or if a composable + * signature algorithm is run (0). + * + * When a full sigalg is run (1), this currently affects the following + * other flags, which are to remain untouched after their initialization: + * + * - flag_allow_md (initialized to 0) + */ + unsigned int flag_sigalg : 1; + /* + * Flag to determine if the hash function can be changed (1) or not (0) + * Because it's dangerous to change during a DigestSign or DigestVerify + * operation, this flag is cleared by their Init function, and set again + * by their Final function. + * Implementations of full sigalgs (such as RSA-SHA256) hard-code this + * flag to not allow changes (0). + */ + unsigned int flag_allow_md : 1; + unsigned int mgf1_md_set : 1; + /* + * Flags to say what are the possible next external calls in what + * consitutes the life cycle of an algorithm. The relevant calls are: + * - init + * - update + * - final + * - oneshot + * All other external calls are regarded as utilitarian and are allowed + * at any time (they may be affected by other flags, like flag_allow_md, + * though). + */ + unsigned int flag_allow_update : 1; + unsigned int flag_allow_final : 1; + unsigned int flag_allow_oneshot : 1; + + /* main digest */ + EVP_MD *md; + EVP_MD_CTX *mdctx; + int mdnid; + char mdname[OSSL_MAX_NAME_SIZE]; /* Purely informational */ + + /* RSA padding mode */ + int pad_mode; + /* message digest for MGF1 */ + EVP_MD *mgf1_md; + int mgf1_mdnid; + char mgf1_mdname[OSSL_MAX_NAME_SIZE]; /* Purely informational */ + /* PSS salt length */ + int saltlen; + /* Minimum salt length or -1 if no PSS parameter restriction */ + int min_saltlen; + + /* Signature, for verification */ + unsigned char *sig; + size_t siglen; + +#ifdef FIPS_MODULE + /* + * FIPS 140-3 IG 2.4.B mandates that verification based on a digest of a + * message is not permitted. However, signing based on a digest is still + * permitted. + */ + int verify_message; +#endif + + /* Temp buffer */ + unsigned char *tbuf; + + OSSL_FIPS_IND_DECLARE +} PROV_RSA_CTX; + +/* True if PSS parameters are restricted */ +#define rsa_pss_restricted(prsactx) (prsactx->min_saltlen != -1) + +static size_t rsa_get_md_size(const PROV_RSA_CTX *prsactx) +{ + int md_size; + + if (prsactx->md != NULL) { + md_size = EVP_MD_get_size(prsactx->md); + if (md_size <= 0) + return 0; + return md_size; + } + return 0; +} + +static int rsa_check_padding(const PROV_RSA_CTX *prsactx, + const char *mdname, const char *mgf1_mdname, + int mdnid) +{ + switch (prsactx->pad_mode) { + case RSA_NO_PADDING: + if (mdname != NULL || mdnid != NID_undef) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_PADDING_MODE); + return 0; + } + break; + case RSA_X931_PADDING: + if (RSA_X931_hash_id(mdnid) == -1) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_X931_DIGEST); + return 0; + } + break; + case RSA_PKCS1_PSS_PADDING: + if (rsa_pss_restricted(prsactx)) + if ((mdname != NULL && !EVP_MD_is_a(prsactx->md, mdname)) + || (mgf1_mdname != NULL + && !EVP_MD_is_a(prsactx->mgf1_md, mgf1_mdname))) { + ERR_raise(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED); + return 0; + } + break; + default: + break; + } + + return 1; +} + +static int rsa_check_parameters(PROV_RSA_CTX *prsactx, int min_saltlen) +{ + if (prsactx->pad_mode == RSA_PKCS1_PSS_PADDING) { + int max_saltlen; + + /* See if minimum salt length exceeds maximum possible */ + max_saltlen = RSA_size(prsactx->rsa) - EVP_MD_get_size(prsactx->md); + if ((RSA_bits(prsactx->rsa) & 0x7) == 1) + max_saltlen--; + if (min_saltlen < 0 || min_saltlen > max_saltlen) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SALT_LENGTH); + return 0; + } + prsactx->min_saltlen = min_saltlen; + } + return 1; +} + +static void *rsa_newctx(void *provctx, const char *propq) +{ + PROV_RSA_CTX *prsactx = NULL; + char *propq_copy = NULL; + + if (!ossl_prov_is_running()) + return NULL; + + if ((prsactx = OPENSSL_zalloc(sizeof(PROV_RSA_CTX))) == NULL + || (propq != NULL + && (propq_copy = OPENSSL_strdup(propq)) == NULL)) { + OPENSSL_free(prsactx); + return NULL; + } + + OSSL_FIPS_IND_INIT(prsactx) + prsactx->libctx = PROV_LIBCTX_OF(provctx); + prsactx->flag_allow_md = 1; +#ifdef FIPS_MODULE + prsactx->verify_message = 1; +#endif + prsactx->propq = propq_copy; + /* Maximum up to digest length for sign, auto for verify */ + prsactx->saltlen = RSA_PSS_SALTLEN_AUTO_DIGEST_MAX; + prsactx->min_saltlen = -1; + return prsactx; +} + +static int rsa_pss_compute_saltlen(PROV_RSA_CTX *ctx) +{ + int saltlen = ctx->saltlen; + int saltlenMax = -1; + + /* FIPS 186-4 section 5 "The RSA Digital Signature Algorithm", subsection + * 5.5 "PKCS #1" says: "For RSASSA-PSS […] the length (in bytes) of the + * salt (sLen) shall satisfy 0 <= sLen <= hLen, where hLen is the length of + * the hash function output block (in bytes)." + * + * Provide a way to use at most the digest length, so that the default does + * not violate FIPS 186-4. */ + if (saltlen == RSA_PSS_SALTLEN_DIGEST) { + if ((saltlen = EVP_MD_get_size(ctx->md)) <= 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST); + return -1; + } + } else if (saltlen == RSA_PSS_SALTLEN_AUTO_DIGEST_MAX) { + saltlen = RSA_PSS_SALTLEN_MAX; + if ((saltlenMax = EVP_MD_get_size(ctx->md)) <= 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST); + return -1; + } + } + if (saltlen == RSA_PSS_SALTLEN_MAX || saltlen == RSA_PSS_SALTLEN_AUTO) { + int mdsize, rsasize; + + if ((mdsize = EVP_MD_get_size(ctx->md)) <= 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST); + return -1; + } + if ((rsasize = RSA_size(ctx->rsa)) <= 2 || rsasize - 2 < mdsize) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY); + return -1; + } + saltlen = rsasize - mdsize - 2; + if ((RSA_bits(ctx->rsa) & 0x7) == 1) + saltlen--; + if (saltlenMax >= 0 && saltlen > saltlenMax) + saltlen = saltlenMax; + } + if (saltlen < 0) { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + return -1; + } else if (saltlen < ctx->min_saltlen) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_PSS_SALTLEN_TOO_SMALL, + "minimum salt length: %d, actual salt length: %d", + ctx->min_saltlen, saltlen); + return -1; + } + return saltlen; +} + +static unsigned char *rsa_generate_signature_aid(PROV_RSA_CTX *ctx, + unsigned char *aid_buf, + size_t buf_len, + size_t *aid_len) +{ + WPACKET pkt; + unsigned char *aid = NULL; + int saltlen; + RSA_PSS_PARAMS_30 pss_params; + int ret; + + if (!WPACKET_init_der(&pkt, aid_buf, buf_len)) { + ERR_raise(ERR_LIB_PROV, ERR_R_CRYPTO_LIB); + return NULL; + } + + switch (ctx->pad_mode) { + case RSA_PKCS1_PADDING: + ret = ossl_DER_w_algorithmIdentifier_MDWithRSAEncryption(&pkt, -1, + ctx->mdnid); + + if (ret > 0) { + break; + } else if (ret == 0) { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + goto cleanup; + } + ERR_raise_data(ERR_LIB_PROV, ERR_R_UNSUPPORTED, + "Algorithm ID generation - md NID: %d", + ctx->mdnid); + goto cleanup; + case RSA_PKCS1_PSS_PADDING: + saltlen = rsa_pss_compute_saltlen(ctx); + if (saltlen < 0) + goto cleanup; + if (!ossl_rsa_pss_params_30_set_defaults(&pss_params) + || !ossl_rsa_pss_params_30_set_hashalg(&pss_params, ctx->mdnid) + || !ossl_rsa_pss_params_30_set_maskgenhashalg(&pss_params, + ctx->mgf1_mdnid) + || !ossl_rsa_pss_params_30_set_saltlen(&pss_params, saltlen) + || !ossl_DER_w_algorithmIdentifier_RSA_PSS(&pkt, -1, + RSA_FLAG_TYPE_RSASSAPSS, + &pss_params)) { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + goto cleanup; + } + break; + default: + ERR_raise_data(ERR_LIB_PROV, ERR_R_UNSUPPORTED, + "Algorithm ID generation - pad mode: %d", + ctx->pad_mode); + goto cleanup; + } + if (WPACKET_finish(&pkt)) { + WPACKET_get_total_written(&pkt, aid_len); + aid = WPACKET_get_curr(&pkt); + } + cleanup: + WPACKET_cleanup(&pkt); + return aid; +} + +static int rsa_setup_md(PROV_RSA_CTX *ctx, const char *mdname, + const char *mdprops, const char *desc) +{ + EVP_MD *md = NULL; + + if (mdprops == NULL) + mdprops = ctx->propq; + + if (mdname != NULL) { + int md_nid; + size_t mdname_len = strlen(mdname); + + md = EVP_MD_fetch(ctx->libctx, mdname, mdprops); + + if (md == NULL) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST, + "%s could not be fetched", mdname); + goto err; + } + md_nid = ossl_digest_rsa_sign_get_md_nid(md); + if (md_nid == NID_undef) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED, + "digest=%s", mdname); + goto err; + } + /* + * XOF digests are not allowed except for RSA PSS. + * We don't support XOF digests with RSA PSS (yet), so just fail. + * When we do support them, uncomment the second clause. + */ + if (EVP_MD_xof(md) + /* && ctx->pad_mode != RSA_PKCS1_PSS_PADDING */) { + ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED); + goto err; + } +#ifdef FIPS_MODULE + { + int sha1_allowed + = ((ctx->operation + & (EVP_PKEY_OP_SIGN | EVP_PKEY_OP_SIGNMSG)) == 0); + + if (!ossl_fips_ind_digest_sign_check(OSSL_FIPS_IND_GET(ctx), + OSSL_FIPS_IND_SETTABLE1, + ctx->libctx, + md_nid, sha1_allowed, 1, desc, + ossl_fips_config_signature_digest_check)) + goto err; + } +#endif + + if (!rsa_check_padding(ctx, mdname, NULL, md_nid)) + goto err; + if (mdname_len >= sizeof(ctx->mdname)) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST, + "%s exceeds name buffer length", mdname); + goto err; + } + + if (!ctx->flag_allow_md) { + if (ctx->mdname[0] != '\0' && !EVP_MD_is_a(md, ctx->mdname)) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED, + "digest %s != %s", mdname, ctx->mdname); + goto err; + } + EVP_MD_free(md); + return 1; + } + + if (!ctx->mgf1_md_set) { + if (!EVP_MD_up_ref(md)) { + goto err; + } + EVP_MD_free(ctx->mgf1_md); + ctx->mgf1_md = md; + ctx->mgf1_mdnid = md_nid; + OPENSSL_strlcpy(ctx->mgf1_mdname, mdname, sizeof(ctx->mgf1_mdname)); + } + + EVP_MD_CTX_free(ctx->mdctx); + EVP_MD_free(ctx->md); + + ctx->mdctx = NULL; + ctx->md = md; + ctx->mdnid = md_nid; + OPENSSL_strlcpy(ctx->mdname, mdname, sizeof(ctx->mdname)); + } + + return 1; +err: + EVP_MD_free(md); + return 0; +} + +static int rsa_setup_mgf1_md(PROV_RSA_CTX *ctx, const char *mdname, + const char *mdprops) +{ + size_t len; + EVP_MD *md = NULL; + int mdnid; + + if (mdprops == NULL) + mdprops = ctx->propq; + + if ((md = EVP_MD_fetch(ctx->libctx, mdname, mdprops)) == NULL) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST, + "%s could not be fetched", mdname); + return 0; + } + /* The default for mgf1 is SHA1 - so allow SHA1 */ + if ((mdnid = ossl_digest_rsa_sign_get_md_nid(md)) <= 0 + || !rsa_check_padding(ctx, NULL, mdname, mdnid)) { + if (mdnid <= 0) + ERR_raise_data(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED, + "digest=%s", mdname); + EVP_MD_free(md); + return 0; + } + len = OPENSSL_strlcpy(ctx->mgf1_mdname, mdname, sizeof(ctx->mgf1_mdname)); + if (len >= sizeof(ctx->mgf1_mdname)) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST, + "%s exceeds name buffer length", mdname); + EVP_MD_free(md); + return 0; + } + + EVP_MD_free(ctx->mgf1_md); + ctx->mgf1_md = md; + ctx->mgf1_mdnid = mdnid; + ctx->mgf1_md_set = 1; + return 1; +} + +static int +rsa_signverify_init(PROV_RSA_CTX *prsactx, void *vrsa, + OSSL_FUNC_signature_set_ctx_params_fn *set_ctx_params, + const OSSL_PARAM params[], int operation, + const char *desc) +{ + int protect; + + if (!ossl_prov_is_running() || prsactx == NULL) + return 0; + + if (vrsa == NULL && prsactx->rsa == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET); + return 0; + } + + if (vrsa != NULL) { + if (!RSA_up_ref(vrsa)) + return 0; + RSA_free(prsactx->rsa); + prsactx->rsa = vrsa; + } + if (!ossl_rsa_key_op_get_protect(prsactx->rsa, operation, &protect)) + return 0; + + prsactx->operation = operation; + prsactx->flag_allow_update = 1; + prsactx->flag_allow_final = 1; + prsactx->flag_allow_oneshot = 1; + + /* Maximize up to digest length for sign, auto for verify */ + prsactx->saltlen = RSA_PSS_SALTLEN_AUTO_DIGEST_MAX; + prsactx->min_saltlen = -1; + + switch (RSA_test_flags(prsactx->rsa, RSA_FLAG_TYPE_MASK)) { + case RSA_FLAG_TYPE_RSA: + prsactx->pad_mode = RSA_PKCS1_PADDING; + break; + case RSA_FLAG_TYPE_RSASSAPSS: + prsactx->pad_mode = RSA_PKCS1_PSS_PADDING; + + { + const RSA_PSS_PARAMS_30 *pss = + ossl_rsa_get0_pss_params_30(prsactx->rsa); + + if (!ossl_rsa_pss_params_30_is_unrestricted(pss)) { + int md_nid = ossl_rsa_pss_params_30_hashalg(pss); + int mgf1md_nid = ossl_rsa_pss_params_30_maskgenhashalg(pss); + int min_saltlen = ossl_rsa_pss_params_30_saltlen(pss); + const char *mdname, *mgf1mdname; + size_t len; + + mdname = ossl_rsa_oaeppss_nid2name(md_nid); + mgf1mdname = ossl_rsa_oaeppss_nid2name(mgf1md_nid); + + if (mdname == NULL) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST, + "PSS restrictions lack hash algorithm"); + return 0; + } + if (mgf1mdname == NULL) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST, + "PSS restrictions lack MGF1 hash algorithm"); + return 0; + } + + len = OPENSSL_strlcpy(prsactx->mdname, mdname, + sizeof(prsactx->mdname)); + if (len >= sizeof(prsactx->mdname)) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST, + "hash algorithm name too long"); + return 0; + } + len = OPENSSL_strlcpy(prsactx->mgf1_mdname, mgf1mdname, + sizeof(prsactx->mgf1_mdname)); + if (len >= sizeof(prsactx->mgf1_mdname)) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST, + "MGF1 hash algorithm name too long"); + return 0; + } + prsactx->saltlen = min_saltlen; + + /* call rsa_setup_mgf1_md before rsa_setup_md to avoid duplication */ + if (!rsa_setup_mgf1_md(prsactx, mgf1mdname, prsactx->propq) + || !rsa_setup_md(prsactx, mdname, prsactx->propq, desc) + || !rsa_check_parameters(prsactx, min_saltlen)) + return 0; + } + } + + break; + default: + ERR_raise(ERR_LIB_RSA, PROV_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE); + return 0; + } + + OSSL_FIPS_IND_SET_APPROVED(prsactx) + if (!set_ctx_params(prsactx, params)) + return 0; +#ifdef FIPS_MODULE + if (!ossl_fips_ind_rsa_key_check(OSSL_FIPS_IND_GET(prsactx), + OSSL_FIPS_IND_SETTABLE0, prsactx->libctx, + prsactx->rsa, desc, protect)) + return 0; +#endif + return 1; +} + +static int setup_tbuf(PROV_RSA_CTX *ctx) +{ + if (ctx->tbuf != NULL) + return 1; + if ((ctx->tbuf = OPENSSL_malloc(RSA_size(ctx->rsa))) == NULL) + return 0; + return 1; +} + +static void clean_tbuf(PROV_RSA_CTX *ctx) +{ + if (ctx->tbuf != NULL) + OPENSSL_cleanse(ctx->tbuf, RSA_size(ctx->rsa)); +} + +static void free_tbuf(PROV_RSA_CTX *ctx) +{ + clean_tbuf(ctx); + OPENSSL_free(ctx->tbuf); + ctx->tbuf = NULL; +} + +#ifdef FIPS_MODULE +static int rsa_pss_saltlen_check_passed(PROV_RSA_CTX *ctx, const char *algoname, int saltlen) +{ + int mdsize = rsa_get_md_size(ctx); + /* + * Perform the check if the salt length is compliant to FIPS 186-5. + * + * According to FIPS 186-5 5.4 (g), the salt length shall be between zero + * and the output block length of the digest function (inclusive). + */ + int approved = (saltlen >= 0 && saltlen <= mdsize); + + if (!approved) { + if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE3, + ctx->libctx, + algoname, "PSS Salt Length", + ossl_fips_config_rsa_pss_saltlen_check)) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SALT_LENGTH); + return 0; + } + } + + return 1; +} +#endif + +static int rsa_sign_init(void *vprsactx, void *vrsa, const OSSL_PARAM params[]) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + +#ifdef FIPS_MODULE + if (prsactx != NULL) + prsactx->verify_message = 1; +#endif + + return rsa_signverify_init(prsactx, vrsa, rsa_set_ctx_params, params, + EVP_PKEY_OP_SIGN, "RSA Sign Init"); +} + +/* + * Sign tbs without digesting it first. This is suitable for "primitive" + * signing and signing the digest of a message, i.e. should be used with + * implementations of the keytype related algorithms. + */ +static int rsa_sign_directly(PROV_RSA_CTX *prsactx, + unsigned char *sig, size_t *siglen, size_t sigsize, + const unsigned char *tbs, size_t tbslen) +{ + int ret; + size_t rsasize = RSA_size(prsactx->rsa); + size_t mdsize = rsa_get_md_size(prsactx); + + if (!ossl_prov_is_running()) + return 0; + + if (sig == NULL) { + *siglen = rsasize; + return 1; + } + + if (sigsize < rsasize) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_SIGNATURE_SIZE, + "is %zu, should be at least %zu", sigsize, rsasize); + return 0; + } + + if (mdsize != 0) { + if (tbslen != mdsize) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_LENGTH); + return 0; + } + +#ifndef FIPS_MODULE + if (EVP_MD_is_a(prsactx->md, OSSL_DIGEST_NAME_MDC2)) { + unsigned int sltmp; + + if (prsactx->pad_mode != RSA_PKCS1_PADDING) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_PADDING_MODE, + "only PKCS#1 padding supported with MDC2"); + return 0; + } + ret = RSA_sign_ASN1_OCTET_STRING(0, tbs, tbslen, sig, &sltmp, + prsactx->rsa); + + if (ret <= 0) { + ERR_raise(ERR_LIB_PROV, ERR_R_RSA_LIB); + return 0; + } + ret = sltmp; + goto end; + } +#endif + switch (prsactx->pad_mode) { + case RSA_X931_PADDING: + if ((size_t)RSA_size(prsactx->rsa) < tbslen + 1) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_KEY_SIZE_TOO_SMALL, + "RSA key size = %d, expected minimum = %d", + RSA_size(prsactx->rsa), tbslen + 1); + return 0; + } + if (!setup_tbuf(prsactx)) { + ERR_raise(ERR_LIB_PROV, ERR_R_PROV_LIB); + return 0; + } + memcpy(prsactx->tbuf, tbs, tbslen); + prsactx->tbuf[tbslen] = RSA_X931_hash_id(prsactx->mdnid); + ret = RSA_private_encrypt(tbslen + 1, prsactx->tbuf, + sig, prsactx->rsa, RSA_X931_PADDING); + clean_tbuf(prsactx); + break; + case RSA_PKCS1_PADDING: + { + unsigned int sltmp; + + ret = RSA_sign(prsactx->mdnid, tbs, tbslen, sig, &sltmp, + prsactx->rsa); + if (ret <= 0) { + ERR_raise(ERR_LIB_PROV, ERR_R_RSA_LIB); + return 0; + } + ret = sltmp; + } + break; + + case RSA_PKCS1_PSS_PADDING: + { + int saltlen; + + /* Check PSS restrictions */ + if (rsa_pss_restricted(prsactx)) { + switch (prsactx->saltlen) { + case RSA_PSS_SALTLEN_DIGEST: + if (prsactx->min_saltlen > EVP_MD_get_size(prsactx->md)) { + ERR_raise_data(ERR_LIB_PROV, + PROV_R_PSS_SALTLEN_TOO_SMALL, + "minimum salt length set to %d, " + "but the digest only gives %d", + prsactx->min_saltlen, + EVP_MD_get_size(prsactx->md)); + return 0; + } + /* FALLTHRU */ + default: + if (prsactx->saltlen >= 0 + && prsactx->saltlen < prsactx->min_saltlen) { + ERR_raise_data(ERR_LIB_PROV, + PROV_R_PSS_SALTLEN_TOO_SMALL, + "minimum salt length set to %d, but the" + "actual salt length is only set to %d", + prsactx->min_saltlen, + prsactx->saltlen); + return 0; + } + break; + } + } + if (!setup_tbuf(prsactx)) + return 0; + saltlen = prsactx->saltlen; + if (!ossl_rsa_padding_add_PKCS1_PSS_mgf1(prsactx->rsa, + prsactx->tbuf, tbs, + prsactx->md, prsactx->mgf1_md, + &saltlen)) { + ERR_raise(ERR_LIB_PROV, ERR_R_RSA_LIB); + return 0; + } +#ifdef FIPS_MODULE + if (!rsa_pss_saltlen_check_passed(prsactx, "RSA Sign", saltlen)) + return 0; +#endif + ret = RSA_private_encrypt(RSA_size(prsactx->rsa), prsactx->tbuf, + sig, prsactx->rsa, RSA_NO_PADDING); + clean_tbuf(prsactx); + } + break; + + default: + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_PADDING_MODE, + "Only X.931, PKCS#1 v1.5 or PSS padding allowed"); + return 0; + } + } else { + ret = RSA_private_encrypt(tbslen, tbs, sig, prsactx->rsa, + prsactx->pad_mode); + } + +#ifndef FIPS_MODULE + end: +#endif + if (ret <= 0) { + ERR_raise(ERR_LIB_PROV, ERR_R_RSA_LIB); + return 0; + } + + *siglen = ret; + return 1; +} + +static int rsa_signverify_message_update(void *vprsactx, + const unsigned char *data, + size_t datalen) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + + if (prsactx == NULL || prsactx->mdctx == NULL) + return 0; + + if (!prsactx->flag_allow_update) { + ERR_raise(ERR_LIB_PROV, PROV_R_UPDATE_CALL_OUT_OF_ORDER); + return 0; + } + prsactx->flag_allow_oneshot = 0; + + return EVP_DigestUpdate(prsactx->mdctx, data, datalen); +} + +static int rsa_sign_message_final(void *vprsactx, unsigned char *sig, + size_t *siglen, size_t sigsize) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + unsigned char digest[EVP_MAX_MD_SIZE]; + unsigned int dlen = 0; + + if (!ossl_prov_is_running() || prsactx == NULL) + return 0; + if (prsactx->mdctx == NULL) + return 0; + if (!prsactx->flag_allow_final) { + ERR_raise(ERR_LIB_PROV, PROV_R_FINAL_CALL_OUT_OF_ORDER); + return 0; + } + + /* + * If sig is NULL then we're just finding out the sig size. Other fields + * are ignored. Defer to rsa_sign. + */ + if (sig != NULL) { + /* + * The digests used here are all known (see rsa_get_md_nid()), so they + * should not exceed the internal buffer size of EVP_MAX_MD_SIZE. + */ + if (!EVP_DigestFinal_ex(prsactx->mdctx, digest, &dlen)) + return 0; + + prsactx->flag_allow_update = 0; + prsactx->flag_allow_oneshot = 0; + prsactx->flag_allow_final = 0; + } + + return rsa_sign_directly(prsactx, sig, siglen, sigsize, digest, dlen); +} + +/* + * If signing a message, digest tbs and sign the result. + * Otherwise, sign tbs directly. + */ +static int rsa_sign(void *vprsactx, unsigned char *sig, size_t *siglen, + size_t sigsize, const unsigned char *tbs, size_t tbslen) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + + if (!ossl_prov_is_running() || prsactx == NULL) + return 0; + if (!prsactx->flag_allow_oneshot) { + ERR_raise(ERR_LIB_PROV, PROV_R_ONESHOT_CALL_OUT_OF_ORDER); + return 0; + } + + if (prsactx->operation == EVP_PKEY_OP_SIGNMSG) { + /* + * If |sig| is NULL, the caller is only looking for the sig length. + * DO NOT update the input in this case. + */ + if (sig == NULL) + return rsa_sign_message_final(prsactx, sig, siglen, sigsize); + + return rsa_signverify_message_update(prsactx, tbs, tbslen) + && rsa_sign_message_final(prsactx, sig, siglen, sigsize); + } + return rsa_sign_directly(prsactx, sig, siglen, sigsize, tbs, tbslen); +} + +static int rsa_verify_recover_init(void *vprsactx, void *vrsa, + const OSSL_PARAM params[]) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + +#ifdef FIPS_MODULE + if (prsactx != NULL) + prsactx->verify_message = 0; +#endif + + return rsa_signverify_init(prsactx, vrsa, rsa_set_ctx_params, params, + EVP_PKEY_OP_VERIFYRECOVER, "RSA VerifyRecover Init"); +} + +/* + * There is no message variant of verify recover, so no need for + * 'rsa_verify_recover_directly', just use this function, er, directly. + */ +static int rsa_verify_recover(void *vprsactx, + unsigned char *rout, size_t *routlen, + size_t routsize, + const unsigned char *sig, size_t siglen) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + int ret; + + if (!ossl_prov_is_running()) + return 0; + + if (rout == NULL) { + *routlen = RSA_size(prsactx->rsa); + return 1; + } + + if (prsactx->md != NULL) { + switch (prsactx->pad_mode) { + case RSA_X931_PADDING: + if (!setup_tbuf(prsactx)) + return 0; + ret = RSA_public_decrypt(siglen, sig, prsactx->tbuf, prsactx->rsa, + RSA_X931_PADDING); + if (ret <= 0) { + ERR_raise(ERR_LIB_PROV, ERR_R_RSA_LIB); + return 0; + } + ret--; + if (prsactx->tbuf[ret] != RSA_X931_hash_id(prsactx->mdnid)) { + ERR_raise(ERR_LIB_PROV, PROV_R_ALGORITHM_MISMATCH); + return 0; + } + if (ret != EVP_MD_get_size(prsactx->md)) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_LENGTH, + "Should be %d, but got %d", + EVP_MD_get_size(prsactx->md), ret); + return 0; + } + + *routlen = ret; + if (rout != prsactx->tbuf) { + if (routsize < (size_t)ret) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL, + "buffer size is %d, should be %d", + routsize, ret); + return 0; + } + memcpy(rout, prsactx->tbuf, ret); + } + break; + + case RSA_PKCS1_PADDING: + { + size_t sltmp; + + ret = ossl_rsa_verify(prsactx->mdnid, NULL, 0, rout, &sltmp, + sig, siglen, prsactx->rsa); + if (ret <= 0) { + ERR_raise(ERR_LIB_PROV, ERR_R_RSA_LIB); + return 0; + } + ret = sltmp; + } + break; + + default: + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_PADDING_MODE, + "Only X.931 or PKCS#1 v1.5 padding allowed"); + return 0; + } + } else { + ret = RSA_public_decrypt(siglen, sig, rout, prsactx->rsa, + prsactx->pad_mode); + if (ret <= 0) { + ERR_raise(ERR_LIB_PROV, ERR_R_RSA_LIB); + return 0; + } + } + *routlen = ret; + return 1; +} + +static int rsa_verify_init(void *vprsactx, void *vrsa, + const OSSL_PARAM params[]) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + +#ifdef FIPS_MODULE + if (prsactx != NULL) + prsactx->verify_message = 0; +#endif + + return rsa_signverify_init(prsactx, vrsa, rsa_set_ctx_params, params, + EVP_PKEY_OP_VERIFY, "RSA Verify Init"); +} + +static int rsa_verify_directly(PROV_RSA_CTX *prsactx, + const unsigned char *sig, size_t siglen, + const unsigned char *tbs, size_t tbslen) +{ + size_t rslen; + + if (!ossl_prov_is_running()) + return 0; + if (prsactx->md != NULL) { + switch (prsactx->pad_mode) { + case RSA_PKCS1_PADDING: + if (!RSA_verify(prsactx->mdnid, tbs, tbslen, sig, siglen, + prsactx->rsa)) { + ERR_raise(ERR_LIB_PROV, ERR_R_RSA_LIB); + return 0; + } + return 1; + case RSA_X931_PADDING: + if (!setup_tbuf(prsactx)) + return 0; + if (rsa_verify_recover(prsactx, prsactx->tbuf, &rslen, 0, + sig, siglen) <= 0) + return 0; + break; + case RSA_PKCS1_PSS_PADDING: + { + int ret; + int saltlen; + size_t mdsize; + + /* + * We need to check this for the RSA_verify_PKCS1_PSS_mgf1() + * call + */ + mdsize = rsa_get_md_size(prsactx); + if (tbslen != mdsize) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_LENGTH, + "Should be %d, but got %d", + mdsize, tbslen); + return 0; + } + + if (!setup_tbuf(prsactx)) + return 0; + ret = RSA_public_decrypt(siglen, sig, prsactx->tbuf, + prsactx->rsa, RSA_NO_PADDING); + if (ret <= 0) { + ERR_raise(ERR_LIB_PROV, ERR_R_RSA_LIB); + return 0; + } + saltlen = prsactx->saltlen; + ret = ossl_rsa_verify_PKCS1_PSS_mgf1(prsactx->rsa, tbs, + prsactx->md, prsactx->mgf1_md, + prsactx->tbuf, + &saltlen); + if (ret <= 0) { + ERR_raise(ERR_LIB_PROV, ERR_R_RSA_LIB); + return 0; + } +#ifdef FIPS_MODULE + if (!rsa_pss_saltlen_check_passed(prsactx, "RSA Verify", saltlen)) + return 0; +#endif + return 1; + } + default: + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_PADDING_MODE, + "Only X.931, PKCS#1 v1.5 or PSS padding allowed"); + return 0; + } + } else { + int ret; + + if (!setup_tbuf(prsactx)) + return 0; + ret = RSA_public_decrypt(siglen, sig, prsactx->tbuf, prsactx->rsa, + prsactx->pad_mode); + if (ret <= 0) { + ERR_raise(ERR_LIB_PROV, ERR_R_RSA_LIB); + return 0; + } + rslen = (size_t)ret; + } + + if ((rslen != tbslen) || memcmp(tbs, prsactx->tbuf, rslen)) + return 0; + + return 1; +} + +static int rsa_verify_set_sig(void *vprsactx, + const unsigned char *sig, size_t siglen) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + OSSL_PARAM params[2]; + + params[0] = + OSSL_PARAM_construct_octet_string(OSSL_SIGNATURE_PARAM_SIGNATURE, + (unsigned char *)sig, siglen); + params[1] = OSSL_PARAM_construct_end(); + return rsa_sigalg_set_ctx_params(prsactx, params); +} + +static int rsa_verify_message_final(void *vprsactx) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + unsigned char digest[EVP_MAX_MD_SIZE]; + unsigned int dlen = 0; + + if (!ossl_prov_is_running() || prsactx == NULL) + return 0; + if (prsactx->mdctx == NULL) + return 0; + if (!prsactx->flag_allow_final) { + ERR_raise(ERR_LIB_PROV, PROV_R_FINAL_CALL_OUT_OF_ORDER); + return 0; + } + + /* + * The digests used here are all known (see rsa_get_md_nid()), so they + * should not exceed the internal buffer size of EVP_MAX_MD_SIZE. + */ + if (!EVP_DigestFinal_ex(prsactx->mdctx, digest, &dlen)) + return 0; + + prsactx->flag_allow_update = 0; + prsactx->flag_allow_final = 0; + prsactx->flag_allow_oneshot = 0; + + return rsa_verify_directly(prsactx, prsactx->sig, prsactx->siglen, + digest, dlen); +} + +/* + * If verifying a message, digest tbs and verify the result. + * Otherwise, verify tbs directly. + */ +static int rsa_verify(void *vprsactx, + const unsigned char *sig, size_t siglen, + const unsigned char *tbs, size_t tbslen) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + + if (!ossl_prov_is_running() || prsactx == NULL) + return 0; + if (!prsactx->flag_allow_oneshot) { + ERR_raise(ERR_LIB_PROV, PROV_R_ONESHOT_CALL_OUT_OF_ORDER); + return 0; + } + + if (prsactx->operation == EVP_PKEY_OP_VERIFYMSG) + return rsa_verify_set_sig(prsactx, sig, siglen) + && rsa_signverify_message_update(prsactx, tbs, tbslen) + && rsa_verify_message_final(prsactx); + return rsa_verify_directly(prsactx, sig, siglen, tbs, tbslen); +} + +/* DigestSign/DigestVerify wrappers */ + +static int rsa_digest_signverify_init(void *vprsactx, const char *mdname, + void *vrsa, const OSSL_PARAM params[], + int operation, const char *desc) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + +#ifdef FIPS_MODULE + if (prsactx != NULL) + prsactx->verify_message = 1; +#endif + + if (!rsa_signverify_init(prsactx, vrsa, rsa_set_ctx_params, params, + operation, desc)) + return 0; + + if (mdname != NULL + /* was rsa_setup_md already called in rsa_signverify_init()? */ + && (mdname[0] == '\0' || OPENSSL_strcasecmp(prsactx->mdname, mdname) != 0) + && !rsa_setup_md(prsactx, mdname, prsactx->propq, desc)) + return 0; + + prsactx->flag_allow_md = 0; + + if (prsactx->mdctx == NULL) { + prsactx->mdctx = EVP_MD_CTX_new(); + if (prsactx->mdctx == NULL) + goto error; + } + + if (!EVP_DigestInit_ex2(prsactx->mdctx, prsactx->md, params)) + goto error; + + return 1; + + error: + EVP_MD_CTX_free(prsactx->mdctx); + prsactx->mdctx = NULL; + return 0; +} + +static int rsa_digest_sign_init(void *vprsactx, const char *mdname, + void *vrsa, const OSSL_PARAM params[]) +{ + if (!ossl_prov_is_running()) + return 0; + return rsa_digest_signverify_init(vprsactx, mdname, vrsa, + params, EVP_PKEY_OP_SIGNMSG, + "RSA Digest Sign Init"); +} + +static int rsa_digest_sign_update(void *vprsactx, const unsigned char *data, + size_t datalen) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + + if (prsactx == NULL) + return 0; + /* Sigalg implementations shouldn't do digest_sign */ + if (prsactx->flag_sigalg) + return 0; + + return rsa_signverify_message_update(prsactx, data, datalen); +} + +static int rsa_digest_sign_final(void *vprsactx, unsigned char *sig, + size_t *siglen, size_t sigsize) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + int ok = 0; + + if (prsactx == NULL) + return 0; + /* Sigalg implementations shouldn't do digest_sign */ + if (prsactx->flag_sigalg) + return 0; + + if (rsa_sign_message_final(prsactx, sig, siglen, sigsize)) + ok = 1; + + prsactx->flag_allow_md = 1; + + return ok; +} + +static int rsa_digest_verify_init(void *vprsactx, const char *mdname, + void *vrsa, const OSSL_PARAM params[]) +{ + if (!ossl_prov_is_running()) + return 0; + return rsa_digest_signverify_init(vprsactx, mdname, vrsa, + params, EVP_PKEY_OP_VERIFYMSG, + "RSA Digest Verify Init"); +} + +static int rsa_digest_verify_update(void *vprsactx, const unsigned char *data, + size_t datalen) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + + if (prsactx == NULL) + return 0; + /* Sigalg implementations shouldn't do digest_sign */ + if (prsactx->flag_sigalg) + return 0; + + return rsa_signverify_message_update(prsactx, data, datalen); +} + +int rsa_digest_verify_final(void *vprsactx, const unsigned char *sig, + size_t siglen) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + int ok = 0; + + if (prsactx == NULL) + return 0; + /* Sigalg implementations shouldn't do digest_verify */ + if (prsactx->flag_sigalg) + return 0; + + if (rsa_verify_set_sig(prsactx, sig, siglen) + && rsa_verify_message_final(vprsactx)) + ok = 1; + + prsactx->flag_allow_md = 1; + + return ok; +} + +static void rsa_freectx(void *vprsactx) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + + if (prsactx == NULL) + return; + + EVP_MD_CTX_free(prsactx->mdctx); + EVP_MD_free(prsactx->md); + EVP_MD_free(prsactx->mgf1_md); + OPENSSL_free(prsactx->sig); + OPENSSL_free(prsactx->propq); + free_tbuf(prsactx); + RSA_free(prsactx->rsa); + + OPENSSL_clear_free(prsactx, sizeof(*prsactx)); +} + +static void *rsa_dupctx(void *vprsactx) +{ + PROV_RSA_CTX *srcctx = (PROV_RSA_CTX *)vprsactx; + PROV_RSA_CTX *dstctx; + + if (!ossl_prov_is_running()) + return NULL; + + dstctx = OPENSSL_zalloc(sizeof(*srcctx)); + if (dstctx == NULL) + return NULL; + + *dstctx = *srcctx; + dstctx->rsa = NULL; + dstctx->md = NULL; + dstctx->mgf1_md = NULL; + dstctx->mdctx = NULL; + dstctx->tbuf = NULL; + dstctx->propq = NULL; + + if (srcctx->rsa != NULL && !RSA_up_ref(srcctx->rsa)) + goto err; + dstctx->rsa = srcctx->rsa; + + if (srcctx->md != NULL && !EVP_MD_up_ref(srcctx->md)) + goto err; + dstctx->md = srcctx->md; + + if (srcctx->mgf1_md != NULL && !EVP_MD_up_ref(srcctx->mgf1_md)) + goto err; + dstctx->mgf1_md = srcctx->mgf1_md; + + if (srcctx->mdctx != NULL) { + dstctx->mdctx = EVP_MD_CTX_new(); + if (dstctx->mdctx == NULL + || !EVP_MD_CTX_copy_ex(dstctx->mdctx, srcctx->mdctx)) + goto err; + } + + if (srcctx->propq != NULL) { + dstctx->propq = OPENSSL_strdup(srcctx->propq); + if (dstctx->propq == NULL) + goto err; + } + + return dstctx; + err: + rsa_freectx(dstctx); + return NULL; +} + +static int rsa_get_ctx_params(void *vprsactx, OSSL_PARAM *params) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + OSSL_PARAM *p; + + if (prsactx == NULL) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_ALGORITHM_ID); + if (p != NULL) { + /* The Algorithm Identifier of the combined signature algorithm */ + unsigned char aid_buf[128]; + unsigned char *aid; + size_t aid_len; + + aid = rsa_generate_signature_aid(prsactx, aid_buf, + sizeof(aid_buf), &aid_len); + if (aid == NULL || !OSSL_PARAM_set_octet_string(p, aid, aid_len)) + return 0; + } + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_PAD_MODE); + if (p != NULL) + switch (p->data_type) { + case OSSL_PARAM_INTEGER: + if (!OSSL_PARAM_set_int(p, prsactx->pad_mode)) + return 0; + break; + case OSSL_PARAM_UTF8_STRING: + { + int i; + const char *word = NULL; + + for (i = 0; padding_item[i].id != 0; i++) { + if (prsactx->pad_mode == (int)padding_item[i].id) { + word = padding_item[i].ptr; + break; + } + } + + if (word != NULL) { + if (!OSSL_PARAM_set_utf8_string(p, word)) + return 0; + } else { + ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR); + } + } + break; + default: + return 0; + } + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_DIGEST); + if (p != NULL && !OSSL_PARAM_set_utf8_string(p, prsactx->mdname)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_MGF1_DIGEST); + if (p != NULL && !OSSL_PARAM_set_utf8_string(p, prsactx->mgf1_mdname)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_PSS_SALTLEN); + if (p != NULL) { + if (p->data_type == OSSL_PARAM_INTEGER) { + if (!OSSL_PARAM_set_int(p, prsactx->saltlen)) + return 0; + } else if (p->data_type == OSSL_PARAM_UTF8_STRING) { + const char *value = NULL; + + switch (prsactx->saltlen) { + case RSA_PSS_SALTLEN_DIGEST: + value = OSSL_PKEY_RSA_PSS_SALT_LEN_DIGEST; + break; + case RSA_PSS_SALTLEN_MAX: + value = OSSL_PKEY_RSA_PSS_SALT_LEN_MAX; + break; + case RSA_PSS_SALTLEN_AUTO: + value = OSSL_PKEY_RSA_PSS_SALT_LEN_AUTO; + break; + case RSA_PSS_SALTLEN_AUTO_DIGEST_MAX: + value = OSSL_PKEY_RSA_PSS_SALT_LEN_AUTO_DIGEST_MAX; + break; + default: + { + int len = BIO_snprintf(p->data, p->data_size, "%d", + prsactx->saltlen); + + if (len <= 0) + return 0; + p->return_size = len; + break; + } + } + if (value != NULL + && !OSSL_PARAM_set_utf8_string(p, value)) + return 0; + } + } + +#ifdef FIPS_MODULE + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_FIPS_VERIFY_MESSAGE); + if (p != NULL && !OSSL_PARAM_set_uint(p, prsactx->verify_message)) + return 0; +#endif + + if (!OSSL_FIPS_IND_GET_CTX_PARAM(prsactx, params)) + return 0; + return 1; +} + +static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_ALGORITHM_ID, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PAD_MODE, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_MGF1_DIGEST, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PSS_SALTLEN, NULL, 0), +#ifdef FIPS_MODULE + OSSL_PARAM_uint(OSSL_SIGNATURE_PARAM_FIPS_VERIFY_MESSAGE, NULL), +#endif + OSSL_FIPS_IND_GETTABLE_CTX_PARAM() + OSSL_PARAM_END +}; + +static const OSSL_PARAM *rsa_gettable_ctx_params(ossl_unused void *vprsactx, + ossl_unused void *provctx) +{ + return known_gettable_ctx_params; +} + +#ifdef FIPS_MODULE +static int rsa_x931_padding_allowed(PROV_RSA_CTX *ctx) +{ + int approved = ((ctx->operation & EVP_PKEY_OP_SIGN) == 0); + + if (!approved) { + if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE2, + ctx->libctx, + "RSA Sign set ctx", "X931 Padding", + ossl_fips_config_rsa_sign_x931_disallowed)) { + ERR_raise(ERR_LIB_PROV, + PROV_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE); + return 0; + } + } + return 1; +} +#endif + +static int rsa_set_ctx_params(void *vprsactx, const OSSL_PARAM params[]) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + const OSSL_PARAM *p; + int pad_mode; + int saltlen; + char mdname[OSSL_MAX_NAME_SIZE] = "", *pmdname = NULL; + char mdprops[OSSL_MAX_PROPQUERY_SIZE] = "", *pmdprops = NULL; + char mgf1mdname[OSSL_MAX_NAME_SIZE] = "", *pmgf1mdname = NULL; + char mgf1mdprops[OSSL_MAX_PROPQUERY_SIZE] = "", *pmgf1mdprops = NULL; + + if (prsactx == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 1; + + if (!OSSL_FIPS_IND_SET_CTX_PARAM(prsactx, OSSL_FIPS_IND_SETTABLE0, params, + OSSL_SIGNATURE_PARAM_FIPS_KEY_CHECK)) + return 0; + + if (!OSSL_FIPS_IND_SET_CTX_PARAM(prsactx, OSSL_FIPS_IND_SETTABLE1, params, + OSSL_SIGNATURE_PARAM_FIPS_DIGEST_CHECK)) + return 0; + + if (!OSSL_FIPS_IND_SET_CTX_PARAM(prsactx, OSSL_FIPS_IND_SETTABLE2, params, + OSSL_SIGNATURE_PARAM_FIPS_SIGN_X931_PAD_CHECK)) + return 0; + + if (!OSSL_FIPS_IND_SET_CTX_PARAM(prsactx, OSSL_FIPS_IND_SETTABLE3, params, + OSSL_SIGNATURE_PARAM_FIPS_RSA_PSS_SALTLEN_CHECK)) + return 0; + + pad_mode = prsactx->pad_mode; + saltlen = prsactx->saltlen; + + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_DIGEST); + if (p != NULL) { + const OSSL_PARAM *propsp = + OSSL_PARAM_locate_const(params, + OSSL_SIGNATURE_PARAM_PROPERTIES); + + pmdname = mdname; + if (!OSSL_PARAM_get_utf8_string(p, &pmdname, sizeof(mdname))) + return 0; + + if (propsp != NULL) { + pmdprops = mdprops; + if (!OSSL_PARAM_get_utf8_string(propsp, + &pmdprops, sizeof(mdprops))) + return 0; + } + } + + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_PAD_MODE); + if (p != NULL) { + const char *err_extra_text = NULL; + + switch (p->data_type) { + case OSSL_PARAM_INTEGER: /* Support for legacy pad mode number */ + if (!OSSL_PARAM_get_int(p, &pad_mode)) + return 0; + break; + case OSSL_PARAM_UTF8_STRING: + { + int i; + + if (p->data == NULL) + return 0; + + for (i = 0; padding_item[i].id != 0; i++) { + if (strcmp(p->data, padding_item[i].ptr) == 0) { + pad_mode = padding_item[i].id; + break; + } + } + } + break; + default: + return 0; + } + + switch (pad_mode) { + case RSA_PKCS1_OAEP_PADDING: + /* + * OAEP padding is for asymmetric cipher only so is not compatible + * with signature use. + */ + err_extra_text = "OAEP padding not allowed for signing / verifying"; + goto bad_pad; + case RSA_PKCS1_PSS_PADDING: + if ((prsactx->operation + & (EVP_PKEY_OP_SIGN | EVP_PKEY_OP_SIGNMSG + | EVP_PKEY_OP_VERIFY | EVP_PKEY_OP_VERIFYMSG)) == 0) { + err_extra_text = + "PSS padding only allowed for sign and verify operations"; + goto bad_pad; + } + break; + case RSA_PKCS1_PADDING: + err_extra_text = "PKCS#1 padding not allowed with RSA-PSS"; + goto cont; + case RSA_NO_PADDING: + err_extra_text = "No padding not allowed with RSA-PSS"; + goto cont; + case RSA_X931_PADDING: +#ifdef FIPS_MODULE + /* X9.31 only allows sizes of 1024 + 256 * s (bits) */ + if ((RSA_bits(prsactx->rsa) & 0xFF) != 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH); + return 0; + } + /* RSA Signing with X9.31 padding is not allowed in FIPS 140-3 */ + if (!rsa_x931_padding_allowed(prsactx)) + return 0; +#endif + err_extra_text = "X.931 padding not allowed with RSA-PSS"; + cont: + if (RSA_test_flags(prsactx->rsa, + RSA_FLAG_TYPE_MASK) == RSA_FLAG_TYPE_RSA) + break; + /* FALLTHRU */ + default: + bad_pad: + if (err_extra_text == NULL) + ERR_raise(ERR_LIB_PROV, + PROV_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE); + else + ERR_raise_data(ERR_LIB_PROV, + PROV_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE, + err_extra_text); + return 0; + } + } + + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_PSS_SALTLEN); + if (p != NULL) { + if (pad_mode != RSA_PKCS1_PSS_PADDING) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_NOT_SUPPORTED, + "PSS saltlen can only be specified if " + "PSS padding has been specified first"); + return 0; + } + + switch (p->data_type) { + case OSSL_PARAM_INTEGER: /* Support for legacy pad mode number */ + if (!OSSL_PARAM_get_int(p, &saltlen)) + return 0; + break; + case OSSL_PARAM_UTF8_STRING: + if (strcmp(p->data, OSSL_PKEY_RSA_PSS_SALT_LEN_DIGEST) == 0) + saltlen = RSA_PSS_SALTLEN_DIGEST; + else if (strcmp(p->data, OSSL_PKEY_RSA_PSS_SALT_LEN_MAX) == 0) + saltlen = RSA_PSS_SALTLEN_MAX; + else if (strcmp(p->data, OSSL_PKEY_RSA_PSS_SALT_LEN_AUTO) == 0) + saltlen = RSA_PSS_SALTLEN_AUTO; + else if (strcmp(p->data, OSSL_PKEY_RSA_PSS_SALT_LEN_AUTO_DIGEST_MAX) == 0) + saltlen = RSA_PSS_SALTLEN_AUTO_DIGEST_MAX; + else + saltlen = atoi(p->data); + break; + default: + return 0; + } + + /* + * RSA_PSS_SALTLEN_AUTO_DIGEST_MAX seems curiously named in this check. + * Contrary to what it's name suggests, it's the currently lowest + * saltlen number possible. + */ + if (saltlen < RSA_PSS_SALTLEN_AUTO_DIGEST_MAX) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SALT_LENGTH); + return 0; + } + + if (rsa_pss_restricted(prsactx)) { + switch (saltlen) { + case RSA_PSS_SALTLEN_AUTO: + case RSA_PSS_SALTLEN_AUTO_DIGEST_MAX: + if ((prsactx->operation + & (EVP_PKEY_OP_VERIFY | EVP_PKEY_OP_VERIFYMSG)) == 0) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_SALT_LENGTH, + "Cannot use autodetected salt length"); + return 0; + } + break; + case RSA_PSS_SALTLEN_DIGEST: + if (prsactx->min_saltlen > EVP_MD_get_size(prsactx->md)) { + ERR_raise_data(ERR_LIB_PROV, + PROV_R_PSS_SALTLEN_TOO_SMALL, + "Should be more than %d, but would be " + "set to match digest size (%d)", + prsactx->min_saltlen, + EVP_MD_get_size(prsactx->md)); + return 0; + } + break; + default: + if (saltlen >= 0 && saltlen < prsactx->min_saltlen) { + ERR_raise_data(ERR_LIB_PROV, + PROV_R_PSS_SALTLEN_TOO_SMALL, + "Should be more than %d, " + "but would be set to %d", + prsactx->min_saltlen, saltlen); + return 0; + } + } + } + } + + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_MGF1_DIGEST); + if (p != NULL) { + const OSSL_PARAM *propsp = + OSSL_PARAM_locate_const(params, + OSSL_SIGNATURE_PARAM_MGF1_PROPERTIES); + + pmgf1mdname = mgf1mdname; + if (!OSSL_PARAM_get_utf8_string(p, &pmgf1mdname, sizeof(mgf1mdname))) + return 0; + + if (propsp != NULL) { + pmgf1mdprops = mgf1mdprops; + if (!OSSL_PARAM_get_utf8_string(propsp, + &pmgf1mdprops, sizeof(mgf1mdprops))) + return 0; + } + + if (pad_mode != RSA_PKCS1_PSS_PADDING) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MGF1_MD); + return 0; + } + } + + prsactx->saltlen = saltlen; + prsactx->pad_mode = pad_mode; + + if (prsactx->md == NULL && pmdname == NULL + && pad_mode == RSA_PKCS1_PSS_PADDING) + pmdname = RSA_DEFAULT_DIGEST_NAME; + + if (pmgf1mdname != NULL + && !rsa_setup_mgf1_md(prsactx, pmgf1mdname, pmgf1mdprops)) + return 0; + + if (pmdname != NULL) { + if (!rsa_setup_md(prsactx, pmdname, pmdprops, "RSA Sign Set Ctx")) + return 0; + } else { + if (!rsa_check_padding(prsactx, NULL, NULL, prsactx->mdnid)) + return 0; + } + return 1; +} + +static const OSSL_PARAM settable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PAD_MODE, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_MGF1_DIGEST, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_MGF1_PROPERTIES, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PSS_SALTLEN, NULL, 0), + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_SIGNATURE_PARAM_FIPS_KEY_CHECK) + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_SIGNATURE_PARAM_FIPS_DIGEST_CHECK) + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_SIGNATURE_PARAM_FIPS_RSA_PSS_SALTLEN_CHECK) + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_SIGNATURE_PARAM_FIPS_SIGN_X931_PAD_CHECK) + OSSL_PARAM_END +}; + +static const OSSL_PARAM settable_ctx_params_no_digest[] = { + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PAD_MODE, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_MGF1_DIGEST, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_MGF1_PROPERTIES, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PSS_SALTLEN, NULL, 0), + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_SIGNATURE_PARAM_FIPS_KEY_CHECK) + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_SIGNATURE_PARAM_FIPS_DIGEST_CHECK) + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_SIGNATURE_PARAM_FIPS_RSA_PSS_SALTLEN_CHECK) + OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_SIGNATURE_PARAM_FIPS_SIGN_X931_PAD_CHECK) + OSSL_PARAM_END +}; + +static const OSSL_PARAM *rsa_settable_ctx_params(void *vprsactx, + ossl_unused void *provctx) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + + if (prsactx != NULL && !prsactx->flag_allow_md) + return settable_ctx_params_no_digest; + return settable_ctx_params; +} + +static int rsa_get_ctx_md_params(void *vprsactx, OSSL_PARAM *params) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + + if (prsactx->mdctx == NULL) + return 0; + + return EVP_MD_CTX_get_params(prsactx->mdctx, params); +} + +static const OSSL_PARAM *rsa_gettable_ctx_md_params(void *vprsactx) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + + if (prsactx->md == NULL) + return 0; + + return EVP_MD_gettable_ctx_params(prsactx->md); +} + +static int rsa_set_ctx_md_params(void *vprsactx, const OSSL_PARAM params[]) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + + if (prsactx->mdctx == NULL) + return 0; + + return EVP_MD_CTX_set_params(prsactx->mdctx, params); +} + +static const OSSL_PARAM *rsa_settable_ctx_md_params(void *vprsactx) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + + if (prsactx->md == NULL) + return 0; + + return EVP_MD_settable_ctx_params(prsactx->md); +} + +const OSSL_DISPATCH ossl_rsa_signature_functions[] = { + { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))rsa_newctx }, + { OSSL_FUNC_SIGNATURE_SIGN_INIT, (void (*)(void))rsa_sign_init }, + { OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))rsa_sign }, + { OSSL_FUNC_SIGNATURE_VERIFY_INIT, (void (*)(void))rsa_verify_init }, + { OSSL_FUNC_SIGNATURE_VERIFY, (void (*)(void))rsa_verify }, + { OSSL_FUNC_SIGNATURE_VERIFY_RECOVER_INIT, + (void (*)(void))rsa_verify_recover_init }, + { OSSL_FUNC_SIGNATURE_VERIFY_RECOVER, + (void (*)(void))rsa_verify_recover }, + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT, + (void (*)(void))rsa_digest_sign_init }, + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_UPDATE, + (void (*)(void))rsa_digest_sign_update }, + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_FINAL, + (void (*)(void))rsa_digest_sign_final }, + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT, + (void (*)(void))rsa_digest_verify_init }, + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_UPDATE, + (void (*)(void))rsa_digest_verify_update }, + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_FINAL, + (void (*)(void))rsa_digest_verify_final }, + { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))rsa_freectx }, + { OSSL_FUNC_SIGNATURE_DUPCTX, (void (*)(void))rsa_dupctx }, + { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, (void (*)(void))rsa_get_ctx_params }, + { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, + (void (*)(void))rsa_gettable_ctx_params }, + { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, (void (*)(void))rsa_set_ctx_params }, + { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, + (void (*)(void))rsa_settable_ctx_params }, + { OSSL_FUNC_SIGNATURE_GET_CTX_MD_PARAMS, + (void (*)(void))rsa_get_ctx_md_params }, + { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_MD_PARAMS, + (void (*)(void))rsa_gettable_ctx_md_params }, + { OSSL_FUNC_SIGNATURE_SET_CTX_MD_PARAMS, + (void (*)(void))rsa_set_ctx_md_params }, + { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_MD_PARAMS, + (void (*)(void))rsa_settable_ctx_md_params }, + OSSL_DISPATCH_END +}; + +/* ------------------------------------------------------------------ */ + +/* + * So called sigalgs (composite RSA+hash) implemented below. They + * are pretty much hard coded, and rely on the hash implementation + * being available as per what OPENSSL_NO_ macros allow. + */ + +static OSSL_FUNC_signature_query_key_types_fn rsa_sigalg_query_key_types; +static OSSL_FUNC_signature_settable_ctx_params_fn rsa_sigalg_settable_ctx_params; +static OSSL_FUNC_signature_set_ctx_params_fn rsa_sigalg_set_ctx_params; + +/* + * rsa_sigalg_signverify_init() is almost like rsa_digest_signverify_init(), + * just doesn't allow fetching an MD from whatever the user chooses. + */ +static int rsa_sigalg_signverify_init(void *vprsactx, void *vrsa, + OSSL_FUNC_signature_set_ctx_params_fn *set_ctx_params, + const OSSL_PARAM params[], + const char *mdname, + int operation, int pad_mode, + const char *desc) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + + if (!ossl_prov_is_running()) + return 0; + + if (!rsa_signverify_init(prsactx, vrsa, set_ctx_params, params, operation, + desc)) + return 0; + + /* PSS is currently not supported as a sigalg */ + if (prsactx->pad_mode == RSA_PKCS1_PSS_PADDING) { + ERR_raise(ERR_LIB_RSA, PROV_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE); + return 0; + } + + if (!rsa_setup_md(prsactx, mdname, NULL, desc)) + return 0; + + prsactx->pad_mode = pad_mode; + prsactx->flag_sigalg = 1; + prsactx->flag_allow_md = 0; + + if (prsactx->mdctx == NULL) { + prsactx->mdctx = EVP_MD_CTX_new(); + if (prsactx->mdctx == NULL) + goto error; + } + + if (!EVP_DigestInit_ex2(prsactx->mdctx, prsactx->md, params)) + goto error; + + return 1; + + error: + EVP_MD_CTX_free(prsactx->mdctx); + prsactx->mdctx = NULL; + return 0; +} + +static const char **rsa_sigalg_query_key_types(void) +{ + static const char *keytypes[] = { "RSA", NULL }; + + return keytypes; +} + +static const OSSL_PARAM settable_sigalg_ctx_params[] = { + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_SIGNATURE, NULL, 0), + OSSL_PARAM_END +}; + +static const OSSL_PARAM *rsa_sigalg_settable_ctx_params(void *vprsactx, + ossl_unused void *provctx) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + + if (prsactx != NULL && prsactx->operation == EVP_PKEY_OP_VERIFYMSG) + return settable_sigalg_ctx_params; + return NULL; +} + +static int rsa_sigalg_set_ctx_params(void *vprsactx, const OSSL_PARAM params[]) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + const OSSL_PARAM *p; + + if (prsactx == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 1; + + if (prsactx->operation == EVP_PKEY_OP_VERIFYMSG) { + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_SIGNATURE); + if (p != NULL) { + OPENSSL_free(prsactx->sig); + prsactx->sig = NULL; + prsactx->siglen = 0; + if (!OSSL_PARAM_get_octet_string(p, (void **)&prsactx->sig, + 0, &prsactx->siglen)) + return 0; + } + } + return 1; +} + +#define IMPL_RSA_SIGALG(md, MD) \ + static OSSL_FUNC_signature_sign_init_fn rsa_##md##_sign_init; \ + static OSSL_FUNC_signature_sign_message_init_fn \ + rsa_##md##_sign_message_init; \ + static OSSL_FUNC_signature_verify_init_fn rsa_##md##_verify_init; \ + static OSSL_FUNC_signature_verify_message_init_fn \ + rsa_##md##_verify_message_init; \ + \ + static int \ + rsa_##md##_sign_init(void *vprsactx, void *vrsa, \ + const OSSL_PARAM params[]) \ + { \ + static const char desc[] = "RSA Sigalg Sign Init"; \ + \ + return rsa_sigalg_signverify_init(vprsactx, vrsa, \ + rsa_sigalg_set_ctx_params, \ + params, #MD, \ + EVP_PKEY_OP_SIGN, \ + RSA_PKCS1_PADDING, \ + desc); \ + } \ + \ + static int \ + rsa_##md##_sign_message_init(void *vprsactx, void *vrsa, \ + const OSSL_PARAM params[]) \ + { \ + static const char desc[] = "RSA Sigalg Sign Message Init"; \ + \ + return rsa_sigalg_signverify_init(vprsactx, vrsa, \ + rsa_sigalg_set_ctx_params, \ + params, #MD, \ + EVP_PKEY_OP_SIGNMSG, \ + RSA_PKCS1_PADDING, \ + desc); \ + } \ + \ + static int \ + rsa_##md##_verify_init(void *vprsactx, void *vrsa, \ + const OSSL_PARAM params[]) \ + { \ + static const char desc[] = "RSA Sigalg Verify Init"; \ + \ + return rsa_sigalg_signverify_init(vprsactx, vrsa, \ + rsa_sigalg_set_ctx_params, \ + params, #MD, \ + EVP_PKEY_OP_VERIFY, \ + RSA_PKCS1_PADDING, \ + desc); \ + } \ + \ + static int \ + rsa_##md##_verify_recover_init(void *vprsactx, void *vrsa, \ + const OSSL_PARAM params[]) \ + { \ + static const char desc[] = "RSA Sigalg Verify Recover Init"; \ + \ + return rsa_sigalg_signverify_init(vprsactx, vrsa, \ + rsa_sigalg_set_ctx_params, \ + params, #MD, \ + EVP_PKEY_OP_VERIFYRECOVER, \ + RSA_PKCS1_PADDING, \ + desc); \ + } \ + \ + static int \ + rsa_##md##_verify_message_init(void *vprsactx, void *vrsa, \ + const OSSL_PARAM params[]) \ + { \ + static const char desc[] = "RSA Sigalg Verify Message Init"; \ + \ + return rsa_sigalg_signverify_init(vprsactx, vrsa, \ + rsa_sigalg_set_ctx_params, \ + params, #MD, \ + EVP_PKEY_OP_VERIFYMSG, \ + RSA_PKCS1_PADDING, \ + desc); \ + } \ + \ + const OSSL_DISPATCH ossl_rsa_##md##_signature_functions[] = { \ + { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))rsa_newctx }, \ + { OSSL_FUNC_SIGNATURE_SIGN_INIT, \ + (void (*)(void))rsa_##md##_sign_init }, \ + { OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))rsa_sign }, \ + { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_INIT, \ + (void (*)(void))rsa_##md##_sign_message_init }, \ + { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_UPDATE, \ + (void (*)(void))rsa_signverify_message_update }, \ + { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_FINAL, \ + (void (*)(void))rsa_sign_message_final }, \ + { OSSL_FUNC_SIGNATURE_VERIFY_INIT, \ + (void (*)(void))rsa_##md##_verify_init }, \ + { OSSL_FUNC_SIGNATURE_VERIFY, \ + (void (*)(void))rsa_verify }, \ + { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_INIT, \ + (void (*)(void))rsa_##md##_verify_message_init }, \ + { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_UPDATE, \ + (void (*)(void))rsa_signverify_message_update }, \ + { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_FINAL, \ + (void (*)(void))rsa_verify_message_final }, \ + { OSSL_FUNC_SIGNATURE_VERIFY_RECOVER_INIT, \ + (void (*)(void))rsa_##md##_verify_recover_init }, \ + { OSSL_FUNC_SIGNATURE_VERIFY_RECOVER, \ + (void (*)(void))rsa_verify_recover }, \ + { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))rsa_freectx }, \ + { OSSL_FUNC_SIGNATURE_DUPCTX, (void (*)(void))rsa_dupctx }, \ + { OSSL_FUNC_SIGNATURE_QUERY_KEY_TYPES, \ + (void (*)(void))rsa_sigalg_query_key_types }, \ + { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, \ + (void (*)(void))rsa_get_ctx_params }, \ + { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, \ + (void (*)(void))rsa_gettable_ctx_params }, \ + { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, \ + (void (*)(void))rsa_sigalg_set_ctx_params }, \ + { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, \ + (void (*)(void))rsa_sigalg_settable_ctx_params }, \ + OSSL_DISPATCH_END \ + } + +#if !defined(OPENSSL_NO_RMD160) && !defined(FIPS_MODULE) +IMPL_RSA_SIGALG(ripemd160, RIPEMD160); +#endif +IMPL_RSA_SIGALG(sha1, SHA1); +IMPL_RSA_SIGALG(sha224, SHA2-224); +IMPL_RSA_SIGALG(sha256, SHA2-256); +IMPL_RSA_SIGALG(sha384, SHA2-384); +IMPL_RSA_SIGALG(sha512, SHA2-512); +IMPL_RSA_SIGALG(sha512_224, SHA2-512/224); +IMPL_RSA_SIGALG(sha512_256, SHA2-512/256); +IMPL_RSA_SIGALG(sha3_224, SHA3-224); +IMPL_RSA_SIGALG(sha3_256, SHA3-256); +IMPL_RSA_SIGALG(sha3_384, SHA3-384); +IMPL_RSA_SIGALG(sha3_512, SHA3-512); +#if !defined(OPENSSL_NO_SM3) && !defined(FIPS_MODULE) +IMPL_RSA_SIGALG(sm3, SM3); +#endif diff --git a/crypto/openssl/providers/implementations/signature/slh_dsa_sig.c b/crypto/openssl/providers/implementations/signature/slh_dsa_sig.c new file mode 100644 index 000000000000..40fc6846e2ad --- /dev/null +++ b/crypto/openssl/providers/implementations/signature/slh_dsa_sig.c @@ -0,0 +1,388 @@ +/* + * Copyright 2024-2025 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 + */ + +#include <openssl/core_names.h> +#include <openssl/err.h> +#include <openssl/rand.h> +#include <openssl/proverr.h> +#include "prov/implementations.h" +#include "prov/providercommon.h" +#include "prov/provider_ctx.h" +#include "prov/der_slh_dsa.h" +#include "crypto/slh_dsa.h" +#include "internal/sizes.h" + +#define SLH_DSA_MAX_ADD_RANDOM_LEN 32 + +#define SLH_DSA_MESSAGE_ENCODE_RAW 0 +#define SLH_DSA_MESSAGE_ENCODE_PURE 1 + +static OSSL_FUNC_signature_sign_message_init_fn slh_dsa_sign_msg_init; +static OSSL_FUNC_signature_sign_fn slh_dsa_sign; +static OSSL_FUNC_signature_verify_message_init_fn slh_dsa_verify_msg_init; +static OSSL_FUNC_signature_verify_fn slh_dsa_verify; +static OSSL_FUNC_signature_digest_sign_init_fn slh_dsa_digest_signverify_init; +static OSSL_FUNC_signature_digest_sign_fn slh_dsa_digest_sign; +static OSSL_FUNC_signature_digest_verify_fn slh_dsa_digest_verify; +static OSSL_FUNC_signature_freectx_fn slh_dsa_freectx; +static OSSL_FUNC_signature_dupctx_fn slh_dsa_dupctx; +static OSSL_FUNC_signature_set_ctx_params_fn slh_dsa_set_ctx_params; +static OSSL_FUNC_signature_settable_ctx_params_fn slh_dsa_settable_ctx_params; + +/* + * NOTE: Any changes to this structure may require updating slh_dsa_dupctx(). + */ +typedef struct { + SLH_DSA_KEY *key; /* Note that the key is not owned by this object */ + SLH_DSA_HASH_CTX *hash_ctx; + uint8_t context_string[SLH_DSA_MAX_CONTEXT_STRING_LEN]; + size_t context_string_len; + uint8_t add_random[SLH_DSA_MAX_ADD_RANDOM_LEN]; + size_t add_random_len; + int msg_encode; + int deterministic; + OSSL_LIB_CTX *libctx; + char *propq; + const char *alg; + /* The Algorithm Identifier of the signature algorithm */ + uint8_t aid_buf[OSSL_MAX_ALGORITHM_ID_SIZE]; + size_t aid_len; +} PROV_SLH_DSA_CTX; + +static void slh_dsa_freectx(void *vctx) +{ + PROV_SLH_DSA_CTX *ctx = (PROV_SLH_DSA_CTX *)vctx; + + ossl_slh_dsa_hash_ctx_free(ctx->hash_ctx); + OPENSSL_free(ctx->propq); + OPENSSL_cleanse(ctx->add_random, ctx->add_random_len); + OPENSSL_free(ctx); +} + +static void *slh_dsa_newctx(void *provctx, const char *alg, const char *propq) +{ + PROV_SLH_DSA_CTX *ctx; + + if (!ossl_prov_is_running()) + return NULL; + + ctx = OPENSSL_zalloc(sizeof(PROV_SLH_DSA_CTX)); + if (ctx == NULL) + return NULL; + + ctx->libctx = PROV_LIBCTX_OF(provctx); + if (propq != NULL && (ctx->propq = OPENSSL_strdup(propq)) == NULL) + goto err; + ctx->alg = alg; + ctx->msg_encode = SLH_DSA_MESSAGE_ENCODE_PURE; + return ctx; + err: + slh_dsa_freectx(ctx); + return NULL; +} + +static void *slh_dsa_dupctx(void *vctx) +{ + PROV_SLH_DSA_CTX *src = (PROV_SLH_DSA_CTX *)vctx; + PROV_SLH_DSA_CTX *ret; + + if (!ossl_prov_is_running()) + return NULL; + + /* + * Note that the SLH_DSA_KEY is ref counted via EVP_PKEY so we can just copy + * the key here. + */ + ret = OPENSSL_memdup(src, sizeof(*src)); + if (ret == NULL) + return NULL; + ret->propq = NULL; + ret->hash_ctx = NULL; + if (src->propq != NULL && (ret->propq = OPENSSL_strdup(src->propq)) == NULL) + goto err; + ret->hash_ctx = ossl_slh_dsa_hash_ctx_dup(src->hash_ctx); + if (ret->hash_ctx == NULL) + goto err; + + return ret; + err: + slh_dsa_freectx(ret); + return NULL; +} + +static int slh_dsa_set_alg_id_buffer(PROV_SLH_DSA_CTX *ctx) +{ + int ret; + WPACKET pkt; + uint8_t *aid = NULL; + + /* + * We do not care about DER writing errors. + * All it really means is that for some reason, there's no + * AlgorithmIdentifier to be had, but the operation itself is + * still valid, just as long as it's not used to construct + * anything that needs an AlgorithmIdentifier. + */ + ctx->aid_len = 0; + ret = WPACKET_init_der(&pkt, ctx->aid_buf, sizeof(ctx->aid_buf)); + ret = ret && ossl_DER_w_algorithmIdentifier_SLH_DSA(&pkt, -1, ctx->key); + if (ret && WPACKET_finish(&pkt)) { + WPACKET_get_total_written(&pkt, &ctx->aid_len); + aid = WPACKET_get_curr(&pkt); + } + WPACKET_cleanup(&pkt); + if (aid != NULL && ctx->aid_len != 0) + memmove(ctx->aid_buf, aid, ctx->aid_len); + return 1; +} + +static int slh_dsa_signverify_msg_init(void *vctx, void *vkey, + const OSSL_PARAM params[], int operation, + const char *desc) +{ + PROV_SLH_DSA_CTX *ctx = (PROV_SLH_DSA_CTX *)vctx; + SLH_DSA_KEY *key = vkey; + + if (!ossl_prov_is_running() + || ctx == NULL) + return 0; + + if (vkey == NULL && ctx->key == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET); + return 0; + } + + if (key != NULL) { + if (!ossl_slh_dsa_key_type_matches(key, ctx->alg)) + return 0; + ctx->hash_ctx = ossl_slh_dsa_hash_ctx_new(key); + if (ctx->hash_ctx == NULL) + return 0; + ctx->key = vkey; + } + + slh_dsa_set_alg_id_buffer(ctx); + if (!slh_dsa_set_ctx_params(ctx, params)) + return 0; + return 1; +} + +static int slh_dsa_sign_msg_init(void *vctx, void *vkey, const OSSL_PARAM params[]) +{ + return slh_dsa_signverify_msg_init(vctx, vkey, params, + EVP_PKEY_OP_SIGN, "SLH_DSA Sign Init"); +} + +static int slh_dsa_digest_signverify_init(void *vctx, const char *mdname, + void *vkey, const OSSL_PARAM params[]) +{ + PROV_SLH_DSA_CTX *ctx = (PROV_SLH_DSA_CTX *)vctx; + + if (mdname != NULL && mdname[0] != '\0') { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST, + "Explicit digest not supported for SLH-DSA operations"); + return 0; + } + + if (vkey == NULL && ctx->key != NULL) + return slh_dsa_set_ctx_params(ctx, params); + + return slh_dsa_signverify_msg_init(vctx, vkey, params, + EVP_PKEY_OP_SIGN, "SLH_DSA Sign Init"); +} + +static int slh_dsa_sign(void *vctx, unsigned char *sig, size_t *siglen, + size_t sigsize, const unsigned char *msg, size_t msg_len) +{ + int ret = 0; + PROV_SLH_DSA_CTX *ctx = (PROV_SLH_DSA_CTX *)vctx; + uint8_t add_rand[SLH_DSA_MAX_ADD_RANDOM_LEN], *opt_rand = NULL; + size_t n = 0; + + if (!ossl_prov_is_running()) + return 0; + + if (sig != NULL) { + if (ctx->add_random_len != 0) { + opt_rand = ctx->add_random; + } else if (ctx->deterministic == 0) { + n = ossl_slh_dsa_key_get_n(ctx->key); + if (RAND_priv_bytes_ex(ctx->libctx, add_rand, n, 0) <= 0) + return 0; + opt_rand = add_rand; + } + } + ret = ossl_slh_dsa_sign(ctx->hash_ctx, msg, msg_len, + ctx->context_string, ctx->context_string_len, + opt_rand, ctx->msg_encode, + sig, siglen, sigsize); + if (opt_rand != add_rand) + OPENSSL_cleanse(opt_rand, n); + return ret; +} + +static int slh_dsa_digest_sign(void *vctx, uint8_t *sig, size_t *siglen, size_t sigsize, + const uint8_t *tbs, size_t tbslen) +{ + return slh_dsa_sign(vctx, sig, siglen, sigsize, tbs, tbslen); +} + +static int slh_dsa_verify_msg_init(void *vctx, void *vkey, const OSSL_PARAM params[]) +{ + return slh_dsa_signverify_msg_init(vctx, vkey, params, EVP_PKEY_OP_VERIFY, + "SLH_DSA Verify Init"); +} + +static int slh_dsa_verify(void *vctx, const uint8_t *sig, size_t siglen, + const uint8_t *msg, size_t msg_len) +{ + PROV_SLH_DSA_CTX *ctx = (PROV_SLH_DSA_CTX *)vctx; + + if (!ossl_prov_is_running()) + return 0; + return ossl_slh_dsa_verify(ctx->hash_ctx, msg, msg_len, + ctx->context_string, ctx->context_string_len, + ctx->msg_encode, sig, siglen); +} +static int slh_dsa_digest_verify(void *vctx, const uint8_t *sig, size_t siglen, + const uint8_t *tbs, size_t tbslen) +{ + return slh_dsa_verify(vctx, sig, siglen, tbs, tbslen); +} + +static int slh_dsa_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + PROV_SLH_DSA_CTX *pctx = (PROV_SLH_DSA_CTX *)vctx; + const OSSL_PARAM *p; + + if (pctx == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_CONTEXT_STRING); + if (p != NULL) { + void *vp = pctx->context_string; + + if (!OSSL_PARAM_get_octet_string(p, &vp, sizeof(pctx->context_string), + &(pctx->context_string_len))) { + pctx->context_string_len = 0; + return 0; + } + } + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_TEST_ENTROPY); + if (p != NULL) { + void *vp = pctx->add_random; + size_t n = ossl_slh_dsa_key_get_n(pctx->key); + + if (!OSSL_PARAM_get_octet_string(p, &vp, n, &(pctx->add_random_len)) + || pctx->add_random_len != n) { + pctx->add_random_len = 0; + return 0; + } + } + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_DETERMINISTIC); + if (p != NULL && !OSSL_PARAM_get_int(p, &pctx->deterministic)) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_MESSAGE_ENCODING); + if (p != NULL && !OSSL_PARAM_get_int(p, &pctx->msg_encode)) + return 0; + return 1; +} + +static const OSSL_PARAM *slh_dsa_settable_ctx_params(void *vctx, + ossl_unused void *provctx) +{ + static const OSSL_PARAM settable_ctx_params[] = { + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, NULL, 0), + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_TEST_ENTROPY, NULL, 0), + OSSL_PARAM_int(OSSL_SIGNATURE_PARAM_DETERMINISTIC, 0), + OSSL_PARAM_int(OSSL_SIGNATURE_PARAM_MESSAGE_ENCODING, 0), + OSSL_PARAM_END + }; + + return settable_ctx_params; +} + +static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_ALGORITHM_ID, NULL, 0), + OSSL_PARAM_END +}; + +static const OSSL_PARAM *slh_dsa_gettable_ctx_params(ossl_unused void *vctx, + ossl_unused void *provctx) +{ + return known_gettable_ctx_params; +} + +static int slh_dsa_get_ctx_params(void *vctx, OSSL_PARAM *params) +{ + PROV_SLH_DSA_CTX *ctx = (PROV_SLH_DSA_CTX *)vctx; + OSSL_PARAM *p; + + if (ctx == NULL) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_ALGORITHM_ID); + if (p != NULL + && !OSSL_PARAM_set_octet_string(p, + ctx->aid_len == 0 ? NULL : ctx->aid_buf, + ctx->aid_len)) + return 0; + + return 1; +} + +#define MAKE_SIGNATURE_FUNCTIONS(alg, fn) \ + static OSSL_FUNC_signature_newctx_fn slh_dsa_##fn##_newctx; \ + static void *slh_dsa_##fn##_newctx(void *provctx, const char *propq) \ + { \ + return slh_dsa_newctx(provctx, alg, propq); \ + } \ + const OSSL_DISPATCH ossl_slh_dsa_##fn##_signature_functions[] = { \ + { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))slh_dsa_##fn##_newctx }, \ + { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_INIT, \ + (void (*)(void))slh_dsa_sign_msg_init }, \ + { OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))slh_dsa_sign }, \ + { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_INIT, \ + (void (*)(void))slh_dsa_verify_msg_init }, \ + { OSSL_FUNC_SIGNATURE_VERIFY, (void (*)(void))slh_dsa_verify }, \ + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT, \ + (void (*)(void))slh_dsa_digest_signverify_init }, \ + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN, \ + (void (*)(void))slh_dsa_digest_sign }, \ + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT, \ + (void (*)(void))slh_dsa_digest_signverify_init }, \ + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY, \ + (void (*)(void))slh_dsa_digest_verify }, \ + { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))slh_dsa_freectx }, \ + { OSSL_FUNC_SIGNATURE_DUPCTX, (void (*)(void))slh_dsa_dupctx }, \ + { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, (void (*)(void))slh_dsa_set_ctx_params },\ + { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, \ + (void (*)(void))slh_dsa_settable_ctx_params }, \ + { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, \ + (void (*)(void))slh_dsa_get_ctx_params }, \ + { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, \ + (void (*)(void))slh_dsa_gettable_ctx_params }, \ + OSSL_DISPATCH_END \ + } + +MAKE_SIGNATURE_FUNCTIONS("SLH-DSA-SHA2-128s", sha2_128s); +MAKE_SIGNATURE_FUNCTIONS("SLH-DSA-SHA2-128f", sha2_128f); +MAKE_SIGNATURE_FUNCTIONS("SLH-DSA-SHA2-192s", sha2_192s); +MAKE_SIGNATURE_FUNCTIONS("SLH-DSA-SHA2-192f", sha2_192f); +MAKE_SIGNATURE_FUNCTIONS("SLH-DSA-SHA2-256s", sha2_256s); +MAKE_SIGNATURE_FUNCTIONS("SLH-DSA-SHA2-256f", sha2_256f); +MAKE_SIGNATURE_FUNCTIONS("SLH-DSA-SHAKE-128s", shake_128s); +MAKE_SIGNATURE_FUNCTIONS("SLH-DSA-SHAKE-128f", shake_128f); +MAKE_SIGNATURE_FUNCTIONS("SLH-DSA-SHAKE-192s", shake_192s); +MAKE_SIGNATURE_FUNCTIONS("SLH-DSA-SHAKE-192f", shake_192f); +MAKE_SIGNATURE_FUNCTIONS("SLH-DSA-SHAKE-256s", shake_256s); +MAKE_SIGNATURE_FUNCTIONS("SLH-DSA-SHAKE-256f", shake_256f); diff --git a/crypto/openssl/providers/implementations/signature/sm2_sig.c b/crypto/openssl/providers/implementations/signature/sm2_sig.c new file mode 100644 index 000000000000..bcbbd1e24503 --- /dev/null +++ b/crypto/openssl/providers/implementations/signature/sm2_sig.c @@ -0,0 +1,585 @@ +/* + * Copyright 2020-2024 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 + */ + +/* + * ECDSA low level APIs are deprecated for public use, but still ok for + * internal use - SM2 implementation uses ECDSA_size() function. + */ +#include "internal/deprecated.h" + +#include <string.h> /* memcpy */ +#include <openssl/crypto.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/dsa.h> +#include <openssl/params.h> +#include <openssl/evp.h> +#include <openssl/err.h> +#include <openssl/proverr.h> +#include "internal/nelem.h" +#include "internal/sizes.h" +#include "internal/cryptlib.h" +#include "internal/sm3.h" +#include "prov/implementations.h" +#include "prov/providercommon.h" +#include "prov/provider_ctx.h" +#include "crypto/ec.h" +#include "crypto/sm2.h" +#include "prov/der_sm2.h" + +static OSSL_FUNC_signature_newctx_fn sm2sig_newctx; +static OSSL_FUNC_signature_sign_init_fn sm2sig_signature_init; +static OSSL_FUNC_signature_verify_init_fn sm2sig_signature_init; +static OSSL_FUNC_signature_sign_fn sm2sig_sign; +static OSSL_FUNC_signature_verify_fn sm2sig_verify; +static OSSL_FUNC_signature_digest_sign_init_fn sm2sig_digest_signverify_init; +static OSSL_FUNC_signature_digest_sign_update_fn sm2sig_digest_signverify_update; +static OSSL_FUNC_signature_digest_sign_final_fn sm2sig_digest_sign_final; +static OSSL_FUNC_signature_digest_verify_init_fn sm2sig_digest_signverify_init; +static OSSL_FUNC_signature_digest_verify_update_fn sm2sig_digest_signverify_update; +static OSSL_FUNC_signature_digest_verify_final_fn sm2sig_digest_verify_final; +static OSSL_FUNC_signature_freectx_fn sm2sig_freectx; +static OSSL_FUNC_signature_dupctx_fn sm2sig_dupctx; +static OSSL_FUNC_signature_get_ctx_params_fn sm2sig_get_ctx_params; +static OSSL_FUNC_signature_gettable_ctx_params_fn sm2sig_gettable_ctx_params; +static OSSL_FUNC_signature_set_ctx_params_fn sm2sig_set_ctx_params; +static OSSL_FUNC_signature_settable_ctx_params_fn sm2sig_settable_ctx_params; +static OSSL_FUNC_signature_get_ctx_md_params_fn sm2sig_get_ctx_md_params; +static OSSL_FUNC_signature_gettable_ctx_md_params_fn sm2sig_gettable_ctx_md_params; +static OSSL_FUNC_signature_set_ctx_md_params_fn sm2sig_set_ctx_md_params; +static OSSL_FUNC_signature_settable_ctx_md_params_fn sm2sig_settable_ctx_md_params; + +/* + * What's passed as an actual key is defined by the KEYMGMT interface. + * We happen to know that our KEYMGMT simply passes EC structures, so + * we use that here too. + */ +typedef struct { + OSSL_LIB_CTX *libctx; + char *propq; + EC_KEY *ec; + + /* + * Flag to determine if the 'z' digest needs to be computed and fed to the + * hash function. + * This flag should be set on initialization and the computation should + * be performed only once, on first update. + */ + unsigned int flag_compute_z_digest : 1; + + char mdname[OSSL_MAX_NAME_SIZE]; + + /* The Algorithm Identifier of the combined signature algorithm */ + unsigned char aid_buf[OSSL_MAX_ALGORITHM_ID_SIZE]; + size_t aid_len; + + /* main digest */ + EVP_MD *md; + EVP_MD_CTX *mdctx; + size_t mdsize; + + /* SM2 ID used for calculating the Z value */ + unsigned char *id; + size_t id_len; +} PROV_SM2_CTX; + +static int sm2sig_set_mdname(PROV_SM2_CTX *psm2ctx, const char *mdname) +{ + if (psm2ctx->md == NULL) /* We need an SM3 md to compare with */ + psm2ctx->md = EVP_MD_fetch(psm2ctx->libctx, psm2ctx->mdname, + psm2ctx->propq); + if (psm2ctx->md == NULL) + return 0; + + /* XOF digests don't work */ + if (EVP_MD_xof(psm2ctx->md)) { + ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED); + return 0; + } + + if (mdname == NULL) + return 1; + + if (strlen(mdname) >= sizeof(psm2ctx->mdname) + || !EVP_MD_is_a(psm2ctx->md, mdname)) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST, "digest=%s", + mdname); + return 0; + } + + OPENSSL_strlcpy(psm2ctx->mdname, mdname, sizeof(psm2ctx->mdname)); + return 1; +} + +static void *sm2sig_newctx(void *provctx, const char *propq) +{ + PROV_SM2_CTX *ctx = OPENSSL_zalloc(sizeof(PROV_SM2_CTX)); + + if (ctx == NULL) + return NULL; + + ctx->libctx = PROV_LIBCTX_OF(provctx); + if (propq != NULL && (ctx->propq = OPENSSL_strdup(propq)) == NULL) { + OPENSSL_free(ctx); + return NULL; + } + ctx->mdsize = SM3_DIGEST_LENGTH; + strcpy(ctx->mdname, OSSL_DIGEST_NAME_SM3); + return ctx; +} + +static int sm2sig_signature_init(void *vpsm2ctx, void *ec, + const OSSL_PARAM params[]) +{ + PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx; + + if (!ossl_prov_is_running() + || psm2ctx == NULL) + return 0; + + if (ec == NULL && psm2ctx->ec == NULL) { + ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET); + return 0; + } + + if (ec != NULL) { + if (!EC_KEY_up_ref(ec)) + return 0; + EC_KEY_free(psm2ctx->ec); + psm2ctx->ec = ec; + } + + return sm2sig_set_ctx_params(psm2ctx, params); +} + +static int sm2sig_sign(void *vpsm2ctx, unsigned char *sig, size_t *siglen, + size_t sigsize, const unsigned char *tbs, size_t tbslen) +{ + PROV_SM2_CTX *ctx = (PROV_SM2_CTX *)vpsm2ctx; + int ret; + unsigned int sltmp; + /* SM2 uses ECDSA_size as well */ + size_t ecsize = ECDSA_size(ctx->ec); + + if (sig == NULL) { + *siglen = ecsize; + return 1; + } + + if (sigsize < (size_t)ecsize) + return 0; + + if (ctx->mdsize != 0 && tbslen != ctx->mdsize) + return 0; + + ret = ossl_sm2_internal_sign(tbs, tbslen, sig, &sltmp, ctx->ec); + if (ret <= 0) + return 0; + + *siglen = sltmp; + return 1; +} + +static int sm2sig_verify(void *vpsm2ctx, const unsigned char *sig, size_t siglen, + const unsigned char *tbs, size_t tbslen) +{ + PROV_SM2_CTX *ctx = (PROV_SM2_CTX *)vpsm2ctx; + + if (ctx->mdsize != 0 && tbslen != ctx->mdsize) + return 0; + + return ossl_sm2_internal_verify(tbs, tbslen, sig, siglen, ctx->ec); +} + +static void free_md(PROV_SM2_CTX *ctx) +{ + EVP_MD_CTX_free(ctx->mdctx); + EVP_MD_free(ctx->md); + ctx->mdctx = NULL; + ctx->md = NULL; +} + +static int sm2sig_digest_signverify_init(void *vpsm2ctx, const char *mdname, + void *ec, const OSSL_PARAM params[]) +{ + PROV_SM2_CTX *ctx = (PROV_SM2_CTX *)vpsm2ctx; + int md_nid; + WPACKET pkt; + int ret = 0; + unsigned char *aid = NULL; + + if (!sm2sig_signature_init(vpsm2ctx, ec, params) + || !sm2sig_set_mdname(ctx, mdname)) + return ret; + + if (ctx->mdctx == NULL) { + ctx->mdctx = EVP_MD_CTX_new(); + if (ctx->mdctx == NULL) + goto error; + } + + md_nid = EVP_MD_get_type(ctx->md); + + /* + * We do not care about DER writing errors. + * All it really means is that for some reason, there's no + * AlgorithmIdentifier to be had, but the operation itself is + * still valid, just as long as it's not used to construct + * anything that needs an AlgorithmIdentifier. + */ + ctx->aid_len = 0; + if (WPACKET_init_der(&pkt, ctx->aid_buf, sizeof(ctx->aid_buf)) + && ossl_DER_w_algorithmIdentifier_SM2_with_MD(&pkt, -1, ctx->ec, md_nid) + && WPACKET_finish(&pkt)) { + WPACKET_get_total_written(&pkt, &ctx->aid_len); + aid = WPACKET_get_curr(&pkt); + } + WPACKET_cleanup(&pkt); + if (aid != NULL && ctx->aid_len != 0) + memmove(ctx->aid_buf, aid, ctx->aid_len); + + if (!EVP_DigestInit_ex2(ctx->mdctx, ctx->md, params)) + goto error; + + ctx->flag_compute_z_digest = 1; + + ret = 1; + + error: + return ret; +} + +static int sm2sig_compute_z_digest(PROV_SM2_CTX *ctx) +{ + uint8_t *z = NULL; + int ret = 1; + + if (ctx->flag_compute_z_digest) { + /* Only do this once */ + ctx->flag_compute_z_digest = 0; + + if ((z = OPENSSL_zalloc(ctx->mdsize)) == NULL + /* get hashed prefix 'z' of tbs message */ + || !ossl_sm2_compute_z_digest(z, ctx->md, ctx->id, ctx->id_len, + ctx->ec) + || !EVP_DigestUpdate(ctx->mdctx, z, ctx->mdsize)) + ret = 0; + OPENSSL_free(z); + } + + return ret; +} + +int sm2sig_digest_signverify_update(void *vpsm2ctx, const unsigned char *data, + size_t datalen) +{ + PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx; + + if (psm2ctx == NULL || psm2ctx->mdctx == NULL) + return 0; + + return sm2sig_compute_z_digest(psm2ctx) + && EVP_DigestUpdate(psm2ctx->mdctx, data, datalen); +} + +int sm2sig_digest_sign_final(void *vpsm2ctx, unsigned char *sig, size_t *siglen, + size_t sigsize) +{ + PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx; + unsigned char digest[EVP_MAX_MD_SIZE]; + unsigned int dlen = 0; + + if (psm2ctx == NULL || psm2ctx->mdctx == NULL) + return 0; + + /* + * If sig is NULL then we're just finding out the sig size. Other fields + * are ignored. Defer to sm2sig_sign. + */ + if (sig != NULL) { + if (!(sm2sig_compute_z_digest(psm2ctx) + && EVP_DigestFinal_ex(psm2ctx->mdctx, digest, &dlen))) + return 0; + } + + return sm2sig_sign(vpsm2ctx, sig, siglen, sigsize, digest, (size_t)dlen); +} + + +int sm2sig_digest_verify_final(void *vpsm2ctx, const unsigned char *sig, + size_t siglen) +{ + PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx; + unsigned char digest[EVP_MAX_MD_SIZE]; + unsigned int dlen = 0; + int md_size; + + if (psm2ctx == NULL || psm2ctx->mdctx == NULL) + return 0; + + md_size = EVP_MD_get_size(psm2ctx->md); + if (md_size <= 0 || md_size > (int)sizeof(digest)) + return 0; + + if (!(sm2sig_compute_z_digest(psm2ctx) + && EVP_DigestFinal_ex(psm2ctx->mdctx, digest, &dlen))) + return 0; + + return sm2sig_verify(vpsm2ctx, sig, siglen, digest, (size_t)dlen); +} + +static void sm2sig_freectx(void *vpsm2ctx) +{ + PROV_SM2_CTX *ctx = (PROV_SM2_CTX *)vpsm2ctx; + + free_md(ctx); + EC_KEY_free(ctx->ec); + OPENSSL_free(ctx->propq); + OPENSSL_free(ctx->id); + OPENSSL_free(ctx); +} + +static void *sm2sig_dupctx(void *vpsm2ctx) +{ + PROV_SM2_CTX *srcctx = (PROV_SM2_CTX *)vpsm2ctx; + PROV_SM2_CTX *dstctx; + + dstctx = OPENSSL_zalloc(sizeof(*srcctx)); + if (dstctx == NULL) + return NULL; + + *dstctx = *srcctx; + dstctx->ec = NULL; + dstctx->propq = NULL; + dstctx->md = NULL; + dstctx->mdctx = NULL; + dstctx->id = NULL; + + if (srcctx->ec != NULL && !EC_KEY_up_ref(srcctx->ec)) + goto err; + dstctx->ec = srcctx->ec; + + if (srcctx->propq != NULL) { + dstctx->propq = OPENSSL_strdup(srcctx->propq); + if (dstctx->propq == NULL) + goto err; + } + + if (srcctx->md != NULL && !EVP_MD_up_ref(srcctx->md)) + goto err; + dstctx->md = srcctx->md; + + if (srcctx->mdctx != NULL) { + dstctx->mdctx = EVP_MD_CTX_new(); + if (dstctx->mdctx == NULL + || !EVP_MD_CTX_copy_ex(dstctx->mdctx, srcctx->mdctx)) + goto err; + } + + if (srcctx->id != NULL) { + dstctx->id = OPENSSL_malloc(srcctx->id_len); + if (dstctx->id == NULL) + goto err; + dstctx->id_len = srcctx->id_len; + memcpy(dstctx->id, srcctx->id, srcctx->id_len); + } + + return dstctx; + err: + sm2sig_freectx(dstctx); + return NULL; +} + +static int sm2sig_get_ctx_params(void *vpsm2ctx, OSSL_PARAM *params) +{ + PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx; + OSSL_PARAM *p; + + if (psm2ctx == NULL) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_ALGORITHM_ID); + if (p != NULL + && !OSSL_PARAM_set_octet_string(p, + psm2ctx->aid_len == 0 ? NULL : psm2ctx->aid_buf, + psm2ctx->aid_len)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_DIGEST_SIZE); + if (p != NULL && !OSSL_PARAM_set_size_t(p, psm2ctx->mdsize)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_DIGEST); + if (p != NULL && !OSSL_PARAM_set_utf8_string(p, psm2ctx->md == NULL + ? psm2ctx->mdname + : EVP_MD_get0_name(psm2ctx->md))) + return 0; + + return 1; +} + +static const OSSL_PARAM known_gettable_ctx_params[] = { + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_ALGORITHM_ID, NULL, 0), + OSSL_PARAM_size_t(OSSL_SIGNATURE_PARAM_DIGEST_SIZE, NULL), + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_END +}; + +static const OSSL_PARAM *sm2sig_gettable_ctx_params(ossl_unused void *vpsm2ctx, + ossl_unused void *provctx) +{ + return known_gettable_ctx_params; +} + +static int sm2sig_set_ctx_params(void *vpsm2ctx, const OSSL_PARAM params[]) +{ + PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx; + const OSSL_PARAM *p; + size_t mdsize; + + if (psm2ctx == NULL) + return 0; + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_DIST_ID); + if (p != NULL) { + void *tmp_id = NULL; + size_t tmp_idlen = 0; + + /* + * If the 'z' digest has already been computed, the ID is set too late + */ + if (!psm2ctx->flag_compute_z_digest) + return 0; + + if (p->data_size != 0 + && !OSSL_PARAM_get_octet_string(p, &tmp_id, 0, &tmp_idlen)) + return 0; + OPENSSL_free(psm2ctx->id); + psm2ctx->id = tmp_id; + psm2ctx->id_len = tmp_idlen; + } + + /* + * The following code checks that the size is the same as the SM3 digest + * size returning an error otherwise. + * If there is ever any different digest algorithm allowed with SM2 + * this needs to be adjusted accordingly. + */ + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_DIGEST_SIZE); + if (p != NULL && (!OSSL_PARAM_get_size_t(p, &mdsize) + || mdsize != psm2ctx->mdsize)) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_DIGEST); + if (p != NULL) { + char *mdname = NULL; + + if (!OSSL_PARAM_get_utf8_string(p, &mdname, 0)) + return 0; + if (!sm2sig_set_mdname(psm2ctx, mdname)) { + OPENSSL_free(mdname); + return 0; + } + OPENSSL_free(mdname); + } + + return 1; +} + +static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_size_t(OSSL_SIGNATURE_PARAM_DIGEST_SIZE, NULL), + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_DIST_ID, NULL, 0), + OSSL_PARAM_END +}; + +static const OSSL_PARAM *sm2sig_settable_ctx_params(ossl_unused void *vpsm2ctx, + ossl_unused void *provctx) +{ + return known_settable_ctx_params; +} + +static int sm2sig_get_ctx_md_params(void *vpsm2ctx, OSSL_PARAM *params) +{ + PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx; + + if (psm2ctx->mdctx == NULL) + return 0; + + return EVP_MD_CTX_get_params(psm2ctx->mdctx, params); +} + +static const OSSL_PARAM *sm2sig_gettable_ctx_md_params(void *vpsm2ctx) +{ + PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx; + + if (psm2ctx->md == NULL) + return 0; + + return EVP_MD_gettable_ctx_params(psm2ctx->md); +} + +static int sm2sig_set_ctx_md_params(void *vpsm2ctx, const OSSL_PARAM params[]) +{ + PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx; + + if (psm2ctx->mdctx == NULL) + return 0; + + return EVP_MD_CTX_set_params(psm2ctx->mdctx, params); +} + +static const OSSL_PARAM *sm2sig_settable_ctx_md_params(void *vpsm2ctx) +{ + PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx; + + if (psm2ctx->md == NULL) + return 0; + + return EVP_MD_settable_ctx_params(psm2ctx->md); +} + +const OSSL_DISPATCH ossl_sm2_signature_functions[] = { + { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))sm2sig_newctx }, + { OSSL_FUNC_SIGNATURE_SIGN_INIT, (void (*)(void))sm2sig_signature_init }, + { OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))sm2sig_sign }, + { OSSL_FUNC_SIGNATURE_VERIFY_INIT, (void (*)(void))sm2sig_signature_init }, + { OSSL_FUNC_SIGNATURE_VERIFY, (void (*)(void))sm2sig_verify }, + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT, + (void (*)(void))sm2sig_digest_signverify_init }, + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_UPDATE, + (void (*)(void))sm2sig_digest_signverify_update }, + { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_FINAL, + (void (*)(void))sm2sig_digest_sign_final }, + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT, + (void (*)(void))sm2sig_digest_signverify_init }, + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_UPDATE, + (void (*)(void))sm2sig_digest_signverify_update }, + { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_FINAL, + (void (*)(void))sm2sig_digest_verify_final }, + { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))sm2sig_freectx }, + { OSSL_FUNC_SIGNATURE_DUPCTX, (void (*)(void))sm2sig_dupctx }, + { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, (void (*)(void))sm2sig_get_ctx_params }, + { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, + (void (*)(void))sm2sig_gettable_ctx_params }, + { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, (void (*)(void))sm2sig_set_ctx_params }, + { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, + (void (*)(void))sm2sig_settable_ctx_params }, + { OSSL_FUNC_SIGNATURE_GET_CTX_MD_PARAMS, + (void (*)(void))sm2sig_get_ctx_md_params }, + { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_MD_PARAMS, + (void (*)(void))sm2sig_gettable_ctx_md_params }, + { OSSL_FUNC_SIGNATURE_SET_CTX_MD_PARAMS, + (void (*)(void))sm2sig_set_ctx_md_params }, + { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_MD_PARAMS, + (void (*)(void))sm2sig_settable_ctx_md_params }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/skeymgmt/aes_skmgmt.c b/crypto/openssl/providers/implementations/skeymgmt/aes_skmgmt.c new file mode 100644 index 000000000000..6d3b5f377fb8 --- /dev/null +++ b/crypto/openssl/providers/implementations/skeymgmt/aes_skmgmt.c @@ -0,0 +1,52 @@ +/* + * Copyright 2025 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 + */ + +#include <openssl/core_dispatch.h> +#include "crypto/types.h" +#include "skeymgmt_lcl.h" +#include "internal/skey.h" +#include "prov/implementations.h" + +static OSSL_FUNC_skeymgmt_import_fn aes_import; +static OSSL_FUNC_skeymgmt_export_fn aes_export; + +static void *aes_import(void *provctx, int selection, + const OSSL_PARAM params[]) +{ + PROV_SKEY *aes = generic_import(provctx, selection, params); + + if (aes == NULL) + return NULL; + + if (aes->length != 16 && aes->length != 24 && aes->length != 32) { + generic_free(aes); + return NULL; + } + aes->type = SKEY_TYPE_AES; + + return aes; +} + +static int aes_export(void *keydata, int selection, + OSSL_CALLBACK *param_callback, void *cbarg) +{ + PROV_SKEY *aes = keydata; + + if (aes->type != SKEY_TYPE_AES) + return 0; + + return generic_export(keydata, selection, param_callback, cbarg); +} + +const OSSL_DISPATCH ossl_aes_skeymgmt_functions[] = { + { OSSL_FUNC_SKEYMGMT_FREE, (void (*)(void))generic_free }, + { OSSL_FUNC_SKEYMGMT_IMPORT, (void (*)(void))aes_import }, + { OSSL_FUNC_SKEYMGMT_EXPORT, (void (*)(void))aes_export }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/skeymgmt/build.info b/crypto/openssl/providers/implementations/skeymgmt/build.info new file mode 100644 index 000000000000..8bccdd72f6c6 --- /dev/null +++ b/crypto/openssl/providers/implementations/skeymgmt/build.info @@ -0,0 +1,8 @@ +# We make separate GOAL variables for each algorithm, to make it easy to +# switch each to the Legacy provider when needed. + +$AES_GOAL=../../libdefault.a ../../libfips.a +$GENERIC_GOAL=../../libdefault.a ../../libfips.a + +SOURCE[$AES_GOAL]=aes_skmgmt.c +SOURCE[$GENERIC_GOAL]=generic.c diff --git a/crypto/openssl/providers/implementations/skeymgmt/generic.c b/crypto/openssl/providers/implementations/skeymgmt/generic.c new file mode 100644 index 000000000000..b41bf8e12dcb --- /dev/null +++ b/crypto/openssl/providers/implementations/skeymgmt/generic.c @@ -0,0 +1,93 @@ +/* + * Copyright 2025 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 + */ + +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include "crypto/types.h" +#include "internal/skey.h" +#include "prov/provider_ctx.h" +#include "prov/providercommon.h" +#include "prov/implementations.h" +#include "skeymgmt_lcl.h" + +void generic_free(void *keydata) +{ + PROV_SKEY *generic = keydata; + + if (generic == NULL) + return; + + OPENSSL_free(generic->data); + OPENSSL_free(generic); +} + +void *generic_import(void *provctx, int selection, const OSSL_PARAM params[]) +{ + OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx); + const OSSL_PARAM *raw_bytes; + PROV_SKEY *generic = NULL; + int ok = 0; + + if (!ossl_prov_is_running()) + return NULL; + + if ((selection & OSSL_SKEYMGMT_SELECT_SECRET_KEY) == 0) + return NULL; + + raw_bytes = OSSL_PARAM_locate_const(params, OSSL_SKEY_PARAM_RAW_BYTES); + if (raw_bytes == NULL) + return NULL; + + generic = OPENSSL_zalloc(sizeof(PROV_SKEY)); + if (generic == NULL) + return NULL; + + generic->libctx = libctx; + + generic->type = SKEY_TYPE_GENERIC; + + if ((generic->data = OPENSSL_memdup(raw_bytes->data, raw_bytes->data_size)) == NULL) + goto end; + generic->length = raw_bytes->data_size; + ok = 1; + +end: + if (ok == 0) { + generic_free(generic); + generic = NULL; + } + return generic; +} + +int generic_export(void *keydata, int selection, + OSSL_CALLBACK *param_callback, void *cbarg) +{ + PROV_SKEY *gen = keydata; + OSSL_PARAM params[2]; + + if (!ossl_prov_is_running() || gen == NULL) + return 0; + + /* If we use generic SKEYMGMT as a "base class", we shouldn't check the type */ + if ((selection & OSSL_SKEYMGMT_SELECT_SECRET_KEY) == 0) + return 0; + + params[0] = OSSL_PARAM_construct_octet_string(OSSL_SKEY_PARAM_RAW_BYTES, + gen->data, gen->length); + params[1] = OSSL_PARAM_construct_end(); + + return param_callback(params, cbarg); +} + +const OSSL_DISPATCH ossl_generic_skeymgmt_functions[] = { + { OSSL_FUNC_SKEYMGMT_FREE, (void (*)(void))generic_free }, + { OSSL_FUNC_SKEYMGMT_IMPORT, (void (*)(void))generic_import }, + { OSSL_FUNC_SKEYMGMT_EXPORT, (void (*)(void))generic_export }, + OSSL_DISPATCH_END +}; diff --git a/crypto/openssl/providers/implementations/skeymgmt/skeymgmt_lcl.h b/crypto/openssl/providers/implementations/skeymgmt/skeymgmt_lcl.h new file mode 100644 index 000000000000..c180c1d30359 --- /dev/null +++ b/crypto/openssl/providers/implementations/skeymgmt/skeymgmt_lcl.h @@ -0,0 +1,19 @@ +/* + * Copyright 2025 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 + */ + +#ifndef OSSL_PROVIDERS_SKEYMGMT_LCL_H +# define OSSL_PROVIDERS_SKEYMGMT_LCL_H +# pragma once +# include <openssl/core_dispatch.h> + +OSSL_FUNC_skeymgmt_import_fn generic_import; +OSSL_FUNC_skeymgmt_export_fn generic_export; +OSSL_FUNC_skeymgmt_free_fn generic_free; + +#endif diff --git a/crypto/openssl/providers/implementations/storemgmt/build.info b/crypto/openssl/providers/implementations/storemgmt/build.info new file mode 100644 index 000000000000..d0b117492660 --- /dev/null +++ b/crypto/openssl/providers/implementations/storemgmt/build.info @@ -0,0 +1,9 @@ +# We make separate GOAL variables for each algorithm, to make it easy to +# switch each to the Legacy provider when needed. + +$STORE_GOAL=../../libdefault.a + +SOURCE[$STORE_GOAL]=file_store.c file_store_any2obj.c +IF[{- !$disabled{winstore} -}] + SOURCE[$STORE_GOAL]=winstore_store.c +ENDIF diff --git a/crypto/openssl/providers/implementations/storemgmt/file_store.c b/crypto/openssl/providers/implementations/storemgmt/file_store.c new file mode 100644 index 000000000000..134b8efefbb2 --- /dev/null +++ b/crypto/openssl/providers/implementations/storemgmt/file_store.c @@ -0,0 +1,829 @@ +/* + * Copyright 2020-2025 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 + */ + +/* This file has quite some overlap with engines/e_loader_attic.c */ + +#include <string.h> +#include <sys/stat.h> +#include <ctype.h> /* isdigit */ +#include <assert.h> + +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/core_object.h> +#include <openssl/bio.h> +#include <openssl/err.h> +#include <openssl/params.h> +#include <openssl/decoder.h> +#include <openssl/proverr.h> +#include <openssl/store.h> /* The OSSL_STORE_INFO type numbers */ +#include "internal/cryptlib.h" +#include "internal/o_dir.h" +#include "crypto/decoder.h" +#include "crypto/ctype.h" /* ossl_isdigit() */ +#include "prov/implementations.h" +#include "prov/bio.h" +#include "prov/providercommon.h" +#include "file_store_local.h" + +DEFINE_STACK_OF(OSSL_STORE_INFO) + +#ifdef _WIN32 +# define stat _stat +#endif + +#ifndef S_ISDIR +# define S_ISDIR(a) (((a) & S_IFMT) == S_IFDIR) +#endif + +static OSSL_FUNC_store_open_fn file_open; +static OSSL_FUNC_store_attach_fn file_attach; +static OSSL_FUNC_store_settable_ctx_params_fn file_settable_ctx_params; +static OSSL_FUNC_store_set_ctx_params_fn file_set_ctx_params; +static OSSL_FUNC_store_load_fn file_load; +static OSSL_FUNC_store_eof_fn file_eof; +static OSSL_FUNC_store_close_fn file_close; + +/* + * This implementation makes full use of OSSL_DECODER, and then some. + * It uses its own internal decoder implementation that reads DER and + * passes that on to the data callback; this decoder is created with + * internal OpenSSL functions, thereby bypassing the need for a surrounding + * provider. This is ok, since this is a local decoder, not meant for + * public consumption. + * Finally, it sets up its own construct and cleanup functions. + * + * Essentially, that makes this implementation a kind of glorified decoder. + */ + +struct file_ctx_st { + void *provctx; + char *uri; /* The URI we currently try to load */ + enum { + IS_FILE = 0, /* Read file and pass results */ + IS_DIR /* Pass directory entry names */ + } type; + + union { + /* Used with |IS_FILE| */ + struct { + BIO *file; + + OSSL_DECODER_CTX *decoderctx; + char *input_type; + char *propq; /* The properties we got as a parameter */ + } file; + + /* Used with |IS_DIR| */ + struct { + OPENSSL_DIR_CTX *ctx; + int end_reached; + + /* + * When a search expression is given, these are filled in. + * |search_name| contains the file basename to look for. + * The string is exactly 8 characters long. + */ + char search_name[9]; + + /* + * The directory reading utility we have combines opening with + * reading the first name. To make sure we can detect the end + * at the right time, we read early and cache the name. + */ + const char *last_entry; + int last_errno; + } dir; + } _; + + /* Expected object type. May be unspecified */ + int expected_type; +}; + +static void free_file_ctx(struct file_ctx_st *ctx) +{ + if (ctx == NULL) + return; + + OPENSSL_free(ctx->uri); + if (ctx->type != IS_DIR) { + OSSL_DECODER_CTX_free(ctx->_.file.decoderctx); + OPENSSL_free(ctx->_.file.propq); + OPENSSL_free(ctx->_.file.input_type); + } + OPENSSL_free(ctx); +} + +static struct file_ctx_st *new_file_ctx(int type, const char *uri, + void *provctx) +{ + struct file_ctx_st *ctx = NULL; + + if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) != NULL + && (uri == NULL || (ctx->uri = OPENSSL_strdup(uri)) != NULL)) { + ctx->type = type; + ctx->provctx = provctx; + return ctx; + } + free_file_ctx(ctx); + return NULL; +} + +static OSSL_DECODER_CONSTRUCT file_load_construct; +static OSSL_DECODER_CLEANUP file_load_cleanup; + +/*- + * Opening / attaching streams and directories + * ------------------------------------------- + */ + +/* + * Function to service both file_open() and file_attach() + * + * + */ +static struct file_ctx_st *file_open_stream(BIO *source, const char *uri, + void *provctx) +{ + struct file_ctx_st *ctx; + + if ((ctx = new_file_ctx(IS_FILE, uri, provctx)) == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_PROV_LIB); + goto err; + } + + ctx->_.file.file = source; + + return ctx; + err: + free_file_ctx(ctx); + return NULL; +} + +static void *file_open_dir(const char *path, const char *uri, void *provctx) +{ + struct file_ctx_st *ctx; + + if ((ctx = new_file_ctx(IS_DIR, uri, provctx)) == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_PROV_LIB); + return NULL; + } + + ctx->_.dir.last_entry = OPENSSL_DIR_read(&ctx->_.dir.ctx, path); + ctx->_.dir.last_errno = errno; + if (ctx->_.dir.last_entry == NULL) { + if (ctx->_.dir.last_errno != 0) { + ERR_raise_data(ERR_LIB_SYS, ctx->_.dir.last_errno, + "Calling OPENSSL_DIR_read(\"%s\")", path); + goto err; + } + ctx->_.dir.end_reached = 1; + } + return ctx; + err: + file_close(ctx); + return NULL; +} + +static void *file_open(void *provctx, const char *uri) +{ + struct file_ctx_st *ctx = NULL; + struct stat st; + struct { + const char *path; + unsigned int check_absolute:1; + } path_data[2]; + size_t path_data_n = 0, i; + const char *path, *p = uri, *q; + BIO *bio; + + ERR_set_mark(); + + /* + * First step, just take the URI as is. + */ + path_data[path_data_n].check_absolute = 0; + path_data[path_data_n++].path = uri; + + /* + * Second step, if the URI appears to start with the "file" scheme, + * extract the path and make that the second path to check. + * There's a special case if the URI also contains an authority, then + * the full URI shouldn't be used as a path anywhere. + */ + if (CHECK_AND_SKIP_CASE_PREFIX(p, "file:")) { + q = p; + if (CHECK_AND_SKIP_CASE_PREFIX(q, "//")) { + path_data_n--; /* Invalidate using the full URI */ + if (CHECK_AND_SKIP_CASE_PREFIX(q, "localhost/") + || CHECK_AND_SKIP_CASE_PREFIX(q, "/")) { + p = q - 1; + } else { + ERR_clear_last_mark(); + ERR_raise(ERR_LIB_PROV, PROV_R_URI_AUTHORITY_UNSUPPORTED); + return NULL; + } + } + + path_data[path_data_n].check_absolute = 1; +#ifdef _WIN32 + /* Windows "file:" URIs with a drive letter start with a '/' */ + if (p[0] == '/' && p[2] == ':' && p[3] == '/') { + char c = tolower((unsigned char)p[1]); + + if (c >= 'a' && c <= 'z') { + p++; + /* We know it's absolute, so no need to check */ + path_data[path_data_n].check_absolute = 0; + } + } +#endif + path_data[path_data_n++].path = p; + } + + + for (i = 0, path = NULL; path == NULL && i < path_data_n; i++) { + /* + * If the scheme "file" was an explicit part of the URI, the path must + * be absolute. So says RFC 8089 + */ + if (path_data[i].check_absolute && path_data[i].path[0] != '/') { + ERR_clear_last_mark(); + ERR_raise_data(ERR_LIB_PROV, PROV_R_PATH_MUST_BE_ABSOLUTE, + "Given path=%s", path_data[i].path); + return NULL; + } + + if (stat(path_data[i].path, &st) < 0) { + ERR_raise_data(ERR_LIB_SYS, errno, + "calling stat(%s)", + path_data[i].path); + } else { + path = path_data[i].path; + } + } + if (path == NULL) { + ERR_clear_last_mark(); + return NULL; + } + + /* Successfully found a working path, clear possible collected errors */ + ERR_pop_to_mark(); + + if (S_ISDIR(st.st_mode)) + ctx = file_open_dir(path, uri, provctx); + else if ((bio = BIO_new_file(path, "rb")) == NULL + || (ctx = file_open_stream(bio, uri, provctx)) == NULL) + BIO_free_all(bio); + + return ctx; +} + +void *file_attach(void *provctx, OSSL_CORE_BIO *cin) +{ + struct file_ctx_st *ctx; + BIO *new_bio = ossl_bio_new_from_core_bio(provctx, cin); + + if (new_bio == NULL) + return NULL; + + ctx = file_open_stream(new_bio, NULL, provctx); + if (ctx == NULL) + BIO_free(new_bio); + return ctx; +} + +/*- + * Setting parameters + * ------------------ + */ + +static const OSSL_PARAM *file_settable_ctx_params(void *provctx) +{ + static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_STORE_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_int(OSSL_STORE_PARAM_EXPECT, NULL), + OSSL_PARAM_octet_string(OSSL_STORE_PARAM_SUBJECT, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_STORE_PARAM_INPUT_TYPE, NULL, 0), + OSSL_PARAM_END + }; + return known_settable_ctx_params; +} + +static int file_set_ctx_params(void *loaderctx, const OSSL_PARAM params[]) +{ + struct file_ctx_st *ctx = loaderctx; + const OSSL_PARAM *p; + + if (ossl_param_is_empty(params)) + return 1; + + if (ctx->type != IS_DIR) { + /* these parameters are ignored for directories */ + p = OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_PROPERTIES); + if (p != NULL) { + OPENSSL_free(ctx->_.file.propq); + ctx->_.file.propq = NULL; + if (!OSSL_PARAM_get_utf8_string(p, &ctx->_.file.propq, 0)) + return 0; + } + p = OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_INPUT_TYPE); + if (p != NULL) { + OPENSSL_free(ctx->_.file.input_type); + ctx->_.file.input_type = NULL; + if (!OSSL_PARAM_get_utf8_string(p, &ctx->_.file.input_type, 0)) + return 0; + } + } + p = OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_EXPECT); + if (p != NULL && !OSSL_PARAM_get_int(p, &ctx->expected_type)) + return 0; + p = OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_SUBJECT); + if (p != NULL) { + const unsigned char *der = NULL; + size_t der_len = 0; + X509_NAME *x509_name; + unsigned long hash; + int ok; + + if (ctx->type != IS_DIR) { + ERR_raise(ERR_LIB_PROV, + PROV_R_SEARCH_ONLY_SUPPORTED_FOR_DIRECTORIES); + return 0; + } + + if (!OSSL_PARAM_get_octet_string_ptr(p, (const void **)&der, &der_len) + || (x509_name = d2i_X509_NAME(NULL, &der, der_len)) == NULL) + return 0; + hash = X509_NAME_hash_ex(x509_name, + ossl_prov_ctx_get0_libctx(ctx->provctx), NULL, + &ok); + BIO_snprintf(ctx->_.dir.search_name, sizeof(ctx->_.dir.search_name), + "%08lx", hash); + X509_NAME_free(x509_name); + if (ok == 0) + return 0; + } + return 1; +} + +/*- + * Loading an object from a stream + * ------------------------------- + */ + +struct file_load_data_st { + OSSL_CALLBACK *object_cb; + void *object_cbarg; +}; + +static int file_load_construct(OSSL_DECODER_INSTANCE *decoder_inst, + const OSSL_PARAM *params, void *construct_data) +{ + struct file_load_data_st *data = construct_data; + + /* + * At some point, we may find it justifiable to recognise PKCS#12 and + * handle it specially here, making |file_load()| return pass its + * contents one piece at ta time, like |e_loader_attic.c| does. + * + * However, that currently means parsing them out, which converts the + * DER encoded PKCS#12 into a bunch of EVP_PKEYs and X509s, just to + * have to re-encode them into DER to create an object abstraction for + * each of them. + * It's much simpler (less churn) to pass on the object abstraction we + * get to the load_result callback and leave it to that one to do the + * work. If that's libcrypto code, we know that it has much better + * possibilities to handle the EVP_PKEYs and X509s without the extra + * churn. + */ + + return data->object_cb(params, data->object_cbarg); +} + +void file_load_cleanup(void *construct_data) +{ + /* Nothing to do */ +} + +static int file_setup_decoders(struct file_ctx_st *ctx) +{ + OSSL_LIB_CTX *libctx = ossl_prov_ctx_get0_libctx(ctx->provctx); + const OSSL_ALGORITHM *to_algo = NULL; + const char *input_structure = NULL; + int ok = 0; + + /* Setup for this session, so only if not already done */ + if (ctx->_.file.decoderctx == NULL) { + if ((ctx->_.file.decoderctx = OSSL_DECODER_CTX_new()) == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB); + goto err; + } + + /* Make sure the input type is set */ + if (!OSSL_DECODER_CTX_set_input_type(ctx->_.file.decoderctx, + ctx->_.file.input_type)) { + ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB); + goto err; + } + + /* + * Where applicable, set the outermost structure name. + * The goal is to avoid the STORE object types that are + * potentially password protected but aren't interesting + * for this load. + */ + switch (ctx->expected_type) { + case OSSL_STORE_INFO_PUBKEY: + input_structure = "SubjectPublicKeyInfo"; + if (!OSSL_DECODER_CTX_set_input_structure(ctx->_.file.decoderctx, + input_structure)) { + ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB); + goto err; + } + break; + case OSSL_STORE_INFO_PKEY: + /* + * The user's OSSL_STORE_INFO_PKEY covers PKCS#8, whether encrypted + * or not. The decoder will figure out whether decryption is + * applicable and fall back as necessary. We just need to indicate + * that it is OK to try and encrypt, which may involve a password + * prompt, so not done unless the data type is explicit, as we + * might then get a password prompt for a key when reading only + * certs from a file. + */ + input_structure = "EncryptedPrivateKeyInfo"; + if (!OSSL_DECODER_CTX_set_input_structure(ctx->_.file.decoderctx, + input_structure)) { + ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB); + goto err; + } + break; + case OSSL_STORE_INFO_CERT: + input_structure = "Certificate"; + if (!OSSL_DECODER_CTX_set_input_structure(ctx->_.file.decoderctx, + input_structure)) { + ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB); + goto err; + } + break; + case OSSL_STORE_INFO_CRL: + input_structure = "CertificateList"; + if (!OSSL_DECODER_CTX_set_input_structure(ctx->_.file.decoderctx, + input_structure)) { + ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB); + goto err; + } + break; + default: + break; + } + + for (to_algo = ossl_any_to_obj_algorithm; + to_algo->algorithm_names != NULL; + to_algo++) { + OSSL_DECODER *to_obj = NULL; + OSSL_DECODER_INSTANCE *to_obj_inst = NULL; + const char *input_type; + + /* + * Create the internal last resort decoder implementation + * together with a "decoder instance". + * The decoder doesn't need any identification or to be + * attached to any provider, since it's only used locally. + */ + to_obj = ossl_decoder_from_algorithm(0, to_algo, NULL); + if (to_obj != NULL) + to_obj_inst = + ossl_decoder_instance_new_forprov(to_obj, ctx->provctx, + input_structure); + OSSL_DECODER_free(to_obj); + if (to_obj_inst == NULL) + goto err; + /* + * The input type has to match unless, the input type is PEM + * and the decoder input type is DER, in which case we'll pick + * up additional decoders. + */ + input_type = OSSL_DECODER_INSTANCE_get_input_type(to_obj_inst); + if (ctx->_.file.input_type != NULL + && OPENSSL_strcasecmp(input_type, ctx->_.file.input_type) != 0 + && (OPENSSL_strcasecmp(ctx->_.file.input_type, "PEM") != 0 + || OPENSSL_strcasecmp(input_type, "der") != 0)) { + ossl_decoder_instance_free(to_obj_inst); + continue; + } + + if (!ossl_decoder_ctx_add_decoder_inst(ctx->_.file.decoderctx, + to_obj_inst)) { + ossl_decoder_instance_free(to_obj_inst); + ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB); + goto err; + } + } + /* Add on the usual extra decoders */ + if (!OSSL_DECODER_CTX_add_extra(ctx->_.file.decoderctx, + libctx, ctx->_.file.propq)) { + ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB); + goto err; + } + + /* + * Then install our constructor hooks, which just passes decoded + * data to the load callback + */ + if (!OSSL_DECODER_CTX_set_construct(ctx->_.file.decoderctx, + file_load_construct) + || !OSSL_DECODER_CTX_set_cleanup(ctx->_.file.decoderctx, + file_load_cleanup)) { + ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB); + goto err; + } + } + + ok = 1; + err: + return ok; +} + +static int file_load_file(struct file_ctx_st *ctx, + OSSL_CALLBACK *object_cb, void *object_cbarg, + OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg) +{ + struct file_load_data_st data; + int ret, err; + + /* Setup the decoders (one time shot per session */ + + if (!file_setup_decoders(ctx)) + return 0; + + /* Setup for this object */ + + data.object_cb = object_cb; + data.object_cbarg = object_cbarg; + OSSL_DECODER_CTX_set_construct_data(ctx->_.file.decoderctx, &data); + OSSL_DECODER_CTX_set_passphrase_cb(ctx->_.file.decoderctx, pw_cb, pw_cbarg); + + /* Launch */ + + ERR_set_mark(); + ret = OSSL_DECODER_from_bio(ctx->_.file.decoderctx, ctx->_.file.file); + if (BIO_eof(ctx->_.file.file) + && ((err = ERR_peek_last_error()) != 0) + && ERR_GET_LIB(err) == ERR_LIB_OSSL_DECODER + && ERR_GET_REASON(err) == ERR_R_UNSUPPORTED) + ERR_pop_to_mark(); + else + ERR_clear_last_mark(); + return ret; +} + +/*- + * Loading a name object from a directory + * -------------------------------------- + */ + +static char *file_name_to_uri(struct file_ctx_st *ctx, const char *name) +{ + char *data = NULL; + + assert(name != NULL); + { + const char *pathsep = ossl_ends_with_dirsep(ctx->uri) ? "" : "/"; + long calculated_length = strlen(ctx->uri) + strlen(pathsep) + + strlen(name) + 1 /* \0 */; + + data = OPENSSL_zalloc(calculated_length); + if (data == NULL) + return NULL; + + OPENSSL_strlcat(data, ctx->uri, calculated_length); + OPENSSL_strlcat(data, pathsep, calculated_length); + OPENSSL_strlcat(data, name, calculated_length); + } + return data; +} + +static int file_name_check(struct file_ctx_st *ctx, const char *name) +{ + const char *p = NULL; + size_t len = strlen(ctx->_.dir.search_name); + + /* If there are no search criteria, all names are accepted */ + if (ctx->_.dir.search_name[0] == '\0') + return 1; + + /* If the expected type isn't supported, no name is accepted */ + if (ctx->expected_type != 0 + && ctx->expected_type != OSSL_STORE_INFO_CERT + && ctx->expected_type != OSSL_STORE_INFO_CRL) + return 0; + + /* + * First, check the basename + */ + if (OPENSSL_strncasecmp(name, ctx->_.dir.search_name, len) != 0 + || name[len] != '.') + return 0; + p = &name[len + 1]; + + /* + * Then, if the expected type is a CRL, check that the extension starts + * with 'r' + */ + if (*p == 'r') { + p++; + if (ctx->expected_type != 0 + && ctx->expected_type != OSSL_STORE_INFO_CRL) + return 0; + } else if (ctx->expected_type == OSSL_STORE_INFO_CRL) { + return 0; + } + + /* + * Last, check that the rest of the extension is a decimal number, at + * least one digit long. + */ + if (!isdigit((unsigned char)*p)) + return 0; + while (isdigit((unsigned char)*p)) + p++; + +#ifdef __VMS + /* + * One extra step here, check for a possible generation number. + */ + if (*p == ';') + for (p++; *p != '\0'; p++) + if (!ossl_isdigit((unsigned char)*p)) + break; +#endif + + /* + * If we've reached the end of the string at this point, we've successfully + * found a fitting file name. + */ + return *p == '\0'; +} + +static int file_load_dir_entry(struct file_ctx_st *ctx, + OSSL_CALLBACK *object_cb, void *object_cbarg, + OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg) +{ + /* Prepare as much as possible in advance */ + static const int object_type = OSSL_OBJECT_NAME; + OSSL_PARAM object[] = { + OSSL_PARAM_int(OSSL_OBJECT_PARAM_TYPE, (int *)&object_type), + OSSL_PARAM_utf8_string(OSSL_OBJECT_PARAM_DATA, NULL, 0), + OSSL_PARAM_END + }; + char *newname = NULL; + int ok; + + /* Loop until we get an error or until we have a suitable name */ + do { + if (ctx->_.dir.last_entry == NULL) { + if (!ctx->_.dir.end_reached) { + assert(ctx->_.dir.last_errno != 0); + ERR_raise(ERR_LIB_SYS, ctx->_.dir.last_errno); + } + /* file_eof() will tell if EOF was reached */ + return 0; + } + + /* flag acceptable names */ + if (ctx->_.dir.last_entry[0] != '.' + && file_name_check(ctx, ctx->_.dir.last_entry)) { + + /* If we can't allocate the new name, we fail */ + if ((newname = + file_name_to_uri(ctx, ctx->_.dir.last_entry)) == NULL) + return 0; + } + + /* + * On the first call (with a NULL context), OPENSSL_DIR_read() + * cares about the second argument. On the following calls, it + * only cares that it isn't NULL. Therefore, we can safely give + * it our URI here. + */ + ctx->_.dir.last_entry = OPENSSL_DIR_read(&ctx->_.dir.ctx, ctx->uri); + ctx->_.dir.last_errno = errno; + if (ctx->_.dir.last_entry == NULL && ctx->_.dir.last_errno == 0) + ctx->_.dir.end_reached = 1; + } while (newname == NULL); + + object[1].data = newname; + object[1].data_size = strlen(newname); + ok = object_cb(object, object_cbarg); + OPENSSL_free(newname); + return ok; +} + +/*- + * Loading, local dispatcher + * ------------------------- + */ + +static int file_load(void *loaderctx, + OSSL_CALLBACK *object_cb, void *object_cbarg, + OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg) +{ + struct file_ctx_st *ctx = loaderctx; + + switch (ctx->type) { + case IS_FILE: + return file_load_file(ctx, object_cb, object_cbarg, pw_cb, pw_cbarg); + case IS_DIR: + return + file_load_dir_entry(ctx, object_cb, object_cbarg, pw_cb, pw_cbarg); + default: + break; + } + + /* ctx->type has an unexpected value */ + assert(0); + return 0; +} + +/*- + * Eof detection and closing + * ------------------------- + */ + +static int file_eof(void *loaderctx) +{ + struct file_ctx_st *ctx = loaderctx; + + switch (ctx->type) { + case IS_DIR: + return ctx->_.dir.end_reached; + case IS_FILE: + /* + * BIO_pending() checks any filter BIO. + * BIO_eof() checks the source BIO. + */ + return !BIO_pending(ctx->_.file.file) + && BIO_eof(ctx->_.file.file); + } + + /* ctx->type has an unexpected value */ + assert(0); + return 1; +} + +static int file_close_dir(struct file_ctx_st *ctx) +{ + if (ctx->_.dir.ctx != NULL) + OPENSSL_DIR_end(&ctx->_.dir.ctx); + free_file_ctx(ctx); + return 1; +} + +static int file_close_stream(struct file_ctx_st *ctx) +{ + /* + * This frees either the provider BIO filter (for file_attach()) OR + * the allocated file BIO (for file_open()). + */ + BIO_free(ctx->_.file.file); + ctx->_.file.file = NULL; + + free_file_ctx(ctx); + return 1; +} + +static int file_close(void *loaderctx) +{ + struct file_ctx_st *ctx = loaderctx; + + switch (ctx->type) { + case IS_DIR: + return file_close_dir(ctx); + case IS_FILE: + return file_close_stream(ctx); + } + + /* ctx->type has an unexpected value */ + assert(0); + return 1; +} + +const OSSL_DISPATCH ossl_file_store_functions[] = { + { OSSL_FUNC_STORE_OPEN, (void (*)(void))file_open }, + { OSSL_FUNC_STORE_ATTACH, (void (*)(void))file_attach }, + { OSSL_FUNC_STORE_SETTABLE_CTX_PARAMS, + (void (*)(void))file_settable_ctx_params }, + { OSSL_FUNC_STORE_SET_CTX_PARAMS, (void (*)(void))file_set_ctx_params }, + { OSSL_FUNC_STORE_LOAD, (void (*)(void))file_load }, + { OSSL_FUNC_STORE_EOF, (void (*)(void))file_eof }, + { OSSL_FUNC_STORE_CLOSE, (void (*)(void))file_close }, + OSSL_DISPATCH_END, +}; diff --git a/crypto/openssl/providers/implementations/storemgmt/file_store_any2obj.c b/crypto/openssl/providers/implementations/storemgmt/file_store_any2obj.c new file mode 100644 index 000000000000..bec6091608a9 --- /dev/null +++ b/crypto/openssl/providers/implementations/storemgmt/file_store_any2obj.c @@ -0,0 +1,314 @@ +/* + * Copyright 2020-2025 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 + */ + +/* + * This is a decoder that's completely internal to the 'file:' store + * implementation. Only code in file_store.c know about this one. Because + * of this close relationship, we can cut certain corners, such as making + * assumptions about the "provider context", which is currently simply the + * provider context that the file_store.c code operates within. + * + * All this does is to read known binary encodings (currently: DER, MSBLOB, + * PVK) from the input if it can, and passes it on to the data callback as + * an object abstraction, leaving it to the callback to figure out what it + * actually is. + * + * This MUST be made the last decoder in a chain, leaving it to other more + * specialized decoders to recognise and process their stuff first. + */ + +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/core_object.h> +#include <openssl/bio.h> +#include <openssl/buffer.h> +#include <openssl/err.h> +#include <openssl/asn1err.h> +#include <openssl/params.h> +#include "internal/asn1.h" +#include "internal/sizes.h" +#include "crypto/pem.h" /* For internal PVK and "blob" headers */ +#include "prov/bio.h" +#include "file_store_local.h" + +/* + * newctx and freectx are not strictly necessary. However, the method creator, + * ossl_decoder_from_algorithm(), demands that they exist, so we make sure to + * oblige. + */ + +static OSSL_FUNC_decoder_newctx_fn any2obj_newctx; +static OSSL_FUNC_decoder_freectx_fn any2obj_freectx; + +struct any2obj_ctx_st { + PROV_CTX *provctx; + char data_structure[OSSL_MAX_CODEC_STRUCT_SIZE]; +}; + +static void *any2obj_newctx(void *provctx) +{ + struct any2obj_ctx_st *ctx = OPENSSL_zalloc(sizeof(*ctx)); + + if (ctx != NULL) + ctx->provctx = provctx; + return ctx; +} + +static void any2obj_freectx(void *ctx) +{ + OPENSSL_free(ctx); +} + +static int any2obj_set_ctx_params(void *vctx, const OSSL_PARAM params[]) +{ + struct any2obj_ctx_st *ctx = vctx; + const OSSL_PARAM *p; + char *str; + + p = OSSL_PARAM_locate_const(params, OSSL_OBJECT_PARAM_DATA_STRUCTURE); + str = ctx->data_structure; + if (p != NULL + && !OSSL_PARAM_get_utf8_string(p, &str, sizeof(ctx->data_structure))) + return 0; + + return 1; +} + +static const OSSL_PARAM *any2obj_settable_ctx_params(ossl_unused void *provctx) +{ + static const OSSL_PARAM settables[] = { + OSSL_PARAM_utf8_string(OSSL_OBJECT_PARAM_DATA_STRUCTURE, NULL, 0), + OSSL_PARAM_END + }; + return settables; +} + +static int any2obj_decode_final(void *vctx, int objtype, const char *input_type, + const char *data_type, BUF_MEM *mem, + OSSL_CALLBACK *data_cb, void *data_cbarg) +{ + struct any2obj_ctx_st *ctx = vctx; + /* + * 1 indicates that we successfully decoded something, or not at all. + * Ending up "empty handed" is not an error. + */ + int ok = 1; + + if (mem != NULL) { + OSSL_PARAM params[6], *p = params; + + if (data_type != NULL) + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE, + (char *)data_type, 0); + if (input_type != NULL) + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_INPUT_TYPE, + (char *)input_type, 0); + if (*ctx->data_structure != '\0') + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_STRUCTURE, + (char *)ctx->data_structure, 0); + *p++ = OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, &objtype); + *p++ = OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_DATA, + mem->data, mem->length); + *p = OSSL_PARAM_construct_end(); + + ok = data_cb(params, data_cbarg); + BUF_MEM_free(mem); + } + return ok; +} + +static OSSL_FUNC_decoder_decode_fn der2obj_decode; +static int der2obj_decode(void *vctx, OSSL_CORE_BIO *cin, int selection, + OSSL_CALLBACK *data_cb, void *data_cbarg, + OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg) +{ + struct any2obj_ctx_st *ctx = vctx; + BIO *in = ossl_bio_new_from_core_bio(ctx->provctx, cin); + BUF_MEM *mem = NULL; + int ok; + + if (in == NULL) + return 0; + + ERR_set_mark(); + ok = (asn1_d2i_read_bio(in, &mem) >= 0); + ERR_pop_to_mark(); + if (!ok && mem != NULL) { + BUF_MEM_free(mem); + mem = NULL; + } + BIO_free(in); + + /* any2obj_decode_final() frees |mem| for us */ + return any2obj_decode_final(ctx, OSSL_OBJECT_UNKNOWN, NULL, NULL, mem, + data_cb, data_cbarg); +} + +static OSSL_FUNC_decoder_decode_fn msblob2obj_decode; +static int msblob2obj_decode(void *vctx, OSSL_CORE_BIO *cin, int selection, + OSSL_CALLBACK *data_cb, void *data_cbarg, + OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg) +{ + struct any2obj_ctx_st *ctx = vctx; + BIO *in = ossl_bio_new_from_core_bio(ctx->provctx, cin); + BUF_MEM *mem = NULL; + size_t mem_len = 0, mem_want; + const unsigned char *p; + unsigned int bitlen, magic; + int isdss = -1; + int ispub = -1; + int ok = 0; + + if (in == NULL) + goto err; + + mem_want = 16; /* The size of the MSBLOB header */ + if ((mem = BUF_MEM_new()) == NULL + || !BUF_MEM_grow(mem, mem_want)) { + ERR_raise(ERR_LIB_PEM, ERR_R_BUF_LIB); + goto err; + } + + ERR_set_mark(); + ok = BIO_read(in, &mem->data[0], mem_want) == (int)mem_want; + mem_len += mem_want; + ERR_pop_to_mark(); + if (!ok) + goto next; + + + ERR_set_mark(); + p = (unsigned char *)&mem->data[0]; + ok = ossl_do_blob_header(&p, 16, &magic, &bitlen, &isdss, &ispub) > 0; + ERR_pop_to_mark(); + if (!ok) + goto next; + + ok = 0; + mem_want = ossl_blob_length(bitlen, isdss, ispub); + if (!BUF_MEM_grow(mem, mem_len + mem_want)) { + ERR_raise(ERR_LIB_PEM, ERR_R_BUF_LIB); + goto err; + } + + ERR_set_mark(); + ok = BIO_read(in, &mem->data[mem_len], mem_want) == (int)mem_want; + mem_len += mem_want; + ERR_pop_to_mark(); + + next: + /* Free resources we no longer need. */ + BIO_free(in); + if (!ok && mem != NULL) { + BUF_MEM_free(mem); + mem = NULL; + } + + /* any2obj_decode_final() frees |mem| for us */ + return any2obj_decode_final(ctx, OSSL_OBJECT_PKEY, "msblob", + isdss ? "DSA" : "RSA", mem, + data_cb, data_cbarg); + + err: + BIO_free(in); + BUF_MEM_free(mem); + return 0; +} + +static OSSL_FUNC_decoder_decode_fn pvk2obj_decode; +static int pvk2obj_decode(void *vctx, OSSL_CORE_BIO *cin, int selection, + OSSL_CALLBACK *data_cb, void *data_cbarg, + OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg) +{ + struct any2obj_ctx_st *ctx = vctx; + BIO *in = ossl_bio_new_from_core_bio(ctx->provctx, cin); + BUF_MEM *mem = NULL; + size_t mem_len = 0, mem_want; + const unsigned char *p; + unsigned int saltlen, keylen; + int ok = 0, isdss = -1; + + if (in == NULL) + goto err; + + mem_want = 24; /* The size of the PVK header */ + if ((mem = BUF_MEM_new()) == NULL + || !BUF_MEM_grow(mem, mem_want)) { + ERR_raise(ERR_LIB_PEM, ERR_R_BUF_LIB); + goto err; + } + + ERR_set_mark(); + ok = BIO_read(in, &mem->data[0], mem_want) == (int)mem_want; + mem_len += mem_want; + ERR_pop_to_mark(); + if (!ok) + goto next; + + + ERR_set_mark(); + p = (unsigned char *)&mem->data[0]; + ok = ossl_do_PVK_header(&p, 24, 0, &isdss, &saltlen, &keylen) > 0; + ERR_pop_to_mark(); + if (!ok) + goto next; + + ok = 0; + mem_want = saltlen + keylen; + if (!BUF_MEM_grow(mem, mem_len + mem_want)) { + ERR_raise(ERR_LIB_PEM, ERR_R_BUF_LIB); + goto err; + } + + ERR_set_mark(); + ok = BIO_read(in, &mem->data[mem_len], mem_want) == (int)mem_want; + mem_len += mem_want; + ERR_pop_to_mark(); + + next: + /* Free resources we no longer need. */ + BIO_free(in); + if (!ok && mem != NULL) { + BUF_MEM_free(mem); + mem = NULL; + } + + /* any2obj_decode_final() frees |mem| for us */ + return any2obj_decode_final(ctx, OSSL_OBJECT_PKEY, "pvk", + ok ? (isdss ? "DSA" : "RSA") : NULL, mem, + data_cb, data_cbarg); + + err: + BIO_free(in); + BUF_MEM_free(mem); + return 0; +} + +#define MAKE_DECODER(fromtype, objtype) \ + static const OSSL_DISPATCH fromtype##_to_obj_decoder_functions[] = { \ + { OSSL_FUNC_DECODER_NEWCTX, (void (*)(void))any2obj_newctx }, \ + { OSSL_FUNC_DECODER_FREECTX, (void (*)(void))any2obj_freectx }, \ + { OSSL_FUNC_DECODER_DECODE, (void (*)(void))fromtype##2obj_decode }, \ + { OSSL_FUNC_DECODER_SETTABLE_CTX_PARAMS, \ + (void (*)(void))any2obj_settable_ctx_params }, \ + { OSSL_FUNC_DECODER_SET_CTX_PARAMS, \ + (void (*)(void))any2obj_set_ctx_params }, \ + OSSL_DISPATCH_END \ + } + +MAKE_DECODER(der, OSSL_OBJECT_UNKNOWN); +MAKE_DECODER(msblob, OSSL_OBJECT_PKEY); +MAKE_DECODER(pvk, OSSL_OBJECT_PKEY); + +const OSSL_ALGORITHM ossl_any_to_obj_algorithm[] = { + { "obj", "input=DER", der_to_obj_decoder_functions }, + { "obj", "input=MSBLOB", msblob_to_obj_decoder_functions }, + { "obj", "input=PVK", pvk_to_obj_decoder_functions }, + { NULL, } +}; diff --git a/crypto/openssl/providers/implementations/storemgmt/file_store_local.h b/crypto/openssl/providers/implementations/storemgmt/file_store_local.h new file mode 100644 index 000000000000..b7d9e585163e --- /dev/null +++ b/crypto/openssl/providers/implementations/storemgmt/file_store_local.h @@ -0,0 +1,11 @@ +/* + * Copyright 2020-2021 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 + */ + +extern const OSSL_ALGORITHM ossl_any_to_obj_algorithm[]; + diff --git a/crypto/openssl/providers/implementations/storemgmt/winstore_store.c b/crypto/openssl/providers/implementations/storemgmt/winstore_store.c new file mode 100644 index 000000000000..59a86a105e9f --- /dev/null +++ b/crypto/openssl/providers/implementations/storemgmt/winstore_store.c @@ -0,0 +1,343 @@ +/* + * Copyright 2022-2025 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 + */ +#include <openssl/store.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_names.h> +#include <openssl/core_object.h> +#include <openssl/bio.h> +#include <openssl/err.h> +#include <openssl/params.h> +#include <openssl/decoder.h> +#include <openssl/proverr.h> +#include <openssl/store.h> /* The OSSL_STORE_INFO type numbers */ +#include "internal/cryptlib.h" +#include "internal/o_dir.h" +#include "crypto/decoder.h" +#include "crypto/ctype.h" /* ossl_isdigit() */ +#include "prov/implementations.h" +#include "prov/providercommon.h" +#include "prov/bio.h" +#include "file_store_local.h" +#ifdef __CYGWIN__ +# include <windows.h> +#endif +#include <wincrypt.h> + +enum { + STATE_IDLE, + STATE_READ, + STATE_EOF, +}; + +struct winstore_ctx_st { + void *provctx; + char *propq; + unsigned char *subject; + size_t subject_len; + + HCERTSTORE win_store; + const CERT_CONTEXT *win_ctx; + int state; + + OSSL_DECODER_CTX *dctx; +}; + +static void winstore_win_reset(struct winstore_ctx_st *ctx) +{ + if (ctx->win_ctx != NULL) { + CertFreeCertificateContext(ctx->win_ctx); + ctx->win_ctx = NULL; + } + + ctx->state = STATE_IDLE; +} + +static void winstore_win_advance(struct winstore_ctx_st *ctx) +{ + CERT_NAME_BLOB name = {0}; + + if (ctx->state == STATE_EOF) + return; + + name.cbData = ctx->subject_len; + name.pbData = ctx->subject; + + ctx->win_ctx = (name.cbData == 0 ? NULL : + CertFindCertificateInStore(ctx->win_store, + X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + 0, CERT_FIND_SUBJECT_NAME, + &name, ctx->win_ctx)); + + ctx->state = (ctx->win_ctx == NULL) ? STATE_EOF : STATE_READ; +} + +static void *winstore_open(void *provctx, const char *uri) +{ + struct winstore_ctx_st *ctx = NULL; + + if (!HAS_CASE_PREFIX(uri, "org.openssl.winstore:")) + return NULL; + + ctx = OPENSSL_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + + ctx->provctx = provctx; + ctx->win_store = CertOpenSystemStoreW(0, L"ROOT"); + if (ctx->win_store == NULL) { + OPENSSL_free(ctx); + return NULL; + } + + winstore_win_reset(ctx); + return ctx; +} + +static void *winstore_attach(void *provctx, OSSL_CORE_BIO *cin) +{ + return NULL; /* not supported */ +} + +static const OSSL_PARAM *winstore_settable_ctx_params(void *loaderctx, const OSSL_PARAM params[]) +{ + static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_octet_string(OSSL_STORE_PARAM_SUBJECT, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_STORE_PARAM_PROPERTIES, NULL, 0), + OSSL_PARAM_END + }; + return known_settable_ctx_params; +} + +static int winstore_set_ctx_params(void *loaderctx, const OSSL_PARAM params[]) +{ + struct winstore_ctx_st *ctx = loaderctx; + const OSSL_PARAM *p; + int do_reset = 0; + + if (ossl_param_is_empty(params)) + return 1; + + p = OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_PROPERTIES); + if (p != NULL) { + do_reset = 1; + OPENSSL_free(ctx->propq); + ctx->propq = NULL; + if (!OSSL_PARAM_get_utf8_string(p, &ctx->propq, 0)) + return 0; + } + + p = OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_SUBJECT); + if (p != NULL) { + const unsigned char *der = NULL; + size_t der_len = 0; + + if (!OSSL_PARAM_get_octet_string_ptr(p, (const void **)&der, &der_len)) + return 0; + + do_reset = 1; + + OPENSSL_free(ctx->subject); + + ctx->subject = OPENSSL_malloc(der_len); + if (ctx->subject == NULL) { + ctx->subject_len = 0; + return 0; + } + + ctx->subject_len = der_len; + memcpy(ctx->subject, der, der_len); + } + + if (do_reset) { + winstore_win_reset(ctx); + winstore_win_advance(ctx); + } + + return 1; +} + +struct load_data_st { + OSSL_CALLBACK *object_cb; + void *object_cbarg; +}; + +static int load_construct(OSSL_DECODER_INSTANCE *decoder_inst, + const OSSL_PARAM *params, void *construct_data) +{ + struct load_data_st *data = construct_data; + return data->object_cb(params, data->object_cbarg); +} + +static void load_cleanup(void *construct_data) +{ + /* No-op. */ +} + +static int setup_decoder(struct winstore_ctx_st *ctx) +{ + OSSL_LIB_CTX *libctx = ossl_prov_ctx_get0_libctx(ctx->provctx); + const OSSL_ALGORITHM *to_algo = NULL; + const char *input_structure = NULL; + + if (ctx->dctx != NULL) + return 1; + + ctx->dctx = OSSL_DECODER_CTX_new(); + if (ctx->dctx == NULL) { + ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB); + return 0; + } + + if (!OSSL_DECODER_CTX_set_input_type(ctx->dctx, "DER")) { + ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB); + goto err; + } + + input_structure = "Certificate"; + if (!OSSL_DECODER_CTX_set_input_structure(ctx->dctx, input_structure)) { + ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB); + goto err; + } + + for (to_algo = ossl_any_to_obj_algorithm; + to_algo->algorithm_names != NULL; + to_algo++) { + OSSL_DECODER *to_obj = NULL; + OSSL_DECODER_INSTANCE *to_obj_inst = NULL; + const char *input_type; + + /* + * Create the internal last resort decoder implementation + * together with a "decoder instance". + * The decoder doesn't need any identification or to be + * attached to any provider, since it's only used locally. + */ + to_obj = ossl_decoder_from_algorithm(0, to_algo, NULL); + if (to_obj != NULL) + to_obj_inst = ossl_decoder_instance_new_forprov(to_obj, ctx->provctx, + input_structure); + + OSSL_DECODER_free(to_obj); + if (to_obj_inst == NULL) + goto err; + + /* + * The input type has to be DER + */ + input_type = OSSL_DECODER_INSTANCE_get_input_type(to_obj_inst); + if (OPENSSL_strcasecmp(input_type, "DER") != 0) { + ossl_decoder_instance_free(to_obj_inst); + continue; + } + + if (!ossl_decoder_ctx_add_decoder_inst(ctx->dctx, + to_obj_inst)) { + ossl_decoder_instance_free(to_obj_inst); + ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB); + goto err; + } + } + + if (!OSSL_DECODER_CTX_add_extra(ctx->dctx, libctx, ctx->propq)) { + ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB); + goto err; + } + + if (!OSSL_DECODER_CTX_set_construct(ctx->dctx, load_construct)) { + ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB); + goto err; + } + + if (!OSSL_DECODER_CTX_set_cleanup(ctx->dctx, load_cleanup)) { + ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB); + goto err; + } + + return 1; + +err: + OSSL_DECODER_CTX_free(ctx->dctx); + ctx->dctx = NULL; + return 0; +} + +static int winstore_load_using(struct winstore_ctx_st *ctx, + OSSL_CALLBACK *object_cb, void *object_cbarg, + OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg, + const void *der, size_t der_len) +{ + struct load_data_st data; + const unsigned char *der_ = der; + size_t der_len_ = der_len; + + if (setup_decoder(ctx) == 0) + return 0; + + data.object_cb = object_cb; + data.object_cbarg = object_cbarg; + + OSSL_DECODER_CTX_set_construct_data(ctx->dctx, &data); + OSSL_DECODER_CTX_set_passphrase_cb(ctx->dctx, pw_cb, pw_cbarg); + + if (OSSL_DECODER_from_data(ctx->dctx, &der_, &der_len_) == 0) + return 0; + + return 1; +} + +static int winstore_load(void *loaderctx, + OSSL_CALLBACK *object_cb, void *object_cbarg, + OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg) +{ + int ret = 0; + struct winstore_ctx_st *ctx = loaderctx; + + if (ctx->state != STATE_READ) + return 0; + + ret = winstore_load_using(ctx, object_cb, object_cbarg, pw_cb, pw_cbarg, + ctx->win_ctx->pbCertEncoded, + ctx->win_ctx->cbCertEncoded); + + if (ret == 1) + winstore_win_advance(ctx); + + return ret; +} + +static int winstore_eof(void *loaderctx) +{ + struct winstore_ctx_st *ctx = loaderctx; + + return ctx->state != STATE_READ; +} + +static int winstore_close(void *loaderctx) +{ + struct winstore_ctx_st *ctx = loaderctx; + + winstore_win_reset(ctx); + CertCloseStore(ctx->win_store, 0); + OSSL_DECODER_CTX_free(ctx->dctx); + OPENSSL_free(ctx->propq); + OPENSSL_free(ctx->subject); + OPENSSL_free(ctx); + return 1; +} + +const OSSL_DISPATCH ossl_winstore_store_functions[] = { + { OSSL_FUNC_STORE_OPEN, (void (*)(void))winstore_open }, + { OSSL_FUNC_STORE_ATTACH, (void (*)(void))winstore_attach }, + { OSSL_FUNC_STORE_SETTABLE_CTX_PARAMS, (void (*)(void))winstore_settable_ctx_params }, + { OSSL_FUNC_STORE_SET_CTX_PARAMS, (void (*)(void))winstore_set_ctx_params }, + { OSSL_FUNC_STORE_LOAD, (void (*)(void))winstore_load }, + { OSSL_FUNC_STORE_EOF, (void (*)(void))winstore_eof }, + { OSSL_FUNC_STORE_CLOSE, (void (*)(void))winstore_close }, + OSSL_DISPATCH_END, +}; |