mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-09-01 06:55:30 +00:00
Merge branch '3785-openssl-refactoring-17' into 'main'
Refactor OpenSSL ECDSA public and private key export Closes #3785 See merge request isc-projects/bind9!7334
This commit is contained in:
@@ -46,6 +46,10 @@
|
|||||||
#error "P-384 group is not known (NID_secp384r1)"
|
#error "P-384 group is not known (NID_secp384r1)"
|
||||||
#endif /* ifndef NID_secp384r1 */
|
#endif /* ifndef NID_secp384r1 */
|
||||||
|
|
||||||
|
#define MAX_PUBKEY_SIZE DNS_KEY_ECDSA384SIZE
|
||||||
|
|
||||||
|
#define MAX_PRIVKEY_SIZE (MAX_PUBKEY_SIZE / 2)
|
||||||
|
|
||||||
#define DST_RET(a) \
|
#define DST_RET(a) \
|
||||||
{ \
|
{ \
|
||||||
ret = a; \
|
ret = a; \
|
||||||
@@ -75,6 +79,18 @@ opensslecdsa_key_alg_to_group_nid(unsigned int key_alg) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t
|
||||||
|
opensslecdsa_key_alg_to_publickey_size(unsigned int key_alg) {
|
||||||
|
switch (key_alg) {
|
||||||
|
case DST_ALG_ECDSA256:
|
||||||
|
return (DNS_KEY_ECDSA256SIZE);
|
||||||
|
case DST_ALG_ECDSA384:
|
||||||
|
return (DNS_KEY_ECDSA384SIZE);
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* OpenSSL requires us to set the public key portion, but since our 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.
|
* file format does not contain it directly, we generate it as needed.
|
||||||
@@ -92,6 +108,17 @@ opensslecdsa_generate_public_key(const EC_GROUP *group, const BIGNUM *privkey) {
|
|||||||
return (pubkey);
|
return (pubkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
BN_bn2bin_fixed(const BIGNUM *bn, unsigned char *buf, int size) {
|
||||||
|
int bytes = size - BN_num_bytes(bn);
|
||||||
|
|
||||||
|
while (bytes-- > 0) {
|
||||||
|
*buf++ = 0;
|
||||||
|
}
|
||||||
|
BN_bn2bin(bn, buf);
|
||||||
|
return (size);
|
||||||
|
}
|
||||||
|
|
||||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_API_LEVEL >= 30000
|
#if OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_API_LEVEL >= 30000
|
||||||
|
|
||||||
static const char *
|
static const char *
|
||||||
@@ -177,7 +204,7 @@ opensslecdsa_create_pkey(unsigned int key_alg, bool private,
|
|||||||
EC_POINT *pubkey = NULL;
|
EC_POINT *pubkey = NULL;
|
||||||
EC_GROUP *group = NULL;
|
EC_GROUP *group = NULL;
|
||||||
BIGNUM *priv = NULL;
|
BIGNUM *priv = NULL;
|
||||||
unsigned char buf[DNS_KEY_ECDSA384SIZE + 1];
|
unsigned char buf[MAX_PUBKEY_SIZE + 1];
|
||||||
|
|
||||||
bld = OSSL_PARAM_BLD_new();
|
bld = OSSL_PARAM_BLD_new();
|
||||||
if (bld == NULL) {
|
if (bld == NULL) {
|
||||||
@@ -292,6 +319,44 @@ opensslecdsa_validate_pkey_group(unsigned int key_alg, EVP_PKEY *pkey) {
|
|||||||
return (ISC_R_SUCCESS);
|
return (ISC_R_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
opensslecdsa_extract_public_key(const dst_key_t *key, unsigned char *buf,
|
||||||
|
size_t buflen) {
|
||||||
|
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) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
if (EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_Y, &y) != 1) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
BN_bn2bin_fixed(x, &buf[0], buflen / 2);
|
||||||
|
BN_bn2bin_fixed(y, &buf[buflen / 2], buflen / 2);
|
||||||
|
ret = true;
|
||||||
|
err:
|
||||||
|
BN_clear_free(x);
|
||||||
|
BN_clear_free(y);
|
||||||
|
return (ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
#else
|
||||||
|
|
||||||
static isc_result_t
|
static isc_result_t
|
||||||
@@ -339,7 +404,7 @@ opensslecdsa_create_pkey(unsigned int key_alg, bool private,
|
|||||||
EVP_PKEY *pkey = NULL;
|
EVP_PKEY *pkey = NULL;
|
||||||
BIGNUM *privkey = NULL;
|
BIGNUM *privkey = NULL;
|
||||||
EC_POINT *pubkey = NULL;
|
EC_POINT *pubkey = NULL;
|
||||||
unsigned char buf[DNS_KEY_ECDSA384SIZE + 1];
|
unsigned char buf[MAX_PUBKEY_SIZE + 1];
|
||||||
int group_nid = opensslecdsa_key_alg_to_group_nid(key_alg);
|
int group_nid = opensslecdsa_key_alg_to_group_nid(key_alg);
|
||||||
|
|
||||||
eckey = EC_KEY_new_by_curve_name(group_nid);
|
eckey = EC_KEY_new_by_curve_name(group_nid);
|
||||||
@@ -409,6 +474,44 @@ opensslecdsa_validate_pkey_group(unsigned int key_alg, EVP_PKEY *pkey) {
|
|||||||
return (ISC_R_SUCCESS);
|
return (ISC_R_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
opensslecdsa_extract_public_key(const dst_key_t *key, unsigned char *dst,
|
||||||
|
size_t dstlen) {
|
||||||
|
const EC_KEY *eckey = EVP_PKEY_get0_EC_KEY(key->keydata.pkeypair.pub);
|
||||||
|
const EC_GROUP *group = EC_KEY_get0_group(eckey);
|
||||||
|
const EC_POINT *pub = EC_KEY_get0_public_key(eckey);
|
||||||
|
unsigned char buf[MAX_PUBKEY_SIZE + 1];
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
len = EC_POINT_point2oct(group, pub, POINT_CONVERSION_UNCOMPRESSED, buf,
|
||||||
|
sizeof(buf), NULL);
|
||||||
|
if (len != dstlen + 1) {
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
memmove(dst, buf + 1, dstlen);
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
privkey = EC_KEY_get0_private_key(eckey);
|
||||||
|
if (privkey == NULL) {
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
BN_bn2bin_fixed(privkey, buf, buflen);
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_API_LEVEL >= 30000 \
|
#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_API_LEVEL >= 30000 \
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -501,17 +604,6 @@ err:
|
|||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
BN_bn2bin_fixed(const BIGNUM *bn, unsigned char *buf, int size) {
|
|
||||||
int bytes = size - BN_num_bytes(bn);
|
|
||||||
|
|
||||||
while (bytes-- > 0) {
|
|
||||||
*buf++ = 0;
|
|
||||||
}
|
|
||||||
BN_bn2bin(bn, buf);
|
|
||||||
return (size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static isc_result_t
|
static isc_result_t
|
||||||
opensslecdsa_sign(dst_context_t *dctx, isc_buffer_t *sig) {
|
opensslecdsa_sign(dst_context_t *dctx, isc_buffer_t *sig) {
|
||||||
isc_result_t ret;
|
isc_result_t ret;
|
||||||
@@ -694,90 +786,25 @@ opensslecdsa_destroy(dst_key_t *key) {
|
|||||||
static isc_result_t
|
static isc_result_t
|
||||||
opensslecdsa_todns(const dst_key_t *key, isc_buffer_t *data) {
|
opensslecdsa_todns(const dst_key_t *key, isc_buffer_t *data) {
|
||||||
isc_result_t ret;
|
isc_result_t ret;
|
||||||
EVP_PKEY *pkey;
|
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000
|
|
||||||
EC_KEY *eckey = NULL;
|
|
||||||
int len;
|
|
||||||
unsigned char *cp;
|
|
||||||
#else
|
|
||||||
int status;
|
|
||||||
BIGNUM *x = NULL;
|
|
||||||
BIGNUM *y = NULL;
|
|
||||||
size_t keysize = 0;
|
|
||||||
size_t len = 0;
|
|
||||||
#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */
|
|
||||||
isc_region_t r;
|
isc_region_t r;
|
||||||
unsigned char buf[DNS_KEY_ECDSA384SIZE + 1];
|
size_t keysize;
|
||||||
|
|
||||||
|
REQUIRE(opensslecdsa_valid_key_alg(key->key_alg));
|
||||||
REQUIRE(key->keydata.pkeypair.pub != NULL);
|
REQUIRE(key->keydata.pkeypair.pub != NULL);
|
||||||
|
|
||||||
pkey = key->keydata.pkeypair.pub;
|
keysize = opensslecdsa_key_alg_to_publickey_size(key->key_alg);
|
||||||
|
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000
|
|
||||||
eckey = EVP_PKEY_get1_EC_KEY(pkey);
|
|
||||||
if (eckey == NULL) {
|
|
||||||
DST_RET(dst__openssl_toresult(ISC_R_FAILURE));
|
|
||||||
}
|
|
||||||
len = i2o_ECPublicKey(eckey, NULL);
|
|
||||||
|
|
||||||
/* skip form */
|
|
||||||
len--;
|
|
||||||
#else
|
|
||||||
if (key->key_alg == DST_ALG_ECDSA256) {
|
|
||||||
keysize = DNS_KEY_ECDSA256SIZE;
|
|
||||||
} else if (key->key_alg == DST_ALG_ECDSA384) {
|
|
||||||
keysize = DNS_KEY_ECDSA384SIZE;
|
|
||||||
} else {
|
|
||||||
DST_RET(ISC_R_NOTIMPLEMENTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
len = keysize;
|
|
||||||
#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */
|
|
||||||
|
|
||||||
isc_buffer_availableregion(data, &r);
|
isc_buffer_availableregion(data, &r);
|
||||||
if (r.length < (unsigned int)len) {
|
if (r.length < keysize) {
|
||||||
DST_RET(ISC_R_NOSPACE);
|
DST_RET(ISC_R_NOSPACE);
|
||||||
}
|
}
|
||||||
|
if (!opensslecdsa_extract_public_key(key, r.base, keysize)) {
|
||||||
|
DST_RET(DST_R_OPENSSLFAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000
|
isc_buffer_add(data, keysize);
|
||||||
cp = buf;
|
|
||||||
if (!i2o_ECPublicKey(eckey, &cp)) {
|
|
||||||
DST_RET(dst__openssl_toresult(ISC_R_FAILURE));
|
|
||||||
}
|
|
||||||
memmove(r.base, buf + 1, len);
|
|
||||||
#else
|
|
||||||
status = EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_X, &x);
|
|
||||||
if (status != 1 || x == NULL) {
|
|
||||||
DST_RET(dst__openssl_toresult2("EVP_PKEY_get_bn_param",
|
|
||||||
DST_R_OPENSSLFAILURE));
|
|
||||||
}
|
|
||||||
status = EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_Y, &y);
|
|
||||||
if (status != 1 || y == NULL) {
|
|
||||||
DST_RET(dst__openssl_toresult2("EVP_PKEY_get_bn_param",
|
|
||||||
DST_R_OPENSSLFAILURE));
|
|
||||||
}
|
|
||||||
BN_bn2bin_fixed(x, &buf[0], keysize / 2);
|
|
||||||
BN_bn2bin_fixed(y, &buf[keysize / 2], keysize / 2);
|
|
||||||
memmove(r.base, buf, len);
|
|
||||||
#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */
|
|
||||||
|
|
||||||
isc_buffer_add(data, len);
|
|
||||||
ret = ISC_R_SUCCESS;
|
ret = ISC_R_SUCCESS;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000
|
|
||||||
if (eckey != NULL) {
|
|
||||||
EC_KEY_free(eckey);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if (x != NULL) {
|
|
||||||
BN_clear_free(x);
|
|
||||||
}
|
|
||||||
if (y != NULL) {
|
|
||||||
BN_clear_free(y);
|
|
||||||
}
|
|
||||||
#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */
|
|
||||||
|
|
||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -786,16 +813,10 @@ opensslecdsa_fromdns(dst_key_t *key, isc_buffer_t *data) {
|
|||||||
isc_result_t ret;
|
isc_result_t ret;
|
||||||
EVP_PKEY *pkey = NULL;
|
EVP_PKEY *pkey = NULL;
|
||||||
isc_region_t r;
|
isc_region_t r;
|
||||||
|
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
REQUIRE(opensslecdsa_valid_key_alg(key->key_alg));
|
REQUIRE(opensslecdsa_valid_key_alg(key->key_alg));
|
||||||
|
len = opensslecdsa_key_alg_to_publickey_size(key->key_alg);
|
||||||
if (key->key_alg == DST_ALG_ECDSA256) {
|
|
||||||
len = DNS_KEY_ECDSA256SIZE;
|
|
||||||
} else {
|
|
||||||
len = DNS_KEY_ECDSA384SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
isc_buffer_remainingregion(data, &r);
|
isc_buffer_remainingregion(data, &r);
|
||||||
if (r.length == 0) {
|
if (r.length == 0) {
|
||||||
@@ -822,16 +843,9 @@ err:
|
|||||||
static isc_result_t
|
static isc_result_t
|
||||||
opensslecdsa_tofile(const dst_key_t *key, const char *directory) {
|
opensslecdsa_tofile(const dst_key_t *key, const char *directory) {
|
||||||
isc_result_t ret;
|
isc_result_t ret;
|
||||||
EVP_PKEY *pkey;
|
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000
|
|
||||||
EC_KEY *eckey = NULL;
|
|
||||||
const BIGNUM *privkey = NULL;
|
|
||||||
#else
|
|
||||||
int status;
|
|
||||||
BIGNUM *privkey = NULL;
|
|
||||||
#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */
|
|
||||||
dst_private_t priv;
|
dst_private_t priv;
|
||||||
unsigned char *buf = NULL;
|
unsigned char buf[MAX_PRIVKEY_SIZE];
|
||||||
|
size_t keylen = 0;
|
||||||
unsigned short i;
|
unsigned short i;
|
||||||
|
|
||||||
if (key->keydata.pkeypair.pub == NULL) {
|
if (key->keydata.pkeypair.pub == NULL) {
|
||||||
@@ -847,34 +861,15 @@ opensslecdsa_tofile(const dst_key_t *key, const char *directory) {
|
|||||||
DST_RET(DST_R_NULLKEY);
|
DST_RET(DST_R_NULLKEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
pkey = key->keydata.pkeypair.priv;
|
keylen = opensslecdsa_key_alg_to_publickey_size(key->key_alg) / 2;
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000
|
INSIST(keylen <= sizeof(buf));
|
||||||
eckey = EVP_PKEY_get1_EC_KEY(pkey);
|
if (!opensslecdsa_extract_private_key(key, buf, keylen)) {
|
||||||
if (eckey == NULL) {
|
DST_RET(DST_R_OPENSSLFAILURE);
|
||||||
DST_RET(dst__openssl_toresult2("EVP_PKEY_get1_EC_KEY",
|
|
||||||
DST_R_OPENSSLFAILURE));
|
|
||||||
}
|
}
|
||||||
privkey = EC_KEY_get0_private_key(eckey);
|
|
||||||
if (privkey == NULL) {
|
|
||||||
DST_RET(dst__openssl_toresult2("EC_KEY_get0_private_key",
|
|
||||||
DST_R_OPENSSLFAILURE));
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
status = EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY,
|
|
||||||
&privkey);
|
|
||||||
if (status != 1 || privkey == NULL) {
|
|
||||||
DST_RET(dst__openssl_toresult2("EVP_PKEY_get_bn_param",
|
|
||||||
DST_R_OPENSSLFAILURE));
|
|
||||||
}
|
|
||||||
#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */
|
|
||||||
|
|
||||||
buf = isc_mem_get(key->mctx, BN_num_bytes(privkey));
|
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
|
|
||||||
priv.elements[i].tag = TAG_ECDSA_PRIVATEKEY;
|
priv.elements[i].tag = TAG_ECDSA_PRIVATEKEY;
|
||||||
priv.elements[i].length = BN_num_bytes(privkey);
|
priv.elements[i].length = keylen;
|
||||||
BN_bn2bin(privkey, buf);
|
|
||||||
priv.elements[i].data = buf;
|
priv.elements[i].data = buf;
|
||||||
i++;
|
i++;
|
||||||
|
|
||||||
@@ -898,19 +893,7 @@ opensslecdsa_tofile(const dst_key_t *key, const char *directory) {
|
|||||||
ret = dst__privstruct_writefile(key, &priv, directory);
|
ret = dst__privstruct_writefile(key, &priv, directory);
|
||||||
|
|
||||||
err:
|
err:
|
||||||
if (buf != NULL && privkey != NULL) {
|
isc_safe_memwipe(buf, keylen);
|
||||||
isc_mem_put(key->mctx, buf, BN_num_bytes(privkey));
|
|
||||||
}
|
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000
|
|
||||||
if (eckey != NULL) {
|
|
||||||
EC_KEY_free(eckey);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if (privkey != NULL) {
|
|
||||||
BN_clear_free(privkey);
|
|
||||||
}
|
|
||||||
#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000 */
|
|
||||||
|
|
||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user