2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-22 01:59:26 +00:00
bind/lib/dns/opensslecdsa_link.c
Ondřej Surý ef7aba7072
Remove OpenSSL Engine support
The OpenSSL 1.x Engines support has been deprecated in the OpenSSL 3.x
and is going to be removed.  Remove the OpenSSL Engine support in favor
of OpenSSL Providers.
2024-08-06 15:17:48 +02:00

1165 lines
29 KiB
C

/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
/*! \file */
#include <stdbool.h>
#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/mem.h>
#include <isc/result.h>
#include <isc/safe.h>
#include <isc/string.h>
#include <isc/util.h>
#include <dns/keyvalues.h>
#include "dst_internal.h"
#include "dst_openssl.h"
#include "dst_parse.h"
#include "openssl_shim.h"
#ifndef NID_X9_62_prime256v1
#error "P-256 group is not known (NID_X9_62_prime256v1)"
#endif /* ifndef NID_X9_62_prime256v1 */
#ifndef NID_secp384r1
#error "P-384 group is not known (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) \
{ \
ret = a; \
goto err; \
}
static bool
opensslecdsa_valid_key_alg(unsigned int key_alg) {
switch (key_alg) {
case DST_ALG_ECDSA256:
case DST_ALG_ECDSA384:
return (true);
default:
return (false);
}
}
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) {
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
* 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);
INSIST(bytes >= 0);
while (bytes-- > 0) {
*buf++ = 0;
}
BN_bn2bin(bn, buf);
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 ret;
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) {
DST_RET(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) {
DST_RET(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) {
DST_RET(dst__openssl_toresult2("EC_GROUP_new_by_"
"curve_name",
DST_R_OPENSSLFAILURE));
}
priv = BN_bin2bn(key, key_len, NULL);
if (priv == NULL) {
DST_RET(dst__openssl_toresult2("BN_bin2bn",
DST_R_OPENSSLFAILURE));
}
status = OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY,
priv);
if (status != 1) {
DST_RET(dst__openssl_toresult2("OSSL_PARAM_BLD_push_BN",
DST_R_OPENSSLFAILURE));
}
pubkey = opensslecdsa_generate_public_key(group, priv);
if (pubkey == NULL) {
DST_RET(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) {
DST_RET(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) {
DST_RET(dst__openssl_toresult2("OSSL_PARAM_BLD_push_"
"octet_string",
DST_R_OPENSSLFAILURE));
}
params = OSSL_PARAM_BLD_to_param(bld);
if (params == NULL) {
DST_RET(dst__openssl_toresult2("OSSL_PARAM_BLD_to_param",
DST_R_OPENSSLFAILURE));
}
ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
if (ctx == NULL) {
DST_RET(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. */
DST_RET(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) {
DST_RET(dst__openssl_toresult2("EVP_PKEY_fromdata",
DST_R_OPENSSLFAILURE));
}
ret = ISC_R_SUCCESS;
err:
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 (ret);
}
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 || OPENSSL_API_LEVEL < 30000
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 ret = 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) {
DST_RET(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) {
DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
}
if (!EC_KEY_set_private_key(eckey, privkey)) {
DST_RET(dst__openssl_toresult(DST_R_INVALIDPRIVATEKEY));
}
pubkey = opensslecdsa_generate_public_key(group, privkey);
if (pubkey == NULL) {
DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
}
if (EC_KEY_set_public_key(eckey, pubkey) != 1) {
DST_RET(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) {
DST_RET(dst__openssl_toresult(DST_R_INVALIDPUBLICKEY));
}
if (EC_KEY_check_key(eckey) != 1) {
DST_RET(dst__openssl_toresult(DST_R_INVALIDPUBLICKEY));
}
}
pkey = EVP_PKEY_new();
if (pkey == NULL) {
DST_RET(dst__openssl_toresult(ISC_R_NOMEMORY));
}
if (!EVP_PKEY_set1_EC_KEY(pkey, eckey)) {
DST_RET(dst__openssl_toresult(ISC_R_FAILURE));
}
*retkey = pkey;
pkey = NULL;
err:
BN_clear_free(privkey);
EC_POINT_free(pubkey);
EC_KEY_free(eckey);
EVP_PKEY_free(pkey);
return (ret);
}
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);
}
#endif
#if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000
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 ret;
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
ret = opensslecdsa_create_pkey_params(key_alg, private, key, key_len,
retkey);
if (ret != ISC_R_FAILURE) {
return (ret);
}
#endif
#if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000
ret = opensslecdsa_create_pkey_legacy(key_alg, private, key, key_len,
retkey);
if (ret == ISC_R_SUCCESS) {
return (ret);
}
#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 ret;
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) {
DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_new_from_name",
DST_R_OPENSSLFAILURE));
}
status = EVP_PKEY_keygen_init(ctx);
if (status != 1) {
DST_RET(dst__openssl_toresult2("EVP_PKEY_keygen_init",
DST_R_OPENSSLFAILURE));
}
status = EVP_PKEY_CTX_set_params(ctx, params);
if (status != 1) {
DST_RET(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) {
DST_RET(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) {
DST_RET(dst__openssl_toresult2("EVP_PKEY_generate",
DST_R_OPENSSLFAILURE));
}
ret = ISC_R_SUCCESS;
err:
EVP_PKEY_CTX_free(ctx);
return (ret);
}
static isc_result_t
opensslecdsa_generate_pkey(unsigned int key_alg, const char *label,
EVP_PKEY **retkey) {
isc_result_t ret;
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) {
DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_new_from_name",
DST_R_OPENSSLFAILURE));
}
status = EVP_PKEY_paramgen_init(ctx);
if (status != 1) {
DST_RET(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) {
DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_set_ec_paramgen_"
"curve_nid",
DST_R_OPENSSLFAILURE));
}
status = EVP_PKEY_paramgen(ctx, &params_pkey);
if (status != 1 || params_pkey == NULL) {
DST_RET(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) {
DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_new",
DST_R_OPENSSLFAILURE));
}
status = EVP_PKEY_keygen_init(ctx);
if (status != 1) {
DST_RET(dst__openssl_toresult2("EVP_PKEY_keygen_init",
DST_R_OPENSSLFAILURE));
}
status = EVP_PKEY_keygen(ctx, retkey);
if (status != 1) {
DST_RET(dst__openssl_toresult2("EVP_PKEY_keygen",
DST_R_OPENSSLFAILURE));
}
ret = ISC_R_SUCCESS;
err:
EVP_PKEY_free(params_pkey);
EVP_PKEY_CTX_free(ctx);
return (ret);
}
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 ret;
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) {
DST_RET(dst__openssl_toresult2("EC_KEY_new_by_curve_name",
DST_R_OPENSSLFAILURE));
}
if (EC_KEY_generate_key(eckey) != 1) {
DST_RET(dst__openssl_toresult2("EC_KEY_generate_key",
DST_R_OPENSSLFAILURE));
}
pkey = EVP_PKEY_new();
if (pkey == NULL) {
DST_RET(dst__openssl_toresult(ISC_R_NOMEMORY));
}
if (EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) {
DST_RET(dst__openssl_toresult2("EVP_PKEY_set1_EC_KEY",
DST_R_OPENSSLFAILURE));
}
*retkey = pkey;
pkey = NULL;
ret = ISC_R_SUCCESS;
err:
EC_KEY_free(eckey);
EVP_PKEY_free(pkey);
return (ret);
}
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 ret = ISC_R_SUCCESS;
EVP_MD_CTX *evp_md_ctx;
const EVP_MD *type = NULL;
UNUSED(key);
REQUIRE(opensslecdsa_valid_key_alg(dctx->key->key_alg));
REQUIRE(dctx->use == DO_SIGN || dctx->use == DO_VERIFY);
evp_md_ctx = EVP_MD_CTX_create();
if (evp_md_ctx == NULL) {
DST_RET(dst__openssl_toresult(ISC_R_NOMEMORY));
}
if (dctx->key->key_alg == DST_ALG_ECDSA256) {
type = EVP_sha256();
} else {
type = EVP_sha384();
}
if (dctx->use == DO_SIGN) {
if (EVP_DigestSignInit(evp_md_ctx, NULL, type, NULL,
dctx->key->keydata.pkeypair.priv) != 1)
{
EVP_MD_CTX_destroy(evp_md_ctx);
DST_RET(dst__openssl_toresult3(dctx->category,
"EVP_DigestSignInit",
ISC_R_FAILURE));
}
} else {
if (EVP_DigestVerifyInit(evp_md_ctx, NULL, type, NULL,
dctx->key->keydata.pkeypair.pub) != 1)
{
EVP_MD_CTX_destroy(evp_md_ctx);
DST_RET(dst__openssl_toresult3(dctx->category,
"EVP_DigestVerifyInit",
ISC_R_FAILURE));
}
}
dctx->ctxdata.evp_md_ctx = evp_md_ctx;
err:
return (ret);
}
static void
opensslecdsa_destroyctx(dst_context_t *dctx) {
EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
REQUIRE(opensslecdsa_valid_key_alg(dctx->key->key_alg));
REQUIRE(dctx->use == DO_SIGN || dctx->use == DO_VERIFY);
if (evp_md_ctx != NULL) {
EVP_MD_CTX_destroy(evp_md_ctx);
dctx->ctxdata.evp_md_ctx = NULL;
}
}
static isc_result_t
opensslecdsa_adddata(dst_context_t *dctx, const isc_region_t *data) {
isc_result_t ret = ISC_R_SUCCESS;
EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
REQUIRE(opensslecdsa_valid_key_alg(dctx->key->key_alg));
REQUIRE(dctx->use == DO_SIGN || dctx->use == DO_VERIFY);
if (dctx->use == DO_SIGN) {
if (EVP_DigestSignUpdate(evp_md_ctx, data->base,
data->length) != 1)
{
DST_RET(dst__openssl_toresult3(dctx->category,
"EVP_DigestSignUpdate",
ISC_R_FAILURE));
}
} else {
if (EVP_DigestVerifyUpdate(evp_md_ctx, data->base,
data->length) != 1)
{
DST_RET(dst__openssl_toresult3(dctx->category,
"EVP_DigestVerifyUpdate",
ISC_R_FAILURE));
}
}
err:
return (ret);
}
static isc_result_t
opensslecdsa_sign(dst_context_t *dctx, isc_buffer_t *sig) {
isc_result_t ret;
dst_key_t *key = dctx->key;
isc_region_t region;
EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
ECDSA_SIG *ecdsasig = NULL;
size_t siglen, sigder_len = 0, sigder_alloced = 0;
unsigned char *sigder = NULL;
const unsigned char *sigder_copy;
const BIGNUM *r, *s;
REQUIRE(opensslecdsa_valid_key_alg(key->key_alg));
REQUIRE(dctx->use == DO_SIGN);
if (key->key_alg == DST_ALG_ECDSA256) {
siglen = DNS_SIG_ECDSA256SIZE;
} else {
siglen = DNS_SIG_ECDSA384SIZE;
}
isc_buffer_availableregion(sig, &region);
if (region.length < siglen) {
DST_RET(ISC_R_NOSPACE);
}
if (EVP_DigestSignFinal(evp_md_ctx, NULL, &sigder_len) != 1) {
DST_RET(dst__openssl_toresult3(
dctx->category, "EVP_DigestSignFinal", ISC_R_FAILURE));
}
if (sigder_len == 0) {
DST_RET(ISC_R_FAILURE);
}
sigder = isc_mem_get(dctx->mctx, sigder_len);
sigder_alloced = sigder_len;
if (EVP_DigestSignFinal(evp_md_ctx, sigder, &sigder_len) != 1) {
DST_RET(dst__openssl_toresult3(
dctx->category, "EVP_DigestSignFinal", ISC_R_FAILURE));
}
sigder_copy = sigder;
if (d2i_ECDSA_SIG(&ecdsasig, &sigder_copy, sigder_len) == NULL) {
DST_RET(dst__openssl_toresult3(dctx->category, "d2i_ECDSA_SIG",
ISC_R_FAILURE));
}
ECDSA_SIG_get0(ecdsasig, &r, &s);
BN_bn2bin_fixed(r, region.base, siglen / 2);
isc_region_consume(&region, siglen / 2);
BN_bn2bin_fixed(s, region.base, siglen / 2);
isc_region_consume(&region, siglen / 2);
ECDSA_SIG_free(ecdsasig);
isc_buffer_add(sig, siglen);
ret = ISC_R_SUCCESS;
err:
if (sigder != NULL && sigder_alloced != 0) {
isc_mem_put(dctx->mctx, sigder, sigder_alloced);
}
return (ret);
}
static isc_result_t
opensslecdsa_verify(dst_context_t *dctx, const isc_region_t *sig) {
isc_result_t ret;
dst_key_t *key = dctx->key;
int status;
unsigned char *cp = sig->base;
ECDSA_SIG *ecdsasig = NULL;
EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx;
size_t siglen, sigder_len = 0, sigder_alloced = 0;
unsigned char *sigder = NULL;
unsigned char *sigder_copy;
BIGNUM *r = NULL, *s = NULL;
REQUIRE(opensslecdsa_valid_key_alg(key->key_alg));
REQUIRE(dctx->use == DO_VERIFY);
if (key->key_alg == DST_ALG_ECDSA256) {
siglen = DNS_SIG_ECDSA256SIZE;
} else {
siglen = DNS_SIG_ECDSA384SIZE;
}
if (sig->length != siglen) {
DST_RET(DST_R_VERIFYFAILURE);
}
ecdsasig = ECDSA_SIG_new();
if (ecdsasig == NULL) {
DST_RET(dst__openssl_toresult(ISC_R_NOMEMORY));
}
r = BN_bin2bn(cp, siglen / 2, NULL);
cp += siglen / 2;
s = BN_bin2bn(cp, siglen / 2, NULL);
/* cp += siglen / 2; */
ECDSA_SIG_set0(ecdsasig, r, s);
status = i2d_ECDSA_SIG(ecdsasig, NULL);
if (status < 0) {
DST_RET(dst__openssl_toresult3(dctx->category, "i2d_ECDSA_SIG",
DST_R_VERIFYFAILURE));
}
sigder_len = (size_t)status;
sigder = isc_mem_get(dctx->mctx, sigder_len);
sigder_alloced = sigder_len;
sigder_copy = sigder;
status = i2d_ECDSA_SIG(ecdsasig, &sigder_copy);
if (status < 0) {
DST_RET(dst__openssl_toresult3(dctx->category, "i2d_ECDSA_SIG",
DST_R_VERIFYFAILURE));
}
status = EVP_DigestVerifyFinal(evp_md_ctx, sigder, sigder_len);
switch (status) {
case 1:
ret = ISC_R_SUCCESS;
break;
case 0:
ret = dst__openssl_toresult(DST_R_VERIFYFAILURE);
break;
default:
ret = dst__openssl_toresult3(dctx->category,
"EVP_DigestVerifyFinal",
DST_R_VERIFYFAILURE);
break;
}
err:
if (ecdsasig != NULL) {
ECDSA_SIG_free(ecdsasig);
}
if (sigder != NULL && sigder_alloced != 0) {
isc_mem_put(dctx->mctx, sigder, sigder_alloced);
}
return (ret);
}
static isc_result_t
opensslecdsa_generate(dst_key_t *key, int unused, void (*callback)(int)) {
isc_result_t ret;
EVP_PKEY *pkey = NULL;
REQUIRE(opensslecdsa_valid_key_alg(key->key_alg));
UNUSED(unused);
UNUSED(callback);
ret = opensslecdsa_generate_pkey(key->key_alg, key->label, &pkey);
if (ret != ISC_R_SUCCESS) {
return (ret);
}
key->key_size = EVP_PKEY_bits(pkey);
key->keydata.pkeypair.priv = pkey;
key->keydata.pkeypair.pub = pkey;
return (ret);
}
static isc_result_t
opensslecdsa_todns(const dst_key_t *key, isc_buffer_t *data) {
isc_result_t ret;
isc_region_t r;
size_t keysize;
REQUIRE(opensslecdsa_valid_key_alg(key->key_alg));
REQUIRE(key->keydata.pkeypair.pub != NULL);
keysize = opensslecdsa_key_alg_to_publickey_size(key->key_alg);
isc_buffer_availableregion(data, &r);
if (r.length < keysize) {
DST_RET(ISC_R_NOSPACE);
}
if (!opensslecdsa_extract_public_key(key, r.base, keysize)) {
DST_RET(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
}
isc_buffer_add(data, keysize);
ret = ISC_R_SUCCESS;
err:
return (ret);
}
static isc_result_t
opensslecdsa_fromdns(dst_key_t *key, isc_buffer_t *data) {
isc_result_t ret;
EVP_PKEY *pkey = NULL;
isc_region_t r;
size_t len;
REQUIRE(opensslecdsa_valid_key_alg(key->key_alg));
len = opensslecdsa_key_alg_to_publickey_size(key->key_alg);
isc_buffer_remainingregion(data, &r);
if (r.length == 0) {
DST_RET(ISC_R_SUCCESS);
}
if (r.length != len) {
DST_RET(DST_R_INVALIDPUBLICKEY);
}
ret = opensslecdsa_create_pkey(key->key_alg, false, r.base, len, &pkey);
if (ret != ISC_R_SUCCESS) {
DST_RET(ret);
}
isc_buffer_forward(data, len);
key->key_size = EVP_PKEY_bits(pkey);
key->keydata.pkeypair.pub = pkey;
ret = ISC_R_SUCCESS;
err:
return (ret);
}
static isc_result_t
opensslecdsa_tofile(const dst_key_t *key, const char *directory) {
isc_result_t ret;
dst_private_t priv;
unsigned char buf[MAX_PRIVKEY_SIZE];
size_t keylen = 0;
unsigned short i;
if (key->keydata.pkeypair.pub == NULL) {
DST_RET(DST_R_NULLKEY);
}
if (key->external) {
priv.nelements = 0;
DST_RET(dst__privstruct_writefile(key, &priv, directory));
}
if (key->keydata.pkeypair.priv == NULL) {
DST_RET(DST_R_NULLKEY);
}
keylen = opensslecdsa_key_alg_to_publickey_size(key->key_alg) / 2;
INSIST(keylen <= sizeof(buf));
i = 0;
if (opensslecdsa_extract_private_key(key, buf, keylen)) {
priv.elements[i].tag = TAG_ECDSA_PRIVATEKEY;
priv.elements[i].length = keylen;
priv.elements[i].data = buf;
i++;
}
if (key->label != NULL) {
priv.elements[i].tag = TAG_ECDSA_LABEL;
priv.elements[i].length = (unsigned short)strlen(key->label) +
1;
priv.elements[i].data = (unsigned char *)key->label;
i++;
}
priv.nelements = i;
ret = dst__privstruct_writefile(key, &priv, directory);
err:
isc_safe_memwipe(buf, keylen);
return (ret);
}
static isc_result_t
opensslecdsa_fromlabel(dst_key_t *key, const char *label, const char *pin);
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 ret;
EVP_PKEY *pkey = NULL;
const char *label = NULL;
int i, privkey_index = -1;
REQUIRE(opensslecdsa_valid_key_alg(key->key_alg));
/* read private key file */
ret = dst__privstruct_parse(key, DST_ALG_ECDSA256, lexer, key->mctx,
&priv);
if (ret != ISC_R_SUCCESS) {
goto err;
}
if (key->external) {
if (priv.nelements != 0 || pub == NULL) {
DST_RET(dst__openssl_toresult(DST_R_INVALIDPRIVATEKEY));
}
key->keydata.pkeypair.priv = pub->keydata.pkeypair.priv;
key->keydata.pkeypair.pub = pub->keydata.pkeypair.pub;
pub->keydata.pkeypair.priv = NULL;
pub->keydata.pkeypair.pub = NULL;
DST_RET(ISC_R_SUCCESS);
}
for (i = 0; i < priv.nelements; i++) {
switch (priv.elements[i].tag) {
case TAG_ECDSA_ENGINE:
/* The Engine: tag is explicitly ignored */
break;
case TAG_ECDSA_LABEL:
label = (char *)priv.elements[i].data;
break;
case TAG_ECDSA_PRIVATEKEY:
privkey_index = i;
break;
default:
break;
}
}
if (label != NULL) {
ret = opensslecdsa_fromlabel(key, label, NULL);
if (ret != ISC_R_SUCCESS) {
goto err;
}
/* Check that the public component matches if given */
if (pub != NULL && EVP_PKEY_eq(key->keydata.pkeypair.pub,
pub->keydata.pkeypair.pub) != 1)
{
DST_RET(DST_R_INVALIDPRIVATEKEY);
}
DST_RET(ISC_R_SUCCESS);
}
if (privkey_index < 0) {
DST_RET(dst__openssl_toresult(DST_R_INVALIDPRIVATEKEY));
}
ret = opensslecdsa_create_pkey(
key->key_alg, true, priv.elements[privkey_index].data,
priv.elements[privkey_index].length, &pkey);
if (ret != ISC_R_SUCCESS) {
goto err;
}
/* Check that the public component matches if given */
if (pub != NULL && EVP_PKEY_eq(pkey, pub->keydata.pkeypair.pub) != 1) {
DST_RET(DST_R_INVALIDPRIVATEKEY);
}
key->key_size = EVP_PKEY_bits(pkey);
key->keydata.pkeypair.priv = pkey;
key->keydata.pkeypair.pub = pkey;
pkey = NULL;
err:
EVP_PKEY_free(pkey);
if (ret != ISC_R_SUCCESS) {
key->keydata.generic = NULL;
}
dst__privstruct_free(&priv, key->mctx);
isc_safe_memwipe(&priv, sizeof(priv));
return (ret);
}
static isc_result_t
opensslecdsa_fromlabel(dst_key_t *key, const char *label, const char *pin) {
EVP_PKEY *privpkey = NULL, *pubpkey = NULL;
isc_result_t ret;
REQUIRE(opensslecdsa_valid_key_alg(key->key_alg));
UNUSED(pin);
ret = dst__openssl_fromlabel(EVP_PKEY_EC, label, pin, &pubpkey,
&privpkey);
if (ret != ISC_R_SUCCESS) {
goto err;
}
ret = opensslecdsa_validate_pkey_group(key->key_alg, privpkey);
if (ret != ISC_R_SUCCESS) {
goto err;
}
ret = opensslecdsa_validate_pkey_group(key->key_alg, pubpkey);
if (ret != ISC_R_SUCCESS) {
goto err;
}
key->label = isc_mem_strdup(key->mctx, label);
key->key_size = EVP_PKEY_bits(privpkey);
key->keydata.pkeypair.priv = privpkey;
key->keydata.pkeypair.pub = pubpkey;
privpkey = NULL;
pubpkey = NULL;
err:
EVP_PKEY_free(privpkey);
EVP_PKEY_free(pubpkey);
return (ret);
}
static dst_func_t opensslecdsa_functions = {
opensslecdsa_createctx,
NULL, /*%< createctx2 */
opensslecdsa_destroyctx,
opensslecdsa_adddata,
opensslecdsa_sign,
opensslecdsa_verify,
NULL, /*%< verify2 */
NULL, /*%< computesecret */
dst__openssl_keypair_compare,
NULL, /*%< paramcompare */
opensslecdsa_generate,
dst__openssl_keypair_isprivate,
dst__openssl_keypair_destroy,
opensslecdsa_todns,
opensslecdsa_fromdns,
opensslecdsa_tofile,
opensslecdsa_parse,
NULL, /*%< cleanup */
opensslecdsa_fromlabel, /*%< fromlabel */
NULL, /*%< dump */
NULL, /*%< restore */
};
isc_result_t
dst__opensslecdsa_init(dst_func_t **funcp) {
REQUIRE(funcp != NULL);
if (*funcp == NULL) {
*funcp = &opensslecdsa_functions;
}
return (ISC_R_SUCCESS);
}