Path: blob/main/crypto/openssl/providers/implementations/exchange/ecdh_exch.c
48383 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;8182static83void *ecdh_newctx(void *provctx)84{85PROV_ECDH_CTX *pectx;8687if (!ossl_prov_is_running())88return NULL;8990pectx = OPENSSL_zalloc(sizeof(*pectx));91if (pectx == NULL)92return NULL;9394pectx->libctx = PROV_LIBCTX_OF(provctx);95pectx->cofactor_mode = -1;96pectx->kdf_type = PROV_ECDH_KDF_NONE;97OSSL_FIPS_IND_INIT(pectx)9899return (void *)pectx;100}101102static103int ecdh_init(void *vpecdhctx, void *vecdh, const OSSL_PARAM params[])104{105PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;106107if (!ossl_prov_is_running()108|| pecdhctx == NULL109|| vecdh == NULL110|| (EC_KEY_get0_group(vecdh) == NULL)111|| !EC_KEY_up_ref(vecdh))112return 0;113EC_KEY_free(pecdhctx->k);114pecdhctx->k = vecdh;115pecdhctx->cofactor_mode = -1;116pecdhctx->kdf_type = PROV_ECDH_KDF_NONE;117118OSSL_FIPS_IND_SET_APPROVED(pecdhctx)119if (!ecdh_set_ctx_params(pecdhctx, params))120return 0;121#ifdef FIPS_MODULE122if (!ossl_fips_ind_ec_key_check(OSSL_FIPS_IND_GET(pecdhctx),123OSSL_FIPS_IND_SETTABLE0, pecdhctx->libctx,124EC_KEY_get0_group(vecdh), "ECDH Init", 1))125return 0;126#endif127return 1;128}129130static131int ecdh_match_params(const EC_KEY *priv, const EC_KEY *peer)132{133int ret;134BN_CTX *ctx = NULL;135const EC_GROUP *group_priv = EC_KEY_get0_group(priv);136const EC_GROUP *group_peer = EC_KEY_get0_group(peer);137138ctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(priv));139if (ctx == NULL) {140ERR_raise(ERR_LIB_PROV, ERR_R_BN_LIB);141return 0;142}143ret = group_priv != NULL144&& group_peer != NULL145&& EC_GROUP_cmp(group_priv, group_peer, ctx) == 0;146if (!ret)147ERR_raise(ERR_LIB_PROV, PROV_R_MISMATCHING_DOMAIN_PARAMETERS);148BN_CTX_free(ctx);149return ret;150}151152static153int ecdh_set_peer(void *vpecdhctx, void *vecdh)154{155PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;156157if (!ossl_prov_is_running()158|| pecdhctx == NULL159|| vecdh == NULL160|| !ecdh_match_params(pecdhctx->k, vecdh))161return 0;162#ifdef FIPS_MODULE163if (!ossl_fips_ind_ec_key_check(OSSL_FIPS_IND_GET(pecdhctx),164OSSL_FIPS_IND_SETTABLE0, pecdhctx->libctx,165EC_KEY_get0_group(vecdh), "ECDH Set Peer",1661))167return 0;168#endif169if (!EC_KEY_up_ref(vecdh))170return 0;171172EC_KEY_free(pecdhctx->peerk);173pecdhctx->peerk = vecdh;174return 1;175}176177static178void ecdh_freectx(void *vpecdhctx)179{180PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;181182EC_KEY_free(pecdhctx->k);183EC_KEY_free(pecdhctx->peerk);184185EVP_MD_free(pecdhctx->kdf_md);186OPENSSL_clear_free(pecdhctx->kdf_ukm, pecdhctx->kdf_ukmlen);187188OPENSSL_free(pecdhctx);189}190191static192void *ecdh_dupctx(void *vpecdhctx)193{194PROV_ECDH_CTX *srcctx = (PROV_ECDH_CTX *)vpecdhctx;195PROV_ECDH_CTX *dstctx;196197if (!ossl_prov_is_running())198return NULL;199200dstctx = OPENSSL_zalloc(sizeof(*srcctx));201if (dstctx == NULL)202return NULL;203204*dstctx = *srcctx;205206/* clear all pointers */207208dstctx->k= NULL;209dstctx->peerk = NULL;210dstctx->kdf_md = NULL;211dstctx->kdf_ukm = NULL;212213/* up-ref all ref-counted objects referenced in dstctx */214215if (srcctx->k != NULL && !EC_KEY_up_ref(srcctx->k))216goto err;217else218dstctx->k = srcctx->k;219220if (srcctx->peerk != NULL && !EC_KEY_up_ref(srcctx->peerk))221goto err;222else223dstctx->peerk = srcctx->peerk;224225if (srcctx->kdf_md != NULL && !EVP_MD_up_ref(srcctx->kdf_md))226goto err;227else228dstctx->kdf_md = srcctx->kdf_md;229230/* Duplicate UKM data if present */231if (srcctx->kdf_ukm != NULL && srcctx->kdf_ukmlen > 0) {232dstctx->kdf_ukm = OPENSSL_memdup(srcctx->kdf_ukm,233srcctx->kdf_ukmlen);234if (dstctx->kdf_ukm == NULL)235goto err;236}237238return dstctx;239240err:241ecdh_freectx(dstctx);242return NULL;243}244245static246int ecdh_set_ctx_params(void *vpecdhctx, const OSSL_PARAM params[])247{248char name[80] = { '\0' }; /* should be big enough */249char *str = NULL;250PROV_ECDH_CTX *pectx = (PROV_ECDH_CTX *)vpecdhctx;251const OSSL_PARAM *p;252253if (pectx == NULL)254return 0;255if (ossl_param_is_empty(params))256return 1;257258if (!OSSL_FIPS_IND_SET_CTX_PARAM(pectx, OSSL_FIPS_IND_SETTABLE0, params,259OSSL_EXCHANGE_PARAM_FIPS_KEY_CHECK))260return 0;261if (!OSSL_FIPS_IND_SET_CTX_PARAM(pectx, OSSL_FIPS_IND_SETTABLE1, params,262OSSL_EXCHANGE_PARAM_FIPS_DIGEST_CHECK))263return 0;264if (!OSSL_FIPS_IND_SET_CTX_PARAM(pectx, OSSL_FIPS_IND_SETTABLE2, params,265OSSL_EXCHANGE_PARAM_FIPS_ECDH_COFACTOR_CHECK))266return 0;267268p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE);269if (p != NULL) {270int mode;271272if (!OSSL_PARAM_get_int(p, &mode))273return 0;274275if (mode < -1 || mode > 1)276return 0;277278pectx->cofactor_mode = mode;279}280281p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_TYPE);282if (p != NULL) {283str = name;284if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(name)))285return 0;286287if (name[0] == '\0')288pectx->kdf_type = PROV_ECDH_KDF_NONE;289else if (strcmp(name, OSSL_KDF_NAME_X963KDF) == 0)290pectx->kdf_type = PROV_ECDH_KDF_X9_63;291else292return 0;293}294295p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_DIGEST);296if (p != NULL) {297char mdprops[80] = { '\0' }; /* should be big enough */298299str = name;300if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(name)))301return 0;302303str = mdprops;304p = OSSL_PARAM_locate_const(params,305OSSL_EXCHANGE_PARAM_KDF_DIGEST_PROPS);306307if (p != NULL) {308if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(mdprops)))309return 0;310}311312EVP_MD_free(pectx->kdf_md);313pectx->kdf_md = EVP_MD_fetch(pectx->libctx, name, mdprops);314if (pectx->kdf_md == NULL)315return 0;316/* XOF digests are not allowed */317if (EVP_MD_xof(pectx->kdf_md)) {318ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED);319return 0;320}321#ifdef FIPS_MODULE322if (!ossl_fips_ind_digest_exch_check(OSSL_FIPS_IND_GET(pectx),323OSSL_FIPS_IND_SETTABLE1, pectx->libctx,324pectx->kdf_md, "ECDH Set Ctx")) {325EVP_MD_free(pectx->kdf_md);326pectx->kdf_md = NULL;327return 0;328}329#endif330}331332p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_OUTLEN);333if (p != NULL) {334size_t outlen;335336if (!OSSL_PARAM_get_size_t(p, &outlen))337return 0;338pectx->kdf_outlen = outlen;339}340341p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_UKM);342if (p != NULL) {343void *tmp_ukm = NULL;344size_t tmp_ukmlen;345346if (!OSSL_PARAM_get_octet_string(p, &tmp_ukm, 0, &tmp_ukmlen))347return 0;348OPENSSL_free(pectx->kdf_ukm);349pectx->kdf_ukm = tmp_ukm;350pectx->kdf_ukmlen = tmp_ukmlen;351}352353return 1;354}355356static const OSSL_PARAM known_settable_ctx_params[] = {357OSSL_PARAM_int(OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE, NULL),358OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE, NULL, 0),359OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST, NULL, 0),360OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST_PROPS, NULL, 0),361OSSL_PARAM_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN, NULL),362OSSL_PARAM_octet_string(OSSL_EXCHANGE_PARAM_KDF_UKM, NULL, 0),363OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_EXCHANGE_PARAM_FIPS_KEY_CHECK)364OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_EXCHANGE_PARAM_FIPS_DIGEST_CHECK)365OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_EXCHANGE_PARAM_FIPS_ECDH_COFACTOR_CHECK)366OSSL_PARAM_END367};368369static370const OSSL_PARAM *ecdh_settable_ctx_params(ossl_unused void *vpecdhctx,371ossl_unused void *provctx)372{373return known_settable_ctx_params;374}375376static377int ecdh_get_ctx_params(void *vpecdhctx, OSSL_PARAM params[])378{379PROV_ECDH_CTX *pectx = (PROV_ECDH_CTX *)vpecdhctx;380OSSL_PARAM *p;381382if (pectx == NULL)383return 0;384385p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE);386if (p != NULL) {387int mode = pectx->cofactor_mode;388389if (mode == -1) {390/* check what is the default for pecdhctx->k */391mode = EC_KEY_get_flags(pectx->k) & EC_FLAG_COFACTOR_ECDH ? 1 : 0;392}393394if (!OSSL_PARAM_set_int(p, mode))395return 0;396}397398p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_TYPE);399if (p != NULL) {400const char *kdf_type = NULL;401402switch (pectx->kdf_type) {403case PROV_ECDH_KDF_NONE:404kdf_type = "";405break;406case PROV_ECDH_KDF_X9_63:407kdf_type = OSSL_KDF_NAME_X963KDF;408break;409default:410return 0;411}412413if (!OSSL_PARAM_set_utf8_string(p, kdf_type))414return 0;415}416417p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_DIGEST);418if (p != NULL419&& !OSSL_PARAM_set_utf8_string(p, pectx->kdf_md == NULL420? ""421: EVP_MD_get0_name(pectx->kdf_md))) {422return 0;423}424425p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_OUTLEN);426if (p != NULL && !OSSL_PARAM_set_size_t(p, pectx->kdf_outlen))427return 0;428429p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_UKM);430if (p != NULL &&431!OSSL_PARAM_set_octet_ptr(p, pectx->kdf_ukm, pectx->kdf_ukmlen))432return 0;433if (!OSSL_FIPS_IND_GET_CTX_PARAM(pectx, params))434return 0;435return 1;436}437438static const OSSL_PARAM known_gettable_ctx_params[] = {439OSSL_PARAM_int(OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE, NULL),440OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE, NULL, 0),441OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST, NULL, 0),442OSSL_PARAM_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN, NULL),443OSSL_PARAM_DEFN(OSSL_EXCHANGE_PARAM_KDF_UKM, OSSL_PARAM_OCTET_PTR,444NULL, 0),445OSSL_FIPS_IND_GETTABLE_CTX_PARAM()446OSSL_PARAM_END447};448449static450const OSSL_PARAM *ecdh_gettable_ctx_params(ossl_unused void *vpecdhctx,451ossl_unused void *provctx)452{453return known_gettable_ctx_params;454}455456static ossl_inline457size_t ecdh_size(const EC_KEY *k)458{459size_t degree = 0;460const EC_GROUP *group;461462if (k == NULL463|| (group = EC_KEY_get0_group(k)) == NULL)464return 0;465466degree = EC_GROUP_get_degree(group);467468return (degree + 7) / 8;469}470471static ossl_inline472int ecdh_plain_derive(void *vpecdhctx, unsigned char *secret,473size_t *psecretlen, size_t outlen)474{475PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;476int retlen, ret = 0;477size_t ecdhsize, size;478const EC_POINT *ppubkey = NULL;479EC_KEY *privk = NULL;480const EC_GROUP *group;481const BIGNUM *cofactor;482int key_cofactor_mode;483int has_cofactor;484#ifdef FIPS_MODULE485int cofactor_approved = 0;486#endif487488if (pecdhctx->k == NULL || pecdhctx->peerk == NULL) {489ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);490return 0;491}492493ecdhsize = ecdh_size(pecdhctx->k);494if (secret == NULL) {495*psecretlen = ecdhsize;496return 1;497}498499if ((group = EC_KEY_get0_group(pecdhctx->k)) == NULL500|| (cofactor = EC_GROUP_get0_cofactor(group)) == NULL)501return 0;502503has_cofactor = !BN_is_one(cofactor);504505/*506* NB: unlike PKCS#3 DH, if outlen is less than maximum size this is not507* an error, the result is truncated.508*/509size = outlen < ecdhsize ? outlen : ecdhsize;510511/*512* The ctx->cofactor_mode flag has precedence over the513* cofactor_mode flag set on ctx->k.514*515* - if ctx->cofactor_mode == -1, use ctx->k directly516* - if ctx->cofactor_mode == key_cofactor_mode, use ctx->k directly517* - if ctx->cofactor_mode != key_cofactor_mode:518* - if ctx->k->cofactor == 1, the cofactor_mode flag is irrelevant, use519* ctx->k directly520* - if ctx->k->cofactor != 1, use a duplicate of ctx->k with the flag521* set to ctx->cofactor_mode522*/523key_cofactor_mode =524(EC_KEY_get_flags(pecdhctx->k) & EC_FLAG_COFACTOR_ECDH) ? 1 : 0;525if (pecdhctx->cofactor_mode != -1526&& pecdhctx->cofactor_mode != key_cofactor_mode527&& has_cofactor) {528if ((privk = EC_KEY_dup(pecdhctx->k)) == NULL)529return 0;530531if (pecdhctx->cofactor_mode == 1) {532EC_KEY_set_flags(privk, EC_FLAG_COFACTOR_ECDH);533#ifdef FIPS_MODULE534cofactor_approved = 1;535#endif536} else {537EC_KEY_clear_flags(privk, EC_FLAG_COFACTOR_ECDH);538}539} else {540privk = pecdhctx->k;541#ifdef FIPS_MODULE542cofactor_approved = key_cofactor_mode;543#endif544}545546#ifdef FIPS_MODULE547/*548* SP800-56A r3 Section 5.7.1.2 requires ECC Cofactor DH to be used.549* This applies to the 'B' and 'K' curves that have cofactors that are not 1.550*/551if (has_cofactor && !cofactor_approved) {552if (!OSSL_FIPS_IND_ON_UNAPPROVED(pecdhctx, OSSL_FIPS_IND_SETTABLE2,553pecdhctx->libctx, "ECDH", "Cofactor",554ossl_fips_config_ecdh_cofactor_check)) {555ERR_raise(ERR_LIB_PROV, PROV_R_COFACTOR_REQUIRED);556goto end;557}558}559#endif560561ppubkey = EC_KEY_get0_public_key(pecdhctx->peerk);562563retlen = ECDH_compute_key(secret, size, ppubkey, privk, NULL);564565if (retlen <= 0)566goto end;567568*psecretlen = retlen;569ret = 1;570571end:572if (privk != pecdhctx->k)573EC_KEY_free(privk);574return ret;575}576577static ossl_inline578int ecdh_X9_63_kdf_derive(void *vpecdhctx, unsigned char *secret,579size_t *psecretlen, size_t outlen)580{581PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;582unsigned char *stmp = NULL;583size_t stmplen;584int ret = 0;585586if (secret == NULL) {587*psecretlen = pecdhctx->kdf_outlen;588return 1;589}590591if (pecdhctx->kdf_outlen > outlen) {592ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);593return 0;594}595if (!ecdh_plain_derive(vpecdhctx, NULL, &stmplen, 0))596return 0;597if ((stmp = OPENSSL_secure_malloc(stmplen)) == NULL)598return 0;599if (!ecdh_plain_derive(vpecdhctx, stmp, &stmplen, stmplen))600goto err;601602/* Do KDF stuff */603if (!ossl_ecdh_kdf_X9_63(secret, pecdhctx->kdf_outlen,604stmp, stmplen,605pecdhctx->kdf_ukm,606pecdhctx->kdf_ukmlen,607pecdhctx->kdf_md,608pecdhctx->libctx, NULL))609goto err;610*psecretlen = pecdhctx->kdf_outlen;611ret = 1;612613err:614OPENSSL_secure_clear_free(stmp, stmplen);615return ret;616}617618static619int ecdh_derive(void *vpecdhctx, unsigned char *secret,620size_t *psecretlen, size_t outlen)621{622PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;623624switch (pecdhctx->kdf_type) {625case PROV_ECDH_KDF_NONE:626return ecdh_plain_derive(vpecdhctx, secret, psecretlen, outlen);627case PROV_ECDH_KDF_X9_63:628return ecdh_X9_63_kdf_derive(vpecdhctx, secret, psecretlen, outlen);629default:630break;631}632return 0;633}634635const OSSL_DISPATCH ossl_ecdh_keyexch_functions[] = {636{ OSSL_FUNC_KEYEXCH_NEWCTX, (void (*)(void))ecdh_newctx },637{ OSSL_FUNC_KEYEXCH_INIT, (void (*)(void))ecdh_init },638{ OSSL_FUNC_KEYEXCH_DERIVE, (void (*)(void))ecdh_derive },639{ OSSL_FUNC_KEYEXCH_SET_PEER, (void (*)(void))ecdh_set_peer },640{ OSSL_FUNC_KEYEXCH_FREECTX, (void (*)(void))ecdh_freectx },641{ OSSL_FUNC_KEYEXCH_DUPCTX, (void (*)(void))ecdh_dupctx },642{ OSSL_FUNC_KEYEXCH_SET_CTX_PARAMS, (void (*)(void))ecdh_set_ctx_params },643{ OSSL_FUNC_KEYEXCH_SETTABLE_CTX_PARAMS,644(void (*)(void))ecdh_settable_ctx_params },645{ OSSL_FUNC_KEYEXCH_GET_CTX_PARAMS, (void (*)(void))ecdh_get_ctx_params },646{ OSSL_FUNC_KEYEXCH_GETTABLE_CTX_PARAMS,647(void (*)(void))ecdh_gettable_ctx_params },648OSSL_DISPATCH_END649};650651652