From: Richard Levitte Date: Sun, 7 Jul 2019 08:56:46 +0000 (+0200) Subject: Adapt DH to use with KEYMGMT X-Git-Tag: openssl-3.0.0-alpha1~1723 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=8b84b075ff065554c0cdd1086950f1a8614d93a4;p=oweals%2Fopenssl.git Adapt DH to use with KEYMGMT The biggest part in this was to move the key->param builder from EVP to the DH ASN.1 method, and to implement the KEYMGMT support in the provider DH. Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/9394) --- diff --git a/crypto/dh/dh_ameth.c b/crypto/dh/dh_ameth.c index 524cac5bd8..6da4878200 100644 --- a/crypto/dh/dh_ameth.c +++ b/crypto/dh/dh_ameth.c @@ -16,6 +16,8 @@ #include "internal/asn1_int.h" #include "internal/evp_int.h" #include +#include +#include "internal/param_build.h" /* * i2d/d2i like DH parameter functions which use the appropriate routine for @@ -181,7 +183,7 @@ static int dh_priv_decode(EVP_PKEY *pkey, const PKCS8_PRIV_KEY_INFO *p8) DHerr(DH_F_DH_PRIV_DECODE, DH_R_BN_ERROR); goto dherr; } - /* Calculate public key */ + /* Calculate public key, increments dirty_cnt */ if (!DH_generate_key(dh)) goto dherr; @@ -255,6 +257,7 @@ static int dh_param_decode(EVP_PKEY *pkey, DHerr(DH_F_DH_PARAM_DECODE, ERR_R_DH_LIB); return 0; } + dh->dirty_cnt++; EVP_PKEY_assign(pkey, pkey->ameth->pkey_id, dh); return 1; } @@ -415,6 +418,7 @@ static int int_dh_param_copy(DH *to, const DH *from, int is_x942) } } else to->length = from->length; + to->dirty_cnt++; return 1; } @@ -540,6 +544,50 @@ static int dh_pkey_param_check(const EVP_PKEY *pkey) return DH_check_ex(dh); } +static size_t dh_pkey_dirty_cnt(const EVP_PKEY *pkey) +{ + return pkey->pkey.dh->dirty_cnt; +} + +static void *dh_pkey_export_to(const EVP_PKEY *pk, EVP_KEYMGMT *keymgmt) +{ + DH *dh = pk->pkey.dh; + OSSL_PARAM_BLD tmpl; + const BIGNUM *p = DH_get0_p(dh), *g = DH_get0_g(dh), *q = DH_get0_q(dh); + const BIGNUM *pub_key = DH_get0_pub_key(dh); + const BIGNUM *priv_key = DH_get0_priv_key(dh); + OSSL_PARAM *params; + void *provkey = NULL; + + if (p == NULL || g == NULL || pub_key == NULL) + return NULL; + + ossl_param_bld_init(&tmpl); + if (!ossl_param_bld_push_BN(&tmpl, OSSL_PKEY_PARAM_DH_P, p) + || !ossl_param_bld_push_BN(&tmpl, OSSL_PKEY_PARAM_DH_G, g) + || !ossl_param_bld_push_BN(&tmpl, OSSL_PKEY_PARAM_DH_PUB_KEY, pub_key)) + return NULL; + + if (q != NULL) { + if (!ossl_param_bld_push_BN(&tmpl, OSSL_PKEY_PARAM_DH_Q, q)) + return NULL; + } + + if (priv_key != NULL) { + if (!ossl_param_bld_push_BN(&tmpl, OSSL_PKEY_PARAM_DH_PRIV_KEY, + priv_key)) + return NULL; + } + + params = ossl_param_bld_to_param(&tmpl); + + /* We export, the provider imports */ + provkey = evp_keymgmt_importkey(keymgmt, params); + + ossl_param_bld_free(params); + return provkey; +} + const EVP_PKEY_ASN1_METHOD dh_asn1_meth = { EVP_PKEY_DH, EVP_PKEY_DH, @@ -576,7 +624,12 @@ const EVP_PKEY_ASN1_METHOD dh_asn1_meth = { 0, dh_pkey_public_check, - dh_pkey_param_check + dh_pkey_param_check, + + 0, 0, 0, 0, + + dh_pkey_dirty_cnt, + dh_pkey_export_to, }; const EVP_PKEY_ASN1_METHOD dhx_asn1_meth = { diff --git a/crypto/dh/dh_asn1.c b/crypto/dh/dh_asn1.c index aabdfa8360..71379d73bb 100644 --- a/crypto/dh/dh_asn1.c +++ b/crypto/dh/dh_asn1.c @@ -27,6 +27,8 @@ static int dh_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it, DH_free((DH *)*pval); *pval = NULL; return 2; + } else if (operation == ASN1_OP_D2I_POST) { + ((DH *)*pval)->dirty_cnt++; } return 1; } diff --git a/crypto/dh/dh_gen.c b/crypto/dh/dh_gen.c index bbf774f138..6e98b59d85 100644 --- a/crypto/dh/dh_gen.c +++ b/crypto/dh/dh_gen.c @@ -111,6 +111,7 @@ static int dh_builtin_genparams(DH *ret, int prime_len, int generator, goto err; if (!BN_set_word(ret->g, g)) goto err; + ret->dirty_cnt++; ok = 1; err: if (ok == -1) { diff --git a/crypto/dh/dh_key.c b/crypto/dh/dh_key.c index 4df993e345..0d6b04de20 100644 --- a/crypto/dh/dh_key.c +++ b/crypto/dh/dh_key.c @@ -154,6 +154,7 @@ static int generate_key(DH *dh) dh->pub_key = pub_key; dh->priv_key = priv_key; + dh->dirty_cnt++; ok = 1; err: if (ok != 1) diff --git a/crypto/dh/dh_lib.c b/crypto/dh/dh_lib.c index 70298edd08..df3166279e 100644 --- a/crypto/dh/dh_lib.c +++ b/crypto/dh/dh_lib.c @@ -209,6 +209,7 @@ int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g) dh->length = BN_num_bits(q); } + dh->dirty_cnt++; return 1; } @@ -242,6 +243,7 @@ int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key) dh->priv_key = priv_key; } + dh->dirty_cnt++; return 1; } diff --git a/crypto/dh/dh_locl.h b/crypto/dh/dh_locl.h index bb64cde9f7..f0247b8d7d 100644 --- a/crypto/dh/dh_locl.h +++ b/crypto/dh/dh_locl.h @@ -35,6 +35,9 @@ struct dh_st { const DH_METHOD *meth; ENGINE *engine; CRYPTO_RWLOCK *lock; + + /* Provider data */ + size_t dirty_cnt; /* If any key material changes, increment this */ }; struct dh_method { diff --git a/crypto/dh/dh_rfc7919.c b/crypto/dh/dh_rfc7919.c index e9f3eafb0e..4e676fd3d1 100644 --- a/crypto/dh/dh_rfc7919.c +++ b/crypto/dh/dh_rfc7919.c @@ -22,6 +22,7 @@ static DH *dh_param_init(const BIGNUM *p, int32_t nbits) dh->p = (BIGNUM *)p; dh->g = (BIGNUM *)&_bignum_const_2; dh->length = nbits; + dh->dirty_cnt++; return dh; } diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt index b852fa2946..d88e98993e 100644 --- a/crypto/err/openssl.txt +++ b/crypto/err/openssl.txt @@ -821,6 +821,7 @@ EVP_F_EVP_KDF_CTRL:224:EVP_KDF_ctrl EVP_F_EVP_KDF_CTRL_STR:225:EVP_KDF_ctrl_str EVP_F_EVP_KDF_CTX_NEW:240:EVP_KDF_CTX_new EVP_F_EVP_KDF_CTX_NEW_ID:226:EVP_KDF_CTX_new_id +EVP_F_EVP_KEYEXCH_FETCH:245:EVP_KEYEXCH_fetch EVP_F_EVP_KEYEXCH_FROM_DISPATCH:244:evp_keyexch_from_dispatch EVP_F_EVP_MAC_CTRL:209:EVP_MAC_ctrl EVP_F_EVP_MAC_CTRL_STR:210:EVP_MAC_ctrl_str @@ -2464,6 +2465,7 @@ EVP_R_NOT_XOF_OR_INVALID_LENGTH:178:not XOF or invalid length EVP_R_NO_CIPHER_SET:131:no cipher set EVP_R_NO_DEFAULT_DIGEST:158:no default digest EVP_R_NO_DIGEST_SET:139:no digest set +EVP_R_NO_KEYMGMT_PRESENT:196:no keymgmt present EVP_R_NO_KEY_SET:154:no key set EVP_R_NO_OPERATION_SET:149:no operation set EVP_R_ONLY_ONESHOT_SUPPORTED:177:only oneshot supported diff --git a/crypto/evp/evp_err.c b/crypto/evp/evp_err.c index d517099053..92df593821 100644 --- a/crypto/evp/evp_err.c +++ b/crypto/evp/evp_err.c @@ -95,6 +95,7 @@ static const ERR_STRING_DATA EVP_str_reasons[] = { {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_CIPHER_SET), "no cipher set"}, {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_DEFAULT_DIGEST), "no default digest"}, {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_DIGEST_SET), "no digest set"}, + {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_KEYMGMT_PRESENT), "no keymgmt present"}, {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_KEY_SET), "no key set"}, {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_NO_OPERATION_SET), "no operation set"}, {ERR_PACK(ERR_LIB_EVP, 0, EVP_R_ONLY_ONESHOT_SUPPORTED), diff --git a/crypto/evp/evp_lib.c b/crypto/evp/evp_lib.c index 0825c102b5..9091f8b475 100644 --- a/crypto/evp/evp_lib.c +++ b/crypto/evp/evp_lib.c @@ -760,137 +760,3 @@ int EVP_hex2ctrl(int (*cb)(void *ctx, int cmd, void *buf, size_t buflen), OPENSSL_free(bin); return rv; } - -#ifndef FIPS_MODE -# ifndef OPENSSL_NO_DH -/* - * TODO(3.0): Temporarily unavailable in FIPS mode. This will need to be added - * in later. - */ - -# define MAX_PARAMS 10 -typedef struct { - /* Number of the current param */ - size_t curr; - struct { - /* Key for the current param */ - const char *key; - /* Value for the current param */ - const BIGNUM *bnparam; - /* Size of the buffer required for the BN */ - size_t bufsz; - } params[MAX_PARAMS]; - /* Running count of the total size required */ - size_t totsz; - int ispublic; -} PARAMS_TEMPLATE; - -static int push_param_bn(PARAMS_TEMPLATE *tmpl, const char *key, - const BIGNUM *bn) -{ - int sz; - - sz = BN_num_bytes(bn); - if (sz <= 0) - return 0; - tmpl->params[tmpl->curr].key = key; - tmpl->params[tmpl->curr].bnparam = bn; - tmpl->params[tmpl->curr++].bufsz = (size_t)sz; - tmpl->totsz += sizeof(OSSL_PARAM) + (size_t)sz; - - return 1; -} - -static OSSL_PARAM *param_template_to_param(PARAMS_TEMPLATE *tmpl, size_t *sz) -{ - size_t i; - void *buf; - OSSL_PARAM *param = NULL; - unsigned char *currbuf = NULL; - - if (tmpl->totsz == 0) - return NULL; - - /* Add some space for the end of OSSL_PARAM marker */ - tmpl->totsz += sizeof(*param); - - if (tmpl->ispublic) - buf = OPENSSL_zalloc(tmpl->totsz); - else - buf = OPENSSL_secure_zalloc(tmpl->totsz); - if (buf == NULL) - return NULL; - param = buf; - - currbuf = (unsigned char *)buf + (sizeof(*param) * (tmpl->curr + 1)); - - for (i = 0; i < tmpl->curr; i++) { - if (!ossl_assert((currbuf - (unsigned char *)buf ) - + tmpl->params[i].bufsz <= tmpl->totsz)) - goto err; - if (BN_bn2nativepad(tmpl->params[i].bnparam, currbuf, - tmpl->params[i].bufsz) < 0) - goto err; - param[i] = OSSL_PARAM_construct_BN(tmpl->params[i].key, currbuf, - tmpl->params[i].bufsz); - currbuf += tmpl->params[i].bufsz; - } - param[i] = OSSL_PARAM_construct_end(); - - if (sz != NULL) - *sz = tmpl->totsz; - return param; - - err: - if (tmpl->ispublic) - OPENSSL_free(param); - else - OPENSSL_clear_free(param, tmpl->totsz); - return NULL; -} - -static OSSL_PARAM *evp_pkey_dh_to_param(EVP_PKEY *pkey, size_t *sz) -{ - DH *dh = pkey->pkey.dh; - PARAMS_TEMPLATE tmpl = {0}; - const BIGNUM *p = DH_get0_p(dh), *g = DH_get0_g(dh), *q = DH_get0_q(dh); - const BIGNUM *pub_key = DH_get0_pub_key(dh); - const BIGNUM *priv_key = DH_get0_priv_key(dh); - - if (p == NULL || g == NULL || pub_key == NULL) - return NULL; - - if (!push_param_bn(&tmpl, OSSL_PKEY_PARAM_DH_P, p) - || !push_param_bn(&tmpl, OSSL_PKEY_PARAM_DH_G, g) - || !push_param_bn(&tmpl, OSSL_PKEY_PARAM_DH_PUB_KEY, pub_key)) - return NULL; - - if (q != NULL) { - if (!push_param_bn(&tmpl, OSSL_PKEY_PARAM_DH_Q, q)) - return NULL; - } - - if (priv_key != NULL) { - if (!push_param_bn(&tmpl, OSSL_PKEY_PARAM_DH_PRIV_KEY, priv_key)) - return NULL; - } else { - tmpl.ispublic = 1; - } - - return param_template_to_param(&tmpl, sz); -} -# endif /* OPENSSL_NO_DH */ - -OSSL_PARAM *evp_pkey_to_param(EVP_PKEY *pkey, size_t *sz) -{ - switch (pkey->type) { -# ifndef OPENSSL_NO_DH - case EVP_PKEY_DH: - return evp_pkey_dh_to_param(pkey, sz); -# endif - default: - return NULL; - } -} - -#endif /* FIPS_MODE */ diff --git a/crypto/evp/evp_locl.h b/crypto/evp/evp_locl.h index ce46163140..848ef299ca 100644 --- a/crypto/evp/evp_locl.h +++ b/crypto/evp/evp_locl.h @@ -94,6 +94,8 @@ struct evp_keyexch_st { CRYPTO_REF_COUNT refcnt; CRYPTO_RWLOCK *lock; + EVP_KEYMGMT *keymgmt; + OSSL_OP_keyexch_newctx_fn *newctx; OSSL_OP_keyexch_init_fn *init; OSSL_OP_keyexch_set_peer_fn *set_peer; diff --git a/crypto/evp/exchange.c b/crypto/evp/exchange.c index d8afcbd633..1c48e7f0ba 100644 --- a/crypto/evp/exchange.c +++ b/crypto/evp/exchange.c @@ -112,6 +112,7 @@ void EVP_KEYEXCH_free(EVP_KEYEXCH *exchange) CRYPTO_DOWN_REF(&exchange->refcnt, &i, exchange->lock); if (i > 0) return; + EVP_KEYMGMT_free(exchange->keymgmt); ossl_provider_free(exchange->prov); OPENSSL_free(exchange->name); CRYPTO_THREAD_lock_free(exchange->lock); @@ -127,20 +128,46 @@ int EVP_KEYEXCH_up_ref(EVP_KEYEXCH *exchange) return 1; } +OSSL_PROVIDER *EVP_KEYEXCH_provider(const EVP_KEYEXCH *exchange) +{ + return exchange->prov; +} + EVP_KEYEXCH *EVP_KEYEXCH_fetch(OPENSSL_CTX *ctx, const char *algorithm, const char *properties) { - return evp_generic_fetch(ctx, OSSL_OP_KEYEXCH, algorithm, properties, - evp_keyexch_from_dispatch, - (int (*)(void *))EVP_KEYEXCH_up_ref, - (void (*)(void *))EVP_KEYEXCH_free); + /* + * Key exchange cannot work without a key, and we key management + * from the same provider to manage its keys. + */ + EVP_KEYEXCH *keyexch = + evp_generic_fetch(ctx, OSSL_OP_KEYEXCH, algorithm, properties, + evp_keyexch_from_dispatch, + (int (*)(void *))EVP_KEYEXCH_up_ref, + (void (*)(void *))EVP_KEYEXCH_free); + + /* If the method is newly created, there's no keymgmt attached */ + if (keyexch->keymgmt == NULL) { + EVP_KEYMGMT *keymgmt = EVP_KEYMGMT_fetch(ctx, algorithm, properties); + + if (keymgmt == NULL + || (EVP_KEYEXCH_provider(keyexch) + != EVP_KEYMGMT_provider(keymgmt))) { + EVP_KEYEXCH_free(keyexch); + EVP_KEYMGMT_free(keymgmt); + EVPerr(EVP_F_EVP_KEYEXCH_FETCH, EVP_R_NO_KEYMGMT_PRESENT); + return NULL; + } + + keyexch->keymgmt = keymgmt; + } + return keyexch; } int EVP_PKEY_derive_init_ex(EVP_PKEY_CTX *ctx, EVP_KEYEXCH *exchange) { int ret; - OSSL_PARAM *param = NULL; - size_t paramsz = 0; + void *provkey = NULL; ctx->operation = EVP_PKEY_OP_DERIVE; @@ -180,26 +207,19 @@ int EVP_PKEY_derive_init_ex(EVP_PKEY_CTX *ctx, EVP_KEYEXCH *exchange) EVP_KEYEXCH_free(ctx->exchange); ctx->exchange = exchange; if (ctx->pkey != NULL) { - param = evp_pkey_to_param(ctx->pkey, ¶msz); - if (param == NULL) { + provkey = evp_keymgmt_export_to_provider(ctx->pkey, exchange->keymgmt); + if (provkey == NULL) { EVPerr(EVP_F_EVP_PKEY_DERIVE_INIT_EX, EVP_R_INITIALIZATION_ERROR); goto err; } } ctx->exchprovctx = exchange->newctx(ossl_provider_ctx(exchange->prov)); if (ctx->exchprovctx == NULL) { - OPENSSL_secure_clear_free(param, paramsz); + /* The provider key can stay in the cache */ EVPerr(EVP_F_EVP_PKEY_DERIVE_INIT_EX, EVP_R_INITIALIZATION_ERROR); goto err; } - ret = exchange->init(ctx->exchprovctx, param); - /* - * TODO(3.0): Really we should detect whether to call OPENSSL_free or - * OPENSSL_secure_clear_free based on the presence of a private key or not. - * Since we always expect a private key to be present we just call - * OPENSSL_secure_clear_free for now. - */ - OPENSSL_secure_clear_free(param, paramsz); + ret = exchange->init(ctx->exchprovctx, provkey); return ret ? 1 : 0; err: @@ -229,7 +249,7 @@ int EVP_PKEY_derive_init(EVP_PKEY_CTX *ctx) int EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *ctx, EVP_PKEY *peer) { int ret; - OSSL_PARAM *param = NULL; + void *provkey = NULL; if (ctx == NULL) { EVPerr(EVP_F_EVP_PKEY_DERIVE_SET_PEER, @@ -252,21 +272,12 @@ int EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *ctx, EVP_PKEY *peer) return -2; } - param = evp_pkey_to_param(peer, NULL); - if (param == NULL) { + provkey = evp_keymgmt_export_to_provider(peer, ctx->exchange->keymgmt); + if (provkey == NULL) { EVPerr(EVP_F_EVP_PKEY_DERIVE_SET_PEER, ERR_R_INTERNAL_ERROR); return 0; } - ret = ctx->exchange->set_peer(ctx->exchprovctx, param); - /* - * TODO(3.0): Really we should detect whether to call OPENSSL_free or - * OPENSSL_secure_clear_free based on the presence of a private key or not. - * Since we always expect a public key to be present we just call - * OPENSSL_free for now. - */ - OPENSSL_free(param); - - return ret; + return ctx->exchange->set_peer(ctx->exchprovctx, provkey); legacy: if (ctx->pmeth == NULL diff --git a/crypto/evp/pmeth_lib.c b/crypto/evp/pmeth_lib.c index cc26f06d9b..31b4ae4411 100644 --- a/crypto/evp/pmeth_lib.c +++ b/crypto/evp/pmeth_lib.c @@ -171,7 +171,7 @@ static EVP_PKEY_CTX *int_ctx_new(EVP_PKEY *pkey, ENGINE *e, int id) if (pkey != NULL) EVP_PKEY_up_ref(pkey); - if (pmeth != NULL && pmeth->init) { + if (pmeth != NULL && pmeth->init != NULL) { if (pmeth->init(ret) <= 0) { ret->pmeth = NULL; EVP_PKEY_CTX_free(ret); diff --git a/doc/man3/EVP_KEYEXCH_free.pod b/doc/man3/EVP_KEYEXCH_free.pod index 912434d5c9..41926f6b12 100644 --- a/doc/man3/EVP_KEYEXCH_free.pod +++ b/doc/man3/EVP_KEYEXCH_free.pod @@ -2,7 +2,7 @@ =head1 NAME -EVP_KEYEXCH_fetch, EVP_KEYEXCH_free, EVP_KEYEXCH_up_ref +EVP_KEYEXCH_fetch, EVP_KEYEXCH_free, EVP_KEYEXCH_up_ref, EVP_KEYEXCH_provider - Functions to manage EVP_KEYEXCH algorithm objects =head1 SYNOPSIS @@ -13,6 +13,7 @@ EVP_KEYEXCH_fetch, EVP_KEYEXCH_free, EVP_KEYEXCH_up_ref const char *properties); void EVP_KEYEXCH_free(EVP_KEYEXCH *exchange); int EVP_KEYEXCH_up_ref(EVP_KEYEXCH *exchange); + OSSL_PROVIDER *EVP_KEYEXCH_provider(const EVP_KEYEXCH *exchange); =head1 DESCRIPTION @@ -31,6 +32,8 @@ structure is freed. EVP_KEYEXCH_up_ref() increments the reference count for an B structure. +EVP_KEYEXCH_provider() returns the provider that I was fetched from. + =head1 RETURN VALUES EVP_KEYEXCH_fetch() returns a pointer to a B for success @@ -40,7 +43,7 @@ EVP_KEYEXCH_up_ref() returns 1 for success or 0 otherwise. =head1 SEE ALSO -L +L, L =head1 HISTORY diff --git a/include/openssl/core_numbers.h b/include/openssl/core_numbers.h index f8a700a2b5..7bd02263c4 100644 --- a/include/openssl/core_numbers.h +++ b/include/openssl/core_numbers.h @@ -301,16 +301,14 @@ OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, OP_keymgmt_exportkey_types, (void)) # define OSSL_FUNC_KEYEXCH_SET_PARAMS 7 OSSL_CORE_MAKE_FUNC(void *, OP_keyexch_newctx, (void *provctx)) -OSSL_CORE_MAKE_FUNC(int, OP_keyexch_init, (void *ctx, - OSSL_PARAM params[])) +OSSL_CORE_MAKE_FUNC(int, OP_keyexch_init, (void *ctx, void *provkey)) OSSL_CORE_MAKE_FUNC(int, OP_keyexch_derive, (void *ctx, unsigned char *key, size_t *keylen, size_t outlen)) -OSSL_CORE_MAKE_FUNC(int, OP_keyexch_set_peer, (void *ctx, - OSSL_PARAM params[])) +OSSL_CORE_MAKE_FUNC(int, OP_keyexch_set_peer, (void *ctx, void *provkey)) OSSL_CORE_MAKE_FUNC(void, OP_keyexch_freectx, (void *ctx)) OSSL_CORE_MAKE_FUNC(void *, OP_keyexch_dupctx, (void *ctx)) OSSL_CORE_MAKE_FUNC(int, OP_keyexch_set_params, (void *ctx, - OSSL_PARAM params[])) + const OSSL_PARAM params[])) /* Highest known operation number */ # define OSSL_OP__HIGHEST 3 diff --git a/include/openssl/evp.h b/include/openssl/evp.h index 24ad23f3ea..8182915430 100644 --- a/include/openssl/evp.h +++ b/include/openssl/evp.h @@ -1728,6 +1728,7 @@ void EVP_KEYEXCH_free(EVP_KEYEXCH *exchange); int EVP_KEYEXCH_up_ref(EVP_KEYEXCH *exchange); EVP_KEYEXCH *EVP_KEYEXCH_fetch(OPENSSL_CTX *ctx, const char *algorithm, const char *properties); +OSSL_PROVIDER *EVP_KEYEXCH_provider(const EVP_KEYEXCH *exchange); void EVP_add_alg_module(void); diff --git a/include/openssl/evperr.h b/include/openssl/evperr.h index b54f387738..8b46d76ec1 100644 --- a/include/openssl/evperr.h +++ b/include/openssl/evperr.h @@ -213,6 +213,7 @@ int ERR_load_EVP_strings(void); # define EVP_R_NO_CIPHER_SET 131 # define EVP_R_NO_DEFAULT_DIGEST 158 # define EVP_R_NO_DIGEST_SET 139 +# define EVP_R_NO_KEYMGMT_PRESENT 196 # define EVP_R_NO_KEY_SET 154 # define EVP_R_NO_OPERATION_SET 149 # define EVP_R_ONLY_ONESHOT_SUPPORTED 177 diff --git a/providers/common/build.info b/providers/common/build.info index c77606a259..bc106d0b0f 100644 --- a/providers/common/build.info +++ b/providers/common/build.info @@ -1,4 +1,4 @@ -SUBDIRS=digests ciphers exchange +SUBDIRS=digests ciphers exchange keymgmt SOURCE[../../libcrypto]=\ provider_err.c provlib.c diff --git a/providers/common/exchange/build.info b/providers/common/exchange/build.info index 7957f51314..c99c9d81b5 100644 --- a/providers/common/exchange/build.info +++ b/providers/common/exchange/build.info @@ -1,7 +1,7 @@ LIBS=../../../libcrypto IF[{- !$disabled{dh} -}] SOURCE[../../../libcrypto]=\ - dh.c + dh_exch.c ENDIF diff --git a/providers/common/exchange/dh.c b/providers/common/exchange/dh.c deleted file mode 100644 index ca6f0fc2cb..0000000000 --- a/providers/common/exchange/dh.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. - * - * Licensed under the Apache License 2.0 (the "License"). You may not use - * this file except in compliance with 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 -#include -#include -#include -#include "internal/provider_algs.h" - -static OSSL_OP_keyexch_newctx_fn dh_newctx; -static OSSL_OP_keyexch_init_fn dh_init; -static OSSL_OP_keyexch_set_peer_fn dh_set_peer; -static OSSL_OP_keyexch_derive_fn dh_derive; -static OSSL_OP_keyexch_freectx_fn dh_freectx; -static OSSL_OP_keyexch_dupctx_fn dh_dupctx; - - -typedef struct { - DH *dh; - DH *dhpeer; - int pad; -} PROV_DH_CTX; - -static void *dh_newctx(void *provctx) -{ - return OPENSSL_zalloc(sizeof(PROV_DH_CTX)); -} - -static DH *param_to_dh(OSSL_PARAM params[], int priv) -{ - DH *dh = DH_new(); - OSSL_PARAM *paramptr; - BIGNUM *p = NULL, *g = NULL, *pub_key = NULL, *priv_key = NULL; - - if (dh == NULL) - return NULL; - - paramptr = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_DH_P); - if (paramptr == NULL - || !OSSL_PARAM_get_BN(paramptr, &p)) - goto err; - - paramptr = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_DH_G); - if (paramptr == NULL || !OSSL_PARAM_get_BN(paramptr, &g)) - goto err; - - if (!DH_set0_pqg(dh, p, NULL, g)) - goto err; - p = g = NULL; - - paramptr = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_DH_PUB_KEY); - if (paramptr == NULL || !OSSL_PARAM_get_BN(paramptr, &pub_key)) - goto err; - - /* Private key is optional */ - if (priv) { - paramptr = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_DH_PRIV_KEY); - if (paramptr == NULL - || (priv_key = BN_secure_new()) == NULL - || !OSSL_PARAM_get_BN(paramptr, &priv_key)) - goto err; - } - - if (!DH_set0_key(dh, pub_key, priv_key)) - goto err; - - return dh; - - err: - BN_free(p); - BN_free(g); - BN_free(pub_key); - BN_free(priv_key); - DH_free(dh); - return NULL; -} - -static int dh_init(void *vpdhctx, OSSL_PARAM params[]) -{ - PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx; - - DH_free(pdhctx->dh); - pdhctx->dh = param_to_dh(params, 1); - - return pdhctx->dh != NULL; -} - -static int dh_set_peer(void *vpdhctx, OSSL_PARAM params[]) -{ - PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx; - - DH_free(pdhctx->dhpeer); - pdhctx->dhpeer = param_to_dh(params, 0); - - return pdhctx->dhpeer != NULL; -} - -static int dh_derive(void *vpdhctx, unsigned char *key, size_t *keylen, - size_t outlen) -{ - PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx; - int ret; - size_t dhsize; - const BIGNUM *pub_key = NULL; - - /* TODO(3.0): Add errors to stack */ - if (pdhctx->dh == NULL || pdhctx->dhpeer == NULL) - return 0; - - dhsize = (size_t)DH_size(pdhctx->dh); - if (key == NULL) { - *keylen = dhsize; - return 1; - } - if (outlen < dhsize) - return 0; - - DH_get0_key(pdhctx->dhpeer, &pub_key, NULL); - ret = (pdhctx->pad) ? DH_compute_key_padded(key, pub_key, pdhctx->dh) - : DH_compute_key(key, pub_key, pdhctx->dh); - if (ret <= 0) - return 0; - - *keylen = ret; - return 1; -} - -static void dh_freectx(void *vpdhctx) -{ - PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx; - - DH_free(pdhctx->dh); - DH_free(pdhctx->dhpeer); - - OPENSSL_free(pdhctx); -} - -static void *dh_dupctx(void *vpdhctx) -{ - PROV_DH_CTX *srcctx = (PROV_DH_CTX *)vpdhctx; - PROV_DH_CTX *dstctx; - - dstctx = OPENSSL_zalloc(sizeof(*srcctx)); - - *dstctx = *srcctx; - if (dstctx->dh != NULL && !DH_up_ref(dstctx->dh)) { - OPENSSL_free(dstctx); - return NULL; - } - - if (dstctx->dhpeer != NULL && !DH_up_ref(dstctx->dhpeer)) { - DH_free(dstctx->dh); - OPENSSL_free(dstctx); - return NULL; - } - - return dstctx; -} - -static int dh_set_params(void *vpdhctx, OSSL_PARAM params[]) -{ - PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx; - const OSSL_PARAM *p; - int pad; - - if (pdhctx == NULL || params == NULL) - return 0; - - p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_PAD); - if (p == NULL || !OSSL_PARAM_get_int(p, &pad)) - return 0; - - pdhctx->pad = pad; - - return 1; -} - -const OSSL_DISPATCH dh_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_PARAMS, (void (*)(void))dh_set_params }, - { 0, NULL } -}; diff --git a/providers/common/exchange/dh_exch.c b/providers/common/exchange/dh_exch.c new file mode 100644 index 0000000000..439b28a27e --- /dev/null +++ b/providers/common/exchange/dh_exch.c @@ -0,0 +1,152 @@ +/* + * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with 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 +#include +#include +#include +#include "internal/provider_algs.h" + +static OSSL_OP_keyexch_newctx_fn dh_newctx; +static OSSL_OP_keyexch_init_fn dh_init; +static OSSL_OP_keyexch_set_peer_fn dh_set_peer; +static OSSL_OP_keyexch_derive_fn dh_derive; +static OSSL_OP_keyexch_freectx_fn dh_freectx; +static OSSL_OP_keyexch_dupctx_fn dh_dupctx; + +/* + * 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 { + DH *dh; + DH *dhpeer; + int pad; +} PROV_DH_CTX; + +static void *dh_newctx(void *provctx) +{ + return OPENSSL_zalloc(sizeof(PROV_DH_CTX)); +} + +static int dh_init(void *vpdhctx, void *vdh) +{ + PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx; + + DH_free(pdhctx->dh); + pdhctx->dh = vdh; + DH_up_ref(pdhctx->dh); + + return pdhctx->dh != NULL; +} + +static int dh_set_peer(void *vpdhctx, void *vdh) +{ + PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx; + + DH_free(pdhctx->dhpeer); + pdhctx->dhpeer = vdh; + DH_up_ref(pdhctx->dhpeer); + + return pdhctx->dhpeer != NULL; +} + +static int dh_derive(void *vpdhctx, unsigned char *key, size_t *keylen, + size_t outlen) +{ + PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx; + int ret; + size_t dhsize; + const BIGNUM *pub_key = NULL; + + /* TODO(3.0): Add errors to stack */ + if (pdhctx->dh == NULL || pdhctx->dhpeer == NULL) + return 0; + + dhsize = (size_t)DH_size(pdhctx->dh); + if (key == NULL) { + *keylen = dhsize; + return 1; + } + if (outlen < dhsize) + return 0; + + DH_get0_key(pdhctx->dhpeer, &pub_key, NULL); + ret = (pdhctx->pad) ? DH_compute_key_padded(key, pub_key, pdhctx->dh) + : DH_compute_key(key, pub_key, pdhctx->dh); + if (ret <= 0) + return 0; + + *keylen = ret; + return 1; +} + +static void dh_freectx(void *vpdhctx) +{ + PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx; + + DH_free(pdhctx->dh); + DH_free(pdhctx->dhpeer); + + OPENSSL_free(pdhctx); +} + +static void *dh_dupctx(void *vpdhctx) +{ + PROV_DH_CTX *srcctx = (PROV_DH_CTX *)vpdhctx; + PROV_DH_CTX *dstctx; + + dstctx = OPENSSL_zalloc(sizeof(*srcctx)); + + *dstctx = *srcctx; + if (dstctx->dh != NULL && !DH_up_ref(dstctx->dh)) { + OPENSSL_free(dstctx); + return NULL; + } + + if (dstctx->dhpeer != NULL && !DH_up_ref(dstctx->dhpeer)) { + DH_free(dstctx->dh); + OPENSSL_free(dstctx); + return NULL; + } + + return dstctx; +} + +static int dh_set_params(void *vpdhctx, const OSSL_PARAM params[]) +{ + PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx; + const OSSL_PARAM *p; + int pad; + + if (pdhctx == NULL || params == NULL) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_PAD); + if (p == NULL || !OSSL_PARAM_get_int(p, &pad)) + return 0; + + pdhctx->pad = pad; + + return 1; +} + +const OSSL_DISPATCH 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_PARAMS, (void (*)(void))dh_set_params }, + { 0, NULL } +}; diff --git a/providers/common/include/internal/provider_algs.h b/providers/common/include/internal/provider_algs.h index dbc79a5742..80946ca6e2 100644 --- a/providers/common/include/internal/provider_algs.h +++ b/providers/common/include/internal/provider_algs.h @@ -58,5 +58,8 @@ extern const OSSL_DISPATCH aes256ctr_functions[]; extern const OSSL_DISPATCH aes192ctr_functions[]; extern const OSSL_DISPATCH aes128ctr_functions[]; +/* Key management */ +extern const OSSL_DISPATCH dh_keymgmt_functions[]; + /* Key Exchange */ -extern const OSSL_DISPATCH dh_functions[]; +extern const OSSL_DISPATCH dh_keyexch_functions[]; diff --git a/providers/common/keymgmt/build.info b/providers/common/keymgmt/build.info new file mode 100644 index 0000000000..a41f3dac6e --- /dev/null +++ b/providers/common/keymgmt/build.info @@ -0,0 +1,5 @@ +LIBS=../../../libcrypto +IF[{- !$disabled{dh} -}] + SOURCE[../../../libcrypto]=\ + dh_kmgmt.c +ENDIF diff --git a/providers/common/keymgmt/dh_kmgmt.c b/providers/common/keymgmt/dh_kmgmt.c new file mode 100644 index 0000000000..67e3205edc --- /dev/null +++ b/providers/common/keymgmt/dh_kmgmt.c @@ -0,0 +1,88 @@ +/* + * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with 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 +#include +#include +#include +#include "internal/provider_algs.h" + +static OSSL_OP_keymgmt_importkey_fn dh_importkey; + +static int params_to_key(DH *dh, const OSSL_PARAM params[]) +{ + const OSSL_PARAM *param_p, *param_g, *param_priv_key, *param_pub_key; + BIGNUM *p = NULL, *g = NULL, *priv_key = NULL, *pub_key = NULL; + + if (dh == NULL) + return 0; + + param_p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_DH_P); + param_g = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_DH_G); + param_priv_key = + OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_DH_PRIV_KEY); + param_pub_key = + OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_DH_PUB_KEY); + + /* + * DH documentation says that a public key must be present if a + * private key is present. + * We want to have at least a public key either way, so we end up + * requiring it unconditionally. + */ + if (param_pub_key == NULL) + return 0; + + if ((param_p != NULL && !OSSL_PARAM_get_BN(param_p, &p)) + || (param_g != NULL && !OSSL_PARAM_get_BN(param_g, &g)) + || (param_priv_key != NULL + && !OSSL_PARAM_get_BN(param_priv_key, &priv_key)) + || !OSSL_PARAM_get_BN(param_pub_key, &pub_key)) + goto err; + + if (!DH_set0_pqg(dh, p, NULL, g)) + goto err; + p = g = NULL; + + if (!DH_set0_key(dh, pub_key, priv_key)) + goto err; + priv_key = pub_key = NULL; + + return 1; + + err: + BN_free(p); + BN_free(g); + BN_free(priv_key); + BN_free(pub_key); + return 0; +} + +static void *dh_importkey(void *provctx, const OSSL_PARAM params[]) +{ + DH *dh; + + if ((dh = DH_new()) == NULL + || !params_to_key(dh, params)) { + DH_free(dh); + dh = NULL; + } + return dh; +} + +const OSSL_DISPATCH dh_keymgmt_functions[] = { + /* + * TODO(3.0) When implementing OSSL_FUNC_KEYMGMT_GENKEY, remember to also + * implement OSSL_FUNC_KEYMGMT_EXPORTKEY. + */ + { OSSL_FUNC_KEYMGMT_IMPORTKEY, (void (*)(void))dh_importkey }, + { OSSL_FUNC_KEYMGMT_FREEKEY, (void (*)(void))DH_free }, + { 0, NULL } +}; diff --git a/providers/default/defltprov.c b/providers/default/defltprov.c index 2c25bf77d1..95534b1155 100644 --- a/providers/default/defltprov.c +++ b/providers/default/defltprov.c @@ -116,7 +116,14 @@ static const OSSL_ALGORITHM deflt_ciphers[] = { static const OSSL_ALGORITHM deflt_keyexch[] = { #ifndef OPENSSL_NO_DH - { "dhKeyAgreement", "default=yes", dh_functions }, + { "dhKeyAgreement", "default=yes", dh_keyexch_functions }, +#endif + { NULL, NULL, NULL } +}; + +static const OSSL_ALGORITHM deflt_keymgmt[] = { +#ifndef OPENSSL_NO_DH + { "dhKeyAgreement", "default=yes", dh_keymgmt_functions }, #endif { NULL, NULL, NULL } }; @@ -131,6 +138,8 @@ static const OSSL_ALGORITHM *deflt_query(OSSL_PROVIDER *prov, return deflt_digests; case OSSL_OP_CIPHER: return deflt_ciphers; + case OSSL_OP_KEYMGMT: + return deflt_keymgmt; case OSSL_OP_KEYEXCH: return deflt_keyexch; } diff --git a/util/libcrypto.num b/util/libcrypto.num index b0a7f81607..1533a88a93 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -4698,3 +4698,4 @@ EVP_CIPHER_provider 4803 3_0_0 EXIST::FUNCTION: OSSL_PROVIDER_name 4804 3_0_0 EXIST::FUNCTION: EVP_CIPHER_do_all_ex 4805 3_0_0 EXIST::FUNCTION: EVP_MD_do_all_ex 4806 3_0_0 EXIST::FUNCTION: +EVP_KEYEXCH_provider 4807 3_0_0 EXIST::FUNCTION: