From: Weili Qian qianweili@huawei.com
Support ECDH key exchange.
Test: openssl speed -provider uadk_provider ecdhp192 openssl speed -provider uadk_provider ecdhp224 openssl speed -provider uadk_provider ecdhp256 openssl speed -provider uadk_provider ecdhp384 openssl speed -provider uadk_provider ecdhp521
Signed-off-by: Weili Qian qianweili@huawei.com --- src/Makefile.am | 2 +- src/uadk_prov.h | 1 + src/uadk_prov_ecdh_exch.c | 889 ++++++++++++++++++++++++++++++++++++++ src/uadk_prov_init.c | 9 +- src/uadk_prov_pkey.c | 68 +++ src/uadk_prov_pkey.h | 6 + 6 files changed, 971 insertions(+), 4 deletions(-) create mode 100644 src/uadk_prov_ecdh_exch.c
diff --git a/src/Makefile.am b/src/Makefile.am index b2e2c06..5a1abe7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -66,7 +66,7 @@ uadk_provider_la_SOURCES=uadk_prov_init.c uadk_async.c uadk_utils.c \ uadk_prov_bio.c uadk_prov_der_writer.c uadk_prov_packet.c \ uadk_prov_pkey.c uadk_prov_sm2.c \ uadk_prov_ffc.c uadk_prov_aead.c \ - uadk_prov_ec_kmgmt.c + uadk_prov_ec_kmgmt.c uadk_prov_ecdh_exch.c
uadk_provider_la_LDFLAGS=-module -version-number $(VERSION) uadk_provider_la_LIBADD=$(WD_LIBS) -lpthread diff --git a/src/uadk_prov.h b/src/uadk_prov.h index 9c310b7..7975884 100644 --- a/src/uadk_prov.h +++ b/src/uadk_prov.h @@ -180,6 +180,7 @@ extern const OSSL_DISPATCH uadk_sm2_signature_functions[FUNC_MAX_NUM]; extern const OSSL_DISPATCH uadk_sm2_asym_cipher_functions[FUNC_MAX_NUM];
extern const OSSL_DISPATCH uadk_ec_keymgmt_functions[FUNC_MAX_NUM]; +extern const OSSL_DISPATCH uadk_ecdh_keyexch_functions[FUNC_MAX_NUM];
void uadk_prov_destroy_digest(void); void uadk_prov_destroy_cipher(void); diff --git a/src/uadk_prov_ecdh_exch.c b/src/uadk_prov_ecdh_exch.c new file mode 100644 index 0000000..f549d25 --- /dev/null +++ b/src/uadk_prov_ecdh_exch.c @@ -0,0 +1,889 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + * Copyright 2024 Huawei Technologies Co.,Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/err.h> +#include <openssl/bn.h> +#include <openssl/kdf.h> +#include <uadk/wd_ecc.h> +#include "uadk_async.h" +#include "uadk_prov.h" +#include "uadk_prov_der_writer.h" +#include "uadk_prov_pkey.h" + +#define UADK_PROV_MAX_PARAM_LEN 80 + +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. + */ +struct ecdh_ctx { + 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; + /* 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; +}; + +struct ecdh_sess_ctx { + EC_KEY *privk; + const EC_POINT *pub_key; + const BIGNUM *cofactor; + const EC_GROUP *group; +}; + +UADK_PKEY_KEYEXCH_DESCR(ecdh, ECDH); +static bool g_keyexch_ecdh_support; + +void uadk_prov_keyexch_alg(void) +{ + g_keyexch_ecdh_support = uadk_prov_support_algorithm("ecdh"); +} + +static size_t ecdh_get_ec_size(const EC_GROUP *group) +{ + size_t degree; + + degree = EC_GROUP_get_degree(group); + + return BITS_TO_BYTES(degree); +} + +static int ecdh_param_check(struct ecdh_ctx *pecdhctx, struct ecdh_sess_ctx *sess_ctx) +{ + const EC_GROUP *group; + int type; + + if (!pecdhctx->k || !pecdhctx->peerk) { + fprintf(stderr, "invalid: k or peerk is NULL.\n"); + return UADK_P_FAIL; + } + + sess_ctx->pub_key = EC_KEY_get0_public_key(pecdhctx->peerk); + if (!sess_ctx->pub_key) { + fprintf(stderr, "invalid: public key is NULL.\n"); + return UADK_P_FAIL; + } + + group = EC_KEY_get0_group(pecdhctx->k); + if (!group) { + fprintf(stderr, "invalid: group is 0.\n"); + return UADK_P_FAIL; + } + + sess_ctx->cofactor = EC_GROUP_get0_cofactor(group); + if (!sess_ctx->cofactor) { + fprintf(stderr, "invalid: cofactor is NULL!\n"); + return UADK_P_FAIL; + } + + /* Field GF(2m) is not supported by uadk */ + type = EC_METHOD_get_field_type(EC_GROUP_method_of(group)); + if (type != NID_X9_62_prime_field) { + fprintf(stderr, "invalid: uadk unsupport Field GF(2m)!\n"); + return UADK_P_FAIL; + } + + sess_ctx->group = group; + + return uadk_prov_ecc_bit_check(group); +} + +static int ecdh_set_privk(struct ecdh_ctx *pecdhctx, + struct ecdh_sess_ctx *sess_ctx) +{ + int key_cofactor_mode; + + /* + * 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) ? + COFACTOR_MODE_ENABLED : COFACTOR_MODE_DISABLED; + if (pecdhctx->cofactor_mode != COFACTOR_MODE_USE_KEY && + pecdhctx->cofactor_mode != key_cofactor_mode && + !BN_is_one(sess_ctx->cofactor)) { + sess_ctx->privk = EC_KEY_dup(pecdhctx->k); + if (!sess_ctx->privk) + return UADK_P_FAIL; + + if (pecdhctx->cofactor_mode == COFACTOR_MODE_ENABLED) + EC_KEY_set_flags(sess_ctx->privk, EC_FLAG_COFACTOR_ECDH); + else + EC_KEY_clear_flags(sess_ctx->privk, EC_FLAG_COFACTOR_ECDH); + } else { + sess_ctx->privk = pecdhctx->k; + } + + return UADK_P_SUCCESS; +} + +static handle_t ecdh_alloc_sess(EC_KEY *privk) +{ + int ret; + + if (!g_keyexch_ecdh_support) { + fprintf(stderr, "invalid: hardware not support ecdh!\n"); + return UADK_P_FAIL; + } + + ret = uadk_prov_ecc_init("ecdh"); + if (!ret) { + fprintf(stderr, "failed to init ecdh to compute key!\n"); + return UADK_P_FAIL; + } + + return uadk_prov_ecc_alloc_sess(privk, "ecdh"); +} + +static void ecdh_free_sess(handle_t sess) +{ + wd_ecc_free_sess(sess); +} + +static int ecdh_init_req(struct ecdh_sess_ctx *sess_ctx, + struct wd_ecc_req *req, handle_t sess) +{ + char buf_x[UADK_ECC_MAX_KEY_BYTES]; + char buf_y[UADK_ECC_MAX_KEY_BYTES]; + struct wd_ecc_point in_pkey; + struct wd_ecc_out *ecdh_out; + struct wd_ecc_in *ecdh_in; + BIGNUM *pkey_x, *pkey_y; + int ret = UADK_P_FAIL; + BN_CTX *ctx; + + ctx = BN_CTX_new(); + if (!ctx) + return -ENOMEM; + + BN_CTX_start(ctx); + pkey_x = BN_CTX_get(ctx); + if (!pkey_x) + goto free_ctx; + + pkey_y = BN_CTX_get(ctx); + if (!pkey_y) + goto free_ctx; + + uadk_prov_get_affine_coordinates(sess_ctx->group, sess_ctx->pub_key, pkey_x, pkey_y, ctx); + in_pkey.x.data = buf_x; + in_pkey.y.data = buf_y; + in_pkey.x.dsize = BN_bn2bin(pkey_x, (unsigned char *)in_pkey.x.data); + in_pkey.y.dsize = BN_bn2bin(pkey_y, (unsigned char *)in_pkey.y.data); + + /* Set public key */ + ecdh_in = wd_ecxdh_new_in(sess, &in_pkey); + if (!ecdh_in) { + fprintf(stderr, "failed to new ecxdh in\n"); + goto free_ctx; + } + + ecdh_out = wd_ecxdh_new_out(sess); + if (!ecdh_out) { + fprintf(stderr, "failed to new ecxdh out\n"); + wd_ecc_del_in(sess, ecdh_in); + goto free_ctx; + } + + uadk_prov_ecc_fill_req(req, WD_ECXDH_COMPUTE_KEY, ecdh_in, ecdh_out); + + ret = UADK_P_SUCCESS; + +free_ctx: + BN_CTX_end(ctx); + BN_CTX_free(ctx); + + return ret; +} + +static void ecdh_uninit_req(struct wd_ecc_req *req, handle_t sess) +{ + wd_ecc_del_in(sess, req->src); + wd_ecc_del_out(sess, req->dst); +} + +static int ecdh_get_shared_key(unsigned char *secret, + size_t size, size_t *psecretlen, + struct wd_ecc_req *req) +{ + struct wd_ecc_point *shared_key = NULL; + + wd_ecxdh_get_out_params(req->dst, &shared_key); + if (!shared_key) { + fprintf(stderr, "failed to get ecdh shared key\n"); + return UADK_P_FAIL; + } + + size = size < shared_key->x.dsize ? size : shared_key->x.dsize; + *psecretlen = size; + + memcpy(secret, (unsigned char *)shared_key->x.data, size); + + return UADK_P_SUCCESS; +} + +static int ecdh_compute_key(struct ecdh_sess_ctx *sess_ctx, + unsigned char *secret, + size_t *psecretlen, size_t size) +{ + struct wd_ecc_req req = {0}; + handle_t sess; + int ret; + + sess = ecdh_alloc_sess(sess_ctx->privk); + if (!sess) { + fprintf(stderr, "failed to alloc sess to compute key!\n"); + return UADK_P_FAIL; + } + + ret = uadk_prov_ecc_set_private_key(sess, sess_ctx->privk); + if (!ret) { + fprintf(stderr, "failed to set private key!\n"); + goto free_sess; + } + + ret = ecdh_init_req(sess_ctx, &req, sess); + if (!ret) { + fprintf(stderr, "failed to init req!\n"); + goto free_sess; + } + + ret = uadk_prov_ecc_crypto(sess, &req, (void *)sess); + if (!ret) { + fprintf(stderr, "failed to calculate shared key!\n"); + goto uninit_req; + } + + ret = ecdh_get_shared_key(secret, size, psecretlen, &req); + +uninit_req: + ecdh_uninit_req(&req, sess); +free_sess: + ecdh_free_sess(sess); + return ret; +} + +static int ecdh_plain_derive(struct ecdh_ctx *pecdhctx, + unsigned char *secret, + size_t *psecretlen, size_t outlen) +{ + struct ecdh_sess_ctx sess_ctx = {0}; + size_t size, ec_size; + int ret; + + ret = ecdh_param_check(pecdhctx, &sess_ctx); + if (!ret) + return ret; + + ec_size = ecdh_get_ec_size(sess_ctx.group); + if (!secret) { + *psecretlen = ec_size; + return UADK_P_SUCCESS; + } + + ret = ecdh_set_privk(pecdhctx, &sess_ctx); + if (!ret) { + fprintf(stderr, "failed to set private key!\n"); + return ret; + } + + size = outlen < ec_size ? outlen : ec_size; + ret = ecdh_compute_key(&sess_ctx, secret, psecretlen, size); + if (sess_ctx.privk != pecdhctx->k) + EC_KEY_free(sess_ctx.privk); + + return ret; +} + +/* Key derivation function from X9.63/SECG */ +static int ecdh_kdf_X9_63(unsigned char *out, struct ecdh_ctx *pecdhctx, + unsigned char *stmp, size_t stmplen) +{ + EVP_KDF *kdf = EVP_KDF_fetch(pecdhctx->libctx, OSSL_KDF_NAME_X963KDF, NULL); + const char *mdname = EVP_MD_get0_name(pecdhctx->kdf_md); + OSSL_PARAM params[4], *p = params; + int ret = UADK_P_FAIL; + EVP_KDF_CTX *kctx; + + if (!kdf) { + fprintf(stderr, "failed to fetch kdf!\n"); + return ret; + } + + kctx = EVP_KDF_CTX_new(kdf); + if (!kctx) { + fprintf(stderr, "failed to new kctx!\n"); + goto free_kdf; + } + + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, (char *)mdname, 0); + *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, (void *)stmp, stmplen); + *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO, + (void *)pecdhctx->kdf_ukm, pecdhctx->kdf_ukmlen); + *p = OSSL_PARAM_construct_end(); + + ret = EVP_KDF_derive(kctx, out, pecdhctx->kdf_outlen, params); + ret = ret <= 0 ? UADK_P_FAIL : UADK_P_SUCCESS; + + EVP_KDF_CTX_free(kctx); + +free_kdf: + EVP_KDF_free(kdf); + + return ret; +} + +static int ecdh_X9_63_kdf_derive(struct ecdh_ctx *pecdhctx, unsigned char *secret, + size_t *psecretlen, size_t outlen) +{ + unsigned char *stmp; + size_t stmplen = 0; + int ret; + + if (!secret) { + *psecretlen = pecdhctx->kdf_outlen; + return UADK_P_SUCCESS; + } + + if (outlen < pecdhctx->kdf_outlen) { + fprintf(stderr, "invalid: outlen %lu is less than kdf_outlen %lu!\n", + outlen, pecdhctx->kdf_outlen); + return UADK_P_FAIL; + } + + ret = ecdh_plain_derive(pecdhctx, NULL, &stmplen, 0); + if (!ret) + return ret; + + stmp = OPENSSL_secure_malloc(stmplen); + if (!stmp) { + fprintf(stderr, "failed to alloc stmp!\n"); + return UADK_P_FAIL; + } + + ret = ecdh_plain_derive(pecdhctx, stmp, &stmplen, stmplen); + if (!ret) + goto free_stmp; + + ret = ecdh_kdf_X9_63(secret, pecdhctx, stmp, stmplen); + if (!ret) + goto free_stmp; + + *psecretlen = pecdhctx->kdf_outlen; + + free_stmp: + OPENSSL_secure_clear_free(stmp, stmplen); + return ret; +} + +static int uadk_keyexch_ecdh_derive(void *vpecdhctx, unsigned char *secret, + size_t *psecretlen, size_t outlen) +{ + struct ecdh_ctx *pecdhctx = vpecdhctx; + + if (!pecdhctx) { + fprintf(stderr, "invalid: vpecdhctx is NULL to derive!\n"); + return UADK_P_FAIL; + } + + switch (pecdhctx->kdf_type) { + case PROV_ECDH_KDF_NONE: + return ecdh_plain_derive(pecdhctx, secret, psecretlen, outlen); + case PROV_ECDH_KDF_X9_63: + return ecdh_X9_63_kdf_derive(pecdhctx, secret, psecretlen, outlen); + default: + break; + } + + return UADK_P_FAIL; +} + +static void *uadk_keyexch_ecdh_newctx(void *provctx) +{ + struct ecdh_ctx *pectx; + + pectx = OPENSSL_zalloc(sizeof(*pectx)); + if (!pectx) + return NULL; + + pectx->libctx = prov_libctx_of(provctx); + pectx->cofactor_mode = COFACTOR_MODE_USE_KEY; + pectx->kdf_type = PROV_ECDH_KDF_NONE; + + return pectx; +} + +static void uadk_keyexch_ecdh_freectx(void *vpecdhctx) +{ + struct ecdh_ctx *pecdhctx = vpecdhctx; + + if (!pecdhctx) + return; + + 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 int uadk_keyexch_ecdh_init(void *vpecdhctx, void *vecdh, const OSSL_PARAM params[]) +{ + struct ecdh_ctx *pecdhctx = vpecdhctx; + int ret; + + if (!pecdhctx || !vecdh) { + fprintf(stderr, "invalid: pecdhctx or vecdh is to init!\n"); + return UADK_P_FAIL; + } + + if (!EC_KEY_up_ref(vecdh)) + return UADK_P_FAIL; + + EC_KEY_free(pecdhctx->k); + pecdhctx->k = vecdh; + pecdhctx->cofactor_mode = COFACTOR_MODE_USE_KEY; + pecdhctx->kdf_type = PROV_ECDH_KDF_NONE; + + ret = uadk_keyexch_ecdh_set_ctx_params(pecdhctx, params); + if (!ret) { + fprintf(stderr, "failed to set_ctx_params!\n"); + return ret; + } + + return uadk_prov_ecc_check_key(pecdhctx->libctx, vecdh, 1); +} + +static int ecdh_match_params(const EC_KEY *privk, const EC_KEY *pubk) +{ + const EC_GROUP *group_privk = EC_KEY_get0_group(privk); + const EC_GROUP *group_pubk = EC_KEY_get0_group(pubk); + int ret = UADK_P_SUCCESS; + BN_CTX *ctx; + + ctx = BN_CTX_new_ex(privk->libctx); + if (!ctx) { + fprintf(stderr, "failed to new ctx!\n"); + return UADK_P_FAIL; + } + + if (group_privk && group_pubk) { + if (EC_GROUP_cmp(group_privk, group_pubk, ctx)) { + fprintf(stderr, "invalid: privk is not match pubk!\n"); + ret = UADK_P_FAIL; + } + } + + BN_CTX_free(ctx); + + return ret; +} + +static int uadk_keyexch_ecdh_set_peer(void *vpecdhctx, void *vecdh) +{ + struct ecdh_ctx *pecdhctx = vpecdhctx; + int ret; + + if (!pecdhctx || !vecdh) { + fprintf(stderr, "invalid: vpecdhctx or vecdh is NULL to set_peer!\n"); + return UADK_P_FAIL; + } + + ret = ecdh_match_params(pecdhctx->k, vecdh); + if (!ret) + return ret; + + ret = uadk_prov_ecc_check_key(pecdhctx->libctx, vecdh, 1); + if (!ret) + return ret; + + if (!EC_KEY_up_ref(vecdh)) + return UADK_P_FAIL; + + EC_KEY_free(pecdhctx->peerk); + pecdhctx->peerk = vecdh; + + return UADK_P_SUCCESS; +} + +static void *uadk_keyexch_ecdh_dupctx(void *vpecdhctx) +{ + struct ecdh_ctx *srcctx = vpecdhctx; + struct ecdh_ctx *dstctx; + + if (!srcctx) { + fprintf(stderr, "invalid: source ecdh ctx is NULL!\n"); + return NULL; + } + + dstctx = OPENSSL_zalloc(sizeof(*srcctx)); + if (!dstctx) { + fprintf(stderr, "failed to alloc dst ctx!\n"); + return NULL; + } + + memcpy(dstctx, srcctx, sizeof(*dstctx)); + + 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 && !EC_KEY_up_ref(srcctx->k)) + goto err; + else + dstctx->k = srcctx->k; + + if (srcctx->peerk && !EC_KEY_up_ref(srcctx->peerk)) + goto err; + else + dstctx->peerk = srcctx->peerk; + + if (srcctx->kdf_md && !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 && srcctx->kdf_ukmlen > 0) { + dstctx->kdf_ukm = OPENSSL_memdup(srcctx->kdf_ukm, + srcctx->kdf_ukmlen); + if (!dstctx->kdf_ukm) + goto err; + } + + return dstctx; + +err: + uadk_keyexch_ecdh_freectx(dstctx); + return NULL; +} + +static int ecdh_set_cofactor_mode(struct ecdh_ctx *pectx, const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p; + int mode, ret; + + p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE); + if (!p) + return UADK_P_SUCCESS; + + ret = OSSL_PARAM_get_int(p, &mode); + if (!ret) + return UADK_P_FAIL; + + if (mode < COFACTOR_MODE_USE_KEY || mode > COFACTOR_MODE_ENABLED) + return UADK_P_FAIL; + + pectx->cofactor_mode = mode; + + return UADK_P_SUCCESS; +} + +static int ecdh_get_cofactor_mode(struct ecdh_ctx *pectx, OSSL_PARAM params[]) +{ + int mode = pectx->cofactor_mode; + OSSL_PARAM *p; + + p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE); + if (!p) + return UADK_P_SUCCESS; + + if (mode == COFACTOR_MODE_USE_KEY) + /* Check what is the default for pecdhctx->k */ + mode = EC_KEY_get_flags(pectx->k) & EC_FLAG_COFACTOR_ECDH ? + COFACTOR_MODE_ENABLED : COFACTOR_MODE_DISABLED; + + return OSSL_PARAM_set_int(p, mode); +} + +static int ecdh_set_kdf_type(struct ecdh_ctx *pectx, const OSSL_PARAM params[]) +{ + char name[UADK_PROV_MAX_PARAM_LEN] = {'\0'}; + const OSSL_PARAM *p; + char *str = name; + int ret; + + p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_TYPE); + if (!p) + return UADK_P_SUCCESS; + + ret = OSSL_PARAM_get_utf8_string(p, &str, sizeof(name)); + if (!ret) + return UADK_P_FAIL; + + if (name[0] == '\0') + pectx->kdf_type = PROV_ECDH_KDF_NONE; + else if (!strcmp(name, OSSL_KDF_NAME_X963KDF)) + pectx->kdf_type = PROV_ECDH_KDF_X9_63; + else + return UADK_P_FAIL; + + return UADK_P_SUCCESS; +} + +static int ecdh_get_kdf_type(struct ecdh_ctx *pectx, OSSL_PARAM params[]) +{ + const char *kdf_type; + OSSL_PARAM *p; + + p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_TYPE); + if (!p) + return UADK_P_SUCCESS; + + 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 UADK_P_FAIL; + } + + return OSSL_PARAM_set_utf8_string(p, kdf_type); +} + +static int ecdh_set_kdf_digest(struct ecdh_ctx *pectx, const OSSL_PARAM params[]) +{ + char mdprops[UADK_PROV_MAX_PARAM_LEN] = {'\0'}; + char name[UADK_PROV_MAX_PARAM_LEN] = {'\0'}; + const OSSL_PARAM *p; + char *str = name; + int ret; + + p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_DIGEST); + if (!p) + return UADK_P_SUCCESS; + + ret = OSSL_PARAM_get_utf8_string(p, &str, sizeof(name)); + if (!ret) + return UADK_P_FAIL; + + p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_DIGEST_PROPS); + if (p) { + str = mdprops; + ret = OSSL_PARAM_get_utf8_string(p, &str, sizeof(mdprops)); + if (!ret) + return UADK_P_FAIL; + } + + EVP_MD_free(pectx->kdf_md); + pectx->kdf_md = EVP_MD_fetch(pectx->libctx, name, mdprops); + if (!pectx->kdf_md) + return UADK_P_FAIL; + + return UADK_P_SUCCESS; +} + +static int ecdh_get_kdf_digest(struct ecdh_ctx *pectx, OSSL_PARAM params[]) +{ + OSSL_PARAM *p; + + p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_DIGEST); + if (!p) + return UADK_P_SUCCESS; + + if (!pectx->kdf_md) + return OSSL_PARAM_set_utf8_string(p, ""); + + return OSSL_PARAM_set_utf8_string(p, EVP_MD_get0_name(pectx->kdf_md)); +} + +static int ecdh_set_kdf_outlen(struct ecdh_ctx *pectx, const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p; + + p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_OUTLEN); + if (!p) + return UADK_P_SUCCESS; + + return OSSL_PARAM_get_size_t(p, &pectx->kdf_outlen); +} + +static int ecdh_get_kdf_outlen(struct ecdh_ctx *pectx, OSSL_PARAM params[]) +{ + OSSL_PARAM *p; + + p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_OUTLEN); + if (!p) + return UADK_P_SUCCESS; + + return OSSL_PARAM_set_size_t(p, pectx->kdf_outlen); +} + +static int ecdh_set_kdf_ukm(struct ecdh_ctx *pectx, const OSSL_PARAM params[]) +{ + const OSSL_PARAM *p; + void *tmp_ukm = NULL; + size_t tmp_ukmlen; + int ret; + + p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_UKM); + if (!p) + return UADK_P_SUCCESS; + + ret = OSSL_PARAM_get_octet_string(p, &tmp_ukm, 0, &tmp_ukmlen); + if (!ret) + return ret; + + OPENSSL_free(pectx->kdf_ukm); + pectx->kdf_ukm = tmp_ukm; + pectx->kdf_ukmlen = tmp_ukmlen; + + return UADK_P_SUCCESS; +} + +static int ecdh_get_kdf_ukm(struct ecdh_ctx *pectx, OSSL_PARAM params[]) +{ + OSSL_PARAM *p; + + p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_UKM); + if (!p) + return UADK_P_SUCCESS; + + return OSSL_PARAM_set_octet_ptr(p, pectx->kdf_ukm, pectx->kdf_ukmlen); +} + +static int uadk_keyexch_ecdh_set_ctx_params(void *vpecdhctx, const OSSL_PARAM params[]) +{ + struct ecdh_ctx *pectx = (struct ecdh_ctx *)vpecdhctx; + int ret; + + if (!pectx) { + fprintf(stderr, "invalid: pectx is NULL to set_ctx_params!\n"); + return UADK_P_FAIL; + } + + if (!params) + return UADK_P_SUCCESS; + + ret = ecdh_set_cofactor_mode(pectx, params); + if (!ret) + return ret; + + ret = ecdh_set_kdf_type(pectx, params); + if (!ret) + return ret; + + ret = ecdh_set_kdf_digest(pectx, params); + if (!ret) + return ret; + + ret = ecdh_set_kdf_outlen(pectx, params); + if (!ret) + return ret; + + return ecdh_set_kdf_ukm(pectx, params); +} + +static int uadk_keyexch_ecdh_get_ctx_params(void *vpecdhctx, OSSL_PARAM params[]) +{ + struct ecdh_ctx *pectx = vpecdhctx; + int ret; + + if (!pectx) { + fprintf(stderr, "invalid: pectx is NULL to get_ctx_params!\n"); + return UADK_P_FAIL; + } + + ret = ecdh_get_cofactor_mode(pectx, params); + if (!ret) + return ret; + + ret = ecdh_get_kdf_type(pectx, params); + if (!ret) + return ret; + + ret = ecdh_get_kdf_digest(pectx, params); + if (!ret) + return ret; + + ret = ecdh_get_kdf_outlen(pectx, params); + if (!ret) + return ret; + + return ecdh_get_kdf_ukm(pectx, params); +} + +static const OSSL_PARAM *uadk_keyexch_ecdh_settable_ctx_params(ossl_unused void *vpecdhctx, + ossl_unused void *provctx) +{ + 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_PARAM_END + }; + + return known_settable_ctx_params; +} + +static const OSSL_PARAM *uadk_keyexch_ecdh_gettable_ctx_params(ossl_unused void *vpecdhctx, + ossl_unused void *provctx) +{ + 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_PARAM_END + }; + + return known_gettable_ctx_params; +} diff --git a/src/uadk_prov_init.c b/src/uadk_prov_init.c index b5d3df5..42e1272 100644 --- a/src/uadk_prov_init.c +++ b/src/uadk_prov_init.c @@ -190,6 +190,8 @@ static const OSSL_ALGORITHM uadk_prov_asym_cipher[] = { static const OSSL_ALGORITHM uadk_prov_keyexch[] = { { "DH", UADK_DEFAULT_PROPERTIES, uadk_dh_keyexch_functions, "UADK DH keyexch implementation"}, + { "ECDH", UADK_DEFAULT_PROPERTIES, + uadk_ecdh_keyexch_functions, "uadk_provider ecdh_keyexch" }, { NULL, NULL, NULL } };
@@ -218,15 +220,16 @@ static const OSSL_ALGORITHM *uadk_query(void *provctx, int operation_id, return uadk_prov_ciphers_v3; return uadk_prov_ciphers_v2; case OSSL_OP_SIGNATURE: - (void)uadk_prov_signature_alg(); + uadk_prov_signature_alg(); return uadk_prov_signature; case OSSL_OP_KEYMGMT: - (void)uadk_prov_keymgmt_alg(); + uadk_prov_keymgmt_alg(); return uadk_prov_keymgmt; case OSSL_OP_ASYM_CIPHER: - (void)uadk_prov_asym_cipher_alg(); + uadk_prov_asym_cipher_alg(); return uadk_prov_asym_cipher; case OSSL_OP_KEYEXCH: + uadk_prov_keyexch_alg(); return uadk_prov_keyexch; case OSSL_OP_STORE: return prov->query_operation(provctx, operation_id, no_cache); diff --git a/src/uadk_prov_pkey.c b/src/uadk_prov_pkey.c index 170c30b..a861551 100644 --- a/src/uadk_prov_pkey.c +++ b/src/uadk_prov_pkey.c @@ -29,6 +29,9 @@ #define PROV_SUPPORT 1 #define SIGNATURE_TYPE 3 #define ASYM_CIPHER_TYPE 3 +#define SECURITY_CHECK_DISABLE 0 +#define UADK_PROV_MIN_BITS 112 +#define UADK_PROV_SECURITY_BITS 80
static int p_keymgmt_support_state[KEYMGMT_TYPE]; static int p_signature_support_state[SIGNATURE_TYPE]; @@ -833,3 +836,68 @@ int uadk_prov_ecc_bit_check(const EC_GROUP *group)
return UADK_P_FAIL; } + +/* Currently, disable the security checks in the default provider and uadk provider */ +int uadk_prov_securitycheck_enabled(OSSL_LIB_CTX *ctx) +{ + return SECURITY_CHECK_DISABLE; +} + +#ifdef OPENSSL_NO_FIPS_SECURITYCHECKS +int uadk_prov_ecc_check_key(OSSL_LIB_CTX *ctx, const EC_KEY *ec, int protect) +{ + return UADK_P_SUCCESS; +} +#else +int uadk_prov_ecc_check_key(OSSL_LIB_CTX *ctx, const EC_KEY *ec, int protect) +{ + const EC_GROUP *group = EC_KEY_get0_group(ec); + const char *curve_name; + int nid, strength; + + if (!uadk_prov_securitycheck_enabled(ctx)) + return UADK_P_SUCCESS; + + if (!group) { + fprintf(stderr, "invalid: group is NULL!\n"); + return UADK_P_FAIL; + } + + nid = EC_GROUP_get_curve_name(group); + if (nid == NID_undef) { + fprintf(stderr, "invalid: explicit curves are not allowed in fips mode!\n"); + return UADK_P_FAIL; + } + + curve_name = EC_curve_nid2nist(nid); + if (!curve_name) { + fprintf(stderr, "invalid: Curve %s is not approved in FIPS mode!\n", + curve_name); + return UADK_P_FAIL; + } + + /* + * For EC the security strength is the (order_bits / 2) + * e.g. P-224 is 112 bits. + */ + strength = (unsigned int)EC_GROUP_order_bits(group) >> 1; + /* The min security strength allowed for legacy verification is 80 bits */ + if (strength < UADK_PROV_SECURITY_BITS) { + fprintf(stderr, "invalid: Curve %s strength %d is not approved in FIPS mode!\n", + curve_name, strength); + return UADK_P_FAIL; + } + + /* + * For signing or key agreement only allow curves with at least 112 bits of + * security strength + */ + if (protect && strength < UADK_PROV_MIN_BITS) { + fprintf(stderr, "invalid: Curve %s strength %d cannot be used for signing\n", + curve_name, strength); + return UADK_P_FAIL; + } + + return UADK_P_SUCCESS; +} +#endif /* OPENSSL_NO_FIPS_SECURITYCHECKS */ diff --git a/src/uadk_prov_pkey.h b/src/uadk_prov_pkey.h index 1d4911c..fbec388 100644 --- a/src/uadk_prov_pkey.h +++ b/src/uadk_prov_pkey.h @@ -452,5 +452,11 @@ void uadk_prov_asym_cipher_alg(void); int uadk_prov_asym_cipher_get_support_state(int alg_tag); int uadk_prov_ecc_init(const char *alg_name); int uadk_prov_ecc_bit_check(const EC_GROUP *group); +bool uadk_prov_support_algorithm(const char *alg); +int uadk_prov_get_affine_coordinates(const EC_GROUP *group, const EC_POINT *p, + BIGNUM *x, BIGNUM *y, BN_CTX *ctx); +void uadk_prov_keyexch_alg(void); +int uadk_prov_securitycheck_enabled(OSSL_LIB_CTX *ctx); +int uadk_prov_ecc_check_key(OSSL_LIB_CTX *ctx, const EC_KEY *ec, int protect);
#endif