Path: blob/main/crypto/openssl/providers/implementations/exchange/ecdh_exch.c
107437 views
/*1* Copyright 2020-2024 The OpenSSL Project Authors. All Rights Reserved.2*3* Licensed under the Apache License 2.0 (the "License"). You may not use4* this file except in compliance with the License. You can obtain a copy5* in the file LICENSE in the source distribution or at6* https://www.openssl.org/source/license.html7*/89/*10* ECDH low level APIs are deprecated for public use, but still ok for11* internal use.12*/13#include "internal/deprecated.h"1415#include <string.h>16#include <openssl/crypto.h>17#include <openssl/evp.h>18#include <openssl/core_dispatch.h>19#include <openssl/core_names.h>20#include <openssl/ec.h>21#include <openssl/params.h>22#include <openssl/err.h>23#include <openssl/proverr.h>24#include "prov/provider_ctx.h"25#include "prov/providercommon.h"26#include "prov/implementations.h"27#include "prov/securitycheck.h"28#include "crypto/ec.h" /* ossl_ecdh_kdf_X9_63() */2930static OSSL_FUNC_keyexch_newctx_fn ecdh_newctx;31static OSSL_FUNC_keyexch_init_fn ecdh_init;32static OSSL_FUNC_keyexch_set_peer_fn ecdh_set_peer;33static OSSL_FUNC_keyexch_derive_fn ecdh_derive;34static OSSL_FUNC_keyexch_freectx_fn ecdh_freectx;35static OSSL_FUNC_keyexch_dupctx_fn ecdh_dupctx;36static OSSL_FUNC_keyexch_set_ctx_params_fn ecdh_set_ctx_params;37static OSSL_FUNC_keyexch_settable_ctx_params_fn ecdh_settable_ctx_params;38static OSSL_FUNC_keyexch_get_ctx_params_fn ecdh_get_ctx_params;39static OSSL_FUNC_keyexch_gettable_ctx_params_fn ecdh_gettable_ctx_params;4041enum kdf_type {42PROV_ECDH_KDF_NONE = 0,43PROV_ECDH_KDF_X9_6344};4546/*47* What's passed as an actual key is defined by the KEYMGMT interface.48* We happen to know that our KEYMGMT simply passes EC_KEY structures, so49* we use that here too.50*/5152typedef struct {53OSSL_LIB_CTX *libctx;5455EC_KEY *k;56EC_KEY *peerk;5758/*59* ECDH cofactor mode:60*61* . 0 disabled62* . 1 enabled63* . -1 use cofactor mode set for k64*/65int cofactor_mode;6667/************68* ECDH KDF *69************/70/* KDF (if any) to use for ECDH */71enum kdf_type kdf_type;72/* Message digest to use for key derivation */73EVP_MD *kdf_md;74/* User key material */75unsigned char *kdf_ukm;76size_t kdf_ukmlen;77/* KDF output length */78size_t kdf_outlen;79OSSL_FIPS_IND_DECLARE80} PROV_ECDH_CTX;8182static void *ecdh_newctx(void *provctx)83{84PROV_ECDH_CTX *pectx;8586if (!ossl_prov_is_running())87return NULL;8889pectx = OPENSSL_zalloc(sizeof(*pectx));90if (pectx == NULL)91return NULL;9293pectx->libctx = PROV_LIBCTX_OF(provctx);94pectx->cofactor_mode = -1;95pectx->kdf_type = PROV_ECDH_KDF_NONE;96OSSL_FIPS_IND_INIT(pectx)9798return (void *)pectx;99}100101static int ecdh_init(void *vpecdhctx, void *vecdh, const OSSL_PARAM params[])102{103PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;104105if (!ossl_prov_is_running()106|| pecdhctx == NULL107|| vecdh == NULL108|| (EC_KEY_get0_group(vecdh) == NULL)109|| !EC_KEY_up_ref(vecdh))110return 0;111EC_KEY_free(pecdhctx->k);112pecdhctx->k = vecdh;113pecdhctx->cofactor_mode = -1;114pecdhctx->kdf_type = PROV_ECDH_KDF_NONE;115116OSSL_FIPS_IND_SET_APPROVED(pecdhctx)117if (!ecdh_set_ctx_params(pecdhctx, params))118return 0;119#ifdef FIPS_MODULE120if (!ossl_fips_ind_ec_key_check(OSSL_FIPS_IND_GET(pecdhctx),121OSSL_FIPS_IND_SETTABLE0, pecdhctx->libctx,122EC_KEY_get0_group(vecdh), "ECDH Init", 1))123return 0;124#endif125return 1;126}127128static int ecdh_match_params(const EC_KEY *priv, const EC_KEY *peer)129{130int ret;131BN_CTX *ctx = NULL;132const EC_GROUP *group_priv = EC_KEY_get0_group(priv);133const EC_GROUP *group_peer = EC_KEY_get0_group(peer);134135ctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(priv));136if (ctx == NULL) {137ERR_raise(ERR_LIB_PROV, ERR_R_BN_LIB);138return 0;139}140ret = group_priv != NULL141&& group_peer != NULL142&& EC_GROUP_cmp(group_priv, group_peer, ctx) == 0;143if (!ret)144ERR_raise(ERR_LIB_PROV, PROV_R_MISMATCHING_DOMAIN_PARAMETERS);145BN_CTX_free(ctx);146return ret;147}148149static int ecdh_set_peer(void *vpecdhctx, void *vecdh)150{151PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;152153if (!ossl_prov_is_running()154|| pecdhctx == NULL155|| vecdh == NULL156|| !ecdh_match_params(pecdhctx->k, vecdh))157return 0;158#ifdef FIPS_MODULE159if (!ossl_fips_ind_ec_key_check(OSSL_FIPS_IND_GET(pecdhctx),160OSSL_FIPS_IND_SETTABLE0, pecdhctx->libctx,161EC_KEY_get0_group(vecdh), "ECDH Set Peer",1621))163return 0;164#endif165if (!EC_KEY_up_ref(vecdh))166return 0;167168EC_KEY_free(pecdhctx->peerk);169pecdhctx->peerk = vecdh;170return 1;171}172173static void ecdh_freectx(void *vpecdhctx)174{175PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;176177EC_KEY_free(pecdhctx->k);178EC_KEY_free(pecdhctx->peerk);179180EVP_MD_free(pecdhctx->kdf_md);181OPENSSL_clear_free(pecdhctx->kdf_ukm, pecdhctx->kdf_ukmlen);182183OPENSSL_free(pecdhctx);184}185186static void *ecdh_dupctx(void *vpecdhctx)187{188PROV_ECDH_CTX *srcctx = (PROV_ECDH_CTX *)vpecdhctx;189PROV_ECDH_CTX *dstctx;190191if (!ossl_prov_is_running())192return NULL;193194dstctx = OPENSSL_zalloc(sizeof(*srcctx));195if (dstctx == NULL)196return NULL;197198*dstctx = *srcctx;199200/* clear all pointers */201202dstctx->k = NULL;203dstctx->peerk = NULL;204dstctx->kdf_md = NULL;205dstctx->kdf_ukm = NULL;206207/* up-ref all ref-counted objects referenced in dstctx */208209if (srcctx->k != NULL && !EC_KEY_up_ref(srcctx->k))210goto err;211else212dstctx->k = srcctx->k;213214if (srcctx->peerk != NULL && !EC_KEY_up_ref(srcctx->peerk))215goto err;216else217dstctx->peerk = srcctx->peerk;218219if (srcctx->kdf_md != NULL && !EVP_MD_up_ref(srcctx->kdf_md))220goto err;221else222dstctx->kdf_md = srcctx->kdf_md;223224/* Duplicate UKM data if present */225if (srcctx->kdf_ukm != NULL && srcctx->kdf_ukmlen > 0) {226dstctx->kdf_ukm = OPENSSL_memdup(srcctx->kdf_ukm,227srcctx->kdf_ukmlen);228if (dstctx->kdf_ukm == NULL)229goto err;230}231232return dstctx;233234err:235ecdh_freectx(dstctx);236return NULL;237}238239static int ecdh_set_ctx_params(void *vpecdhctx, const OSSL_PARAM params[])240{241char name[80] = { '\0' }; /* should be big enough */242char *str = NULL;243PROV_ECDH_CTX *pectx = (PROV_ECDH_CTX *)vpecdhctx;244const OSSL_PARAM *p;245246if (pectx == NULL)247return 0;248if (ossl_param_is_empty(params))249return 1;250251if (!OSSL_FIPS_IND_SET_CTX_PARAM(pectx, OSSL_FIPS_IND_SETTABLE0, params,252OSSL_EXCHANGE_PARAM_FIPS_KEY_CHECK))253return 0;254if (!OSSL_FIPS_IND_SET_CTX_PARAM(pectx, OSSL_FIPS_IND_SETTABLE1, params,255OSSL_EXCHANGE_PARAM_FIPS_DIGEST_CHECK))256return 0;257if (!OSSL_FIPS_IND_SET_CTX_PARAM(pectx, OSSL_FIPS_IND_SETTABLE2, params,258OSSL_EXCHANGE_PARAM_FIPS_ECDH_COFACTOR_CHECK))259return 0;260261p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE);262if (p != NULL) {263int mode;264265if (!OSSL_PARAM_get_int(p, &mode))266return 0;267268if (mode < -1 || mode > 1)269return 0;270271pectx->cofactor_mode = mode;272}273274p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_TYPE);275if (p != NULL) {276str = name;277if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(name)))278return 0;279280if (name[0] == '\0')281pectx->kdf_type = PROV_ECDH_KDF_NONE;282else if (strcmp(name, OSSL_KDF_NAME_X963KDF) == 0)283pectx->kdf_type = PROV_ECDH_KDF_X9_63;284else285return 0;286}287288p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_DIGEST);289if (p != NULL) {290char mdprops[80] = { '\0' }; /* should be big enough */291292str = name;293if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(name)))294return 0;295296str = mdprops;297p = OSSL_PARAM_locate_const(params,298OSSL_EXCHANGE_PARAM_KDF_DIGEST_PROPS);299300if (p != NULL) {301if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(mdprops)))302return 0;303}304305EVP_MD_free(pectx->kdf_md);306pectx->kdf_md = EVP_MD_fetch(pectx->libctx, name, mdprops);307if (pectx->kdf_md == NULL)308return 0;309/* XOF digests are not allowed */310if (EVP_MD_xof(pectx->kdf_md)) {311ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED);312return 0;313}314#ifdef FIPS_MODULE315if (!ossl_fips_ind_digest_exch_check(OSSL_FIPS_IND_GET(pectx),316OSSL_FIPS_IND_SETTABLE1, pectx->libctx,317pectx->kdf_md, "ECDH Set Ctx")) {318EVP_MD_free(pectx->kdf_md);319pectx->kdf_md = NULL;320return 0;321}322#endif323}324325p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_OUTLEN);326if (p != NULL) {327size_t outlen;328329if (!OSSL_PARAM_get_size_t(p, &outlen))330return 0;331pectx->kdf_outlen = outlen;332}333334p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_UKM);335if (p != NULL) {336void *tmp_ukm = NULL;337size_t tmp_ukmlen;338339if (!OSSL_PARAM_get_octet_string(p, &tmp_ukm, 0, &tmp_ukmlen))340return 0;341OPENSSL_free(pectx->kdf_ukm);342pectx->kdf_ukm = tmp_ukm;343pectx->kdf_ukmlen = tmp_ukmlen;344}345346return 1;347}348349static const OSSL_PARAM known_settable_ctx_params[] = {350OSSL_PARAM_int(OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE, NULL),351OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE, NULL, 0),352OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST, NULL, 0),353OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST_PROPS, NULL, 0),354OSSL_PARAM_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN, NULL),355OSSL_PARAM_octet_string(OSSL_EXCHANGE_PARAM_KDF_UKM, NULL, 0),356OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_EXCHANGE_PARAM_FIPS_KEY_CHECK)357OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_EXCHANGE_PARAM_FIPS_DIGEST_CHECK)358OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_EXCHANGE_PARAM_FIPS_ECDH_COFACTOR_CHECK)359OSSL_PARAM_END360};361362static const OSSL_PARAM *ecdh_settable_ctx_params(ossl_unused void *vpecdhctx,363ossl_unused void *provctx)364{365return known_settable_ctx_params;366}367368static int ecdh_get_ctx_params(void *vpecdhctx, OSSL_PARAM params[])369{370PROV_ECDH_CTX *pectx = (PROV_ECDH_CTX *)vpecdhctx;371OSSL_PARAM *p;372373if (pectx == NULL)374return 0;375376p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE);377if (p != NULL) {378int mode = pectx->cofactor_mode;379380if (mode == -1) {381/* check what is the default for pecdhctx->k */382mode = EC_KEY_get_flags(pectx->k) & EC_FLAG_COFACTOR_ECDH ? 1 : 0;383}384385if (!OSSL_PARAM_set_int(p, mode))386return 0;387}388389p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_TYPE);390if (p != NULL) {391const char *kdf_type = NULL;392393switch (pectx->kdf_type) {394case PROV_ECDH_KDF_NONE:395kdf_type = "";396break;397case PROV_ECDH_KDF_X9_63:398kdf_type = OSSL_KDF_NAME_X963KDF;399break;400default:401return 0;402}403404if (!OSSL_PARAM_set_utf8_string(p, kdf_type))405return 0;406}407408p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_DIGEST);409if (p != NULL410&& !OSSL_PARAM_set_utf8_string(p, pectx->kdf_md == NULL ? "" : EVP_MD_get0_name(pectx->kdf_md))) {411return 0;412}413414p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_OUTLEN);415if (p != NULL && !OSSL_PARAM_set_size_t(p, pectx->kdf_outlen))416return 0;417418p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_UKM);419if (p != NULL && !OSSL_PARAM_set_octet_ptr(p, pectx->kdf_ukm, pectx->kdf_ukmlen))420return 0;421if (!OSSL_FIPS_IND_GET_CTX_PARAM(pectx, params))422return 0;423return 1;424}425426static const OSSL_PARAM known_gettable_ctx_params[] = {427OSSL_PARAM_int(OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE, NULL),428OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE, NULL, 0),429OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST, NULL, 0),430OSSL_PARAM_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN, NULL),431OSSL_PARAM_DEFN(OSSL_EXCHANGE_PARAM_KDF_UKM, OSSL_PARAM_OCTET_PTR,432NULL, 0),433OSSL_FIPS_IND_GETTABLE_CTX_PARAM()434OSSL_PARAM_END435};436437static const OSSL_PARAM *ecdh_gettable_ctx_params(ossl_unused void *vpecdhctx,438ossl_unused void *provctx)439{440return known_gettable_ctx_params;441}442443static ossl_inline444size_t445ecdh_size(const EC_KEY *k)446{447size_t degree = 0;448const EC_GROUP *group;449450if (k == NULL451|| (group = EC_KEY_get0_group(k)) == NULL)452return 0;453454degree = EC_GROUP_get_degree(group);455456return (degree + 7) / 8;457}458459static ossl_inline int ecdh_plain_derive(void *vpecdhctx, unsigned char *secret,460size_t *psecretlen, size_t outlen)461{462PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;463int retlen, ret = 0;464size_t ecdhsize, size;465const EC_POINT *ppubkey = NULL;466EC_KEY *privk = NULL;467const EC_GROUP *group;468const BIGNUM *cofactor;469int key_cofactor_mode;470int has_cofactor;471#ifdef FIPS_MODULE472int cofactor_approved = 0;473#endif474475if (pecdhctx->k == NULL || pecdhctx->peerk == NULL) {476ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);477return 0;478}479480ecdhsize = ecdh_size(pecdhctx->k);481if (secret == NULL) {482*psecretlen = ecdhsize;483return 1;484}485486if ((group = EC_KEY_get0_group(pecdhctx->k)) == NULL487|| (cofactor = EC_GROUP_get0_cofactor(group)) == NULL)488return 0;489490has_cofactor = !BN_is_one(cofactor);491492/*493* NB: unlike PKCS#3 DH, if outlen is less than maximum size this is not494* an error, the result is truncated.495*/496size = outlen < ecdhsize ? outlen : ecdhsize;497498/*499* The ctx->cofactor_mode flag has precedence over the500* cofactor_mode flag set on ctx->k.501*502* - if ctx->cofactor_mode == -1, use ctx->k directly503* - if ctx->cofactor_mode == key_cofactor_mode, use ctx->k directly504* - if ctx->cofactor_mode != key_cofactor_mode:505* - if ctx->k->cofactor == 1, the cofactor_mode flag is irrelevant, use506* ctx->k directly507* - if ctx->k->cofactor != 1, use a duplicate of ctx->k with the flag508* set to ctx->cofactor_mode509*/510key_cofactor_mode = (EC_KEY_get_flags(pecdhctx->k) & EC_FLAG_COFACTOR_ECDH) ? 1 : 0;511if (pecdhctx->cofactor_mode != -1512&& pecdhctx->cofactor_mode != key_cofactor_mode513&& has_cofactor) {514if ((privk = EC_KEY_dup(pecdhctx->k)) == NULL)515return 0;516517if (pecdhctx->cofactor_mode == 1) {518EC_KEY_set_flags(privk, EC_FLAG_COFACTOR_ECDH);519#ifdef FIPS_MODULE520cofactor_approved = 1;521#endif522} else {523EC_KEY_clear_flags(privk, EC_FLAG_COFACTOR_ECDH);524}525} else {526privk = pecdhctx->k;527#ifdef FIPS_MODULE528cofactor_approved = key_cofactor_mode;529#endif530}531532#ifdef FIPS_MODULE533/*534* SP800-56A r3 Section 5.7.1.2 requires ECC Cofactor DH to be used.535* This applies to the 'B' and 'K' curves that have cofactors that are not 1.536*/537if (has_cofactor && !cofactor_approved) {538if (!OSSL_FIPS_IND_ON_UNAPPROVED(pecdhctx, OSSL_FIPS_IND_SETTABLE2,539pecdhctx->libctx, "ECDH", "Cofactor",540ossl_fips_config_ecdh_cofactor_check)) {541ERR_raise(ERR_LIB_PROV, PROV_R_COFACTOR_REQUIRED);542goto end;543}544}545#endif546547ppubkey = EC_KEY_get0_public_key(pecdhctx->peerk);548549retlen = ECDH_compute_key(secret, size, ppubkey, privk, NULL);550551if (retlen <= 0)552goto end;553554*psecretlen = retlen;555ret = 1;556557end:558if (privk != pecdhctx->k)559EC_KEY_free(privk);560return ret;561}562563static ossl_inline int ecdh_X9_63_kdf_derive(void *vpecdhctx, unsigned char *secret,564size_t *psecretlen, size_t outlen)565{566PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;567unsigned char *stmp = NULL;568size_t stmplen;569int ret = 0;570571if (secret == NULL) {572*psecretlen = pecdhctx->kdf_outlen;573return 1;574}575576if (pecdhctx->kdf_outlen > outlen) {577ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);578return 0;579}580if (!ecdh_plain_derive(vpecdhctx, NULL, &stmplen, 0))581return 0;582if ((stmp = OPENSSL_secure_malloc(stmplen)) == NULL)583return 0;584if (!ecdh_plain_derive(vpecdhctx, stmp, &stmplen, stmplen))585goto err;586587/* Do KDF stuff */588if (!ossl_ecdh_kdf_X9_63(secret, pecdhctx->kdf_outlen,589stmp, stmplen,590pecdhctx->kdf_ukm,591pecdhctx->kdf_ukmlen,592pecdhctx->kdf_md,593pecdhctx->libctx, NULL))594goto err;595*psecretlen = pecdhctx->kdf_outlen;596ret = 1;597598err:599OPENSSL_secure_clear_free(stmp, stmplen);600return ret;601}602603static int ecdh_derive(void *vpecdhctx, unsigned char *secret,604size_t *psecretlen, size_t outlen)605{606PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;607608switch (pecdhctx->kdf_type) {609case PROV_ECDH_KDF_NONE:610return ecdh_plain_derive(vpecdhctx, secret, psecretlen, outlen);611case PROV_ECDH_KDF_X9_63:612return ecdh_X9_63_kdf_derive(vpecdhctx, secret, psecretlen, outlen);613default:614break;615}616return 0;617}618619const OSSL_DISPATCH ossl_ecdh_keyexch_functions[] = {620{ OSSL_FUNC_KEYEXCH_NEWCTX, (void (*)(void))ecdh_newctx },621{ OSSL_FUNC_KEYEXCH_INIT, (void (*)(void))ecdh_init },622{ OSSL_FUNC_KEYEXCH_DERIVE, (void (*)(void))ecdh_derive },623{ OSSL_FUNC_KEYEXCH_SET_PEER, (void (*)(void))ecdh_set_peer },624{ OSSL_FUNC_KEYEXCH_FREECTX, (void (*)(void))ecdh_freectx },625{ OSSL_FUNC_KEYEXCH_DUPCTX, (void (*)(void))ecdh_dupctx },626{ OSSL_FUNC_KEYEXCH_SET_CTX_PARAMS, (void (*)(void))ecdh_set_ctx_params },627{ OSSL_FUNC_KEYEXCH_SETTABLE_CTX_PARAMS,628(void (*)(void))ecdh_settable_ctx_params },629{ OSSL_FUNC_KEYEXCH_GET_CTX_PARAMS, (void (*)(void))ecdh_get_ctx_params },630{ OSSL_FUNC_KEYEXCH_GETTABLE_CTX_PARAMS,631(void (*)(void))ecdh_gettable_ctx_params },632OSSL_DISPATCH_END633};634635636