mirror of
https://github.com/isc-projects/bind9.git
synced 2026-07-15 02:20:57 -04:00
previously, there were over 40 separate definitions of CHECK macros, of
which most used "goto cleanup", and the rest "goto failure" or "goto
out". there were another 10 definitions of RETERR, of which most were
identical to CHECK, but some simply returned a result code instead of
jumping to a cleanup label.
this has now been standardized throughout the code base: RETERR is for
returning an error code in the case of an error, and CHECK is for jumping
to a cleanup tag, which is now always called "cleanup". both macros are
defined in isc/util.h.
(cherry picked from commit 52bba5cc34)
1157 lines
29 KiB
C
1157 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) \
|
|
{ \
|
|
result = a; \
|
|
goto cleanup; \
|
|
}
|
|
|
|
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 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) {
|
|
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));
|
|
}
|
|
|
|
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 || 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 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) {
|
|
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;
|
|
|
|
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;
|
|
}
|
|
#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 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;
|
|
}
|
|
#endif
|
|
#if OPENSSL_VERSION_NUMBER < 0x30000000L || OPENSSL_API_LEVEL < 30000
|
|
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) {
|
|
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));
|
|
}
|
|
|
|
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) {
|
|
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, ¶ms_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));
|
|
}
|
|
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) {
|
|
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;
|
|
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;
|
|
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;
|
|
|
|
cleanup:
|
|
return result;
|
|
}
|
|
|
|
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 result = 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));
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
return result;
|
|
}
|
|
|
|
static isc_result_t
|
|
opensslecdsa_sign(dst_context_t *dctx, isc_buffer_t *sig) {
|
|
isc_result_t result;
|
|
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, ®ion);
|
|
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(®ion, siglen / 2);
|
|
BN_bn2bin_fixed(s, region.base, siglen / 2);
|
|
isc_region_consume(®ion, siglen / 2);
|
|
ECDSA_SIG_free(ecdsasig);
|
|
isc_buffer_add(sig, siglen);
|
|
result = ISC_R_SUCCESS;
|
|
|
|
cleanup:
|
|
if (sigder != NULL && sigder_alloced != 0) {
|
|
isc_mem_put(dctx->mctx, sigder, sigder_alloced);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static isc_result_t
|
|
opensslecdsa_verify(dst_context_t *dctx, const isc_region_t *sig) {
|
|
isc_result_t result;
|
|
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:
|
|
result = ISC_R_SUCCESS;
|
|
break;
|
|
case 0:
|
|
result = dst__openssl_toresult(DST_R_VERIFYFAILURE);
|
|
break;
|
|
default:
|
|
result = dst__openssl_toresult3(dctx->category,
|
|
"EVP_DigestVerifyFinal",
|
|
DST_R_VERIFYFAILURE);
|
|
break;
|
|
}
|
|
|
|
cleanup:
|
|
if (ecdsasig != NULL) {
|
|
ECDSA_SIG_free(ecdsasig);
|
|
}
|
|
if (sigder != NULL && sigder_alloced != 0) {
|
|
isc_mem_put(dctx->mctx, sigder, sigder_alloced);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
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 result;
|
|
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);
|
|
result = ISC_R_SUCCESS;
|
|
|
|
cleanup:
|
|
return result;
|
|
}
|
|
|
|
static isc_result_t
|
|
opensslecdsa_fromdns(dst_key_t *key, isc_buffer_t *data) {
|
|
isc_result_t result;
|
|
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);
|
|
}
|
|
|
|
CHECK(opensslecdsa_create_pkey(key->key_alg, false, r.base, len,
|
|
&pkey));
|
|
|
|
isc_buffer_forward(data, len);
|
|
key->key_size = EVP_PKEY_bits(pkey);
|
|
key->keydata.pkeypair.pub = pkey;
|
|
result = ISC_R_SUCCESS;
|
|
|
|
cleanup:
|
|
return result;
|
|
}
|
|
|
|
static isc_result_t
|
|
opensslecdsa_tofile(const dst_key_t *key, const char *directory) {
|
|
isc_result_t result;
|
|
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->engine != NULL) {
|
|
priv.elements[i].tag = TAG_ECDSA_ENGINE;
|
|
priv.elements[i].length = (unsigned short)strlen(key->engine) +
|
|
1;
|
|
priv.elements[i].data = (unsigned char *)key->engine;
|
|
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;
|
|
result = dst__privstruct_writefile(key, &priv, directory);
|
|
|
|
cleanup:
|
|
isc_safe_memwipe(buf, keylen);
|
|
return result;
|
|
}
|
|
|
|
static isc_result_t
|
|
opensslecdsa_fromlabel(dst_key_t *key, const char *engine, 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 result;
|
|
EVP_PKEY *pkey = NULL;
|
|
const char *engine = NULL;
|
|
const char *label = NULL;
|
|
int i, privkey_index = -1;
|
|
|
|
REQUIRE(opensslecdsa_valid_key_alg(key->key_alg));
|
|
|
|
/* read private key file */
|
|
CHECK(dst__privstruct_parse(key, DST_ALG_ECDSA256, lexer, key->mctx,
|
|
&priv));
|
|
|
|
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:
|
|
engine = (char *)priv.elements[i].data;
|
|
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) {
|
|
CHECK(opensslecdsa_fromlabel(key, engine, label, NULL));
|
|
/* 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));
|
|
}
|
|
|
|
CHECK(opensslecdsa_create_pkey(
|
|
key->key_alg, true, priv.elements[privkey_index].data,
|
|
priv.elements[privkey_index].length, &pkey));
|
|
|
|
/* 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;
|
|
|
|
cleanup:
|
|
EVP_PKEY_free(pkey);
|
|
if (result != ISC_R_SUCCESS) {
|
|
key->keydata.generic = NULL;
|
|
}
|
|
dst__privstruct_free(&priv, key->mctx);
|
|
isc_safe_memwipe(&priv, sizeof(priv));
|
|
|
|
return result;
|
|
}
|
|
|
|
static isc_result_t
|
|
opensslecdsa_fromlabel(dst_key_t *key, const char *engine, const char *label,
|
|
const char *pin) {
|
|
EVP_PKEY *privpkey = NULL, *pubpkey = NULL;
|
|
isc_result_t result;
|
|
|
|
REQUIRE(opensslecdsa_valid_key_alg(key->key_alg));
|
|
UNUSED(pin);
|
|
|
|
CHECK(dst__openssl_fromlabel(EVP_PKEY_EC, engine, label, pin, &pubpkey,
|
|
&privpkey));
|
|
|
|
CHECK(opensslecdsa_validate_pkey_group(key->key_alg, privpkey));
|
|
CHECK(opensslecdsa_validate_pkey_group(key->key_alg, pubpkey));
|
|
|
|
if (engine != NULL) {
|
|
key->engine = isc_mem_strdup(key->mctx, engine);
|
|
}
|
|
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;
|
|
|
|
cleanup:
|
|
EVP_PKEY_free(privpkey);
|
|
EVP_PKEY_free(pubpkey);
|
|
return result;
|
|
}
|
|
|
|
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;
|
|
}
|