#include <openssl/bn.h>
#include <openssl/ecdsa.h>
-#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/objects.h>
-#include <openssl/opensslv.h>
-#if OPENSSL_VERSION_NUMBER >= 0x30000000L
-#include <openssl/core_names.h>
-#include <openssl/param_build.h>
-#endif
#include <isc/crypto.h>
#include <isc/md.h>
#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) {
}
}
-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) {
}
}
-/*
- * 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);
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));
}
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) {
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,
}
dctx->ctxdata.evp_md_ctx = evp_md_ctx;
+ result = ISC_R_SUCCESS;
cleanup:
return result;
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;
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));
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);
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);
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);
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;
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;
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) {
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);
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);
*/
#include <openssl/bn.h>
+#include <openssl/ec.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <isc/ossl_wrap.h>
+#include <isc/region.h>
#include <isc/util.h>
+#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);
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) {
#include <openssl/bn.h>
#include <openssl/core_names.h>
+#include <openssl/ec.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/param_build.h>
#include <openssl/rsa.h>
#include <isc/ossl_wrap.h>
+#include <isc/region.h>
#include <isc/util.h>
+#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);
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) {