From: Aydın Mercan Date: Mon, 1 Dec 2025 14:07:54 +0000 (+0300) Subject: remove libcrypto version specific code in opensslecdsa_link X-Git-Tag: v9.21.18~2^2~8 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3bd37549942b3ed1e20e2dbdabddeaecb55d8319;p=thirdparty%2Fbind9.git remove libcrypto version specific code in opensslecdsa_link Using `EVP_SIGNATURE` explicit algoritms for signatures have been added in OpenSSL 3.4 and so is skipped for the initial OpenSSL version specific code splitting. --- diff --git a/lib/dns/opensslecdsa_link.c b/lib/dns/opensslecdsa_link.c index 9203cdba045..7b89b3f25ad 100644 --- a/lib/dns/opensslecdsa_link.c +++ b/lib/dns/opensslecdsa_link.c @@ -17,14 +17,8 @@ #include #include -#include #include #include -#include -#if OPENSSL_VERSION_NUMBER >= 0x30000000L -#include -#include -#endif #include #include @@ -55,37 +49,6 @@ extern EVP_MD *isc__crypto_md[]; #define MAX_PRIVKEY_SIZE (MAX_PUBKEY_SIZE / 2) -#if OPENSSL_VERSION_NUMBER >= 0x30200000L -static isc_result_t -opensslecdsa_set_deterministic(EVP_PKEY_CTX *pctx, unsigned int key_alg) { - unsigned int rfc6979 = 1; - const char *md = NULL; - OSSL_PARAM params[3]; - - switch (key_alg) { - case DST_ALG_ECDSA256: - md = "SHA256"; - break; - case DST_ALG_ECDSA384: - md = "SHA384"; - break; - default: - UNREACHABLE(); - } - - params[0] = OSSL_PARAM_construct_utf8_string("digest", UNCONST(md), 0); - params[1] = OSSL_PARAM_construct_uint("nonce-type", &rfc6979); - params[2] = OSSL_PARAM_construct_end(); - - if (EVP_PKEY_CTX_set_params(pctx, params) != 1) { - return dst__openssl_toresult2("EVP_PKEY_CTX_set_params", - DST_R_OPENSSLFAILURE); - } - - return ISC_R_SUCCESS; -} -#endif /* OPENSSL_VERSION_NUMBER >= 0x30200000L */ - static bool opensslecdsa_valid_key_alg(unsigned int key_alg) { switch (key_alg) { @@ -97,18 +60,6 @@ opensslecdsa_valid_key_alg(unsigned int key_alg) { } } -static int -opensslecdsa_key_alg_to_group_nid(unsigned int key_alg) { - switch (key_alg) { - case DST_ALG_ECDSA256: - return NID_X9_62_prime256v1; - case DST_ALG_ECDSA384: - return NID_secp384r1; - default: - UNREACHABLE(); - } -} - static size_t opensslecdsa_key_alg_to_publickey_size(unsigned int key_alg) { switch (key_alg) { @@ -121,23 +72,6 @@ opensslecdsa_key_alg_to_publickey_size(unsigned int key_alg) { } } -/* - * OpenSSL requires us to set the public key portion, but since our private key - * file format does not contain it directly, we generate it as needed. - */ -static EC_POINT * -opensslecdsa_generate_public_key(const EC_GROUP *group, const BIGNUM *privkey) { - EC_POINT *pubkey = EC_POINT_new(group); - if (pubkey == NULL) { - return NULL; - } - if (EC_POINT_mul(group, pubkey, privkey, NULL, NULL, NULL) != 1) { - EC_POINT_free(pubkey); - return NULL; - } - return pubkey; -} - static int BN_bn2bin_fixed(const BIGNUM *bn, unsigned char *buf, int size) { int bytes = size - BN_num_bytes(bn); @@ -151,534 +85,13 @@ BN_bn2bin_fixed(const BIGNUM *bn, unsigned char *buf, int size) { return size; } -#if OPENSSL_VERSION_NUMBER >= 0x30000000L - -static const char * -opensslecdsa_key_alg_to_group_name(unsigned int key_alg) { - switch (key_alg) { - case DST_ALG_ECDSA256: - return "prime256v1"; - case DST_ALG_ECDSA384: - return "secp384r1"; - default: - UNREACHABLE(); - } -} - -static isc_result_t -opensslecdsa_create_pkey_params(unsigned int key_alg, bool private, - const unsigned char *key, size_t key_len, - EVP_PKEY **pkey) { - isc_result_t result; - int status; - int group_nid = opensslecdsa_key_alg_to_group_nid(key_alg); - const char *groupname = opensslecdsa_key_alg_to_group_name(key_alg); - OSSL_PARAM_BLD *bld = NULL; - OSSL_PARAM *params = NULL; - EVP_PKEY_CTX *ctx = NULL; - EC_POINT *pubkey = NULL; - EC_GROUP *group = NULL; - BIGNUM *priv = NULL; - unsigned char buf[MAX_PUBKEY_SIZE + 1]; - - bld = OSSL_PARAM_BLD_new(); - if (bld == NULL) { - CLEANUP(dst__openssl_toresult2("OSSL_PARAM_BLD_new", - DST_R_OPENSSLFAILURE)); - } - status = OSSL_PARAM_BLD_push_utf8_string( - bld, OSSL_PKEY_PARAM_GROUP_NAME, groupname, 0); - if (status != 1) { - CLEANUP(dst__openssl_toresult2("OSSL_PARAM_BLD_push_" - "utf8_string", - DST_R_OPENSSLFAILURE)); - } - - if (private) { - group = EC_GROUP_new_by_curve_name(group_nid); - if (group == NULL) { - CLEANUP(dst__openssl_toresult2("EC_GROUP_new_by_" - "curve_name", - DST_R_OPENSSLFAILURE)); - } - - priv = BN_bin2bn(key, key_len, NULL); - if (priv == NULL) { - CLEANUP(dst__openssl_toresult2("BN_bin2bn", - DST_R_OPENSSLFAILURE)); - } - - status = OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, - priv); - if (status != 1) { - CLEANUP(dst__openssl_toresult2("OSSL_PARAM_BLD_push_BN", - DST_R_OPENSSLFAILURE)); - } - - pubkey = opensslecdsa_generate_public_key(group, priv); - if (pubkey == NULL) { - CLEANUP(dst__openssl_toresult(DST_R_OPENSSLFAILURE)); - } - - key = buf; - key_len = EC_POINT_point2oct(group, pubkey, - POINT_CONVERSION_UNCOMPRESSED, buf, - sizeof(buf), NULL); - if (key_len == 0) { - CLEANUP(dst__openssl_toresult2("EC_POINT_point2oct", - DST_R_OPENSSLFAILURE)); - } - } else { - INSIST(key_len + 1 <= sizeof(buf)); - buf[0] = POINT_CONVERSION_UNCOMPRESSED; - memmove(buf + 1, key, key_len); - key = buf; - key_len = key_len + 1; - } - - status = OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, - key, key_len); - if (status != 1) { - CLEANUP(dst__openssl_toresult2("OSSL_PARAM_BLD_push_" - "octet_string", - DST_R_OPENSSLFAILURE)); - } - - params = OSSL_PARAM_BLD_to_param(bld); - if (params == NULL) { - CLEANUP(dst__openssl_toresult2("OSSL_PARAM_BLD_to_param", - DST_R_OPENSSLFAILURE)); - } - ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL); - if (ctx == NULL) { - CLEANUP(dst__openssl_toresult2("EVP_PKEY_CTX_new_from_name", - DST_R_OPENSSLFAILURE)); - } - status = EVP_PKEY_fromdata_init(ctx); - if (status != 1) { - /* This will fail if the default provider is an engine. - * Return ISC_R_FAILURE to retry using the legacy API. */ - CLEANUP(dst__openssl_toresult(ISC_R_FAILURE)); - } - status = EVP_PKEY_fromdata( - ctx, pkey, private ? EVP_PKEY_KEYPAIR : EVP_PKEY_PUBLIC_KEY, - params); - if (status != 1 || *pkey == NULL) { - CLEANUP(dst__openssl_toresult2("EVP_PKEY_fromdata", - DST_R_OPENSSLFAILURE)); - } - - result = ISC_R_SUCCESS; - -cleanup: - OSSL_PARAM_free(params); - OSSL_PARAM_BLD_free(bld); - EVP_PKEY_CTX_free(ctx); - BN_clear_free(priv); - EC_POINT_free(pubkey); - EC_GROUP_free(group); - - return result; -} - -static bool -opensslecdsa_extract_public_key_params(const dst_key_t *key, unsigned char *dst, - size_t dstlen) { - EVP_PKEY *pkey = key->keydata.pkeypair.pub; - BIGNUM *x = NULL; - BIGNUM *y = NULL; - bool ret = false; - - if (EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_X, &x) == 1 && - EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_Y, &y) == 1) - { - BN_bn2bin_fixed(x, &dst[0], dstlen / 2); - BN_bn2bin_fixed(y, &dst[dstlen / 2], dstlen / 2); - ret = true; - } - BN_clear_free(x); - BN_clear_free(y); - return ret; -} - -#endif - -#if OPENSSL_VERSION_NUMBER < 0x30000000L - -static isc_result_t -opensslecdsa_create_pkey_legacy(unsigned int key_alg, bool private, - const unsigned char *key, size_t key_len, - EVP_PKEY **retkey) { - isc_result_t result = ISC_R_SUCCESS; - EC_KEY *eckey = NULL; - EVP_PKEY *pkey = NULL; - BIGNUM *privkey = NULL; - EC_POINT *pubkey = NULL; - unsigned char buf[MAX_PUBKEY_SIZE + 1]; - int group_nid = opensslecdsa_key_alg_to_group_nid(key_alg); - - eckey = EC_KEY_new_by_curve_name(group_nid); - if (eckey == NULL) { - CLEANUP(dst__openssl_toresult(DST_R_OPENSSLFAILURE)); - } - - if (private) { - const EC_GROUP *group = EC_KEY_get0_group(eckey); - - privkey = BN_bin2bn(key, key_len, NULL); - if (privkey == NULL) { - CLEANUP(dst__openssl_toresult(DST_R_OPENSSLFAILURE)); - } - if (!EC_KEY_set_private_key(eckey, privkey)) { - CLEANUP(dst__openssl_toresult(DST_R_INVALIDPRIVATEKEY)); - } - - pubkey = opensslecdsa_generate_public_key(group, privkey); - if (pubkey == NULL) { - CLEANUP(dst__openssl_toresult(DST_R_OPENSSLFAILURE)); - } - if (EC_KEY_set_public_key(eckey, pubkey) != 1) { - CLEANUP(dst__openssl_toresult(DST_R_OPENSSLFAILURE)); - } - } else { - const unsigned char *cp = buf; - INSIST(key_len + 1 <= sizeof(buf)); - buf[0] = POINT_CONVERSION_UNCOMPRESSED; - memmove(buf + 1, key, key_len); - if (o2i_ECPublicKey(&eckey, &cp, key_len + 1) == NULL) { - CLEANUP(dst__openssl_toresult(DST_R_INVALIDPUBLICKEY)); - } - if (EC_KEY_check_key(eckey) != 1) { - CLEANUP(dst__openssl_toresult(DST_R_INVALIDPUBLICKEY)); - } - } - - pkey = EVP_PKEY_new(); - if (pkey == NULL) { - CLEANUP(dst__openssl_toresult(ISC_R_NOMEMORY)); - } - if (!EVP_PKEY_set1_EC_KEY(pkey, eckey)) { - CLEANUP(dst__openssl_toresult(ISC_R_FAILURE)); - } - - *retkey = pkey; - pkey = NULL; - -cleanup: - BN_clear_free(privkey); - EC_POINT_free(pubkey); - EC_KEY_free(eckey); - EVP_PKEY_free(pkey); - return result; -} - -static bool -opensslecdsa_extract_public_key_legacy(const dst_key_t *key, unsigned char *dst, - size_t dstlen) { - EVP_PKEY *pkey = key->keydata.pkeypair.pub; - const EC_KEY *eckey = EVP_PKEY_get0_EC_KEY(pkey); - const EC_GROUP *group = (eckey == NULL) ? NULL - : EC_KEY_get0_group(eckey); - const EC_POINT *pub = (eckey == NULL) ? NULL - : EC_KEY_get0_public_key(eckey); - unsigned char buf[MAX_PUBKEY_SIZE + 1]; - size_t len; - - if (group == NULL || pub == NULL) { - return false; - } - - len = EC_POINT_point2oct(group, pub, POINT_CONVERSION_UNCOMPRESSED, buf, - sizeof(buf), NULL); - if (len == dstlen + 1) { - memmove(dst, buf + 1, dstlen); - return true; - } - return false; -} - -#endif - -static bool -opensslecdsa_extract_public_key(const dst_key_t *key, unsigned char *dst, - size_t dstlen) { -#if OPENSSL_VERSION_NUMBER >= 0x30000000L - if (opensslecdsa_extract_public_key_params(key, dst, dstlen)) { - return true; - } -#else - if (opensslecdsa_extract_public_key_legacy(key, dst, dstlen)) { - return true; - } -#endif - return false; -} - -static isc_result_t -opensslecdsa_create_pkey(unsigned int key_alg, bool private, - const unsigned char *key, size_t key_len, - EVP_PKEY **retkey) { - isc_result_t result; -#if OPENSSL_VERSION_NUMBER >= 0x30000000L - result = opensslecdsa_create_pkey_params(key_alg, private, key, key_len, - retkey); - if (result != ISC_R_FAILURE) { - return result; - } -#else - result = opensslecdsa_create_pkey_legacy(key_alg, private, key, key_len, - retkey); - if (result == ISC_R_SUCCESS) { - return result; - } -#endif - return DST_R_OPENSSLFAILURE; -} - -#if OPENSSL_VERSION_NUMBER >= 0x30000000L - -static isc_result_t -opensslecdsa_generate_pkey_with_uri(int group_nid, const char *label, - EVP_PKEY **retkey) { - int status; - isc_result_t result; - char *uri = UNCONST(label); - EVP_PKEY_CTX *ctx = NULL; - OSSL_PARAM params[3]; - - /* Generate the key's parameters. */ - params[0] = OSSL_PARAM_construct_utf8_string("pkcs11_uri", uri, 0); - params[1] = OSSL_PARAM_construct_utf8_string( - "pkcs11_key_usage", (char *)"digitalSignature", 0); - params[2] = OSSL_PARAM_construct_end(); - - ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", "provider=pkcs11"); - if (ctx == NULL) { - CLEANUP(dst__openssl_toresult2("EVP_PKEY_CTX_new_from_name", - DST_R_OPENSSLFAILURE)); - } - - status = EVP_PKEY_keygen_init(ctx); - if (status != 1) { - CLEANUP(dst__openssl_toresult2("EVP_PKEY_keygen_init", - DST_R_OPENSSLFAILURE)); - } - - status = EVP_PKEY_CTX_set_params(ctx, params); - if (status != 1) { - CLEANUP(dst__openssl_toresult2("EVP_PKEY_CTX_set_params", - DST_R_OPENSSLFAILURE)); - } - /* - * Setting the P-384 curve doesn't work correctly when using: - * OSSL_PARAM_construct_utf8_string("ec_paramgen_curve", "P-384", 0); - * - * Instead use the OpenSSL function to set the curve nid param. - */ - status = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, group_nid); - if (status != 1) { - CLEANUP(dst__openssl_toresult2("EVP_PKEY_CTX_set_ec_paramgen_" - "curve_nid", - DST_R_OPENSSLFAILURE)); - } - - /* Generate the key. */ - status = EVP_PKEY_generate(ctx, retkey); - if (status != 1) { - CLEANUP(dst__openssl_toresult2("EVP_PKEY_generate", - DST_R_OPENSSLFAILURE)); - } - - result = ISC_R_SUCCESS; - -cleanup: - EVP_PKEY_CTX_free(ctx); - return result; -} - -static isc_result_t -opensslecdsa_generate_pkey(unsigned int key_alg, const char *label, - EVP_PKEY **retkey) { - isc_result_t result; - EVP_PKEY_CTX *ctx = NULL; - EVP_PKEY *params_pkey = NULL; - int group_nid = opensslecdsa_key_alg_to_group_nid(key_alg); - int status; - - if (label != NULL) { - return opensslecdsa_generate_pkey_with_uri(group_nid, label, - retkey); - } - - /* Generate the key's parameters. */ - ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL); - if (ctx == NULL) { - CLEANUP(dst__openssl_toresult2("EVP_PKEY_CTX_new_from_name", - DST_R_OPENSSLFAILURE)); - } - status = EVP_PKEY_paramgen_init(ctx); - if (status != 1) { - CLEANUP(dst__openssl_toresult2("EVP_PKEY_paramgen_init", - DST_R_OPENSSLFAILURE)); - } - status = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, group_nid); - if (status != 1) { - CLEANUP(dst__openssl_toresult2("EVP_PKEY_CTX_set_ec_paramgen_" - "curve_nid", - DST_R_OPENSSLFAILURE)); - } - status = EVP_PKEY_paramgen(ctx, ¶ms_pkey); - if (status != 1 || params_pkey == NULL) { - CLEANUP(dst__openssl_toresult2("EVP_PKEY_paramgen", - DST_R_OPENSSLFAILURE)); - } - EVP_PKEY_CTX_free(ctx); - - /* Generate the key. */ - ctx = EVP_PKEY_CTX_new(params_pkey, NULL); - if (ctx == NULL) { - CLEANUP(dst__openssl_toresult2("EVP_PKEY_CTX_new", - DST_R_OPENSSLFAILURE)); - } - - status = EVP_PKEY_keygen_init(ctx); - if (status != 1) { - CLEANUP(dst__openssl_toresult2("EVP_PKEY_keygen_init", - DST_R_OPENSSLFAILURE)); - } - status = EVP_PKEY_keygen(ctx, retkey); - if (status != 1) { - CLEANUP(dst__openssl_toresult2("EVP_PKEY_keygen", - DST_R_OPENSSLFAILURE)); - } - result = ISC_R_SUCCESS; - -cleanup: - EVP_PKEY_free(params_pkey); - EVP_PKEY_CTX_free(ctx); - return result; -} - -static isc_result_t -opensslecdsa_validate_pkey_group(unsigned int key_alg, EVP_PKEY *pkey) { - const char *groupname = opensslecdsa_key_alg_to_group_name(key_alg); - char gname[64]; - - if (EVP_PKEY_get_group_name(pkey, gname, sizeof(gname), NULL) != 1) { - return DST_R_INVALIDPRIVATEKEY; - } - if (strcmp(gname, groupname) != 0) { - return DST_R_INVALIDPRIVATEKEY; - } - return ISC_R_SUCCESS; -} - -static bool -opensslecdsa_extract_private_key(const dst_key_t *key, unsigned char *buf, - size_t buflen) { - EVP_PKEY *pkey = key->keydata.pkeypair.priv; - BIGNUM *priv = NULL; - - if (EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, &priv) != 1) { - return false; - } - - BN_bn2bin_fixed(priv, buf, buflen); - BN_clear_free(priv); - return true; -} - -#else - -static isc_result_t -opensslecdsa_generate_pkey(unsigned int key_alg, const char *label, - EVP_PKEY **retkey) { - isc_result_t result; - EC_KEY *eckey = NULL; - EVP_PKEY *pkey = NULL; - int group_nid; - - UNUSED(label); - - group_nid = opensslecdsa_key_alg_to_group_nid(key_alg); - - eckey = EC_KEY_new_by_curve_name(group_nid); - if (eckey == NULL) { - CLEANUP(dst__openssl_toresult2("EC_KEY_new_by_curve_name", - DST_R_OPENSSLFAILURE)); - } - - if (EC_KEY_generate_key(eckey) != 1) { - CLEANUP(dst__openssl_toresult2("EC_KEY_generate_key", - DST_R_OPENSSLFAILURE)); - } - - pkey = EVP_PKEY_new(); - if (pkey == NULL) { - CLEANUP(dst__openssl_toresult(ISC_R_NOMEMORY)); - } - if (EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) { - CLEANUP(dst__openssl_toresult2("EVP_PKEY_set1_EC_KEY", - DST_R_OPENSSLFAILURE)); - } - *retkey = pkey; - pkey = NULL; - result = ISC_R_SUCCESS; - -cleanup: - EC_KEY_free(eckey); - EVP_PKEY_free(pkey); - return result; -} - -static isc_result_t -opensslecdsa_validate_pkey_group(unsigned int key_alg, EVP_PKEY *pkey) { - const EC_KEY *eckey = EVP_PKEY_get0_EC_KEY(pkey); - int group_nid; - - if (eckey == NULL) { - return dst__openssl_toresult(DST_R_INVALIDPRIVATEKEY); - } - - group_nid = opensslecdsa_key_alg_to_group_nid(key_alg); - - if (EC_GROUP_get_curve_name(EC_KEY_get0_group(eckey)) != group_nid) { - return DST_R_INVALIDPRIVATEKEY; - } - - return ISC_R_SUCCESS; -} - -static bool -opensslecdsa_extract_private_key(const dst_key_t *key, unsigned char *buf, - size_t buflen) { - const EC_KEY *eckey = NULL; - const BIGNUM *privkey = NULL; - - eckey = EVP_PKEY_get0_EC_KEY(key->keydata.pkeypair.priv); - if (eckey == NULL) { - ERR_clear_error(); - return false; - } - - privkey = EC_KEY_get0_private_key(eckey); - if (privkey == NULL) { - ERR_clear_error(); - return false; - } - - BN_bn2bin_fixed(privkey, buf, buflen); - return true; -} - -#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */ - static isc_result_t opensslecdsa_createctx(dst_key_t *key, dst_context_t *dctx) { isc_result_t result = ISC_R_SUCCESS; EVP_MD_CTX *evp_md_ctx; EVP_PKEY_CTX *pctx = NULL; const EVP_MD *type = NULL; + const char *md = NULL; UNUSED(key); REQUIRE(opensslecdsa_valid_key_alg(dctx->key->key_alg)); @@ -690,8 +103,10 @@ opensslecdsa_createctx(dst_key_t *key, dst_context_t *dctx) { } if (dctx->key->key_alg == DST_ALG_ECDSA256) { type = isc__crypto_md[ISC_MD_SHA256]; + md = "SHA256"; } else { type = isc__crypto_md[ISC_MD_SHA384]; + md = "SHA384"; } if (dctx->use == DO_SIGN) { @@ -704,12 +119,15 @@ opensslecdsa_createctx(dst_key_t *key, dst_context_t *dctx) { ISC_R_FAILURE)); } -#if OPENSSL_VERSION_NUMBER >= 0x30200000L if (!isc_crypto_fips_mode()) { - CHECK(opensslecdsa_set_deterministic( - pctx, dctx->key->key_alg)); + result = isc_ossl_wrap_ecdsa_set_deterministic(pctx, + md); + if (result != ISC_R_SUCCESS && + result != ISC_R_NOTIMPLEMENTED) + { + CLEANUP(result); + } } -#endif /* OPENSSL_VERSION_NUMBER >= 0x30200000L */ } else { if (EVP_DigestVerifyInit(evp_md_ctx, NULL, type, NULL, @@ -723,6 +141,7 @@ opensslecdsa_createctx(dst_key_t *key, dst_context_t *dctx) { } dctx->ctxdata.evp_md_ctx = evp_md_ctx; + result = ISC_R_SUCCESS; cleanup: return result; @@ -921,7 +340,31 @@ opensslecdsa_generate(dst_key_t *key, int unused, void (*callback)(int)) { UNUSED(unused); UNUSED(callback); - RETERR(opensslecdsa_generate_pkey(key->key_alg, key->label, &pkey)); + if (key->label != NULL) { + switch (key->key_alg) { + case DST_ALG_ECDSA256: + RETERR(isc_ossl_wrap_generate_pkcs11_p256_key( + key->label, &pkey)); + break; + case DST_ALG_ECDSA384: + RETERR(isc_ossl_wrap_generate_pkcs11_p384_key( + key->label, &pkey)); + break; + default: + UNREACHABLE(); + } + } else { + switch (key->key_alg) { + case DST_ALG_ECDSA256: + RETERR(isc_ossl_wrap_generate_p256_key(&pkey)); + break; + case DST_ALG_ECDSA384: + RETERR(isc_ossl_wrap_generate_p384_key(&pkey)); + break; + default: + UNREACHABLE(); + } + } key->key_size = EVP_PKEY_bits(pkey); key->keydata.pkeypair.priv = pkey; @@ -933,6 +376,7 @@ static isc_result_t opensslecdsa_todns(const dst_key_t *key, isc_buffer_t *data) { isc_result_t result; isc_region_t r; + EVP_PKEY *pkey; size_t keysize; REQUIRE(opensslecdsa_valid_key_alg(key->key_alg)); @@ -943,8 +387,23 @@ opensslecdsa_todns(const dst_key_t *key, isc_buffer_t *data) { if (r.length < keysize) { CLEANUP(ISC_R_NOSPACE); } - if (!opensslecdsa_extract_public_key(key, r.base, keysize)) { - CLEANUP(dst__openssl_toresult(DST_R_OPENSSLFAILURE)); + + pkey = key->keydata.pkeypair.pub; + switch (key->key_alg) { + case DST_ALG_ECDSA256: + if (isc_ossl_wrap_p256_public_region(pkey, r) != ISC_R_SUCCESS) + { + CLEANUP(dst__openssl_toresult(DST_R_OPENSSLFAILURE)); + } + break; + case DST_ALG_ECDSA384: + if (isc_ossl_wrap_p384_public_region(pkey, r) != ISC_R_SUCCESS) + { + CLEANUP(dst__openssl_toresult(DST_R_OPENSSLFAILURE)); + } + break; + default: + UNREACHABLE(); } isc_buffer_add(data, keysize); @@ -972,8 +431,16 @@ opensslecdsa_fromdns(dst_key_t *key, isc_buffer_t *data) { CLEANUP(DST_R_INVALIDPUBLICKEY); } - CHECK(opensslecdsa_create_pkey(key->key_alg, false, r.base, len, - &pkey)); + switch (key->key_alg) { + case DST_ALG_ECDSA256: + CHECK(isc_ossl_wrap_load_p256_public_from_region(r, &pkey)); + break; + case DST_ALG_ECDSA384: + CHECK(isc_ossl_wrap_load_p384_public_from_region(r, &pkey)); + break; + default: + UNREACHABLE(); + } isc_buffer_forward(data, len); key->key_size = EVP_PKEY_bits(pkey); @@ -991,6 +458,7 @@ opensslecdsa_tofile(const dst_key_t *key, const char *directory) { unsigned char buf[MAX_PRIVKEY_SIZE]; size_t keylen = 0; unsigned short i; + EVP_PKEY *pkey; if (key->keydata.pkeypair.pub == NULL) { CLEANUP(DST_R_NULLKEY); @@ -1008,8 +476,23 @@ opensslecdsa_tofile(const dst_key_t *key, const char *directory) { keylen = opensslecdsa_key_alg_to_publickey_size(key->key_alg) / 2; INSIST(keylen <= sizeof(buf)); + pkey = key->keydata.pkeypair.priv; + i = 0; - if (opensslecdsa_extract_private_key(key, buf, keylen)) { + switch (key->key_alg) { + case DST_ALG_ECDSA256: + result = isc_ossl_wrap_p256_secret_region( + pkey, (isc_region_t){ buf, keylen }); + break; + case DST_ALG_ECDSA384: + result = isc_ossl_wrap_p384_secret_region( + pkey, (isc_region_t){ buf, keylen }); + break; + default: + UNREACHABLE(); + } + + if (result == ISC_R_SUCCESS) { priv.elements[i].tag = TAG_ECDSA_PRIVATEKEY; priv.elements[i].length = keylen; priv.elements[i].data = buf; @@ -1039,6 +522,7 @@ static isc_result_t opensslecdsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) { dst_private_t priv; isc_result_t result; + isc_region_t r; EVP_PKEY *pkey = NULL; const char *label = NULL; int i, privkey_index = -1; @@ -1091,9 +575,21 @@ opensslecdsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) { CLEANUP(dst__openssl_toresult(DST_R_INVALIDPRIVATEKEY)); } - CHECK(opensslecdsa_create_pkey( - key->key_alg, true, priv.elements[privkey_index].data, - priv.elements[privkey_index].length, &pkey)); + r = (isc_region_t){ + .base = priv.elements[privkey_index].data, + .length = priv.elements[privkey_index].length, + }; + + switch (key->key_alg) { + case DST_ALG_ECDSA256: + CHECK(isc_ossl_wrap_load_p256_secret_from_region(r, &pkey)); + break; + case DST_ALG_ECDSA384: + CHECK(isc_ossl_wrap_load_p384_secret_from_region(r, &pkey)); + break; + default: + UNREACHABLE(); + } /* Check that the public component matches if given */ if (pub != NULL && EVP_PKEY_eq(pkey, pub->keydata.pkeypair.pub) != 1) { @@ -1127,8 +623,18 @@ opensslecdsa_fromlabel(dst_key_t *key, const char *label, const char *pin) { CHECK(dst__openssl_fromlabel(EVP_PKEY_EC, label, pin, &pubpkey, &privpkey)); - CHECK(opensslecdsa_validate_pkey_group(key->key_alg, privpkey)); - CHECK(opensslecdsa_validate_pkey_group(key->key_alg, pubpkey)); + switch (key->key_alg) { + case DST_ALG_ECDSA256: + CHECK(isc_ossl_wrap_validate_p256_pkey(privpkey)); + CHECK(isc_ossl_wrap_validate_p256_pkey(pubpkey)); + break; + case DST_ALG_ECDSA384: + CHECK(isc_ossl_wrap_validate_p384_pkey(privpkey)); + CHECK(isc_ossl_wrap_validate_p384_pkey(pubpkey)); + break; + default: + UNREACHABLE(); + } key->label = isc_mem_strdup(key->mctx, label); key->key_size = EVP_PKEY_bits(privpkey); diff --git a/lib/isc/include/isc/ossl_wrap.h b/lib/isc/include/isc/ossl_wrap.h index cef83179388..96f282a13b0 100644 --- a/lib/isc/include/isc/ossl_wrap.h +++ b/lib/isc/include/isc/ossl_wrap.h @@ -31,6 +31,179 @@ typedef struct isc_ossl_wrap_rsa_components { BIGNUM *e, *n, *d, *p, *q, *dmp1, *dmq1, *iqmp; } isc_ossl_wrap_rsa_components_t; +isc_result_t +isc_ossl_wrap_generate_p256_key(EVP_PKEY **pkeyp); +/*% + * Generates an uncompressed, named P-256 secret key. + * + * Requires: + * \li pkeyp != NULL + * \li *pkeyp == NULL + */ + +isc_result_t +isc_ossl_wrap_generate_pkcs11_p256_key(char *uri, EVP_PKEY **pkeyp); +/*% + * Generates a P-256 secret key using the PKCS#11 label specified at `uri`. + * + * Requires: + * \li pkeyp != NULL + * \li *pkeyp == NULL + * \li `uri != NULL` and is a NUL-terminated string + */ + +isc_result_t +isc_ossl_wrap_validate_p256_pkey(EVP_PKEY *pkey); +/*% + * Validatest that a EVP_PKEY is a P-256 EC key. + * + * Requires: + * \li `pkey != NULL` + * \li pkey is a valid EVP_PKEY + */ + +isc_result_t +isc_ossl_wrap_load_p256_public_from_region(isc_region_t region, + EVP_PKEY **pkeyp); +/*% + * Create a verifying `EVP_PKEY` using the P-256 public key pointed by + * `region`. + * + * Requires: + * \li `pkeyp != NULL` + * \li `*pkeyp == NULL` + * \li `region.base != NULL` + * \li `region.length == 64` + */ + +isc_result_t +isc_ossl_wrap_load_p256_secret_from_region(isc_region_t region, + EVP_PKEY **pkeyp); +/*% + * Create a signing `EVP_PKEY` using the P-256 secret key pointed by + * `region`. + * + * Requires: + * \li `pkeyp != NULL` + * \li `*pkeyp == NULL` + * \li `region.base != NULL` + * \li `region.length == 32` + */ + +isc_result_t +isc_ossl_wrap_p256_public_region(EVP_PKEY *pkey, isc_region_t pub); +/*% + * Export the P-256 public key to the region pointed by `pub` + * + * Requires: + * \li `pkey` is a non-NULL, valid, P-256 public key. + * \li `pub` has to a non-NULL pointer with enough space to fit the public key. + */ + +isc_result_t +isc_ossl_wrap_p256_secret_region(EVP_PKEY *pkey, isc_region_t sec); +/*% + * Export the P-256 curve secret key to the region pointed by `sec` + * + * Requires: + * \li `pkey` is a non-NULL, valid P-256 secret key. + * \li `sec` has to a non-NULL pointer with enough space to fit the secret key. + */ + +isc_result_t +isc_ossl_wrap_generate_p384_key(EVP_PKEY **pkeyp); +/*% + * Generates an uncompressed, named P-256 secret key. + * + * Requires: + * \li pkeyp != NULL + * \li *pkeyp == NULL + */ + +isc_result_t +isc_ossl_wrap_generate_pkcs11_p384_key(char *uri, EVP_PKEY **pkeyp); +/*% + * Generates a P-384 secret key using the PKCS#11 label specified at `uri`. + * + * Requires: + * \li pkeyp != NULL + * \li *pkeyp == NULL + * \li `uri != NULL` and is a NUL-terminated string + */ + +isc_result_t +isc_ossl_wrap_load_p384_public_from_region(isc_region_t region, + EVP_PKEY **pkeyp); +/*% + * Create a verifying `EVP_PKEY` using the P-384 public key pointed by + * `region`. + * + * Requires: + * \li `pkeyp != NULL` + * \li `*pkeyp == NULL` + * \li `region.base != NULL` + * \li `region.length == 64` + */ + +isc_result_t +isc_ossl_wrap_load_p384_secret_from_region(isc_region_t region, + EVP_PKEY **pkeyp); +/*% + * Create a signing `EVP_PKEY` using the P-384 secret key pointed by + * `region`. + * + * Requires: + * \li `pkeyp != NULL` + * \li `*pkeyp == NULL` + * \li `region.base != NULL` + * \li `region.length == 32` + */ + +isc_result_t +isc_ossl_wrap_validate_p384_pkey(EVP_PKEY *pkey); + +isc_result_t +isc_ossl_wrap_p384_public_region(EVP_PKEY *pkey, isc_region_t pub); +/*% + * Export the P-384 public key to the region pointed by `pub` + * + * Requires: + * \li `pkey` is a non-NULL, valid, P-384 public key. + * \li `pub` has to a non-NULL pointer with enough space to fit the public key. + */ + +isc_result_t +isc_ossl_wrap_p384_secret_region(EVP_PKEY *pkey, isc_region_t sec); +/*% + * Export the P-384 curve secret key to the region pointed by `sec` + * + * Requires: + * \li `pkey` is a non-NULL, valid P-384 secret key. + * \li `sec` has to a non-NULL pointer with enough space to fit the secret key. + */ + +isc_result_t +isc_ossl_wrap_ecdsa_set_deterministic(EVP_PKEY_CTX *pctx, const char *hash); +/* + * Use deterministic ECDSA to generate signatures. + * + * Returns: + * \li #ISC_R_SUCCESS -- signature set to use RFC6979 + * \li #ISC_R_IGNORE -- FIPS mode is active + * \li #ISC_R_NOTIMPLEMENTED -- libcrypto doesn't support + */ + +isc_result_t +isc_ossl_wrap_ecdsa_set_deterministic(EVP_PKEY_CTX *pctx, const char *hash); +/* + * Use deterministic ECDSA to generate signatures. + * + * Returns: + * \li #ISC_R_SUCCESS -- signature set to use RFC6979 + * \li #ISC_R_IGNORE -- FIPS mode is active + * \li #ISC_R_NOTIMPLEMENTED -- libcrypto doesn't support RFC6979 + */ + isc_result_t isc_ossl_wrap_generate_rsa_key(void (*callback)(int), size_t bit_size, EVP_PKEY **pkeyp); diff --git a/lib/isc/ossl_wrap/ossl1_1.c b/lib/isc/ossl_wrap/ossl1_1.c index 32fac0d1d61..e5d63e2706e 100644 --- a/lib/isc/ossl_wrap/ossl1_1.c +++ b/lib/isc/ossl_wrap/ossl1_1.c @@ -12,17 +12,88 @@ */ #include +#include #include #include #include +#include #include +#define MAX_PUBLIC_KEY_SIZE 96 +#define MAX_SECRET_KEY_SIZE 48 + #define OSSL_WRAP_ERROR(fn) \ isc__ossl_wrap_logged_toresult( \ ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_CRYPTO, fn, \ ISC_R_CRYPTOFAILURE, __FILE__, __LINE__) +#define P_CURVE_IMPL(curve, nid) \ + isc_result_t isc_ossl_wrap_generate_##curve##_key(EVP_PKEY **pkeyp) { \ + REQUIRE(pkeyp != NULL && *pkeyp == NULL); \ + return generate_ec_key(pkeyp, nid); \ + } \ + isc_result_t isc_ossl_wrap_generate_pkcs11_##curve##_key( \ + char *uri, EVP_PKEY **pkeyp) { \ + UNUSED(uri); \ + return isc_ossl_wrap_generate_##curve##_key(pkeyp); \ + } \ + isc_result_t isc_ossl_wrap_validate_##curve##_pkey(EVP_PKEY *pkey) { \ + REQUIRE(pkey != NULL); \ + return validate_ec_pkey(pkey, nid); \ + } \ + isc_result_t isc_ossl_wrap_load_##curve##_public_from_region( \ + isc_region_t region, EVP_PKEY **pkeyp) { \ + REQUIRE(pkeyp != NULL && *pkeyp == NULL); \ + REQUIRE(region.base != NULL && \ + region.length >= curve##_public_key_size); \ + region.length = curve##_public_key_size; \ + return load_ec_public_from_region(region, pkeyp, nid); \ + } \ + isc_result_t isc_ossl_wrap_load_##curve##_secret_from_region( \ + isc_region_t region, EVP_PKEY **pkeyp) { \ + REQUIRE(pkeyp != NULL && *pkeyp == NULL); \ + REQUIRE(region.base != NULL && \ + region.length >= curve##_secret_key_size); \ + region.length = curve##_secret_key_size; \ + return load_ec_secret_from_region(region, pkeyp, nid); \ + } \ + isc_result_t isc_ossl_wrap_##curve##_public_region(EVP_PKEY *pkey, \ + isc_region_t pub) { \ + REQUIRE(pkey != NULL); \ + REQUIRE(pub.base != NULL && \ + pub.length >= curve##_public_key_size); \ + pub.length = curve##_public_key_size; \ + return ec_public_region(pkey, pub); \ + } \ + isc_result_t isc_ossl_wrap_##curve##_secret_region(EVP_PKEY *pkey, \ + isc_region_t sec) { \ + REQUIRE(pkey != NULL); \ + REQUIRE(sec.base != NULL && \ + sec.length >= curve##_secret_key_size); \ + sec.length = curve##_secret_key_size; \ + return ec_secret_region(pkey, sec); \ + } + +constexpr size_t p256_public_key_size = 64; +constexpr size_t p384_public_key_size = 96; + +constexpr size_t p256_secret_key_size = 32; +constexpr size_t p384_secret_key_size = 48; + +static int +BN_bn2bin_fixed(const BIGNUM *bn, unsigned char *buf, int size) { + int bytes = size - BN_num_bytes(bn); + + INSIST(bytes >= 0); + + while (bytes-- > 0) { + *buf++ = 0; + } + BN_bn2bin(bn, buf); + return size; +} + static int rsa_keygen_progress_cb(int p, int n, BN_GENCB *cb) { void (*fptr)(int); @@ -36,6 +107,251 @@ rsa_keygen_progress_cb(int p, int n, BN_GENCB *cb) { return 1; } +static isc_result_t +generate_ec_key(EVP_PKEY **pkeyp, const int nid) { + isc_result_t result; + EC_KEY *eckey = NULL; + EVP_PKEY *pkey = NULL; + + eckey = EC_KEY_new_by_curve_name(nid); + if (eckey == NULL) { + CLEANUP(OSSL_WRAP_ERROR("EC_KEY_new_by_curve_name")); + } + + if (EC_KEY_generate_key(eckey) != 1) { + CLEANUP(OSSL_WRAP_ERROR("EC_KEY_generate_key")); + } + + pkey = EVP_PKEY_new(); + if (pkey == NULL) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_new")); + } + + if (EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_set1_EC_KEY")); + } + + *pkeyp = pkey; + pkey = NULL; + result = ISC_R_SUCCESS; + +cleanup: + EC_KEY_free(eckey); + EVP_PKEY_free(pkey); + return result; +} + +static isc_result_t +validate_ec_pkey(EVP_PKEY *pkey, const int nid) { + const EC_GROUP *group; + const EC_KEY *eckey; + isc_result_t result; + + eckey = EVP_PKEY_get0_EC_KEY(pkey); + if (eckey == NULL) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_get0_EC_KEY")); + } + + group = EC_KEY_get0_group(eckey); + if (group == NULL) { + CLEANUP(OSSL_WRAP_ERROR("EC_KEY_get0_group")); + } + + if (EC_GROUP_get_curve_name(group) != nid) { + return DST_R_INVALIDPRIVATEKEY; + } + + result = ISC_R_SUCCESS; +cleanup: + return result; +} + +static isc_result_t +load_ec_public_from_region(isc_region_t region, EVP_PKEY **pkeyp, + const int nid) { + isc_result_t result; + const unsigned char *buf_launder; + uint8_t buffer[MAX_PUBLIC_KEY_SIZE + 1]; + EC_KEY *eckey = NULL; + EVP_PKEY *pkey = NULL; + + eckey = EC_KEY_new_by_curve_name(nid); + if (eckey == NULL) { + CLEANUP(OSSL_WRAP_ERROR("EC_KEY_new_curve_by_name")); + } + + buffer[0] = POINT_CONVERSION_UNCOMPRESSED; + memmove(buffer + 1, region.base, region.length); + + buf_launder = buffer; + if (o2i_ECPublicKey(&eckey, &buf_launder, region.length + 1) == NULL) { + CLEANUP(OSSL_WRAP_ERROR("o2i_ECPublicKey")); + } + + if (EC_KEY_check_key(eckey) != 1) { + CLEANUP(OSSL_WRAP_ERROR("EC_KEY_check_key")); + } + + pkey = EVP_PKEY_new(); + if (pkey == NULL) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_new")); + } + + if (EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_set1_EC_KEY")); + } + + *pkeyp = pkey; + pkey = NULL; + result = ISC_R_SUCCESS; + +cleanup: + EVP_PKEY_free(pkey); + EC_KEY_free(eckey); + return result; +} + +static isc_result_t +load_ec_secret_from_region(isc_region_t region, EVP_PKEY **pkeyp, + const int nid) { + isc_result_t result; + const EC_GROUP *group = NULL; + EC_POINT *public = NULL; + EVP_PKEY *pkey = NULL; + BIGNUM *private = NULL; + EC_KEY *eckey = NULL; + + eckey = EC_KEY_new_by_curve_name(nid); + if (eckey == NULL) { + CLEANUP(OSSL_WRAP_ERROR("EC_KEY_new_curve_by_name")); + } + + group = EC_KEY_get0_group(eckey); + if (group == NULL) { + CLEANUP(OSSL_WRAP_ERROR("EC_KEY_get0_group")); + } + + private = BN_bin2bn(region.base, region.length, NULL); + if (private == NULL) { + CLEANUP(OSSL_WRAP_ERROR("BN_bin2bn")); + } + + if (EC_KEY_set_private_key(eckey, private) != 1) { + CLEANUP(OSSL_WRAP_ERROR("EC_KEY_set_private_key")); + } + + /* + * OpenSSL requires us to set the public key portion, but since our + * private key file format does not contain it directly, we generate it + * as needed. + */ + public = EC_POINT_new(group); + if (public == NULL) { + CLEANUP(OSSL_WRAP_ERROR("EC_POINT_new")); + } + + if (EC_POINT_mul(group, public, private, NULL, NULL, NULL) != 1) { + CLEANUP(OSSL_WRAP_ERROR("EC_POINT_mul")); + } + + if (EC_KEY_set_public_key(eckey, public) != 1) { + CLEANUP(OSSL_WRAP_ERROR("EC_KEY_set_public_key")); + } + + pkey = EVP_PKEY_new(); + if (pkey == NULL) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_new")); + } + + if (EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_set1_EC_KEY")); + } + + *pkeyp = pkey; + pkey = NULL; + result = ISC_R_SUCCESS; + +cleanup: + EVP_PKEY_free(pkey); + EC_POINT_free(public); + BN_clear_free(private); + EC_KEY_free(eckey); + return result; +} + +static isc_result_t +ec_public_region(EVP_PKEY *pkey, isc_region_t pub) { + isc_result_t result; + uint8_t buffer[MAX_PUBLIC_KEY_SIZE + 1]; + const EC_POINT *public; + const EC_GROUP *group; + const EC_KEY *eckey; + size_t len; + + eckey = EVP_PKEY_get0_EC_KEY(pkey); + if (eckey == NULL) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_get0_EC_KEY")); + } + + group = EC_KEY_get0_group(eckey); + if (group == NULL) { + CLEANUP(OSSL_WRAP_ERROR("EC_KEY_get0_group")); + } + + public = EC_KEY_get0_public_key(eckey); + if (public == NULL) { + CLEANUP(OSSL_WRAP_ERROR("EC_KEY_get0_public_key")); + } + + len = EC_POINT_point2oct(group, public, POINT_CONVERSION_UNCOMPRESSED, + buffer, sizeof(buffer), NULL); + if (len != pub.length + 1) { + CLEANUP(OSSL_WRAP_ERROR("EC_POINT_point2oct")); + } + + memmove(pub.base, buffer + 1, pub.length); + + result = ISC_R_SUCCESS; + +cleanup: + return result; +} + +static isc_result_t +ec_secret_region(EVP_PKEY *pkey, isc_region_t pub) { + const BIGNUM *private; + isc_result_t result; + const EC_KEY *eckey; + + eckey = EVP_PKEY_get0_EC_KEY(pkey); + if (eckey == NULL) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_get0_EC_KEY")); + } + + private = EC_KEY_get0_private_key(eckey); + if (private == NULL) { + CLEANUP(OSSL_WRAP_ERROR("EC_KEY_get0_private_key")); + } + + BN_bn2bin_fixed(private, pub.base, pub.length); + + result = ISC_R_SUCCESS; + +cleanup: + return result; +} + +P_CURVE_IMPL(p256, NID_X9_62_prime256v1); +P_CURVE_IMPL(p384, NID_secp384r1); + +isc_result_t +isc_ossl_wrap_ecdsa_set_deterministic(EVP_PKEY_CTX *pctx, const char *hash) { + UNUSED(pctx); + UNUSED(hash); + + return ISC_R_NOTIMPLEMENTED; +} + isc_result_t isc_ossl_wrap_generate_rsa_key(void (*callback)(int), size_t bit_size, EVP_PKEY **pkeyp) { diff --git a/lib/isc/ossl_wrap/ossl3.c b/lib/isc/ossl_wrap/ossl3.c index d18beb3e198..0bd116f3816 100644 --- a/lib/isc/ossl_wrap/ossl3.c +++ b/lib/isc/ossl_wrap/ossl3.c @@ -15,19 +15,97 @@ #include #include +#include #include #include #include #include #include +#include #include +#define MAX_PUBLIC_KEY_SIZE 96 +#define MAX_SECRET_KEY_SIZE 48 + #define OSSL_WRAP_ERROR(fn) \ isc__ossl_wrap_logged_toresult( \ ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_CRYPTO, fn, \ ISC_R_CRYPTOFAILURE, __FILE__, __LINE__) +#define P_CURVE_IMPL(curve, nid) \ + isc_result_t isc_ossl_wrap_generate_##curve##_key(EVP_PKEY **pkeyp) { \ + REQUIRE(pkeyp != NULL && *pkeyp == NULL); \ + return generate_ec_key(pkeyp, nid); \ + } \ + isc_result_t isc_ossl_wrap_generate_pkcs11_##curve##_key( \ + char *uri, EVP_PKEY **pkeyp) { \ + REQUIRE(pkeyp != NULL && *pkeyp == NULL); \ + REQUIRE(uri != NULL); \ + return generate_pkcs11_ec_key(uri, pkeyp, nid); \ + } \ + isc_result_t isc_ossl_wrap_validate_##curve##_pkey(EVP_PKEY *pkey) { \ + REQUIRE(pkey != NULL); \ + return validate_ec_pkey(pkey, curve##_group_name); \ + } \ + isc_result_t isc_ossl_wrap_load_##curve##_public_from_region( \ + isc_region_t region, EVP_PKEY **pkeyp) { \ + REQUIRE(region.base != NULL && \ + region.length <= MAX_PUBLIC_KEY_SIZE); \ + REQUIRE(pkeyp != NULL && *pkeyp == NULL); \ + region.length = curve##_public_key_size; \ + return load_ec_public_from_region(region, pkeyp, \ + curve##_group_name); \ + } \ + isc_result_t isc_ossl_wrap_load_##curve##_secret_from_region( \ + isc_region_t region, EVP_PKEY **pkeyp) { \ + REQUIRE(pkeyp != NULL && *pkeyp == NULL); \ + REQUIRE(region.base != NULL && \ + region.length >= curve##_secret_key_size); \ + region.length = curve##_secret_key_size; \ + return load_ec_secret_from_region(region, pkeyp, \ + curve##_group_name, nid); \ + } \ + isc_result_t isc_ossl_wrap_##curve##_public_region(EVP_PKEY *pkey, \ + isc_region_t pub) { \ + REQUIRE(pkey != NULL); \ + REQUIRE(pub.base != NULL && \ + pub.length >= curve##_public_key_size); \ + pub.length = curve##_public_key_size; \ + return ec_public_region(pkey, pub); \ + } \ + isc_result_t isc_ossl_wrap_##curve##_secret_region(EVP_PKEY *pkey, \ + isc_region_t sec) { \ + REQUIRE(pkey != NULL); \ + REQUIRE(sec.base != NULL && \ + sec.length >= curve##_secret_key_size); \ + sec.length = curve##_secret_key_size; \ + return ec_secret_region(pkey, sec); \ + } + +static char pkcs11_key_usage[] = "digitalSignature"; + +constexpr char *p256_group_name = "prime256v1"; +constexpr char *p384_group_name = "secp384r1"; + +constexpr size_t p256_public_key_size = 64; +constexpr size_t p384_public_key_size = 96; + +constexpr size_t p256_secret_key_size = 32; +constexpr size_t p384_secret_key_size = 48; + +static void +BN_bn2bin_fixed(const BIGNUM *bn, unsigned char *buf, int size) { + int bytes = size - BN_num_bytes(bn); + + INSIST(bytes >= 0); + + while (bytes-- > 0) { + *buf++ = 0; + } + BN_bn2bin(bn, buf); +} + static int rsa_keygen_progress_cb(EVP_PKEY_CTX *ctx) { void (*fptr)(int); @@ -40,6 +118,346 @@ rsa_keygen_progress_cb(EVP_PKEY_CTX *ctx) { return 1; } +static isc_result_t +generate_ec_key(EVP_PKEY **pkeyp, const int nid) { + isc_result_t result; + EVP_PKEY_CTX *pctx = NULL; + EVP_PKEY *params_pkey = NULL; + + /* Generate the key's parameters. */ + pctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL); + if (pctx == NULL) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_CTX_new_from_name")); + } + + if (EVP_PKEY_paramgen_init(pctx) != 1) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_paramgen_init")); + } + + if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, nid) != 1) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_CTX_set_ec_paramgen_curve_" + "nid")); + } + + if (EVP_PKEY_paramgen(pctx, ¶ms_pkey) != 1) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_paramgen")); + } + + if (params_pkey == NULL) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_paramgen")); + } + + EVP_PKEY_CTX_free(pctx); + + /* Generate the key. */ + pctx = EVP_PKEY_CTX_new(params_pkey, NULL); + if (pctx == NULL) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_CTX_new")); + } + + if (EVP_PKEY_keygen_init(pctx) != 1) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_keygen_init")); + } + + if (EVP_PKEY_keygen(pctx, pkeyp) != 1) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_keygen")); + } + + result = ISC_R_SUCCESS; + +cleanup: + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_free(params_pkey); + return result; +} + +static isc_result_t +generate_pkcs11_ec_key(char *uri, EVP_PKEY **pkeyp, int nid) { + isc_result_t result; + EVP_PKEY_CTX *pctx; + OSSL_PARAM params[3]; + + params[0] = OSSL_PARAM_construct_utf8_string("pkcs11_uri", uri, 0); + params[1] = OSSL_PARAM_construct_utf8_string( + "pkcs11_key_usage", pkcs11_key_usage, + sizeof(pkcs11_key_usage) - 1); + params[2] = OSSL_PARAM_construct_end(); + + pctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", "provider=pkcs11"); + if (pctx == NULL) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_CTX_new_from_name")); + } + + if (EVP_PKEY_keygen_init(pctx) != 1) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_keygen_init")); + } + + if (EVP_PKEY_CTX_set_params(pctx, params) != 1) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_CTX_set_params")); + } + + /* + * Setting the P-384 curve doesn't work correctly when using: + * OSSL_PARAM_construct_utf8_string("ec_paramgen_curve", "P-384", 0); + * + * Instead use the OpenSSL function to set the curve nid param. + */ + if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, nid) != 1) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_CTX_set_ec_paramgen_curve_" + "nid")); + } + + if (EVP_PKEY_generate(pctx, pkeyp) != 1) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_generate")); + } + + result = ISC_R_SUCCESS; + +cleanup: + EVP_PKEY_CTX_free(pctx); + return result; +} + +static isc_result_t +validate_ec_pkey(EVP_PKEY *pkey, const char *expected) { + isc_result_t result; + char actual[64]; + + if (EVP_PKEY_get_group_name(pkey, actual, sizeof(actual), NULL) != 1) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_get_group_name")); + } + + if (strcmp(expected, actual) != 0) { + return ISC_R_FAILURE; + } + + result = ISC_R_SUCCESS; + +cleanup: + return result; +} + +static isc_result_t +load_ec_public_from_region(isc_region_t region, EVP_PKEY **pkeyp, + const char *group_name) { + isc_result_t result; + OSSL_PARAM_BLD *bld = NULL; + OSSL_PARAM *params = NULL; + EVP_PKEY_CTX *pctx = NULL; + uint8_t buffer[MAX_PUBLIC_KEY_SIZE + 1]; + + buffer[0] = POINT_CONVERSION_UNCOMPRESSED; + memmove(buffer + 1, region.base, region.length); + + bld = OSSL_PARAM_BLD_new(); + if (bld == NULL) { + CLEANUP(OSSL_WRAP_ERROR("OSSL_PARAM_BLD_new")); + } + + if (OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_GROUP_NAME, + group_name, 0) != 1) + { + CLEANUP(OSSL_WRAP_ERROR("OSSL_PARAM_BLD_push_utf8_string")); + } + + if (OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, + buffer, region.length + 1) != 1) + { + CLEANUP(OSSL_WRAP_ERROR("OSSL_PARAM_BLD_push_octet_string")); + } + + params = OSSL_PARAM_BLD_to_param(bld); + if (params == NULL) { + CLEANUP(OSSL_WRAP_ERROR("OSSL_PARAM_BLD_to_param")); + } + + pctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL); + if (pctx == NULL) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_CTX_new_from_name")); + } + + if (EVP_PKEY_fromdata_init(pctx) != 1) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_fromdata_init")); + } + + if (EVP_PKEY_fromdata(pctx, pkeyp, EVP_PKEY_PUBLIC_KEY, params) != 1) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_fromdata")); + } + + result = ISC_R_SUCCESS; + +cleanup: + OSSL_PARAM_free(params); + OSSL_PARAM_BLD_free(bld); + EVP_PKEY_CTX_free(pctx); + return result; +} + +static isc_result_t +load_ec_secret_from_region(isc_region_t region, EVP_PKEY **pkeyp, + const char *group_name, const int nid) { + uint8_t public[MAX_PUBLIC_KEY_SIZE + 1]; + EVP_PKEY_CTX *pctx = NULL; + OSSL_PARAM_BLD *bld = NULL; + OSSL_PARAM *params = NULL; + EC_POINT *pub_point = NULL; + EC_GROUP *group = NULL; + BIGNUM *private = NULL; + isc_result_t result; + size_t public_len; + + /* + * OpenSSL requires us to set the public key portion, but since our + * private key file format does not contain it directly, we generate it + * as needed. + */ + group = EC_GROUP_new_by_curve_name(nid); + if (group == NULL) { + CLEANUP(OSSL_WRAP_ERROR("EC_GROUP_new_by_curve_name")); + } + + private = BN_bin2bn(region.base, region.length, NULL); + if (private == NULL) { + CLEANUP(OSSL_WRAP_ERROR("BN_bin2bn")); + } + + pub_point = EC_POINT_new(group); + if (pub_point == NULL) { + CLEANUP(OSSL_WRAP_ERROR("EC_POINT_new")); + } + + if (EC_POINT_mul(group, pub_point, private, NULL, NULL, NULL) != 1) { + CLEANUP(OSSL_WRAP_ERROR("EC_POINT_mul")); + } + + public_len = EC_POINT_point2oct(group, pub_point, + POINT_CONVERSION_UNCOMPRESSED, public, + sizeof(public), NULL); + if (public_len == 0) { + CLEANUP(OSSL_WRAP_ERROR("EC_POINT_point2oct")); + } + + bld = OSSL_PARAM_BLD_new(); + if (bld == NULL) { + CLEANUP(OSSL_WRAP_ERROR("OSSL_PARAM_BLD_new")); + } + + if (OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_GROUP_NAME, + group_name, 0) != 1) + { + CLEANUP(OSSL_WRAP_ERROR("OSSL_PARAM_BLD_push_utf8_string")); + } + + if (OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, private) != 1) + { + CLEANUP(OSSL_WRAP_ERROR("OSSL_PARAM_BLD_push_BN")); + } + + if (OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, + public, public_len) != 1) + { + CLEANUP(OSSL_WRAP_ERROR("OSSL_PARAM_BLD_push_octet_string")); + } + + params = OSSL_PARAM_BLD_to_param(bld); + if (params == NULL) { + CLEANUP(OSSL_WRAP_ERROR("OSSL_PARAM_BLD_to_param")); + } + + pctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL); + if (pctx == NULL) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_CTX_new_from_name")); + } + + if (EVP_PKEY_fromdata_init(pctx) != 1) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_fromdata_init")); + } + + if (EVP_PKEY_fromdata(pctx, pkeyp, EVP_PKEY_KEYPAIR, params) != 1) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_fromdata")); + } + + result = ISC_R_SUCCESS; + +cleanup: + OSSL_PARAM_free(params); + OSSL_PARAM_BLD_free(bld); + EC_POINT_free(pub_point); + BN_clear_free(private); + EC_GROUP_free(group); + EVP_PKEY_CTX_free(pctx); + return result; +} + +static isc_result_t +ec_public_region(EVP_PKEY *pkey, isc_region_t pub) { + isc_result_t result; + BIGNUM *x = NULL; + BIGNUM *y = NULL; + + if (EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_X, &x) != 1) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_get_bn_param")); + } + + if (EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_Y, &y) != 1) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_get_bn_param")); + } + + BN_bn2bin_fixed(x, &pub.base[0], pub.length / 2); + BN_bn2bin_fixed(y, &pub.base[pub.length / 2], pub.length / 2); + + result = ISC_R_SUCCESS; + +cleanup: + BN_clear_free(x); + BN_clear_free(y); + return result; +} + +static isc_result_t +ec_secret_region(EVP_PKEY *pkey, isc_region_t sec) { + isc_result_t result; + BIGNUM *priv = NULL; + + if (EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, &priv) != 1) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_get_bn_param")); + } + + BN_bn2bin_fixed(priv, sec.base, sec.length); + + result = ISC_R_SUCCESS; + +cleanup: + BN_clear_free(priv); + return result; +} + +P_CURVE_IMPL(p256, NID_X9_62_prime256v1); +P_CURVE_IMPL(p384, NID_secp384r1); + +isc_result_t +isc_ossl_wrap_ecdsa_set_deterministic(EVP_PKEY_CTX *pctx, const char *hash) { + unsigned int rfc6979 = 1; + isc_result_t result; + OSSL_PARAM params[3]; + + REQUIRE(pctx != NULL && hash != NULL); + + params[0] = OSSL_PARAM_construct_utf8_string("digest", UNCONST(hash), + 0); + params[1] = OSSL_PARAM_construct_uint("nonce-type", &rfc6979); + params[2] = OSSL_PARAM_construct_end(); + + if (EVP_PKEY_CTX_set_params(pctx, params) != 1) { + CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_CTX_set_params")); + } + + result = ISC_R_SUCCESS; + +cleanup: + return result; +} + isc_result_t isc_ossl_wrap_generate_rsa_key(void (*callback)(int), size_t bit_size, EVP_PKEY **pkeyp) {