chg: dev: initial openssl version splitting

Dealing with OpenSSL has been rapidly turning into an unwieldy situation
as post-3.0 changes turn the library into a different beast.

Start treating pre and post-3.0 versions differently for easier
maintenance.

To help with this Sisyphean task, this MR had to shift things around.

`OPENSSL_NO_DEPRECATED` is now declared in BIND alongside an appropriate
`OPENSSL_API_COMPAT` value. The former value will set to declare either
OpenSSL 1.1.0 or 3.0 as the bare minimum version.

Instead of splitting `md.c` and `hmac.c` into separate version-specific
files, they now live inside `crypto/ossl1_1.c` and `crypto/ossl3.c`.
This way, these functions will be able to utilize the same static
`OSSL_PARAM` tables, removing redundant reconstruction for HMAC.

For pre-3.0, `isc_hmac` has been reverted back to using the `HMAC_`
interface. Using `EVP_MD_CTX`-based functions for HMAC will end up
libcrypto calling the same `HMAC_` functions in the end, giving no
advantage while confusingly using the digest functions.

A new API, `isc_ossl_wrap` has been added. This family of functions
aim to provide a common interface for libcrypto version specific code
while not abstracting away OpenSSL's structures such as `EVP_PKEY`.

Currently the main user of this API is the `dst` family of functions
where some ECDSA and RSA opeations need to use the new `OSSL_PARAM`
functionality by requirement or to avoid speed penalties.

Furthermore OpenSSL based logging has been moved from `isc_tls` to
`isc_ossl_wrap` as its a more appropriate place for such functionality.

Merge branch 'aydin/openssl-version-split' into 'main'

See merge request isc-projects/bind9!11094
This commit is contained in:
Aydın Mercan 2026-02-02 12:43:48 +03:00
commit fe9fee63c6
35 changed files with 2994 additions and 1944 deletions

View file

@ -1389,15 +1389,13 @@ gcc:trixie:amd64cross32:
<<: *debian_trixie_amd64cross32_image
<<: *build_job
# Jobs for strict OpenSSL 3.x (no deprecated) GCC builds on Debian "trixie" (amd64)
# Run with pkcs11-provider tests
gcc:ossl3:trixie:amd64:
<<: *debian_trixie_amd64_image
<<: *build_job
variables:
CC: gcc
CFLAGS: "${CFLAGS_COMMON} -DOPENSSL_NO_DEPRECATED=1 -DOPENSSL_API_COMPAT=30000"
CFLAGS: "${CFLAGS_COMMON}"
# See https://gitlab.isc.org/isc-projects/bind9/-/issues/3444
EXTRA_CONFIGURE: "-Doptimization=3 -Djemalloc=disabled -Dleak-detection=disabled"
RUN_MESON_INSTALL: 1

View file

@ -40,6 +40,7 @@
#include <isc/attributes.h>
#include <isc/base32.h>
#include <isc/commandline.h>
#include <isc/crypto.h>
#include <isc/dir.h>
#include <isc/file.h>
#include <isc/hash.h>

View file

@ -32,6 +32,7 @@
#include <isc/attributes.h>
#include <isc/base64.h>
#include <isc/commandline.h>
#include <isc/crypto.h>
#include <isc/dir.h>
#include <isc/file.h>
#include <isc/hash.h>

View file

@ -1859,7 +1859,7 @@ dns_catz_generate_masterfilename(dns_catz_zone_t *catz, dns_catz_entry_t *entry,
isc_buffer_subtract(tbuf, 1);
/* __catz__<digest>.db */
rlen = (isc_md_type_get_size(ISC_MD_SHA256) * 2 + 1) + 12;
rlen = (ISC_SHA256_DIGESTLENGTH * 2 + 1) + 12;
/* optionally prepend with <zonedir>/ */
if (entry->opts.zonedir != NULL) {

View file

@ -41,7 +41,7 @@ dns_ds_fromkeyrdata(const dns_name_t *owner, dns_rdata_t *key,
unsigned int privatelen = 0;
isc_region_t r;
isc_md_t *md;
const isc_md_type_t *md_type = NULL;
isc_md_type_t md_type = ISC_MD_UNKNOWN;
REQUIRE(key != NULL);
REQUIRE(key->type == dns_rdatatype_dnskey ||

View file

@ -1321,22 +1321,22 @@ dst_key_sigsize(const dst_key_t *key, unsigned int *n) {
*n = DNS_SIG_ED448SIZE;
break;
case DST_ALG_HMACMD5:
*n = isc_md_type_get_size(ISC_MD_MD5);
*n = ISC_MD5_DIGESTLENGTH;
break;
case DST_ALG_HMACSHA1:
*n = isc_md_type_get_size(ISC_MD_SHA1);
*n = ISC_SHA1_DIGESTLENGTH;
break;
case DST_ALG_HMACSHA224:
*n = isc_md_type_get_size(ISC_MD_SHA224);
*n = ISC_SHA224_DIGESTLENGTH;
break;
case DST_ALG_HMACSHA256:
*n = isc_md_type_get_size(ISC_MD_SHA256);
*n = ISC_SHA256_DIGESTLENGTH;
break;
case DST_ALG_HMACSHA384:
*n = isc_md_type_get_size(ISC_MD_SHA384);
*n = ISC_SHA384_DIGESTLENGTH;
break;
case DST_ALG_HMACSHA512:
*n = isc_md_type_get_size(ISC_MD_SHA512);
*n = ISC_SHA512_DIGESTLENGTH;
break;
case DST_ALG_GSSAPI:
*n = 128; /*%< XXX */

View file

@ -62,8 +62,6 @@
typedef struct dst_func dst_func_t;
typedef struct dst_hmac_key dst_hmac_key_t;
/*%
* Indicate whether a DST context will be used for signing
* or for verification
@ -93,7 +91,7 @@ struct dst_key {
union {
void *generic;
dns_gss_ctx_id_t gssctx;
dst_hmac_key_t *hmac_key;
isc_hmac_key_t *hmac_key;
struct {
EVP_PKEY *pub;
EVP_PKEY *priv;

View file

@ -21,18 +21,20 @@
#include <openssl/rand.h>
#include <isc/log.h>
#include <isc/ossl_wrap.h>
#include <isc/result.h>
#include <isc/tls.h>
#define dst__openssl_toresult(fallback) \
isc__tlserr2result(ISC_LOGCATEGORY_INVALID, ISC_LOGMODULE_INVALID, \
NULL, fallback, __FILE__, __LINE__)
#define dst__openssl_toresult2(funcname, fallback) \
isc__tlserr2result(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_CRYPTO, \
funcname, fallback, __FILE__, __LINE__)
#define dst__openssl_toresult3(category, funcname, fallback) \
isc__tlserr2result(category, DNS_LOGMODULE_CRYPTO, funcname, fallback, \
__FILE__, __LINE__)
#define dst__openssl_toresult(fallback) \
isc__ossl_wrap_logged_toresult(ISC_LOGCATEGORY_INVALID, \
ISC_LOGMODULE_INVALID, NULL, fallback, \
__FILE__, __LINE__)
#define dst__openssl_toresult2(funcname, fallback) \
isc__ossl_wrap_logged_toresult(DNS_LOGCATEGORY_GENERAL, \
DNS_LOGMODULE_CRYPTO, funcname, \
fallback, __FILE__, __LINE__)
#define dst__openssl_toresult3(category, funcname, fallback) \
isc__ossl_wrap_logged_toresult(category, DNS_LOGMODULE_CRYPTO, \
funcname, fallback, __FILE__, __LINE__)
isc_result_t
dst__openssl_fromlabel(int key_base_id, const char *label, const char *pin,

View file

@ -55,7 +55,7 @@
#define hmac_register_algorithm(alg) \
static isc_result_t hmac##alg##_createctx(dst_key_t *key, \
dst_context_t *dctx) { \
return (hmac_createctx(ISC_MD_##alg, key, dctx)); \
return (hmac_createctx(key, dctx)); \
} \
static void hmac##alg##_destroyctx(dst_context_t *dctx) { \
hmac_destroyctx(dctx); \
@ -74,7 +74,7 @@
} \
static bool hmac##alg##_compare(const dst_key_t *key1, \
const dst_key_t *key2) { \
return (hmac_compare(ISC_MD_##alg, key1, key2)); \
return (hmac_compare(key1, key2)); \
} \
static isc_result_t hmac##alg##_generate( \
dst_key_t *key, int pseudorandom_ok, void (*callback)(int)) { \
@ -130,22 +130,19 @@
void dst__hmac##alg##_init(dst_func_t **funcp) { \
REQUIRE(funcp != NULL); \
if (*funcp == NULL) { \
isc_hmac_t *ctx = isc_hmac_new(); \
if (isc_hmac_init(ctx, "test", 4, ISC_MD_##alg) == \
ISC_R_SUCCESS) \
uint8_t data[] = "data"; \
uint8_t mac_buffer[ISC_MAX_MD_SIZE]; \
unsigned int mac_len = sizeof(mac_buffer); \
if (isc_hmac(ISC_MD_##alg, "test", 4, data, 4, \
mac_buffer, &mac_len) == ISC_R_SUCCESS) \
{ \
*funcp = &hmac##alg##_functions; \
} \
isc_hmac_free(ctx); \
} \
}
static isc_result_t
hmac_fromdns(const isc_md_type_t *type, dst_key_t *key, isc_buffer_t *data);
struct dst_hmac_key {
uint8_t key[ISC_MAX_BLOCK_SIZE];
};
hmac_fromdns(isc_md_type_t type, dst_key_t *key, isc_buffer_t *data);
static isc_result_t
getkeybits(dst_key_t *key, struct dst_private_element *element) {
@ -161,14 +158,11 @@ getkeybits(dst_key_t *key, struct dst_private_element *element) {
}
static isc_result_t
hmac_createctx(const isc_md_type_t *type, const dst_key_t *key,
dst_context_t *dctx) {
hmac_createctx(const dst_key_t *key, dst_context_t *dctx) {
isc_result_t result;
const dst_hmac_key_t *hkey = key->keydata.hmac_key;
isc_hmac_t *ctx = isc_hmac_new(); /* Either returns or abort()s */
result = isc_hmac_init(ctx, hkey->key, isc_md_type_get_block_size(type),
type);
result = isc_hmac_init(ctx, key->keydata.hmac_key);
if (result != ISC_R_SUCCESS) {
isc_hmac_free(ctx);
return DST_R_UNSUPPORTEDALG;
@ -205,44 +199,27 @@ hmac_adddata(const dst_context_t *dctx, const isc_region_t *data) {
static isc_result_t
hmac_sign(const dst_context_t *dctx, isc_buffer_t *sig) {
isc_hmac_t *ctx = dctx->ctxdata.hmac_ctx;
REQUIRE(ctx != NULL);
unsigned char digest[ISC_MAX_MD_SIZE];
unsigned int digestlen = sizeof(digest);
if (isc_hmac_final(ctx, digest, &digestlen) != ISC_R_SUCCESS) {
return DST_R_OPENSSLFAILURE;
}
if (isc_hmac_reset(ctx) != ISC_R_SUCCESS) {
return DST_R_OPENSSLFAILURE;
}
if (isc_buffer_availablelength(sig) < digestlen) {
return ISC_R_NOSPACE;
}
isc_buffer_putmem(sig, digest, digestlen);
return ISC_R_SUCCESS;
return isc_hmac_final(ctx, sig);
}
static isc_result_t
hmac_verify(const dst_context_t *dctx, const isc_region_t *sig) {
isc_hmac_t *ctx = dctx->ctxdata.hmac_ctx;
unsigned char digest[ISC_MAX_MD_SIZE];
unsigned int digestlen = sizeof(digest);
isc_buffer_t hmac;
REQUIRE(ctx != NULL);
if (isc_hmac_final(ctx, digest, &digestlen) != ISC_R_SUCCESS) {
isc_buffer_init(&hmac, digest, sizeof(digest));
if (isc_hmac_final(ctx, &hmac) != ISC_R_SUCCESS) {
return DST_R_OPENSSLFAILURE;
}
if (isc_hmac_reset(ctx) != ISC_R_SUCCESS) {
return DST_R_OPENSSLFAILURE;
}
if (sig->length > digestlen) {
if (sig->length > isc_buffer_usedlength(&hmac)) {
return DST_R_VERIFYFAILURE;
}
@ -252,9 +229,8 @@ hmac_verify(const dst_context_t *dctx, const isc_region_t *sig) {
}
static bool
hmac_compare(const isc_md_type_t *type, const dst_key_t *key1,
const dst_key_t *key2) {
dst_hmac_key_t *hkey1, *hkey2;
hmac_compare(const dst_key_t *key1, const dst_key_t *key2) {
isc_hmac_key_t *hkey1, *hkey2;
hkey1 = key1->keydata.hmac_key;
hkey2 = key2->keydata.hmac_key;
@ -265,12 +241,11 @@ hmac_compare(const isc_md_type_t *type, const dst_key_t *key1,
return false;
}
return isc_safe_memequal(hkey1->key, hkey2->key,
isc_md_type_get_block_size(type));
return isc_hmac_key_equal(hkey1, hkey2);
}
static isc_result_t
hmac_generate(const isc_md_type_t *type, dst_key_t *key) {
hmac_generate(isc_md_type_t type, dst_key_t *key) {
isc_buffer_t b;
isc_result_t result;
unsigned int bytes, len;
@ -305,31 +280,28 @@ hmac_isprivate(const dst_key_t *key) {
static void
hmac_destroy(dst_key_t *key) {
dst_hmac_key_t *hkey = key->keydata.hmac_key;
isc_safe_memwipe(hkey, sizeof(*hkey));
isc_mem_put(key->mctx, hkey, sizeof(*hkey));
key->keydata.hmac_key = NULL;
isc_hmac_key_destroy(&key->keydata.hmac_key);
}
static isc_result_t
hmac_todns(const dst_key_t *key, isc_buffer_t *data) {
REQUIRE(key != NULL && key->keydata.hmac_key != NULL);
dst_hmac_key_t *hkey = key->keydata.hmac_key;
unsigned int bytes;
isc_region_t raw_key;
bytes = (key->key_size + 7) / 8;
if (isc_buffer_availablelength(data) < bytes) {
REQUIRE(key != NULL && key->keydata.hmac_key != NULL);
raw_key = isc_hmac_key_expose(key->keydata.hmac_key);
if (isc_buffer_availablelength(data) < raw_key.length) {
return ISC_R_NOSPACE;
}
isc_buffer_putmem(data, hkey->key, bytes);
return ISC_R_SUCCESS;
return isc_buffer_copyregion(data, &raw_key);
}
static isc_result_t
hmac_fromdns(const isc_md_type_t *type, dst_key_t *key, isc_buffer_t *data) {
dst_hmac_key_t *hkey;
unsigned int keylen;
hmac_fromdns(isc_md_type_t type, dst_key_t *key, isc_buffer_t *data) {
isc_hmac_key_t *hkey = NULL;
isc_result_t result;
isc_region_t r;
isc_buffer_remainingregion(data, &r);
@ -337,24 +309,12 @@ hmac_fromdns(const isc_md_type_t *type, dst_key_t *key, isc_buffer_t *data) {
return ISC_R_SUCCESS;
}
hkey = isc_mem_get(key->mctx, sizeof(dst_hmac_key_t));
memset(hkey->key, 0, sizeof(hkey->key));
/* Hash the key if the key is longer then chosen MD block size */
if (r.length > (unsigned int)isc_md_type_get_block_size(type)) {
if (isc_md(type, r.base, r.length, hkey->key, &keylen) !=
ISC_R_SUCCESS)
{
isc_mem_put(key->mctx, hkey, sizeof(dst_hmac_key_t));
return DST_R_OPENSSLFAILURE;
}
} else {
memmove(hkey->key, r.base, r.length);
keylen = r.length;
result = isc_hmac_key_create(type, r.base, r.length, key->mctx, &hkey);
if (result != ISC_R_SUCCESS) {
return DST_R_OPENSSLFAILURE;
}
key->key_size = keylen * 8;
key->key_size = isc_hmac_key_expose(hkey).length * 8;
key->keydata.hmac_key = hkey;
isc_buffer_forward(data, r.length);
@ -363,49 +323,49 @@ hmac_fromdns(const isc_md_type_t *type, dst_key_t *key, isc_buffer_t *data) {
}
static int
hmac__get_tag_key(const isc_md_type_t *type) {
if (type == ISC_MD_MD5) {
hmac__get_tag_key(isc_md_type_t type) {
switch (type) {
case ISC_MD_MD5:
return TAG_HMACMD5_KEY;
} else if (type == ISC_MD_SHA1) {
case ISC_MD_SHA1:
return TAG_HMACSHA1_KEY;
} else if (type == ISC_MD_SHA224) {
case ISC_MD_SHA224:
return TAG_HMACSHA224_KEY;
} else if (type == ISC_MD_SHA256) {
case ISC_MD_SHA256:
return TAG_HMACSHA256_KEY;
} else if (type == ISC_MD_SHA384) {
case ISC_MD_SHA384:
return TAG_HMACSHA384_KEY;
} else if (type == ISC_MD_SHA512) {
case ISC_MD_SHA512:
return TAG_HMACSHA512_KEY;
} else {
default:
UNREACHABLE();
}
}
static int
hmac__get_tag_bits(const isc_md_type_t *type) {
if (type == ISC_MD_MD5) {
hmac__get_tag_bits(isc_md_type_t type) {
switch (type) {
case ISC_MD_MD5:
return TAG_HMACMD5_BITS;
} else if (type == ISC_MD_SHA1) {
case ISC_MD_SHA1:
return TAG_HMACSHA1_BITS;
} else if (type == ISC_MD_SHA224) {
case ISC_MD_SHA224:
return TAG_HMACSHA224_BITS;
} else if (type == ISC_MD_SHA256) {
case ISC_MD_SHA256:
return TAG_HMACSHA256_BITS;
} else if (type == ISC_MD_SHA384) {
case ISC_MD_SHA384:
return TAG_HMACSHA384_BITS;
} else if (type == ISC_MD_SHA512) {
case ISC_MD_SHA512:
return TAG_HMACSHA512_BITS;
} else {
default:
UNREACHABLE();
}
}
static isc_result_t
hmac_tofile(const isc_md_type_t *type, const dst_key_t *key,
const char *directory) {
dst_hmac_key_t *hkey;
hmac_tofile(isc_md_type_t type, const dst_key_t *key, const char *directory) {
isc_region_t raw_key;
dst_private_t priv;
int bytes = (key->key_size + 7) / 8;
uint16_t bits;
if (key->keydata.hmac_key == NULL) {
@ -416,11 +376,11 @@ hmac_tofile(const isc_md_type_t *type, const dst_key_t *key,
return DST_R_EXTERNALKEY;
}
hkey = key->keydata.hmac_key;
raw_key = isc_hmac_key_expose(key->keydata.hmac_key);
priv.elements[0].tag = hmac__get_tag_key(type);
priv.elements[0].length = bytes;
priv.elements[0].data = hkey->key;
priv.elements[0].length = raw_key.length;
priv.elements[0].data = raw_key.base;
bits = htons(key->key_bits);
@ -434,7 +394,7 @@ hmac_tofile(const isc_md_type_t *type, const dst_key_t *key,
}
static int
hmac__to_dst_alg(const isc_md_type_t *type) {
hmac__to_dst_alg(isc_md_type_t type) {
if (type == ISC_MD_MD5) {
return DST_ALG_HMACMD5;
} else if (type == ISC_MD_SHA1) {
@ -453,7 +413,7 @@ hmac__to_dst_alg(const isc_md_type_t *type) {
}
static isc_result_t
hmac_parse(const isc_md_type_t *type, dst_key_t *key, isc_lex_t *lexer,
hmac_parse(isc_md_type_t type, dst_key_t *key, isc_lex_t *lexer,
dst_key_t *pub) {
dst_private_t priv;
isc_result_t result = ISC_R_SUCCESS, tresult;

View file

@ -17,16 +17,13 @@
#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>
#include <isc/mem.h>
#include <isc/ossl_wrap.h>
#include <isc/result.h>
#include <isc/safe.h>
#include <isc/string.h>
@ -50,37 +47,6 @@
#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) {
@ -92,18 +58,6 @@ opensslecdsa_valid_key_alg(unsigned int key_alg) {
}
}
static int
opensslecdsa_key_alg_to_group_nid(unsigned int key_alg) {
switch (key_alg) {
case DST_ALG_ECDSA256:
return NID_X9_62_prime256v1;
case DST_ALG_ECDSA384:
return NID_secp384r1;
default:
UNREACHABLE();
}
}
static size_t
opensslecdsa_key_alg_to_publickey_size(unsigned int key_alg) {
switch (key_alg) {
@ -116,23 +70,6 @@ opensslecdsa_key_alg_to_publickey_size(unsigned int key_alg) {
}
}
/*
* OpenSSL requires us to set the public key portion, but since our private key
* file format does not contain it directly, we generate it as needed.
*/
static EC_POINT *
opensslecdsa_generate_public_key(const EC_GROUP *group, const BIGNUM *privkey) {
EC_POINT *pubkey = EC_POINT_new(group);
if (pubkey == NULL) {
return NULL;
}
if (EC_POINT_mul(group, pubkey, privkey, NULL, NULL, NULL) != 1) {
EC_POINT_free(pubkey);
return NULL;
}
return pubkey;
}
static int
BN_bn2bin_fixed(const BIGNUM *bn, unsigned char *buf, int size) {
int bytes = size - BN_num_bytes(bn);
@ -146,534 +83,13 @@ BN_bn2bin_fixed(const BIGNUM *bn, unsigned char *buf, int size) {
return size;
}
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
static const char *
opensslecdsa_key_alg_to_group_name(unsigned int key_alg) {
switch (key_alg) {
case DST_ALG_ECDSA256:
return "prime256v1";
case DST_ALG_ECDSA384:
return "secp384r1";
default:
UNREACHABLE();
}
}
static isc_result_t
opensslecdsa_create_pkey_params(unsigned int key_alg, bool private,
const unsigned char *key, size_t key_len,
EVP_PKEY **pkey) {
isc_result_t result;
int status;
int group_nid = opensslecdsa_key_alg_to_group_nid(key_alg);
const char *groupname = opensslecdsa_key_alg_to_group_name(key_alg);
OSSL_PARAM_BLD *bld = NULL;
OSSL_PARAM *params = NULL;
EVP_PKEY_CTX *ctx = NULL;
EC_POINT *pubkey = NULL;
EC_GROUP *group = NULL;
BIGNUM *priv = NULL;
unsigned char buf[MAX_PUBKEY_SIZE + 1];
bld = OSSL_PARAM_BLD_new();
if (bld == NULL) {
CLEANUP(dst__openssl_toresult2("OSSL_PARAM_BLD_new",
DST_R_OPENSSLFAILURE));
}
status = OSSL_PARAM_BLD_push_utf8_string(
bld, OSSL_PKEY_PARAM_GROUP_NAME, groupname, 0);
if (status != 1) {
CLEANUP(dst__openssl_toresult2("OSSL_PARAM_BLD_push_"
"utf8_string",
DST_R_OPENSSLFAILURE));
}
if (private) {
group = EC_GROUP_new_by_curve_name(group_nid);
if (group == NULL) {
CLEANUP(dst__openssl_toresult2("EC_GROUP_new_by_"
"curve_name",
DST_R_OPENSSLFAILURE));
}
priv = BN_bin2bn(key, key_len, NULL);
if (priv == NULL) {
CLEANUP(dst__openssl_toresult2("BN_bin2bn",
DST_R_OPENSSLFAILURE));
}
status = OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY,
priv);
if (status != 1) {
CLEANUP(dst__openssl_toresult2("OSSL_PARAM_BLD_push_BN",
DST_R_OPENSSLFAILURE));
}
pubkey = opensslecdsa_generate_public_key(group, priv);
if (pubkey == NULL) {
CLEANUP(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
}
key = buf;
key_len = EC_POINT_point2oct(group, pubkey,
POINT_CONVERSION_UNCOMPRESSED, buf,
sizeof(buf), NULL);
if (key_len == 0) {
CLEANUP(dst__openssl_toresult2("EC_POINT_point2oct",
DST_R_OPENSSLFAILURE));
}
} else {
INSIST(key_len + 1 <= sizeof(buf));
buf[0] = POINT_CONVERSION_UNCOMPRESSED;
memmove(buf + 1, key, key_len);
key = buf;
key_len = key_len + 1;
}
status = OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY,
key, key_len);
if (status != 1) {
CLEANUP(dst__openssl_toresult2("OSSL_PARAM_BLD_push_"
"octet_string",
DST_R_OPENSSLFAILURE));
}
params = OSSL_PARAM_BLD_to_param(bld);
if (params == NULL) {
CLEANUP(dst__openssl_toresult2("OSSL_PARAM_BLD_to_param",
DST_R_OPENSSLFAILURE));
}
ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
if (ctx == NULL) {
CLEANUP(dst__openssl_toresult2("EVP_PKEY_CTX_new_from_name",
DST_R_OPENSSLFAILURE));
}
status = EVP_PKEY_fromdata_init(ctx);
if (status != 1) {
/* This will fail if the default provider is an engine.
* Return ISC_R_FAILURE to retry using the legacy API. */
CLEANUP(dst__openssl_toresult(ISC_R_FAILURE));
}
status = EVP_PKEY_fromdata(
ctx, pkey, private ? EVP_PKEY_KEYPAIR : EVP_PKEY_PUBLIC_KEY,
params);
if (status != 1 || *pkey == NULL) {
CLEANUP(dst__openssl_toresult2("EVP_PKEY_fromdata",
DST_R_OPENSSLFAILURE));
}
result = ISC_R_SUCCESS;
cleanup:
OSSL_PARAM_free(params);
OSSL_PARAM_BLD_free(bld);
EVP_PKEY_CTX_free(ctx);
BN_clear_free(priv);
EC_POINT_free(pubkey);
EC_GROUP_free(group);
return result;
}
static bool
opensslecdsa_extract_public_key_params(const dst_key_t *key, unsigned char *dst,
size_t dstlen) {
EVP_PKEY *pkey = key->keydata.pkeypair.pub;
BIGNUM *x = NULL;
BIGNUM *y = NULL;
bool ret = false;
if (EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_X, &x) == 1 &&
EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_Y, &y) == 1)
{
BN_bn2bin_fixed(x, &dst[0], dstlen / 2);
BN_bn2bin_fixed(y, &dst[dstlen / 2], dstlen / 2);
ret = true;
}
BN_clear_free(x);
BN_clear_free(y);
return ret;
}
#endif
#if OPENSSL_VERSION_NUMBER < 0x30000000L
static isc_result_t
opensslecdsa_create_pkey_legacy(unsigned int key_alg, bool private,
const unsigned char *key, size_t key_len,
EVP_PKEY **retkey) {
isc_result_t result = ISC_R_SUCCESS;
EC_KEY *eckey = NULL;
EVP_PKEY *pkey = NULL;
BIGNUM *privkey = NULL;
EC_POINT *pubkey = NULL;
unsigned char buf[MAX_PUBKEY_SIZE + 1];
int group_nid = opensslecdsa_key_alg_to_group_nid(key_alg);
eckey = EC_KEY_new_by_curve_name(group_nid);
if (eckey == NULL) {
CLEANUP(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
}
if (private) {
const EC_GROUP *group = EC_KEY_get0_group(eckey);
privkey = BN_bin2bn(key, key_len, NULL);
if (privkey == NULL) {
CLEANUP(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
}
if (!EC_KEY_set_private_key(eckey, privkey)) {
CLEANUP(dst__openssl_toresult(DST_R_INVALIDPRIVATEKEY));
}
pubkey = opensslecdsa_generate_public_key(group, privkey);
if (pubkey == NULL) {
CLEANUP(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
}
if (EC_KEY_set_public_key(eckey, pubkey) != 1) {
CLEANUP(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
}
} else {
const unsigned char *cp = buf;
INSIST(key_len + 1 <= sizeof(buf));
buf[0] = POINT_CONVERSION_UNCOMPRESSED;
memmove(buf + 1, key, key_len);
if (o2i_ECPublicKey(&eckey, &cp, key_len + 1) == NULL) {
CLEANUP(dst__openssl_toresult(DST_R_INVALIDPUBLICKEY));
}
if (EC_KEY_check_key(eckey) != 1) {
CLEANUP(dst__openssl_toresult(DST_R_INVALIDPUBLICKEY));
}
}
pkey = EVP_PKEY_new();
if (pkey == NULL) {
CLEANUP(dst__openssl_toresult(ISC_R_NOMEMORY));
}
if (!EVP_PKEY_set1_EC_KEY(pkey, eckey)) {
CLEANUP(dst__openssl_toresult(ISC_R_FAILURE));
}
*retkey = pkey;
pkey = NULL;
cleanup:
BN_clear_free(privkey);
EC_POINT_free(pubkey);
EC_KEY_free(eckey);
EVP_PKEY_free(pkey);
return result;
}
static bool
opensslecdsa_extract_public_key_legacy(const dst_key_t *key, unsigned char *dst,
size_t dstlen) {
EVP_PKEY *pkey = key->keydata.pkeypair.pub;
const EC_KEY *eckey = EVP_PKEY_get0_EC_KEY(pkey);
const EC_GROUP *group = (eckey == NULL) ? NULL
: EC_KEY_get0_group(eckey);
const EC_POINT *pub = (eckey == NULL) ? NULL
: EC_KEY_get0_public_key(eckey);
unsigned char buf[MAX_PUBKEY_SIZE + 1];
size_t len;
if (group == NULL || pub == NULL) {
return false;
}
len = EC_POINT_point2oct(group, pub, POINT_CONVERSION_UNCOMPRESSED, buf,
sizeof(buf), NULL);
if (len == dstlen + 1) {
memmove(dst, buf + 1, dstlen);
return true;
}
return false;
}
#endif
static bool
opensslecdsa_extract_public_key(const dst_key_t *key, unsigned char *dst,
size_t dstlen) {
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
if (opensslecdsa_extract_public_key_params(key, dst, dstlen)) {
return true;
}
#else
if (opensslecdsa_extract_public_key_legacy(key, dst, dstlen)) {
return true;
}
#endif
return false;
}
static isc_result_t
opensslecdsa_create_pkey(unsigned int key_alg, bool private,
const unsigned char *key, size_t key_len,
EVP_PKEY **retkey) {
isc_result_t result;
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
result = opensslecdsa_create_pkey_params(key_alg, private, key, key_len,
retkey);
if (result != ISC_R_FAILURE) {
return result;
}
#else
result = opensslecdsa_create_pkey_legacy(key_alg, private, key, key_len,
retkey);
if (result == ISC_R_SUCCESS) {
return result;
}
#endif
return DST_R_OPENSSLFAILURE;
}
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
static isc_result_t
opensslecdsa_generate_pkey_with_uri(int group_nid, const char *label,
EVP_PKEY **retkey) {
int status;
isc_result_t result;
char *uri = UNCONST(label);
EVP_PKEY_CTX *ctx = NULL;
OSSL_PARAM params[3];
/* Generate the key's parameters. */
params[0] = OSSL_PARAM_construct_utf8_string("pkcs11_uri", uri, 0);
params[1] = OSSL_PARAM_construct_utf8_string(
"pkcs11_key_usage", (char *)"digitalSignature", 0);
params[2] = OSSL_PARAM_construct_end();
ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", "provider=pkcs11");
if (ctx == NULL) {
CLEANUP(dst__openssl_toresult2("EVP_PKEY_CTX_new_from_name",
DST_R_OPENSSLFAILURE));
}
status = EVP_PKEY_keygen_init(ctx);
if (status != 1) {
CLEANUP(dst__openssl_toresult2("EVP_PKEY_keygen_init",
DST_R_OPENSSLFAILURE));
}
status = EVP_PKEY_CTX_set_params(ctx, params);
if (status != 1) {
CLEANUP(dst__openssl_toresult2("EVP_PKEY_CTX_set_params",
DST_R_OPENSSLFAILURE));
}
/*
* Setting the P-384 curve doesn't work correctly when using:
* OSSL_PARAM_construct_utf8_string("ec_paramgen_curve", "P-384", 0);
*
* Instead use the OpenSSL function to set the curve nid param.
*/
status = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, group_nid);
if (status != 1) {
CLEANUP(dst__openssl_toresult2("EVP_PKEY_CTX_set_ec_paramgen_"
"curve_nid",
DST_R_OPENSSLFAILURE));
}
/* Generate the key. */
status = EVP_PKEY_generate(ctx, retkey);
if (status != 1) {
CLEANUP(dst__openssl_toresult2("EVP_PKEY_generate",
DST_R_OPENSSLFAILURE));
}
result = ISC_R_SUCCESS;
cleanup:
EVP_PKEY_CTX_free(ctx);
return result;
}
static isc_result_t
opensslecdsa_generate_pkey(unsigned int key_alg, const char *label,
EVP_PKEY **retkey) {
isc_result_t result;
EVP_PKEY_CTX *ctx = NULL;
EVP_PKEY *params_pkey = NULL;
int group_nid = opensslecdsa_key_alg_to_group_nid(key_alg);
int status;
if (label != NULL) {
return opensslecdsa_generate_pkey_with_uri(group_nid, label,
retkey);
}
/* Generate the key's parameters. */
ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
if (ctx == NULL) {
CLEANUP(dst__openssl_toresult2("EVP_PKEY_CTX_new_from_name",
DST_R_OPENSSLFAILURE));
}
status = EVP_PKEY_paramgen_init(ctx);
if (status != 1) {
CLEANUP(dst__openssl_toresult2("EVP_PKEY_paramgen_init",
DST_R_OPENSSLFAILURE));
}
status = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, group_nid);
if (status != 1) {
CLEANUP(dst__openssl_toresult2("EVP_PKEY_CTX_set_ec_paramgen_"
"curve_nid",
DST_R_OPENSSLFAILURE));
}
status = EVP_PKEY_paramgen(ctx, &params_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));
@ -684,9 +100,11 @@ opensslecdsa_createctx(dst_key_t *key, dst_context_t *dctx) {
CLEANUP(dst__openssl_toresult(ISC_R_NOMEMORY));
}
if (dctx->key->key_alg == DST_ALG_ECDSA256) {
type = isc__crypto_sha256;
type = isc__crypto_md[ISC_MD_SHA256];
md = "SHA256";
} else {
type = isc__crypto_sha384;
type = isc__crypto_md[ISC_MD_SHA384];
md = "SHA384";
}
if (dctx->use == DO_SIGN) {
@ -699,12 +117,15 @@ opensslecdsa_createctx(dst_key_t *key, dst_context_t *dctx) {
ISC_R_FAILURE));
}
#if OPENSSL_VERSION_NUMBER >= 0x30200000L
if (!isc_crypto_fips_mode()) {
CHECK(opensslecdsa_set_deterministic(
pctx, dctx->key->key_alg));
result = isc_ossl_wrap_ecdsa_set_deterministic(pctx,
md);
if (result != ISC_R_SUCCESS &&
result != ISC_R_NOTIMPLEMENTED)
{
CLEANUP(result);
}
}
#endif /* OPENSSL_VERSION_NUMBER >= 0x30200000L */
} else {
if (EVP_DigestVerifyInit(evp_md_ctx, NULL, type, NULL,
@ -718,6 +139,7 @@ opensslecdsa_createctx(dst_key_t *key, dst_context_t *dctx) {
}
dctx->ctxdata.evp_md_ctx = evp_md_ctx;
result = ISC_R_SUCCESS;
cleanup:
return result;
@ -916,7 +338,31 @@ opensslecdsa_generate(dst_key_t *key, int unused, void (*callback)(int)) {
UNUSED(unused);
UNUSED(callback);
RETERR(opensslecdsa_generate_pkey(key->key_alg, key->label, &pkey));
if (key->label != NULL) {
switch (key->key_alg) {
case DST_ALG_ECDSA256:
RETERR(isc_ossl_wrap_generate_pkcs11_p256_key(
key->label, &pkey));
break;
case DST_ALG_ECDSA384:
RETERR(isc_ossl_wrap_generate_pkcs11_p384_key(
key->label, &pkey));
break;
default:
UNREACHABLE();
}
} else {
switch (key->key_alg) {
case DST_ALG_ECDSA256:
RETERR(isc_ossl_wrap_generate_p256_key(&pkey));
break;
case DST_ALG_ECDSA384:
RETERR(isc_ossl_wrap_generate_p384_key(&pkey));
break;
default:
UNREACHABLE();
}
}
key->key_size = EVP_PKEY_bits(pkey);
key->keydata.pkeypair.priv = pkey;
@ -928,6 +374,7 @@ static isc_result_t
opensslecdsa_todns(const dst_key_t *key, isc_buffer_t *data) {
isc_result_t result;
isc_region_t r;
EVP_PKEY *pkey;
size_t keysize;
REQUIRE(opensslecdsa_valid_key_alg(key->key_alg));
@ -938,8 +385,23 @@ opensslecdsa_todns(const dst_key_t *key, isc_buffer_t *data) {
if (r.length < keysize) {
CLEANUP(ISC_R_NOSPACE);
}
if (!opensslecdsa_extract_public_key(key, r.base, keysize)) {
CLEANUP(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
pkey = key->keydata.pkeypair.pub;
switch (key->key_alg) {
case DST_ALG_ECDSA256:
if (isc_ossl_wrap_p256_public_region(pkey, r) != ISC_R_SUCCESS)
{
CLEANUP(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
}
break;
case DST_ALG_ECDSA384:
if (isc_ossl_wrap_p384_public_region(pkey, r) != ISC_R_SUCCESS)
{
CLEANUP(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
}
break;
default:
UNREACHABLE();
}
isc_buffer_add(data, keysize);
@ -967,8 +429,16 @@ opensslecdsa_fromdns(dst_key_t *key, isc_buffer_t *data) {
CLEANUP(DST_R_INVALIDPUBLICKEY);
}
CHECK(opensslecdsa_create_pkey(key->key_alg, false, r.base, len,
&pkey));
switch (key->key_alg) {
case DST_ALG_ECDSA256:
CHECK(isc_ossl_wrap_load_p256_public_from_region(r, &pkey));
break;
case DST_ALG_ECDSA384:
CHECK(isc_ossl_wrap_load_p384_public_from_region(r, &pkey));
break;
default:
UNREACHABLE();
}
isc_buffer_forward(data, len);
key->key_size = EVP_PKEY_bits(pkey);
@ -986,6 +456,7 @@ opensslecdsa_tofile(const dst_key_t *key, const char *directory) {
unsigned char buf[MAX_PRIVKEY_SIZE];
size_t keylen = 0;
unsigned short i;
EVP_PKEY *pkey;
if (key->keydata.pkeypair.pub == NULL) {
CLEANUP(DST_R_NULLKEY);
@ -1003,8 +474,23 @@ opensslecdsa_tofile(const dst_key_t *key, const char *directory) {
keylen = opensslecdsa_key_alg_to_publickey_size(key->key_alg) / 2;
INSIST(keylen <= sizeof(buf));
pkey = key->keydata.pkeypair.priv;
i = 0;
if (opensslecdsa_extract_private_key(key, buf, keylen)) {
switch (key->key_alg) {
case DST_ALG_ECDSA256:
result = isc_ossl_wrap_p256_secret_region(
pkey, (isc_region_t){ buf, keylen });
break;
case DST_ALG_ECDSA384:
result = isc_ossl_wrap_p384_secret_region(
pkey, (isc_region_t){ buf, keylen });
break;
default:
UNREACHABLE();
}
if (result == ISC_R_SUCCESS) {
priv.elements[i].tag = TAG_ECDSA_PRIVATEKEY;
priv.elements[i].length = keylen;
priv.elements[i].data = buf;
@ -1034,6 +520,7 @@ static isc_result_t
opensslecdsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
dst_private_t priv;
isc_result_t result;
isc_region_t r;
EVP_PKEY *pkey = NULL;
const char *label = NULL;
int i, privkey_index = -1;
@ -1086,9 +573,21 @@ opensslecdsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
CLEANUP(dst__openssl_toresult(DST_R_INVALIDPRIVATEKEY));
}
CHECK(opensslecdsa_create_pkey(
key->key_alg, true, priv.elements[privkey_index].data,
priv.elements[privkey_index].length, &pkey));
r = (isc_region_t){
.base = priv.elements[privkey_index].data,
.length = priv.elements[privkey_index].length,
};
switch (key->key_alg) {
case DST_ALG_ECDSA256:
CHECK(isc_ossl_wrap_load_p256_secret_from_region(r, &pkey));
break;
case DST_ALG_ECDSA384:
CHECK(isc_ossl_wrap_load_p384_secret_from_region(r, &pkey));
break;
default:
UNREACHABLE();
}
/* Check that the public component matches if given */
if (pub != NULL && EVP_PKEY_eq(pkey, pub->keydata.pkeypair.pub) != 1) {
@ -1122,8 +621,18 @@ opensslecdsa_fromlabel(dst_key_t *key, const char *label, const char *pin) {
CHECK(dst__openssl_fromlabel(EVP_PKEY_EC, label, pin, &pubpkey,
&privpkey));
CHECK(opensslecdsa_validate_pkey_group(key->key_alg, privpkey));
CHECK(opensslecdsa_validate_pkey_group(key->key_alg, pubpkey));
switch (key->key_alg) {
case DST_ALG_ECDSA256:
CHECK(isc_ossl_wrap_validate_p256_pkey(privpkey));
CHECK(isc_ossl_wrap_validate_p256_pkey(pubpkey));
break;
case DST_ALG_ECDSA384:
CHECK(isc_ossl_wrap_validate_p384_pkey(privpkey));
CHECK(isc_ossl_wrap_validate_p384_pkey(pubpkey));
break;
default:
UNREACHABLE();
}
key->label = isc_mem_strdup(key->mctx, label);
key->key_size = EVP_PKEY_bits(privpkey);

View file

@ -20,6 +20,7 @@
#include <openssl/objects.h>
#include <openssl/x509.h>
#include <isc/crypto.h>
#include <isc/mem.h>
#include <isc/result.h>
#include <isc/safe.h>

View file

@ -19,14 +19,10 @@
#include <openssl/bn.h>
#include <openssl/err.h>
#include <openssl/objects.h>
#include <openssl/opensslv.h>
#include <openssl/rsa.h>
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
#include <openssl/core_names.h>
#include <openssl/param_build.h>
#endif
#include <isc/mem.h>
#include <isc/ossl_wrap.h>
#include <isc/result.h>
#include <isc/safe.h>
#include <isc/string.h>
@ -39,11 +35,6 @@
#define OPENSSLRSA_MAX_MODULUS_BITS 4096
typedef struct rsa_components {
bool bnfree;
const BIGNUM *e, *n, *d, *p, *q, *dmp1, *dmq1, *iqmp;
} rsa_components_t;
/* length byte + 1.2.840.113549.1.1.11 BER encoded RFC 4055 */
static unsigned char oid_rsasha256[] = { 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48,
0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b };
@ -52,103 +43,6 @@ static unsigned char oid_rsasha256[] = { 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48,
static unsigned char oid_rsasha512[] = { 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48,
0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0d };
static isc_result_t
opensslrsa_components_get(const dst_key_t *key, rsa_components_t *c,
bool private) {
REQUIRE(c->e == NULL && c->n == NULL && c->d == NULL && c->p == NULL &&
c->q == NULL && c->dmp1 == NULL && c->dmq1 == NULL &&
c->iqmp == NULL);
EVP_PKEY *pub = key->keydata.pkeypair.pub;
EVP_PKEY *priv = key->keydata.pkeypair.priv;
if (private && priv == NULL) {
return DST_R_INVALIDPRIVATEKEY;
}
/*
* NOTE: Errors regarding private compoments are ignored.
*
* OpenSSL allows omitting the parameters for CRT based calculations
* (factors, exponents, coefficients). Only the 'd' parameter is
* mandatory for software keys.
*
* However, for a label based keys, all private key component queries
* can fail if they key is e.g. on a hardware device.
*/
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
if (EVP_PKEY_get_bn_param(pub, OSSL_PKEY_PARAM_RSA_E,
(BIGNUM **)&c->e) != 1)
{
return dst__openssl_toresult(DST_R_OPENSSLFAILURE);
}
c->bnfree = true;
if (EVP_PKEY_get_bn_param(pub, OSSL_PKEY_PARAM_RSA_N,
(BIGNUM **)&c->n) != 1)
{
return dst__openssl_toresult(DST_R_OPENSSLFAILURE);
}
if (!private) {
return ISC_R_SUCCESS;
}
(void)EVP_PKEY_get_bn_param(priv, OSSL_PKEY_PARAM_RSA_D,
(BIGNUM **)&c->d);
(void)EVP_PKEY_get_bn_param(priv, OSSL_PKEY_PARAM_RSA_FACTOR1,
(BIGNUM **)&c->p);
(void)EVP_PKEY_get_bn_param(priv, OSSL_PKEY_PARAM_RSA_FACTOR2,
(BIGNUM **)&c->q);
(void)EVP_PKEY_get_bn_param(priv, OSSL_PKEY_PARAM_RSA_EXPONENT1,
(BIGNUM **)&c->dmp1);
(void)EVP_PKEY_get_bn_param(priv, OSSL_PKEY_PARAM_RSA_EXPONENT2,
(BIGNUM **)&c->dmq1);
(void)EVP_PKEY_get_bn_param(priv, OSSL_PKEY_PARAM_RSA_COEFFICIENT1,
(BIGNUM **)&c->iqmp);
ERR_clear_error();
return ISC_R_SUCCESS;
#else
const RSA *rsa = EVP_PKEY_get0_RSA(pub);
if (rsa == NULL) {
return dst__openssl_toresult(DST_R_OPENSSLFAILURE);
}
RSA_get0_key(rsa, &c->n, &c->e, &c->d);
if (c->e == NULL || c->n == NULL) {
return dst__openssl_toresult(DST_R_OPENSSLFAILURE);
}
if (!private) {
return ISC_R_SUCCESS;
}
rsa = EVP_PKEY_get0_RSA(priv);
if (rsa == NULL) {
return dst__openssl_toresult(DST_R_OPENSSLFAILURE);
}
RSA_get0_factors(rsa, &c->p, &c->q);
RSA_get0_crt_params(rsa, &c->dmp1, &c->dmq1, &c->iqmp);
return ISC_R_SUCCESS;
#endif
}
static void
opensslrsa_components_free(rsa_components_t *c) {
if (!c->bnfree) {
return;
}
/*
* NOTE: BN_free() frees the components of the BIGNUM, and if it was
* created by BN_new(), also the structure itself. BN_clear_free()
* additionally overwrites the data before the memory is returned to the
* system. If a is NULL, nothing is done.
*/
BN_free((BIGNUM *)c->e);
BN_free((BIGNUM *)c->n);
BN_clear_free((BIGNUM *)c->d);
BN_clear_free((BIGNUM *)c->p);
BN_clear_free((BIGNUM *)c->q);
BN_clear_free((BIGNUM *)c->dmp1);
BN_clear_free((BIGNUM *)c->dmq1);
BN_clear_free((BIGNUM *)c->iqmp);
c->bnfree = false;
}
static bool
opensslrsa_valid_key_alg(unsigned int key_alg) {
switch (key_alg) {
@ -210,15 +104,15 @@ opensslrsa_createctx(dst_key_t *key, dst_context_t *dctx) {
switch (dctx->key->key_alg) {
case DST_ALG_RSASHA1:
case DST_ALG_NSEC3RSASHA1:
type = isc__crypto_sha1; /* SHA1 + RSA */
type = isc__crypto_md[ISC_MD_SHA1]; /* SHA1 + RSA */
break;
case DST_ALG_RSASHA256:
case DST_ALG_RSASHA256PRIVATEOID:
type = isc__crypto_sha256; /* SHA256 + RSA */
type = isc__crypto_md[ISC_MD_SHA256]; /* SHA256 + RSA */
break;
case DST_ALG_RSASHA512:
case DST_ALG_RSASHA512PRIVATEOID:
type = isc__crypto_sha512;
type = isc__crypto_md[ISC_MD_SHA512];
break;
default:
UNREACHABLE();
@ -322,29 +216,6 @@ opensslrsa_sign(dst_context_t *dctx, isc_buffer_t *sig) {
return ISC_R_SUCCESS;
}
static bool
opensslrsa_check_exponent_bits(EVP_PKEY *pkey, int maxbits) {
/* Always use the new API first with OpenSSL 3.x. */
int bits = INT_MAX;
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
BIGNUM *e = NULL;
if (EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_E, &e) == 1) {
bits = BN_num_bits(e);
BN_free(e);
}
#else
const RSA *rsa = EVP_PKEY_get0_RSA(pkey);
if (rsa != NULL) {
const BIGNUM *ce = NULL;
RSA_get0_key(rsa, NULL, &ce, NULL);
if (ce != NULL) {
bits = BN_num_bits(ce);
}
}
#endif
return bits <= maxbits;
}
static isc_result_t
opensslrsa_verify(dst_context_t *dctx, const isc_region_t *sig) {
dst_key_t *key = NULL;
@ -361,7 +232,7 @@ opensslrsa_verify(dst_context_t *dctx, const isc_region_t *sig) {
evp_md_ctx = dctx->ctxdata.evp_md_ctx;
pkey = key->keydata.pkeypair.pub;
if (!opensslrsa_check_exponent_bits(pkey, OPENSSLRSA_MAX_MODULUS_BITS))
if (!isc_ossl_wrap_rsa_key_bits_leq(pkey, OPENSSLRSA_MAX_MODULUS_BITS))
{
return DST_R_VERIFYFAILURE;
}
@ -402,344 +273,13 @@ opensslrsa_verify(dst_context_t *dctx, const isc_region_t *sig) {
}
}
#if OPENSSL_VERSION_NUMBER < 0x30000000L
static int
progress_cb(int p, int n, BN_GENCB *cb) {
void (*fptr)(int);
UNUSED(n);
fptr = BN_GENCB_get_arg(cb);
if (fptr != NULL) {
fptr(p);
}
return 1;
}
static isc_result_t
opensslrsa_generate_pkey(unsigned int key_size, const char *label, BIGNUM *e,
void (*callback)(int), EVP_PKEY **retkey) {
RSA *rsa = NULL;
EVP_PKEY *pkey = NULL;
BN_GENCB *cb = NULL;
isc_result_t result;
UNUSED(label);
rsa = RSA_new();
pkey = EVP_PKEY_new();
if (rsa == NULL || pkey == NULL) {
CLEANUP(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
}
if (EVP_PKEY_set1_RSA(pkey, rsa) != 1) {
CLEANUP(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
}
if (callback != NULL) {
cb = BN_GENCB_new();
if (cb == NULL) {
CLEANUP(dst__openssl_toresult(ISC_R_NOMEMORY));
}
BN_GENCB_set(cb, progress_cb, (void *)callback);
}
if (RSA_generate_key_ex(rsa, key_size, e, cb) != 1) {
CLEANUP(dst__openssl_toresult2("RSA_generate_key_ex",
DST_R_OPENSSLFAILURE));
}
*retkey = pkey;
pkey = NULL;
result = ISC_R_SUCCESS;
cleanup:
EVP_PKEY_free(pkey);
RSA_free(rsa);
BN_GENCB_free(cb);
return result;
}
static isc_result_t
opensslrsa_build_pkey(bool private, rsa_components_t *c, EVP_PKEY **retpkey) {
isc_result_t result;
EVP_PKEY *pkey = NULL;
RSA *rsa = RSA_new();
int status;
REQUIRE(c->bnfree);
if (c->n == NULL || c->e == NULL) {
if (private) {
CLEANUP(DST_R_INVALIDPRIVATEKEY);
}
CLEANUP(DST_R_INVALIDPUBLICKEY);
}
if (rsa == NULL) {
CLEANUP(dst__openssl_toresult2("RSA_new",
DST_R_OPENSSLFAILURE));
}
if (RSA_set0_key(rsa, (BIGNUM *)c->n, (BIGNUM *)c->e, (BIGNUM *)c->d) !=
1)
{
CLEANUP(dst__openssl_toresult2("RSA_set0_key",
DST_R_OPENSSLFAILURE));
}
c->n = NULL;
c->e = NULL;
c->d = NULL;
if (c->p != NULL || c->q != NULL) {
if (RSA_set0_factors(rsa, (BIGNUM *)c->p, (BIGNUM *)c->q) != 1)
{
CLEANUP(dst__openssl_toresult2("RSA_set0_factors",
DST_R_OPENSSLFAILURE));
}
c->p = NULL;
c->q = NULL;
}
if (c->dmp1 != NULL || c->dmq1 != NULL || c->iqmp != NULL) {
if (RSA_set0_crt_params(rsa, (BIGNUM *)c->dmp1,
(BIGNUM *)c->dmq1,
(BIGNUM *)c->iqmp) == 0)
{
CLEANUP(dst__openssl_toresult2("RSA_set0_crt_params",
DST_R_OPENSSLFAILURE));
}
c->dmp1 = NULL;
c->dmq1 = NULL;
c->iqmp = NULL;
}
pkey = EVP_PKEY_new();
if (pkey == NULL) {
CLEANUP(dst__openssl_toresult2("EVP_PKEY_new",
DST_R_OPENSSLFAILURE));
}
status = EVP_PKEY_set1_RSA(pkey, rsa);
if (status != 1) {
CLEANUP(dst__openssl_toresult2("EVP_PKEY_set1_RSA",
DST_R_OPENSSLFAILURE));
}
*retpkey = pkey;
pkey = NULL;
result = ISC_R_SUCCESS;
cleanup:
EVP_PKEY_free(pkey);
RSA_free(rsa);
opensslrsa_components_free(c);
return result;
}
#else
static int
progress_cb(EVP_PKEY_CTX *ctx) {
void (*fptr)(int);
fptr = EVP_PKEY_CTX_get_app_data(ctx);
if (fptr != NULL) {
int p = EVP_PKEY_CTX_get_keygen_info(ctx, 0);
fptr(p);
}
return 1;
}
static isc_result_t
opensslrsa_generate_pkey_with_uri(size_t key_size, const char *label,
EVP_PKEY **retkey) {
EVP_PKEY_CTX *ctx = NULL;
OSSL_PARAM params[4];
char *uri = UNCONST(label);
isc_result_t result;
int status;
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_size_t("rsa_keygen_bits", &key_size);
params[3] = OSSL_PARAM_construct_end();
ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", "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));
}
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
opensslrsa_generate_pkey(unsigned int key_size, const char *label, BIGNUM *e,
void (*callback)(int), EVP_PKEY **retkey) {
EVP_PKEY_CTX *ctx;
isc_result_t result;
if (label != NULL) {
return opensslrsa_generate_pkey_with_uri(key_size, label,
retkey);
}
ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL);
if (ctx == NULL) {
CLEANUP(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
}
if (EVP_PKEY_keygen_init(ctx) != 1) {
CLEANUP(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
}
if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, (int)key_size) != 1) {
CLEANUP(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
}
if (EVP_PKEY_CTX_set1_rsa_keygen_pubexp(ctx, e) != 1) {
CLEANUP(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
}
if (callback != NULL) {
EVP_PKEY_CTX_set_app_data(ctx, (void *)callback);
EVP_PKEY_CTX_set_cb(ctx, progress_cb);
}
if (EVP_PKEY_keygen(ctx, retkey) != 1) {
CLEANUP(dst__openssl_toresult2("EVP_PKEY_keygen",
DST_R_OPENSSLFAILURE));
}
result = ISC_R_SUCCESS;
cleanup:
EVP_PKEY_CTX_free(ctx);
return result;
}
static isc_result_t
opensslrsa_build_pkey(bool private, rsa_components_t *c, EVP_PKEY **retpkey) {
isc_result_t result;
int status;
OSSL_PARAM_BLD *bld = NULL;
OSSL_PARAM *params = NULL;
EVP_PKEY_CTX *ctx = NULL;
bld = OSSL_PARAM_BLD_new();
if (bld == NULL) {
CLEANUP(dst__openssl_toresult2("OSSL_PARAM_BLD_new",
DST_R_OPENSSLFAILURE));
}
if (OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, c->n) != 1 ||
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, c->e) != 1)
{
CLEANUP(dst__openssl_toresult2("OSSL_PARAM_BLD_push_BN",
DST_R_OPENSSLFAILURE));
}
if (c->d != NULL &&
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_D, c->d) != 1)
{
CLEANUP(dst__openssl_toresult2("OSSL_PARAM_BLD_push_BN",
DST_R_OPENSSLFAILURE));
}
if (c->p != NULL &&
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_FACTOR1, c->p) != 1)
{
CLEANUP(dst__openssl_toresult2("OSSL_PARAM_BLD_push_BN",
DST_R_OPENSSLFAILURE));
}
if (c->q != NULL &&
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_FACTOR2, c->q) != 1)
{
CLEANUP(dst__openssl_toresult2("OSSL_PARAM_BLD_push_BN",
DST_R_OPENSSLFAILURE));
}
if (c->dmp1 != NULL &&
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_EXPONENT1,
c->dmp1) != 1)
{
CLEANUP(dst__openssl_toresult2("OSSL_PARAM_BLD_push_BN",
DST_R_OPENSSLFAILURE));
}
if (c->dmq1 != NULL &&
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_EXPONENT2,
c->dmq1) != 1)
{
CLEANUP(dst__openssl_toresult2("OSSL_PARAM_BLD_push_BN",
DST_R_OPENSSLFAILURE));
}
if (c->iqmp != NULL &&
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_COEFFICIENT1,
c->iqmp) != 1)
{
CLEANUP(dst__openssl_toresult2("OSSL_PARAM_BLD_push_BN",
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, "RSA", 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) {
CLEANUP(dst__openssl_toresult2("EVP_PKEY_fromdata_init",
DST_R_OPENSSLFAILURE));
}
status = EVP_PKEY_fromdata(
ctx, retpkey, private ? EVP_PKEY_KEYPAIR : EVP_PKEY_PUBLIC_KEY,
params);
if (status != 1) {
CLEANUP(dst__openssl_toresult2("EVP_PKEY_fromdata",
DST_R_OPENSSLFAILURE));
}
result = ISC_R_SUCCESS;
cleanup:
EVP_PKEY_CTX_free(ctx);
OSSL_PARAM_free(params);
OSSL_PARAM_BLD_free(bld);
return result;
}
#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
static isc_result_t
opensslrsa_generate(dst_key_t *key, int unused, void (*callback)(int)) {
isc_result_t result;
BIGNUM *e = BN_new();
EVP_PKEY *pkey = NULL;
UNUSED(unused);
if (e == NULL) {
CLEANUP(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
}
/*
* Reject incorrect RSA key lengths.
*/
@ -769,12 +309,13 @@ opensslrsa_generate(dst_key_t *key, int unused, void (*callback)(int)) {
UNREACHABLE();
}
/* e = 65537 (0x10001, F4) */
BN_set_bit(e, 0);
BN_set_bit(e, 16);
CHECK(opensslrsa_generate_pkey(key->key_size, key->label, e, callback,
&pkey));
if (key->label != NULL) {
CHECK(isc_ossl_wrap_generate_pkcs11_rsa_key(
key->label, key->key_size, &pkey));
} else {
CHECK(isc_ossl_wrap_generate_rsa_key(callback, key->key_size,
&pkey));
}
key->keydata.pkeypair.pub = pkey;
key->keydata.pkeypair.priv = pkey;
@ -783,7 +324,6 @@ opensslrsa_generate(dst_key_t *key, int unused, void (*callback)(int)) {
cleanup:
EVP_PKEY_free(pkey);
BN_free(e);
return result;
}
@ -793,7 +333,7 @@ opensslrsa_todns(const dst_key_t *key, isc_buffer_t *data) {
unsigned int e_bytes;
unsigned int mod_bytes;
isc_result_t result;
rsa_components_t c = { 0 };
isc_ossl_wrap_rsa_components_t c = { 0 };
REQUIRE(key->keydata.pkeypair.pub != NULL);
@ -819,7 +359,8 @@ opensslrsa_todns(const dst_key_t *key, isc_buffer_t *data) {
break;
}
CHECK(opensslrsa_components_get(key, &c, false));
CHECK(isc_ossl_wrap_rsa_public_components(key->keydata.pkeypair.pub,
&c));
mod_bytes = BN_num_bytes(c.n);
e_bytes = BN_num_bytes(c.e);
@ -850,9 +391,8 @@ opensslrsa_todns(const dst_key_t *key, isc_buffer_t *data) {
isc_buffer_add(data, e_bytes + mod_bytes);
result = ISC_R_SUCCESS;
cleanup:
opensslrsa_components_free(&c);
isc_ossl_wrap_rsa_components_cleanup(&c);
return result;
}
@ -862,7 +402,7 @@ opensslrsa_fromdns(dst_key_t *key, isc_buffer_t *data) {
isc_region_t r;
unsigned int e_bytes;
unsigned int length;
rsa_components_t c = { .bnfree = true };
isc_ossl_wrap_rsa_components_t c = { .needs_cleanup = true };
REQUIRE(opensslrsa_valid_key_alg(key->key_alg));
@ -925,10 +465,11 @@ opensslrsa_fromdns(dst_key_t *key, isc_buffer_t *data) {
isc_buffer_forward(data, length);
key->key_size = BN_num_bits(c.n);
result = opensslrsa_build_pkey(false, &c, &key->keydata.pkeypair.pub);
result = isc_ossl_wrap_load_rsa_public_from_components(
&c, &key->keydata.pkeypair.pub);
cleanup:
opensslrsa_components_free(&c);
isc_ossl_wrap_rsa_components_cleanup(&c);
return result;
}
@ -938,13 +479,16 @@ opensslrsa_tofile(const dst_key_t *key, const char *directory) {
dst_private_t priv = { 0 };
unsigned char *bufs[8] = { NULL };
unsigned short i = 0;
rsa_components_t c = { 0 };
isc_ossl_wrap_rsa_components_t c = { 0 };
if (key->external) {
return dst__privstruct_writefile(key, &priv, directory);
}
CHECK(opensslrsa_components_get(key, &c, true));
CHECK(isc_ossl_wrap_rsa_public_components(key->keydata.pkeypair.pub,
&c));
CHECK(isc_ossl_wrap_rsa_secret_components(key->keydata.pkeypair.priv,
&c));
priv.elements[i].tag = TAG_RSA_MODULUS;
priv.elements[i].length = BN_num_bytes(c.n);
@ -1038,7 +582,7 @@ cleanup:
priv.elements[i].length);
}
}
opensslrsa_components_free(&c);
isc_ossl_wrap_rsa_components_cleanup(&c);
return result;
}
@ -1054,7 +598,7 @@ opensslrsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
isc_mem_t *mctx = NULL;
const char *label = NULL;
EVP_PKEY *pkey = NULL;
rsa_components_t c = { .bnfree = true };
isc_ossl_wrap_rsa_components_t c = { .needs_cleanup = true };
REQUIRE(key != NULL);
REQUIRE(opensslrsa_valid_key_alg(key->key_alg));
@ -1157,7 +701,8 @@ opensslrsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
}
key->key_size = BN_num_bits(c.n);
CHECK(opensslrsa_build_pkey(true, &c, &pkey));
CHECK(isc_ossl_wrap_load_rsa_secret_from_components(&c, &pkey));
/* Check that the public component matches if given */
if (pub != NULL && EVP_PKEY_eq(pkey, pub->keydata.pkeypair.pub) != 1) {
@ -1169,7 +714,7 @@ opensslrsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
pkey = NULL;
cleanup:
opensslrsa_components_free(&c);
isc_ossl_wrap_rsa_components_cleanup(&c);
EVP_PKEY_free(pkey);
if (result != ISC_R_SUCCESS) {
key->keydata.generic = NULL;
@ -1189,7 +734,7 @@ opensslrsa_fromlabel(dst_key_t *key, const char *label, const char *pin) {
CHECK(dst__openssl_fromlabel(EVP_PKEY_RSA, label, pin, &pubpkey,
&privpkey));
if (!opensslrsa_check_exponent_bits(pubpkey, RSA_MAX_PUBEXP_BITS)) {
if (!isc_ossl_wrap_rsa_key_bits_leq(pubpkey, RSA_MAX_PUBEXP_BITS)) {
CLEANUP(ISC_R_RANGE);
}
@ -1301,7 +846,7 @@ static const unsigned char sha512_sig[] =
static isc_result_t
check_algorithm(unsigned short algorithm) {
rsa_components_t c = { .bnfree = true };
isc_ossl_wrap_rsa_components_t c = { .needs_cleanup = true };
EVP_MD_CTX *evp_md_ctx = EVP_MD_CTX_create();
EVP_PKEY *pkey = NULL;
const EVP_MD *type = NULL;
@ -1312,19 +857,19 @@ check_algorithm(unsigned short algorithm) {
switch (algorithm) {
case DST_ALG_RSASHA1:
case DST_ALG_NSEC3RSASHA1:
type = isc__crypto_sha1; /* SHA1 + RSA */
type = isc__crypto_md[ISC_MD_SHA1]; /* SHA1 + RSA */
sig = sha1_sig;
len = sizeof(sha1_sig) - 1;
break;
case DST_ALG_RSASHA256:
case DST_ALG_RSASHA256PRIVATEOID:
type = isc__crypto_sha256; /* SHA256 + RSA */
type = isc__crypto_md[ISC_MD_SHA256]; /* SHA256 + RSA */
sig = sha256_sig;
len = sizeof(sha256_sig) - 1;
break;
case DST_ALG_RSASHA512:
case DST_ALG_RSASHA512PRIVATEOID:
type = isc__crypto_sha512;
type = isc__crypto_md[ISC_MD_SHA512];
sig = sha512_sig;
len = sizeof(sha512_sig) - 1;
break;
@ -1338,7 +883,7 @@ check_algorithm(unsigned short algorithm) {
c.e = BN_bin2bn(e_bytes, sizeof(e_bytes) - 1, NULL);
c.n = BN_bin2bn(n_bytes, sizeof(n_bytes) - 1, NULL);
result = opensslrsa_build_pkey(false, &c, &pkey);
result = isc_ossl_wrap_load_rsa_public_from_components(&c, &pkey);
INSIST(result == ISC_R_SUCCESS);
/*
@ -1352,7 +897,7 @@ check_algorithm(unsigned short algorithm) {
}
cleanup:
opensslrsa_components_free(&c);
isc_ossl_wrap_rsa_components_cleanup(&c);
EVP_PKEY_free(pkey);
EVP_MD_CTX_destroy(evp_md_ctx);
ERR_clear_error();

View file

@ -0,0 +1,16 @@
# 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.
isc_srcset.add(
when: 'HAVE_OPENSSL_3',
if_true: files('ossl3.c', 'ossl_common.c'),
if_false: files('ossl1_1.c', 'ossl_common.c'),
)

View file

@ -11,92 +11,232 @@
* information regarding copyright ownership.
*/
#include <stdint.h>
#include <openssl/crypto.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/ssl.h>
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
#include <openssl/provider.h>
#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
#include <isc/buffer.h>
#include <isc/crypto.h>
#include <isc/hmac.h>
#include <isc/log.h>
#include <isc/magic.h>
#include <isc/md.h>
#include <isc/mem.h>
#include <isc/tls.h>
#include <isc/ossl_wrap.h>
#include <isc/safe.h>
#include <isc/util.h>
#define HMAC_KEY_MAGIC ISC_MAGIC('H', 'M', 'A', 'C')
struct isc_hmac_key {
uint32_t magic;
uint32_t len;
isc_mem_t *mctx;
EVP_MD *md;
uint8_t secret[];
};
static isc_mem_t *isc__crypto_mctx = NULL;
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
static OSSL_PROVIDER *base = NULL, *fips = NULL;
#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
const EVP_MD *isc__crypto_md5 = NULL;
const EVP_MD *isc__crypto_sha1 = NULL;
const EVP_MD *isc__crypto_sha224 = NULL;
const EVP_MD *isc__crypto_sha256 = NULL;
const EVP_MD *isc__crypto_sha384 = NULL;
const EVP_MD *isc__crypto_sha512 = NULL;
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
#define md_register_algorithm(alg, algname) \
{ \
REQUIRE(isc__crypto_##alg == NULL); \
isc__crypto_##alg = EVP_MD_fetch(NULL, algname, NULL); \
if (isc__crypto_##alg == NULL) { \
ERR_clear_error(); \
} \
#define md_register_algorithm(alg, upperalg) \
{ \
isc__crypto_md[ISC_MD_##upperalg] = UNCONST(EVP_##alg()); \
if (isc__crypto_md[ISC_MD_##upperalg] == NULL) { \
ERR_clear_error(); \
} \
}
#define md_unregister_algorithm(alg) \
{ \
if (isc__crypto_##alg != NULL) { \
EVP_MD_free(UNCONST(isc__crypto_##alg)); \
isc__crypto_##alg = NULL; \
} \
}
#else /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
#define md_register_algorithm(alg, algname) \
{ \
isc__crypto_##alg = EVP_##alg(); \
if (isc__crypto_##alg == NULL) { \
ERR_clear_error(); \
} \
}
#define md_unregister_algorithm(alg)
#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
static isc_result_t
register_algorithms(void) {
if (!isc_crypto_fips_mode()) {
md_register_algorithm(md5, "MD5");
md_register_algorithm(md5, MD5);
}
md_register_algorithm(sha1, "SHA1");
md_register_algorithm(sha224, "SHA224");
md_register_algorithm(sha256, "SHA256");
md_register_algorithm(sha384, "SHA384");
md_register_algorithm(sha512, "SHA512");
md_register_algorithm(sha1, SHA1);
md_register_algorithm(sha224, SHA224);
md_register_algorithm(sha256, SHA256);
md_register_algorithm(sha384, SHA384);
md_register_algorithm(sha512, SHA512);
return ISC_R_SUCCESS;
}
static void
unregister_algorithms(void) {
md_unregister_algorithm(sha512);
md_unregister_algorithm(sha384);
md_unregister_algorithm(sha256);
md_unregister_algorithm(sha224);
md_unregister_algorithm(sha1);
md_unregister_algorithm(md5);
#undef md_unregister_algorithm
/*
* HMAC Notes
*
* For pre-3.0 libcrypto, we use HMAC_CTX instead of the EVP_PKEY API.
*
* EVP_PKEY will call HMAC_* functions internally so there is no need to add
* even more vtables.
*/
isc_result_t
isc_hmac(isc_md_type_t type, const void *key, const size_t keylen,
const unsigned char *buf, const size_t len, unsigned char *digest,
unsigned int *digestlen) {
EVP_MD *md;
REQUIRE(type < ISC_MD_MAX);
md = isc__crypto_md[type];
if (md == NULL) {
return ISC_R_NOTIMPLEMENTED;
}
if (HMAC(md, key, keylen, buf, len, digest, digestlen) == NULL) {
ERR_clear_error();
return ISC_R_CRYPTOFAILURE;
}
return ISC_R_SUCCESS;
}
#undef md_unregister_algorithm
#undef md_register_algorithm
isc_result_t
isc_hmac_key_create(isc_md_type_t type, const void *secret, const size_t len,
isc_mem_t *mctx, isc_hmac_key_t **keyp) {
isc_hmac_key_t *key;
EVP_MD *md;
#if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x30000000L
REQUIRE(keyp != NULL && *keyp == NULL);
REQUIRE(type < ISC_MD_MAX);
md = isc__crypto_md[type];
if (md == NULL) {
return ISC_R_NOTIMPLEMENTED;
}
key = isc_mem_get(mctx, STRUCT_FLEX_SIZE(key, secret, len));
*key = (isc_hmac_key_t){
.magic = HMAC_KEY_MAGIC,
.len = len,
.md = md,
};
memmove(key->secret, secret, len);
isc_mem_attach(mctx, &key->mctx);
*keyp = key;
return ISC_R_SUCCESS;
}
void
isc_hmac_key_destroy(isc_hmac_key_t **keyp) {
isc_hmac_key_t *key;
REQUIRE(keyp != NULL && *keyp != NULL);
REQUIRE((*keyp)->magic == HMAC_KEY_MAGIC);
key = *keyp;
*keyp = NULL;
key->magic = 0x00;
isc_safe_memwipe(key->secret, sizeof(key->len));
isc_mem_putanddetach(&key->mctx, key,
STRUCT_FLEX_SIZE(key, secret, key->len));
}
isc_region_t
isc_hmac_key_expose(isc_hmac_key_t *key) {
REQUIRE(key != NULL && key->magic == HMAC_KEY_MAGIC);
return (isc_region_t){ .base = key->secret, .length = key->len };
}
bool
isc_hmac_key_equal(isc_hmac_key_t *a, isc_hmac_key_t *b) {
REQUIRE(a != NULL && a->magic == HMAC_KEY_MAGIC);
REQUIRE(b != NULL && b->magic == HMAC_KEY_MAGIC);
if (a->md != b->md) {
return false;
}
if (a->len != b->len) {
return false;
}
return isc_safe_memequal(a->secret, b->secret, a->len);
}
isc_hmac_t *
isc_hmac_new(void) {
HMAC_CTX *ctx = HMAC_CTX_new();
RUNTIME_CHECK(ctx != NULL);
return ctx;
}
void
isc_hmac_free(isc_hmac_t *hmac) {
if (hmac != NULL) {
HMAC_CTX_free(hmac);
}
}
isc_result_t
isc_hmac_init(isc_hmac_t *hmac, isc_hmac_key_t *key) {
REQUIRE(hmac != NULL);
REQUIRE(key != NULL && key->magic == HMAC_KEY_MAGIC);
if (HMAC_Init_ex(hmac, key->secret, key->len, key->md, NULL) != 1) {
ERR_clear_error();
return ISC_R_CRYPTOFAILURE;
}
return ISC_R_SUCCESS;
}
isc_result_t
isc_hmac_update(isc_hmac_t *hmac, const unsigned char *buf, const size_t len) {
REQUIRE(hmac != NULL);
if (buf == NULL || len == 0) {
return ISC_R_SUCCESS;
}
if (HMAC_Update(hmac, buf, len) != 1) {
ERR_clear_error();
return ISC_R_CRYPTOFAILURE;
}
return ISC_R_SUCCESS;
}
isc_result_t
isc_hmac_final(isc_hmac_t *hmac, isc_buffer_t *out) {
unsigned int len;
REQUIRE(hmac != NULL);
REQUIRE(out != NULL);
/*
* LibreSSL changes HMAC_size's return from size_t to int but keeps the
* size_t signature in its manpage.
*
* Cast it instead of accepting LibreSSL's man(page)splaining.
*/
len = isc_buffer_availablelength(out);
if (len < (unsigned int)HMAC_size(hmac)) {
return ISC_R_NOSPACE;
}
if (HMAC_Final(hmac, isc_buffer_used(out), &len) != 1) {
return ISC_R_CRYPTOFAILURE;
}
isc_buffer_add(out, len);
return ISC_R_SUCCESS;
}
#ifndef LIBRESSL_VERSION_NUMBER
/*
* This was crippled with LibreSSL, so just skip it:
* https://cvsweb.openbsd.org/src/lib/libcrypto/Attic/mem.c
@ -165,50 +305,9 @@ isc__crypto_free_ex(void *ptr, const char *file, int line) {
#endif /* ISC_MEM_TRACKLINES */
#endif /* !defined(LIBRESSL_VERSION_NUMBER) */
#endif /* !LIBRESSL_VERSION_NUMBER */
#if defined(HAVE_EVP_DEFAULT_PROPERTIES_ENABLE_FIPS)
bool
isc_crypto_fips_mode(void) {
return EVP_default_properties_is_fips_enabled(NULL) != 0;
}
isc_result_t
isc_crypto_fips_enable(void) {
if (isc_crypto_fips_mode()) {
return ISC_R_SUCCESS;
}
INSIST(fips == NULL);
fips = OSSL_PROVIDER_load(NULL, "fips");
if (fips == NULL) {
return isc_tlserr2result(
ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_CRYPTO,
"OSSL_PROVIDER_load", ISC_R_CRYPTOFAILURE);
}
INSIST(base == NULL);
base = OSSL_PROVIDER_load(NULL, "base");
if (base == NULL) {
OSSL_PROVIDER_unload(fips);
return isc_tlserr2result(
ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_CRYPTO,
"OSS_PROVIDER_load", ISC_R_CRYPTOFAILURE);
}
if (EVP_default_properties_enable_fips(NULL, 1) == 0) {
return isc_tlserr2result(ISC_LOGCATEGORY_GENERAL,
ISC_LOGMODULE_CRYPTO,
"EVP_default_properties_enable_fips",
ISC_R_CRYPTOFAILURE);
}
unregister_algorithms();
register_algorithms();
return ISC_R_SUCCESS;
}
#elif defined(HAVE_FIPS_MODE)
#ifdef HAVE_FIPS_MODE
bool
isc_crypto_fips_mode(void) {
return FIPS_mode() != 0;
@ -221,12 +320,11 @@ isc_crypto_fips_enable(void) {
}
if (FIPS_mode_set(1) == 0) {
return isc_tlserr2result(ISC_LOGCATEGORY_GENERAL,
ISC_LOGMODULE_CRYPTO, "FIPS_mode_set",
ISC_R_CRYPTOFAILURE);
return isc_ossl_wrap_logged_toresult(
ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_CRYPTO,
"FIPS_mode_set", ISC_R_CRYPTOFAILURE);
}
unregister_algorithms();
register_algorithms();
return ISC_R_SUCCESS;
@ -256,7 +354,7 @@ isc__crypto_initialize(void) {
isc_mem_setdebugging(isc__crypto_mctx, 0);
isc_mem_setdestroycheck(isc__crypto_mctx, false);
#if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x30000000L
#ifndef LIBRESSL_VERSION_NUMBER
/*
* CRYPTO_set_mem_(_ex)_functions() returns 1 on success or 0 on
* failure, which means OpenSSL already allocated some memory. There's
@ -265,8 +363,7 @@ isc__crypto_initialize(void) {
(void)CRYPTO_set_mem_functions(isc__crypto_malloc_ex,
isc__crypto_realloc_ex,
isc__crypto_free_ex);
#endif /* !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= \
0x30000000L */
#endif /* !LIBRESSL_VERSION_NUMBER */
#if defined(OPENSSL_INIT_NO_ATEXIT)
/*
@ -290,8 +387,9 @@ isc__crypto_initialize(void) {
/* Protect ourselves against unseeded PRNG */
if (RAND_status() != 1) {
isc_tlserr2result(ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_CRYPTO,
"RAND_status", ISC_R_CRYPTOFAILURE);
isc_ossl_wrap_logged_toresult(
ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_CRYPTO,
"RAND_status", ISC_R_CRYPTOFAILURE);
FATAL_ERROR("OpenSSL pseudorandom number generator "
"cannot be initialized (see the `PRNG not "
"seeded' message in the OpenSSL FAQ)");
@ -300,18 +398,6 @@ isc__crypto_initialize(void) {
void
isc__crypto_shutdown(void) {
unregister_algorithms();
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
if (base != NULL) {
OSSL_PROVIDER_unload(base);
}
if (fips != NULL) {
OSSL_PROVIDER_unload(fips);
}
#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
OPENSSL_cleanup();
isc_mem_detach(&isc__crypto_mctx);

491
lib/isc/crypto/ossl3.c Normal file
View file

@ -0,0 +1,491 @@
/*
* 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.
*/
#include <stdbool.h>
#include <stdint.h>
#include <openssl/core_names.h>
#include <openssl/crypto.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/provider.h>
#include <openssl/rand.h>
#include <openssl/ssl.h>
#include <isc/buffer.h>
#include <isc/crypto.h>
#include <isc/hmac.h>
#include <isc/log.h>
#include <isc/magic.h>
#include <isc/md.h>
#include <isc/mem.h>
#include <isc/ossl_wrap.h>
#include <isc/region.h>
#include <isc/safe.h>
#include <isc/util.h>
struct isc_hmac_key {
uint32_t magic;
uint32_t len;
isc_mem_t *mctx;
const OSSL_PARAM *params;
uint8_t secret[];
};
constexpr uint32_t hmac_key_magic = ISC_MAGIC('H', 'M', 'A', 'C');
static isc_mem_t *isc__crypto_mctx = NULL;
static OSSL_PROVIDER *base = NULL, *fips = NULL;
static EVP_MAC *evp_hmac = NULL;
static OSSL_PARAM md_to_hmac_params[ISC_MD_MAX][2] = {
[ISC_MD_UNKNOWN] = { OSSL_PARAM_END },
[ISC_MD_MD5] = {
OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, UNCONST("MD5"), sizeof("MD5") - 1),
OSSL_PARAM_END,
},
[ISC_MD_SHA1] = {
OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, UNCONST("SHA1"), sizeof("SHA1") - 1),
OSSL_PARAM_END,
},
[ISC_MD_SHA224] = {
OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, UNCONST("SHA2-224"), sizeof("SHA2-224") - 1),
OSSL_PARAM_END,
},
[ISC_MD_SHA256] = {
OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, UNCONST("SHA2-256"), sizeof("SHA2-256") - 1),
OSSL_PARAM_END,
},
[ISC_MD_SHA384] = {
OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, UNCONST("SHA2-384"), sizeof("SHA2-384") - 1),
OSSL_PARAM_END,
},
[ISC_MD_SHA512] = {
OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, UNCONST("SHA2-512"), sizeof("SHA2-512") - 1),
OSSL_PARAM_END,
},
};
#define md_register_algorithm(alg) \
{ \
REQUIRE(isc__crypto_md[ISC_MD_##alg] == NULL); \
isc__crypto_md[ISC_MD_##alg] = EVP_MD_fetch(NULL, #alg, NULL); \
if (isc__crypto_md[ISC_MD_##alg] == NULL) { \
ERR_clear_error(); \
} \
}
static isc_result_t
register_algorithms(void) {
if (!isc_crypto_fips_mode()) {
md_register_algorithm(MD5);
}
md_register_algorithm(SHA1);
md_register_algorithm(SHA224);
md_register_algorithm(SHA256);
md_register_algorithm(SHA384);
md_register_algorithm(SHA512);
/* We _must_ have HMAC */
evp_hmac = EVP_MAC_fetch(NULL, "HMAC", NULL);
if (evp_hmac == NULL) {
ERR_clear_error();
FATAL_ERROR("OpenSSL failed to find an HMAC implementation. "
"Please make sure the default provider has an "
"EVP_MAC-HMAC implementation");
}
return ISC_R_SUCCESS;
}
static void
unregister_algorithms(void) {
for (size_t i = 0; i < ISC_MD_MAX; i++) {
if (isc__crypto_md[i] != NULL) {
EVP_MD_free(isc__crypto_md[i]);
isc__crypto_md[i] = NULL;
}
}
INSIST(evp_hmac != NULL);
EVP_MAC_free(evp_hmac);
evp_hmac = NULL;
}
#undef md_register_algorithm
/*
* HMAC
*/
/*
* Do not call EVP_Q_mac or HMAC (since it calls EVP_Q_mac internally)
*
* Each invocation of the EVP_Q_mac function causes an explicit fetch.
*/
isc_result_t
isc_hmac(isc_md_type_t type, const void *key, const size_t keylen,
const unsigned char *buf, const size_t len, unsigned char *digest,
unsigned int *digestlen) {
EVP_MAC_CTX *ctx;
size_t maclen;
REQUIRE(type < ISC_MD_MAX);
if (isc__crypto_md[type] == NULL) {
return ISC_R_NOTIMPLEMENTED;
}
ctx = EVP_MAC_CTX_new(evp_hmac);
RUNTIME_CHECK(ctx != NULL);
if (EVP_MAC_init(ctx, key, keylen, md_to_hmac_params[type]) != 1) {
goto fail;
}
if (EVP_MAC_update(ctx, buf, len) != 1) {
goto fail;
}
maclen = *digestlen;
if (EVP_MAC_final(ctx, digest, &maclen, maclen) != 1) {
goto fail;
}
*digestlen = maclen;
EVP_MAC_CTX_free(ctx);
return ISC_R_SUCCESS;
fail:
ERR_clear_error();
EVP_MAC_CTX_free(ctx);
return ISC_R_CRYPTOFAILURE;
}
/*
* You do not need to process the key to fit the block size.
*
* https://github.com/openssl/openssl/blob/925e4fba1098036e8f8d22652cff6f64c5c7d571/crypto/hmac/hmac.c#L61-L80
*/
isc_result_t
isc_hmac_key_create(isc_md_type_t type, const void *secret, const size_t len,
isc_mem_t *mctx, isc_hmac_key_t **keyp) {
isc_hmac_key_t *key;
uint8_t digest[ISC_MAX_MD_SIZE];
unsigned int digest_len = sizeof(digest);
size_t key_len;
REQUIRE(keyp != NULL && *keyp == NULL);
REQUIRE(type < ISC_MD_MAX);
if (isc__crypto_md[type] == NULL) {
return ISC_R_NOTIMPLEMENTED;
}
if (len > (size_t)EVP_MD_block_size(isc__crypto_md[type])) {
RETERR(isc_md(type, secret, len, digest, &digest_len));
secret = digest;
key_len = digest_len;
} else {
key_len = len;
}
key = isc_mem_get(mctx, STRUCT_FLEX_SIZE(key, secret, key_len));
*key = (isc_hmac_key_t){
.magic = hmac_key_magic,
.len = key_len,
.params = md_to_hmac_params[type],
};
memmove(key->secret, secret, key_len);
isc_mem_attach(mctx, &key->mctx);
*keyp = key;
return ISC_R_SUCCESS;
}
void
isc_hmac_key_destroy(isc_hmac_key_t **keyp) {
isc_hmac_key_t *key;
REQUIRE(keyp != NULL && *keyp != NULL);
REQUIRE((*keyp)->magic == hmac_key_magic);
key = *keyp;
*keyp = NULL;
key->magic = 0x00;
isc_safe_memwipe(key->secret, key->len);
isc_mem_putanddetach(&key->mctx, key,
STRUCT_FLEX_SIZE(key, secret, key->len));
}
isc_region_t
isc_hmac_key_expose(isc_hmac_key_t *key) {
REQUIRE(key != NULL && key->magic == hmac_key_magic);
return (isc_region_t){ .base = key->secret, .length = key->len };
}
bool
isc_hmac_key_equal(isc_hmac_key_t *a, isc_hmac_key_t *b) {
REQUIRE(a != NULL && a->magic == hmac_key_magic);
REQUIRE(b != NULL && b->magic == hmac_key_magic);
if (a->params != b->params) {
return false;
}
if (a->len != b->len) {
return false;
}
return isc_safe_memequal(a->secret, b->secret, a->len);
}
isc_hmac_t *
isc_hmac_new(void) {
EVP_MAC_CTX *ctx = EVP_MAC_CTX_new(evp_hmac);
RUNTIME_CHECK(ctx != NULL);
return ctx;
}
void
isc_hmac_free(isc_hmac_t *hmac) {
EVP_MAC_CTX_free(hmac);
}
isc_result_t
isc_hmac_init(isc_hmac_t *hmac, isc_hmac_key_t *key) {
REQUIRE(key != NULL && key->magic == hmac_key_magic);
REQUIRE(hmac != NULL);
if (EVP_MAC_init(hmac, key->secret, key->len, key->params) != 1) {
ERR_clear_error();
return ISC_R_CRYPTOFAILURE;
}
return ISC_R_SUCCESS;
}
isc_result_t
isc_hmac_update(isc_hmac_t *hmac, const unsigned char *buf, const size_t len) {
REQUIRE(hmac != NULL);
if (buf == NULL || len == 0) {
return ISC_R_SUCCESS;
}
if (EVP_MAC_update(hmac, buf, len) != 1) {
ERR_clear_error();
return ISC_R_CRYPTOFAILURE;
}
return ISC_R_SUCCESS;
}
isc_result_t
isc_hmac_final(isc_hmac_t *hmac, isc_buffer_t *out) {
size_t len;
REQUIRE(hmac != NULL);
len = isc_buffer_availablelength(out);
if (len < EVP_MAC_CTX_get_mac_size(hmac)) {
return ISC_R_NOSPACE;
}
if (EVP_MAC_final(hmac, isc_buffer_used(out), &len, len) != 1) {
ERR_clear_error();
return ISC_R_CRYPTOFAILURE;
}
isc_buffer_add(out, len);
return ISC_R_SUCCESS;
}
#if ISC_MEM_TRACKLINES
/*
* We use the internal isc__mem API here, so we can pass the file and line
* arguments passed from OpenSSL >= 1.1.0 to our memory functions for better
* tracking of the OpenSSL allocations. Without this, we would always just see
* isc__crypto_{malloc,realloc,free} in the tracking output, but with this in
* place we get to see the places in the OpenSSL code where the allocations
* happen.
*/
static void *
isc__crypto_malloc_ex(size_t size, const char *file, int line) {
return isc__mem_allocate(isc__crypto_mctx, size, 0, __func__, file,
(unsigned int)line);
}
static void *
isc__crypto_realloc_ex(void *ptr, size_t size, const char *file, int line) {
return isc__mem_reallocate(isc__crypto_mctx, ptr, size, 0, __func__,
file, (unsigned int)line);
}
static void
isc__crypto_free_ex(void *ptr, const char *file, int line) {
if (ptr == NULL) {
return;
}
if (isc__crypto_mctx != NULL) {
isc__mem_free(isc__crypto_mctx, ptr, 0, __func__, file,
(unsigned int)line);
}
}
#else /* ISC_MEM_TRACKLINES */
static void *
isc__crypto_malloc_ex(size_t size, const char *file, int line) {
UNUSED(file);
UNUSED(line);
return isc_mem_allocate(isc__crypto_mctx, size);
}
static void *
isc__crypto_realloc_ex(void *ptr, size_t size, const char *file, int line) {
UNUSED(file);
UNUSED(line);
return isc_mem_reallocate(isc__crypto_mctx, ptr, size);
}
static void
isc__crypto_free_ex(void *ptr, const char *file, int line) {
UNUSED(file);
UNUSED(line);
if (ptr == NULL) {
return;
}
if (isc__crypto_mctx != NULL) {
isc__mem_free(isc__crypto_mctx, ptr, 0);
}
}
#endif /* ISC_MEM_TRACKLINES */
bool
isc_crypto_fips_mode(void) {
return EVP_default_properties_is_fips_enabled(NULL) != 0;
}
isc_result_t
isc_crypto_fips_enable(void) {
if (isc_crypto_fips_mode()) {
return ISC_R_SUCCESS;
}
INSIST(fips == NULL);
fips = OSSL_PROVIDER_load(NULL, "fips");
if (fips == NULL) {
return isc_ossl_wrap_logged_toresult(
ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_CRYPTO,
"OSSL_PROVIDER_load", ISC_R_CRYPTOFAILURE);
}
INSIST(base == NULL);
base = OSSL_PROVIDER_load(NULL, "base");
if (base == NULL) {
OSSL_PROVIDER_unload(fips);
return isc_ossl_wrap_logged_toresult(
ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_CRYPTO,
"OSS_PROVIDER_load", ISC_R_CRYPTOFAILURE);
}
if (EVP_default_properties_enable_fips(NULL, 1) == 0) {
return isc_ossl_wrap_logged_toresult(
ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_CRYPTO,
"EVP_default_properties_enable_fips",
ISC_R_CRYPTOFAILURE);
}
unregister_algorithms();
register_algorithms();
return ISC_R_SUCCESS;
}
void
isc__crypto_setdestroycheck(bool check) {
isc_mem_setdestroycheck(isc__crypto_mctx, check);
}
void
isc__crypto_initialize(void) {
/*
* We call OPENSSL_cleanup() manually, in a correct order, thus disable
* the automatic atexit() handler.
*/
uint64_t opts = OPENSSL_INIT_LOAD_CONFIG | OPENSSL_INIT_NO_ATEXIT;
isc_mem_create("OpenSSL", &isc__crypto_mctx);
isc_mem_setdebugging(isc__crypto_mctx, 0);
isc_mem_setdestroycheck(isc__crypto_mctx, false);
/*
* CRYPTO_set_mem_(_ex)_functions() returns 1 on success or 0 on
* failure, which means OpenSSL already allocated some memory. There's
* nothing we can do about it.
*/
(void)CRYPTO_set_mem_functions(isc__crypto_malloc_ex,
isc__crypto_realloc_ex,
isc__crypto_free_ex);
RUNTIME_CHECK(OPENSSL_init_ssl(opts, NULL) == 1);
register_algorithms();
#if defined(ENABLE_FIPS_MODE)
if (isc_crypto_fips_enable() != ISC_R_SUCCESS) {
ERR_clear_error();
FATAL_ERROR("Failed to toggle FIPS mode but is "
"required for this build");
}
#endif
/* Protect ourselves against unseeded PRNG */
if (RAND_status() != 1) {
isc_ossl_wrap_logged_toresult(
ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_CRYPTO,
"RAND_status", ISC_R_CRYPTOFAILURE);
FATAL_ERROR("OpenSSL pseudorandom number generator "
"cannot be initialized (see the `PRNG not "
"seeded' message in the OpenSSL FAQ)");
}
}
void
isc__crypto_shutdown(void) {
unregister_algorithms();
if (base != NULL) {
OSSL_PROVIDER_unload(base);
}
if (fips != NULL) {
OSSL_PROVIDER_unload(fips);
}
OPENSSL_cleanup();
isc_mem_detach(&isc__crypto_mctx);
}

View file

@ -0,0 +1,25 @@
/*
* 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.
*/
#include <openssl/err.h>
#include <openssl/evp.h>
#include <isc/crypto.h>
#include <isc/md.h>
#include <isc/ossl_wrap.h>
EVP_MD *isc__crypto_md[] = {
[ISC_MD_UNKNOWN] = NULL, [ISC_MD_MD5] = NULL, [ISC_MD_SHA1] = NULL,
[ISC_MD_SHA224] = NULL, [ISC_MD_SHA256] = NULL, [ISC_MD_SHA384] = NULL,
[ISC_MD_SHA512] = NULL,
};

View file

@ -1,168 +0,0 @@
/*
* 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.
*/
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/opensslv.h>
#include <isc/assertions.h>
#include <isc/hmac.h>
#include <isc/md.h>
#include <isc/safe.h>
#include <isc/string.h>
#include <isc/types.h>
#include <isc/util.h>
#include "openssl_shim.h"
isc_hmac_t *
isc_hmac_new(void) {
EVP_MD_CTX *hmac_st = EVP_MD_CTX_new();
RUNTIME_CHECK(hmac_st != NULL);
return (isc_hmac_t *)hmac_st;
}
void
isc_hmac_free(isc_hmac_t *hmac_st) {
if (hmac_st == NULL) {
return;
}
EVP_MD_CTX_free((EVP_MD_CTX *)hmac_st);
}
isc_result_t
isc_hmac_init(isc_hmac_t *hmac_st, const void *key, const size_t keylen,
const isc_md_type_t *md_type) {
EVP_PKEY *pkey;
REQUIRE(hmac_st != NULL);
REQUIRE(key != NULL);
REQUIRE(keylen <= INT_MAX);
if (md_type == NULL) {
return ISC_R_NOTIMPLEMENTED;
}
pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_HMAC, NULL, key, keylen);
if (pkey == NULL) {
ERR_clear_error();
return ISC_R_CRYPTOFAILURE;
}
if (EVP_DigestSignInit(hmac_st, NULL, md_type, NULL, pkey) != 1) {
EVP_PKEY_free(pkey);
ERR_clear_error();
return ISC_R_CRYPTOFAILURE;
}
EVP_PKEY_free(pkey);
return ISC_R_SUCCESS;
}
isc_result_t
isc_hmac_reset(isc_hmac_t *hmac_st) {
REQUIRE(hmac_st != NULL);
if (EVP_MD_CTX_reset(hmac_st) != 1) {
ERR_clear_error();
return ISC_R_CRYPTOFAILURE;
}
return ISC_R_SUCCESS;
}
isc_result_t
isc_hmac_update(isc_hmac_t *hmac_st, const unsigned char *buf,
const size_t len) {
REQUIRE(hmac_st != NULL);
if (buf == NULL || len == 0) {
return ISC_R_SUCCESS;
}
if (EVP_DigestSignUpdate(hmac_st, buf, len) != 1) {
ERR_clear_error();
return ISC_R_CRYPTOFAILURE;
}
return ISC_R_SUCCESS;
}
isc_result_t
isc_hmac_final(isc_hmac_t *hmac_st, unsigned char *digest,
unsigned int *digestlen) {
REQUIRE(hmac_st != NULL);
REQUIRE(digest != NULL);
REQUIRE(digestlen != NULL);
size_t len = *digestlen;
if (EVP_DigestSignFinal(hmac_st, digest, &len) != 1) {
ERR_clear_error();
return ISC_R_CRYPTOFAILURE;
}
*digestlen = (unsigned int)len;
return ISC_R_SUCCESS;
}
const isc_md_type_t *
isc_hmac_get_md_type(isc_hmac_t *hmac_st) {
REQUIRE(hmac_st != NULL);
return EVP_MD_CTX_get0_md(hmac_st);
}
size_t
isc_hmac_get_size(isc_hmac_t *hmac_st) {
REQUIRE(hmac_st != NULL);
return (size_t)EVP_MD_CTX_size(hmac_st);
}
int
isc_hmac_get_block_size(isc_hmac_t *hmac_st) {
REQUIRE(hmac_st != NULL);
return EVP_MD_CTX_block_size(hmac_st);
}
isc_result_t
isc_hmac(const isc_md_type_t *type, const void *key, const size_t keylen,
const unsigned char *buf, const size_t len, unsigned char *digest,
unsigned int *digestlen) {
isc_result_t res;
isc_hmac_t *hmac_st = isc_hmac_new();
res = isc_hmac_init(hmac_st, key, keylen, type);
if (res != ISC_R_SUCCESS) {
goto end;
}
res = isc_hmac_update(hmac_st, buf, len);
if (res != ISC_R_SUCCESS) {
goto end;
}
res = isc_hmac_final(hmac_st, digest, digestlen);
if (res != ISC_R_SUCCESS) {
goto end;
}
end:
isc_hmac_free(hmac_st);
return res;
}

View file

@ -13,17 +13,8 @@
#pragma once
#include <openssl/evp.h>
#include <isc/types.h>
extern const EVP_MD *isc__crypto_md5;
extern const EVP_MD *isc__crypto_sha1;
extern const EVP_MD *isc__crypto_sha224;
extern const EVP_MD *isc__crypto_sha256;
extern const EVP_MD *isc__crypto_sha384;
extern const EVP_MD *isc__crypto_sha512;
bool
isc_crypto_fips_mode(void);
/*

View file

@ -18,12 +18,16 @@
#pragma once
#include <stdbool.h>
#include <isc/md.h>
#include <isc/result.h>
#include <isc/types.h>
typedef void isc_hmac_t;
typedef struct isc_hmac_key isc_hmac_key_t;
/**
* isc_hmac:
* @type: the digest type
@ -43,10 +47,50 @@ typedef void isc_hmac_t;
* of digest written to @digest.
*/
isc_result_t
isc_hmac(const isc_md_type_t *type, const void *key, const size_t keylen,
isc_hmac(isc_md_type_t type, const void *key, const size_t keylen,
const unsigned char *buf, const size_t len, unsigned char *digest,
unsigned int *digestlen);
/*
* isc_hmac_key_create:
* @type: the digest type
* @secret: the secret key
* @len: length of the secret key
* @mctx: memory context
* @keyp: pointer to the key pointer
*
* This initializes the an HMAC key bound to a specific digest.
*/
isc_result_t
isc_hmac_key_create(isc_md_type_t type, const void *secret, const size_t len,
isc_mem_t *mctx, isc_hmac_key_t **keyp);
void
isc_hmac_key_destroy(isc_hmac_key_t **keyp);
/**
* isc_hmac_key_expose:
* @key: The key to be exposed
*
* This function exposes the raw bytes of the HMAC key.
*
* The region is bound to the lifetime of the @key and MUST NOT be used after
* calling isc_hmac_key_destroy.
*/
isc_region_t
isc_hmac_key_expose(isc_hmac_key_t *key);
/**
* isc_hmac_key_equal:
* @key1: The first key to be compared
* @key2: The second key to be compared
*
* Returns true is the two HMAC keys have the same contents and use the same
* digest function.
*/
bool
isc_hmac_key_equal(isc_hmac_key_t *key1, isc_hmac_key_t *key2);
/**
* isc_hmac_new:
*
@ -68,26 +112,11 @@ isc_hmac_free(isc_hmac_t *hmac);
* isc_hmac_init:
* @md: HMAC context
* @key: HMAC key
* @keylen: HMAC key length
* @type: digest type
*
* This function sets up HMAC context to use a hash function of @type and key
* @key which is @keylen bytes long.
*/
isc_result_t
isc_hmac_init(isc_hmac_t *hmac, const void *key, const size_t keylen,
const isc_md_type_t *type);
/**
* isc_hmac_reset:
* @hmac: HMAC context
*
* This function resets the HMAC context. This can be used to reuse an already
* existing context.
* This function sets up HMAC context to use the secret specified in @key.
*/
isc_result_t
isc_hmac_reset(isc_hmac_t *hmac);
isc_hmac_init(isc_hmac_t *hmac, isc_hmac_key_t *key);
/**
* isc_hmac_update:
@ -104,44 +133,13 @@ isc_hmac_update(isc_hmac_t *hmac, const unsigned char *buf, const size_t len);
/**
* isc_hmac_final:
* @hmac: HMAC context
* @digest: the output buffer
* @digestlen: in: the length of @digest
* out: the length of the data written to @digest
* @out: the output buffer
*
* This function retrieves the message authentication code from @hmac and places
* it in @digest, which must have space for the hash function output. @digestlen
* is used to pass in the length of the digest buffer and returns the length
* of digest written to @digest. After calling this function no additional
* calls to isc_hmac_update() can be made.
* it in @out, which must have space for the hash function output.
*
* After calling this function no additional calls to isc_hmac_update() can be
* made. Use isc_hmac_init() to reset/re-initialize the context.
*/
isc_result_t
isc_hmac_final(isc_hmac_t *hmac, unsigned char *digest,
unsigned int *digestlen);
/**
* isc_hmac_md_type:
* @hmac: HMAC context
*
* This function return the isc_md_type_t previously set for the supplied
* HMAC context or NULL if no isc_md_type_t has been set.
*/
const isc_md_type_t *
isc_hmac_get_md_type(isc_hmac_t *hmac);
/**
* isc_hmac_get_size:
*
* This function return the size of the message digest when passed an isc_hmac_t
* structure, i.e. the size of the hash.
*/
size_t
isc_hmac_get_size(isc_hmac_t *hmac);
/**
* isc_hmac_get_block_size:
*
* This function return the block size of the message digest when passed an
* isc_hmac_t structure.
*/
int
isc_hmac_get_block_size(isc_hmac_t *hmac);
isc_hmac_final(isc_hmac_t *hmac, isc_buffer_t *out);

View file

@ -18,7 +18,6 @@
#pragma once
#include <isc/crypto.h>
#include <isc/result.h>
#include <isc/types.h>
@ -35,27 +34,29 @@ typedef void isc_md_t;
*
* Enumeration of supported message digest algorithms.
*/
typedef void isc_md_type_t;
typedef enum isc_md_type {
ISC_MD_UNKNOWN = 0x00,
ISC_MD_MD5 = 0x01,
ISC_MD_SHA1 = 0x02,
ISC_MD_SHA224 = 0x03,
ISC_MD_SHA256 = 0x04,
ISC_MD_SHA384 = 0x05,
ISC_MD_SHA512 = 0x06,
ISC_MD_MAX = 0x07,
} isc_md_type_t;
#define ISC_MD_MD5 isc__crypto_md5
#define ISC_MD_SHA1 isc__crypto_sha1
#define ISC_MD_SHA224 isc__crypto_sha224
#define ISC_MD_SHA256 isc__crypto_sha256
#define ISC_MD_SHA384 isc__crypto_sha384
#define ISC_MD_SHA512 isc__crypto_sha512
#define ISC_MD5_DIGESTLENGTH isc_md_type_get_size(ISC_MD_MD5)
#define ISC_MD5_BLOCK_LENGTH isc_md_type_get_block_size(ISC_MD_MD5)
#define ISC_SHA1_DIGESTLENGTH isc_md_type_get_size(ISC_MD_SHA1)
#define ISC_SHA1_BLOCK_LENGTH isc_md_type_get_block_size(ISC_MD_SHA1)
#define ISC_SHA224_DIGESTLENGTH isc_md_type_get_size(ISC_MD_SHA224)
#define ISC_SHA224_BLOCK_LENGTH isc_md_type_get_block_size(ISC_MD_SHA224)
#define ISC_SHA256_DIGESTLENGTH isc_md_type_get_size(ISC_MD_SHA256)
#define ISC_SHA256_BLOCK_LENGTH isc_md_type_get_block_size(ISC_MD_SHA256)
#define ISC_SHA384_DIGESTLENGTH isc_md_type_get_size(ISC_MD_SHA384)
#define ISC_SHA384_BLOCK_LENGTH isc_md_type_get_block_size(ISC_MD_SHA384)
#define ISC_SHA512_DIGESTLENGTH isc_md_type_get_size(ISC_MD_SHA512)
#define ISC_SHA512_BLOCK_LENGTH isc_md_type_get_block_size(ISC_MD_SHA512)
#define ISC_MD5_DIGESTLENGTH 16
#define ISC_MD5_BLOCK_LENGTH 64
#define ISC_SHA1_DIGESTLENGTH 20
#define ISC_SHA1_BLOCK_LENGTH 64
#define ISC_SHA224_DIGESTLENGTH 28
#define ISC_SHA224_BLOCK_LENGTH 64
#define ISC_SHA256_DIGESTLENGTH 32
#define ISC_SHA256_BLOCK_LENGTH 64
#define ISC_SHA384_DIGESTLENGTH 48
#define ISC_SHA384_BLOCK_LENGTH 128
#define ISC_SHA512_DIGESTLENGTH 64
#define ISC_SHA512_BLOCK_LENGTH 128
#define ISC_MAX_MD_SIZE 64U /* EVP_MAX_MD_SIZE */
#define ISC_MAX_BLOCK_SIZE 128U /* ISC_SHA512_BLOCK_LENGTH */
@ -74,7 +75,7 @@ typedef void isc_md_type_t;
* at @digestlen, at most ISC_MAX_MD_SIZE bytes will be written.
*/
isc_result_t
isc_md(const isc_md_type_t *type, const unsigned char *buf, const size_t len,
isc_md(isc_md_type_t type, const unsigned char *buf, const size_t len,
unsigned char *digest, unsigned int *digestlen);
/**
@ -93,7 +94,7 @@ isc_md_new(void);
* to it.
*/
void
isc_md_free(isc_md_t *);
isc_md_free(isc_md_t *md);
/**
* isc_md_init:
@ -104,7 +105,7 @@ isc_md_free(isc_md_t *);
* initialized before calling this function.
*/
isc_result_t
isc_md_init(isc_md_t *, const isc_md_type_t *md_type);
isc_md_init(isc_md_t *md, isc_md_type_t type);
/**
* isc_md_reset:
@ -144,16 +145,6 @@ isc_md_update(isc_md_t *md, const unsigned char *buf, const size_t len);
isc_result_t
isc_md_final(isc_md_t *md, unsigned char *digest, unsigned int *digestlen);
/**
* isc_md_get_type:
* @md: message digest contezt
*
* This function return the isc_md_type_t previously set for the supplied
* message digest context or NULL if no isc_md_type_t has been set.
*/
const isc_md_type_t *
isc_md_get_md_type(isc_md_t *md);
/**
* isc_md_size:
*
@ -172,15 +163,6 @@ isc_md_get_size(isc_md_t *md);
size_t
isc_md_get_block_size(isc_md_t *md);
/**
* isc_md_size:
*
* This function return the size of the message digest when passed an
* isc_md_type_t , i.e. the size of the hash.
*/
size_t
isc_md_type_get_size(const isc_md_type_t *md_type);
/**
* isc_md_block_size:
*
@ -188,4 +170,4 @@ isc_md_type_get_size(const isc_md_type_t *md_type);
* isc_md_type_t.
*/
size_t
isc_md_type_get_block_size(const isc_md_type_t *md_type);
isc_md_type_get_block_size(isc_md_type_t type);

View file

@ -0,0 +1,287 @@
/*
* 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.
*/
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <openssl/bn.h>
#include <openssl/evp.h>
#include <isc/log.h>
#include <isc/types.h>
#define isc_ossl_wrap_logged_toresult(category, module, funcname, fallback) \
isc__ossl_wrap_logged_toresult(category, module, funcname, fallback, \
__FILE__, __LINE__)
typedef struct isc_ossl_wrap_rsa_components {
bool needs_cleanup;
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);
/*%
* Creates a RSA key with the specified bit-size
*
* Requires:
* \li `pkeyp != NULL`
* \li `*pkeyp == NULL`
*/
isc_result_t
isc_ossl_wrap_generate_pkcs11_rsa_key(char *uri, size_t bit_size,
EVP_PKEY **pkeyp);
/*%
* Creates a RSA key with the specified bit-size using the PKCS11 label
* specified at `uri`.
*
* Requires:
* \li `pkeyp != NULL`
* \li `*pkeyp == NULL`
* \li `uri != NULL` and is a NUL-terminated string
*/
bool
isc_ossl_wrap_rsa_key_bits_leq(EVP_PKEY *pkey, size_t limit);
isc_result_t
isc_ossl_wrap_rsa_public_components(EVP_PKEY *pkey,
isc_ossl_wrap_rsa_components_t *c);
isc_result_t
isc_ossl_wrap_rsa_secret_components(EVP_PKEY *pkey,
isc_ossl_wrap_rsa_components_t *c);
isc_result_t
isc_ossl_wrap_load_rsa_public_from_components(isc_ossl_wrap_rsa_components_t *c,
EVP_PKEY **pkeyp);
/*%
* Create a verifying `EVP_PKEY` using the public RSA components at `c`
*
* Requires:
* \li `pkeyp != NULL`
* \li `*pkeyp == NULL`
* \li `c != NULL`
* \li `c.n != NULL`
* \li `c.e != NULL`
*/
isc_result_t
isc_ossl_wrap_load_rsa_secret_from_components(isc_ossl_wrap_rsa_components_t *c,
EVP_PKEY **pkeyp);
/*%
* Create a signing `EVP_PKEY` using the public and secret RSA components at `c`
*
* Requires:
* \li `pkeyp != NULL`
* \li `*pkeyp == NULL`
* \li `c != NULL`
* \li `c.n != NULL`
* \li `c.e != NULL`
*/
void
isc_ossl_wrap_rsa_components_cleanup(isc_ossl_wrap_rsa_components_t *comp);
isc_result_t
isc_ossl_wrap_toresult(isc_result_t fallback);
isc_result_t
isc__ossl_wrap_logged_toresult(isc_logcategory_t category,
isc_logmodule_t module, const char *funcname,
isc_result_t fallback, const char *file,
int line);
/*
* This is a bit of a namespace convention violation but it fits the spirit of
* this header since it is exposing OpenSSL-isms to others.
*/
extern EVP_MD *isc__crypto_md[];

View file

@ -615,11 +615,3 @@ isc_tls_valid_sni_hostname(const char *hostname);
* string. Returns 'true' if the hostname is likely a domain name, and
* 'false' if it represents an IP address.
*/
#define isc_tlserr2result(category, module, funcname, fallback) \
isc__tlserr2result(category, module, funcname, fallback, __FILE__, \
__LINE__)
isc_result_t
isc__tlserr2result(isc_logcategory_t category, isc_logmodule_t module,
const char *funcname, isc_result_t fallback,
const char *file, int line);

View file

@ -17,8 +17,9 @@
#include <openssl/err.h>
#include <openssl/opensslv.h>
#include <isc/crypto.h>
#include <isc/iterated_hash.h>
#include <isc/md.h>
#include <isc/ossl_wrap.h>
#include <isc/thread.h>
#include <isc/util.h>
@ -150,7 +151,8 @@ isc__iterated_hash_initialize(void) {
mdctx = EVP_MD_CTX_new();
INSIST(mdctx != NULL);
RUNTIME_CHECK(EVP_DigestInit_ex(basectx, isc__crypto_sha1, NULL) == 1);
RUNTIME_CHECK(EVP_DigestInit_ex(basectx, isc__crypto_md[ISC_MD_SHA1],
NULL) == 1);
initialized = true;
}

View file

@ -18,6 +18,7 @@
#include <openssl/opensslv.h>
#include <isc/md.h>
#include <isc/ossl_wrap.h>
#include <isc/util.h>
#include "openssl_shim.h"
@ -39,14 +40,18 @@ isc_md_free(isc_md_t *md) {
}
isc_result_t
isc_md_init(isc_md_t *md, const isc_md_type_t *md_type) {
REQUIRE(md != NULL);
isc_md_init(isc_md_t *md, isc_md_type_t type) {
EVP_MD *evp;
if (md_type == NULL) {
REQUIRE(md != NULL);
REQUIRE(type < ISC_MD_MAX);
evp = isc__crypto_md[type];
if (evp == NULL) {
return ISC_R_NOTIMPLEMENTED;
}
if (EVP_DigestInit_ex(md, md_type, NULL) != 1) {
if (EVP_DigestInit_ex(md, evp, NULL) != 1) {
ERR_clear_error();
return ISC_R_CRYPTOFAILURE;
}
@ -95,13 +100,6 @@ isc_md_final(isc_md_t *md, unsigned char *digest, unsigned int *digestlen) {
return ISC_R_SUCCESS;
}
const isc_md_type_t *
isc_md_get_md_type(isc_md_t *md) {
REQUIRE(md != NULL);
return EVP_MD_CTX_get0_md(md);
}
size_t
isc_md_get_size(isc_md_t *md) {
REQUIRE(md != NULL);
@ -117,38 +115,31 @@ isc_md_get_block_size(isc_md_t *md) {
}
size_t
isc_md_type_get_size(const isc_md_type_t *md_type) {
isc_md_type_get_block_size(isc_md_type_t type) {
EVP_MD *evp;
REQUIRE(type < ISC_MD_MAX);
STATIC_ASSERT(ISC_MAX_MD_SIZE >= EVP_MAX_MD_SIZE,
"Change ISC_MAX_MD_SIZE to be greater than or equal to "
"EVP_MAX_MD_SIZE");
if (md_type != NULL) {
return (size_t)EVP_MD_size(md_type);
}
return ISC_MAX_MD_SIZE;
}
size_t
isc_md_type_get_block_size(const isc_md_type_t *md_type) {
STATIC_ASSERT(ISC_MAX_MD_SIZE >= EVP_MAX_MD_SIZE,
"Change ISC_MAX_MD_SIZE to be greater than or equal to "
"EVP_MAX_MD_SIZE");
if (md_type != NULL) {
return (size_t)EVP_MD_block_size(md_type);
evp = isc__crypto_md[type];
if (evp != NULL) {
return (size_t)EVP_MD_block_size(evp);
}
return ISC_MAX_MD_SIZE;
}
isc_result_t
isc_md(const isc_md_type_t *md_type, const unsigned char *buf, const size_t len,
isc_md(isc_md_type_t type, const unsigned char *buf, const size_t len,
unsigned char *digest, unsigned int *digestlen) {
isc_md_t *md;
isc_result_t res;
md = isc_md_new();
res = isc_md_init(md, md_type);
res = isc_md_init(md, type);
if (res != ISC_R_SUCCESS) {
goto end;
}

View file

@ -20,7 +20,9 @@ endif
# isc_inc += include_directories('include')
isc_inc_p += include_directories('.')
subdir('crypto')
subdir('netmgr')
subdir('ossl_wrap')
isc_srcset.add(
m_dep,
@ -75,7 +77,6 @@ isc_srcset.add(
'base64.c',
'commandline.c',
'counter.c',
'crypto.c',
'dir.c',
'errno.c',
'errno2result.c',
@ -88,7 +89,6 @@ isc_srcset.add(
'helper.c',
'hex.c',
'histo.c',
'hmac.c',
'ht.c',
'httpd.c',
'interfaceiter.c',

View file

@ -18,10 +18,6 @@
#include <openssl/opensslv.h>
#include <openssl/ssl.h>
#if !HAVE_EVP_MD_CTX_GET0_MD
#define EVP_MD_CTX_get0_md EVP_MD_CTX_md
#endif /* if !HAVE_EVP_MD_CTX_GET0_MD */
#if !HAVE_BIO_READ_EX
int
BIO_read_ex(BIO *b, void *data, size_t dlen, size_t *readbytes);

View file

@ -0,0 +1,16 @@
# 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.
isc_srcset.add(
when: 'HAVE_OPENSSL_3',
if_true: files('ossl3.c', 'ossl_common.c'),
if_false: files('ossl1_1.c', 'ossl_common.c'),
)

599
lib/isc/ossl_wrap/ossl1_1.c Normal file
View file

@ -0,0 +1,599 @@
/*
* 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.
*/
#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);
UNUSED(n);
fptr = BN_GENCB_get_arg(cb);
if (fptr != NULL) {
fptr(p);
}
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"));
}
EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
EC_KEY_set_conv_form(eckey, POINT_CONVERSION_UNCOMPRESSED);
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) {
RSA *rsa = NULL;
EVP_PKEY *pkey = NULL;
BN_GENCB *cb = NULL;
isc_result_t result;
BIGNUM *e;
e = BN_new();
/* e = 65537 (0x10001, F4) */
BN_set_bit(e, 0);
BN_set_bit(e, 16);
rsa = RSA_new();
if (rsa == NULL) {
CLEANUP(OSSL_WRAP_ERROR("RSA_new"));
}
pkey = EVP_PKEY_new();
if (pkey == NULL) {
CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_new"));
}
if (EVP_PKEY_set1_RSA(pkey, rsa) != 1) {
CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_set1_RSA"));
}
if (callback != NULL) {
cb = BN_GENCB_new();
if (cb == NULL) {
CLEANUP(OSSL_WRAP_ERROR("BN_GENCB_new"));
}
BN_GENCB_set(cb, rsa_keygen_progress_cb, (void *)callback);
}
if (RSA_generate_key_ex(rsa, bit_size, e, cb) != 1) {
CLEANUP(OSSL_WRAP_ERROR("RSA_generate_key_ex"));
}
*pkeyp = pkey;
pkey = NULL;
result = ISC_R_SUCCESS;
cleanup:
EVP_PKEY_free(pkey);
RSA_free(rsa);
BN_GENCB_free(cb);
BN_free(e);
return result;
}
isc_result_t
isc_ossl_wrap_generate_pkcs11_rsa_key(char *uri, size_t bit_size,
EVP_PKEY **pkeyp) {
UNUSED(uri);
return isc_ossl_wrap_generate_rsa_key(NULL, bit_size, pkeyp);
}
bool
isc_ossl_wrap_rsa_key_bits_leq(EVP_PKEY *pkey, size_t limit) {
const RSA *rsa;
const BIGNUM *ce;
size_t bits = SIZE_MAX;
REQUIRE(pkey != NULL);
rsa = EVP_PKEY_get0_RSA(pkey);
if (rsa != NULL) {
ce = NULL;
RSA_get0_key(rsa, NULL, &ce, NULL);
if (ce != NULL) {
bits = BN_num_bits(ce);
}
}
return bits <= limit;
}
isc_result_t
isc_ossl_wrap_rsa_public_components(EVP_PKEY *pkey,
isc_ossl_wrap_rsa_components_t *c) {
isc_result_t result;
const RSA *rsa;
REQUIRE(pkey != NULL);
REQUIRE(c != NULL && c->e == NULL && c->n == NULL);
rsa = EVP_PKEY_get0_RSA(pkey);
if (rsa == NULL) {
CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_get0_RSA"));
}
RSA_get0_key(rsa, (const BIGNUM **)&c->n, (const BIGNUM **)&c->e, NULL);
result = ISC_R_SUCCESS;
cleanup:
return result;
}
isc_result_t
isc_ossl_wrap_rsa_secret_components(EVP_PKEY *pkey,
isc_ossl_wrap_rsa_components_t *c) {
isc_result_t result;
const RSA *rsa;
REQUIRE(pkey != NULL);
REQUIRE(c != NULL && c->d == NULL && c->p == NULL && c->q == NULL &&
c->dmp1 == NULL && c->dmq1 == NULL && c->iqmp == NULL);
rsa = EVP_PKEY_get0_RSA(pkey);
if (rsa == NULL) {
CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_get0_RSA"));
}
/*
* We don't support PKCS11 with OpenSSL <=1.1.1a
* d *must* succeed.
*/
RSA_get0_key(rsa, NULL, NULL, (const BIGNUM **)&c->d);
if (c->d == NULL) {
CLEANUP(OSSL_WRAP_ERROR("RSA_get0_key"));
}
RSA_get0_factors(rsa, (const BIGNUM **)&c->p, (const BIGNUM **)&c->q);
RSA_get0_crt_params(rsa, (const BIGNUM **)&c->dmp1,
(const BIGNUM **)&c->dmq1,
(const BIGNUM **)&c->iqmp);
result = ISC_R_SUCCESS;
cleanup:
return result;
}
isc_result_t
isc_ossl_wrap_load_rsa_public_from_components(isc_ossl_wrap_rsa_components_t *c,
EVP_PKEY **pkeyp) {
isc_result_t result;
EVP_PKEY *pkey = NULL;
RSA *rsa = NULL;
REQUIRE(pkeyp != NULL && *pkeyp == NULL);
REQUIRE(c != NULL && c->e != NULL && c->n != NULL);
REQUIRE(c->needs_cleanup);
rsa = RSA_new();
if (rsa == NULL) {
CLEANUP(OSSL_WRAP_ERROR("RSA_new"));
}
if (RSA_set0_key(rsa, c->n, c->e, NULL) != 1) {
CLEANUP(OSSL_WRAP_ERROR("RSA_set0_key"));
}
c->n = NULL;
c->e = NULL;
pkey = EVP_PKEY_new();
if (pkey == NULL) {
CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_new"));
}
if (EVP_PKEY_set1_RSA(pkey, rsa) != 1) {
CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_set1_RSA"));
}
*pkeyp = pkey;
pkey = NULL;
result = ISC_R_SUCCESS;
cleanup:
EVP_PKEY_free(pkey);
RSA_free(rsa);
return result;
}
isc_result_t
isc_ossl_wrap_load_rsa_secret_from_components(isc_ossl_wrap_rsa_components_t *c,
EVP_PKEY **pkeyp) {
isc_result_t result;
EVP_PKEY *pkey = NULL;
RSA *rsa = NULL;
REQUIRE(pkeyp != NULL && *pkeyp == NULL);
REQUIRE(c != NULL);
result = ISC_R_SUCCESS;
rsa = RSA_new();
if (rsa == NULL) {
CLEANUP(OSSL_WRAP_ERROR("RSA_new"));
}
if (RSA_set0_key(rsa, c->n, c->e, c->d) != 1) {
CLEANUP(OSSL_WRAP_ERROR("RSA_set0_key"));
}
c->n = NULL;
c->e = NULL;
c->d = NULL;
if (c->p != NULL || c->q != NULL) {
if (RSA_set0_factors(rsa, c->p, c->q) != 1) {
CLEANUP(OSSL_WRAP_ERROR("RSA_set0_factors"));
}
c->p = NULL;
c->q = NULL;
}
if (c->dmp1 != NULL || c->dmq1 != NULL || c->iqmp != NULL) {
if (RSA_set0_crt_params(rsa, c->dmp1, c->dmq1, c->iqmp) != 1) {
CLEANUP(OSSL_WRAP_ERROR("RSA_set0_crt_params"));
}
c->dmp1 = NULL;
c->dmq1 = NULL;
c->iqmp = NULL;
}
pkey = EVP_PKEY_new();
if (pkey == NULL) {
CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_new"));
}
if (EVP_PKEY_set1_RSA(pkey, rsa) != 1) {
CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_set1_RSA"));
}
*pkeyp = pkey;
pkey = NULL;
result = ISC_R_SUCCESS;
cleanup:
EVP_PKEY_free(pkey);
RSA_free(rsa);
isc_ossl_wrap_rsa_components_cleanup(c);
return result;
}

782
lib/isc/ossl_wrap/ossl3.c Normal file
View file

@ -0,0 +1,782 @@
/*
* 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.
*/
#include <stdbool.h>
#include <string.h>
#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/crypto.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, curve##_params); \
} \
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##_params); \
} \
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##_params); \
} \
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##_params); \
} \
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 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;
/*
* "group" MUST be the first parameter, we rely on it to get the group name.
*/
/* clang-format off */
static const OSSL_PARAM p256_params[] = {
OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
UNCONST("prime256v1"), sizeof("prime256v1") - 1),
OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_ENCODING,
UNCONST("named_curve"), sizeof("named_curve") - 1),
OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT,
UNCONST("uncompressed"), sizeof("uncompressed") - 1),
OSSL_PARAM_END,
};
static const OSSL_PARAM p384_params[] = {
OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
UNCONST("secp384r1"), sizeof("secp384r1") - 1),
OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_ENCODING,
UNCONST("named_curve"), sizeof("named_curve") - 1),
OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT,
UNCONST("uncompressed"), sizeof("uncompressed") - 1),
OSSL_PARAM_END,
};
/* clang-format on */
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);
fptr = EVP_PKEY_CTX_get_app_data(ctx);
if (fptr != NULL) {
int p = EVP_PKEY_CTX_get_keygen_info(ctx, 0);
fptr(p);
}
return 1;
}
static isc_result_t
generate_ec_key(EVP_PKEY **pkeyp, const OSSL_PARAM *const params) {
isc_result_t result;
EVP_PKEY_CTX *pctx = NULL;
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_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"));
}
/*
* EVP_PKEY_keygen is an older function now equivalent to
* EVP_PKEY_generate with an additional check that EVP_PKEY_CTX has been
* initialized with EVP_PKEY_keygen_init.
*
* Since we can guarantee such condition we use EVP_PKEY_generate
* directly.
*/
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
generate_pkcs11_ec_key(char *uri, EVP_PKEY **pkeyp, int nid) {
isc_result_t result;
EVP_PKEY_CTX *pctx;
size_t len;
INSIST(uri != NULL);
len = strlen(uri);
const OSSL_PARAM params[] = {
OSSL_PARAM_utf8_string("pkcs11_uri", uri, len),
OSSL_PARAM_utf8_string("pkcs11_key_usage", pkcs11_key_usage,
sizeof(pkcs11_key_usage) - 1),
OSSL_PARAM_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"));
}
/*
* EVP_PKEY_keygen is an older function now equivalent to
* EVP_PKEY_generate with an additional check that EVP_PKEY_CTX has been
* initialized with EVP_PKEY_keygen_init.
*
* Since we can guarantee such condition we use EVP_PKEY_generate
* directly.
*/
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 OSSL_PARAM *const curve_params) {
isc_result_t result;
const char *expected = curve_params[0].data;
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 (strncmp(expected, actual, curve_params[0].data_size) != 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 OSSL_PARAM *const curve_params) {
isc_result_t result;
EVP_PKEY_CTX *pctx = NULL;
uint8_t buffer[MAX_PUBLIC_KEY_SIZE + 1];
OSSL_PARAM params[] = {
curve_params[0], /* group */
OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, buffer,
region.length + 1),
OSSL_PARAM_END,
};
buffer[0] = POINT_CONVERSION_UNCOMPRESSED;
memmove(buffer + 1, region.base, region.length);
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:
EVP_PKEY_CTX_free(pctx);
return result;
}
static isc_result_t
load_ec_secret_from_region(isc_region_t region, EVP_PKEY **pkeyp,
const OSSL_PARAM *const curve_params) {
uint8_t public[MAX_PUBLIC_KEY_SIZE + 1];
OSSL_PARAM_BLD *bld = NULL;
OSSL_PARAM *params = NULL;
EVP_PKEY_CTX *pctx = 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_from_params(curve_params, NULL, NULL);
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, curve_params[0].key,
curve_params[0].data,
curve_params[0].data_size) != 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] = {
OSSL_PARAM_construct_utf8_string("digest", UNCONST(hash), 0),
OSSL_PARAM_construct_uint("nonce-type", &rfc6979),
OSSL_PARAM_END,
};
REQUIRE(pctx != NULL && hash != NULL);
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) {
isc_result_t result;
EVP_PKEY_CTX *ctx;
uint32_t e = 65537;
REQUIRE(pkeyp != NULL && *pkeyp == NULL);
/*
* https://docs.openssl.org/master/man7/EVP_PKEY-RSA/#rsa-key-generation-parameters
*/
const OSSL_PARAM params[3] = {
OSSL_PARAM_uint(OSSL_PKEY_PARAM_RSA_E, &e),
OSSL_PARAM_size_t(OSSL_PKEY_PARAM_RSA_BITS, &bit_size),
OSSL_PARAM_END,
};
ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL);
if (ctx == NULL) {
CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_CTX_new_from_name"));
}
if (EVP_PKEY_keygen_init(ctx) != 1) {
CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_keygen_init"));
}
if (EVP_PKEY_CTX_set_params(ctx, params) != 1) {
CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_CTX_set_params"));
}
if (callback != NULL) {
EVP_PKEY_CTX_set_app_data(ctx, (void *)callback);
EVP_PKEY_CTX_set_cb(ctx, rsa_keygen_progress_cb);
}
/*
* EVP_PKEY_keygen is an older function now equivalent to
* EVP_PKEY_generate with an additional check that EVP_PKEY_CTX has been
* initialized with EVP_PKEY_keygen_init.
*
* Since we can guarantee such condition we use EVP_PKEY_generate
* directly.
*/
if (EVP_PKEY_generate(ctx, pkeyp) != 1) {
CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_keygen"));
}
result = ISC_R_SUCCESS;
cleanup:
EVP_PKEY_CTX_free(ctx);
return result;
}
isc_result_t
isc_ossl_wrap_generate_pkcs11_rsa_key(char *uri, size_t bit_size,
EVP_PKEY **pkeyp) {
EVP_PKEY_CTX *ctx = NULL;
isc_result_t result;
int status;
size_t len;
len = strlen(uri);
INSIST(len != 0);
/* NUL-terminator should be left out */
const OSSL_PARAM params[] = {
OSSL_PARAM_utf8_string("pkcs11_uri", uri, len),
OSSL_PARAM_utf8_string("pkcs11_key_usage", pkcs11_key_usage,
sizeof(pkcs11_key_usage) - 1),
OSSL_PARAM_size_t(OSSL_PKEY_PARAM_RSA_BITS, &bit_size),
OSSL_PARAM_END,
};
ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", "provider=pkcs11");
if (ctx == NULL) {
CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_CTX_new_from_name"));
}
status = EVP_PKEY_keygen_init(ctx);
if (status != 1) {
CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_keygen_init"));
}
status = EVP_PKEY_CTX_set_params(ctx, params);
if (status != 1) {
CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_CTX_set_params"));
}
/*
* EVP_PKEY_keygen is an older function now equivalent to
* EVP_PKEY_generate with an additional check that EVP_PKEY_CTX has been
* initialized with EVP_PKEY_keygen_init.
*
* Since we can guarantee such condition we use EVP_PKEY_generate
* directly.
*/
status = EVP_PKEY_generate(ctx, pkeyp);
if (status != 1) {
CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_generate"));
}
result = ISC_R_SUCCESS;
cleanup:
EVP_PKEY_CTX_free(ctx);
return result;
}
bool
isc_ossl_wrap_rsa_key_bits_leq(EVP_PKEY *pkey, size_t limit) {
size_t bits = SIZE_MAX;
BIGNUM *e = NULL;
if (EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_E, &e) == 1) {
bits = BN_num_bits(e);
BN_free(e);
}
return bits <= limit;
}
isc_result_t
isc_ossl_wrap_rsa_public_components(EVP_PKEY *pkey,
isc_ossl_wrap_rsa_components_t *c) {
isc_result_t result;
REQUIRE(pkey != NULL);
REQUIRE(c != NULL && c->e == NULL && c->n == NULL);
c->needs_cleanup = true;
if (EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_E, &c->e) != 1) {
CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_get_bn_param"));
}
if (EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_N, &c->n) != 1) {
CLEANUP(OSSL_WRAP_ERROR("EVP_PKEY_get_bn_param"));
}
result = ISC_R_SUCCESS;
cleanup:
return result;
}
isc_result_t
isc_ossl_wrap_rsa_secret_components(EVP_PKEY *pkey,
isc_ossl_wrap_rsa_components_t *c) {
REQUIRE(pkey != NULL);
REQUIRE(c != NULL && c->d == NULL && c->p == NULL && c->q == NULL &&
c->dmp1 == NULL && c->dmq1 == NULL && c->iqmp == NULL);
c->needs_cleanup = true;
/*
* NOTE: Errors regarding private compoments are ignored.
*
* OpenSSL allows omitting the parameters for CRT based calculations
* (factors, exponents, coefficients). Only the 'd' parameter is
* mandatory for software keys.
*
* However, for a label based keys, all private key component queries
* can fail if they key is e.g. on a hardware device.
*/
(void)EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_D, &c->d);
(void)EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_FACTOR1, &c->p);
(void)EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_FACTOR2, &c->q);
(void)EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_EXPONENT1,
&c->dmp1);
(void)EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_EXPONENT2,
&c->dmq1);
(void)EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_COEFFICIENT1,
&c->iqmp);
ERR_clear_error();
return ISC_R_SUCCESS;
}
isc_result_t
isc_ossl_wrap_load_rsa_public_from_components(isc_ossl_wrap_rsa_components_t *c,
EVP_PKEY **pkeyp) {
OSSL_PARAM_BLD *bld = NULL;
EVP_PKEY_CTX *pctx = NULL;
OSSL_PARAM *params = NULL;
isc_result_t result;
result = ISC_R_SUCCESS;
REQUIRE(pkeyp != NULL && *pkeyp == NULL);
REQUIRE(c != NULL && c->n != NULL && c->e != NULL);
bld = OSSL_PARAM_BLD_new();
if (bld == NULL) {
CLEANUP(OSSL_WRAP_ERROR("OSSL_PARAM_BLD_new"));
}
if (OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, c->n) != 1) {
CLEANUP(OSSL_WRAP_ERROR("OSSL_PARAM_BLD_push_BN"));
}
if (OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, c->e) != 1) {
CLEANUP(OSSL_WRAP_ERROR("OSSL_PARAM_BLD_push_BN"));
}
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, "RSA", 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:
EVP_PKEY_CTX_free(pctx);
OSSL_PARAM_free(params);
OSSL_PARAM_BLD_free(bld);
return result;
}
isc_result_t
isc_ossl_wrap_load_rsa_secret_from_components(isc_ossl_wrap_rsa_components_t *c,
EVP_PKEY **pkeyp) {
isc_result_t result;
OSSL_PARAM_BLD *bld = NULL;
EVP_PKEY_CTX *pctx = NULL;
OSSL_PARAM *params = NULL;
REQUIRE(pkeyp != NULL && *pkeyp == NULL);
bld = OSSL_PARAM_BLD_new();
if (bld == NULL) {
CLEANUP(OSSL_WRAP_ERROR("OSSL_PARAM_BLD_new"));
}
if (OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, c->n) != 1) {
CLEANUP(OSSL_WRAP_ERROR("OSSL_PARAM_BLD_push_BN"));
}
if (OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, c->e) != 1) {
CLEANUP(OSSL_WRAP_ERROR("OSSL_PARAM_BLD_push_BN"));
}
if (c->d != NULL &&
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_D, c->d) != 1)
{
CLEANUP(OSSL_WRAP_ERROR("OSSL_PARAM_BLD_push_BN"));
}
if (c->p != NULL &&
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_FACTOR1, c->p) != 1)
{
CLEANUP(OSSL_WRAP_ERROR("OSSL_PARAM_BLD_push_BN"));
}
if (c->q != NULL &&
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_FACTOR2, c->q) != 1)
{
CLEANUP(OSSL_WRAP_ERROR("OSSL_PARAM_BLD_push_BN"));
}
if (c->dmp1 != NULL &&
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_EXPONENT1,
c->dmp1) != 1)
{
CLEANUP(OSSL_WRAP_ERROR("OSSL_PARAM_BLD_push_BN"));
}
if (c->dmq1 != NULL &&
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_EXPONENT2,
c->dmq1) != 1)
{
CLEANUP(OSSL_WRAP_ERROR("OSSL_PARAM_BLD_push_BN"));
}
if (c->iqmp != NULL &&
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_COEFFICIENT1,
c->iqmp) != 1)
{
CLEANUP(OSSL_WRAP_ERROR("OSSL_PARAM_BLD_push_BN"));
}
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, "RSA", 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:
EVP_PKEY_CTX_free(pctx);
OSSL_PARAM_free(params);
OSSL_PARAM_BLD_free(bld);
return result;
}

View file

@ -0,0 +1,124 @@
/*
* 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.
*/
#include <openssl/bn.h>
#include <openssl/err.h>
#include <isc/ossl_wrap.h>
#include <isc/util.h>
#include "../openssl_shim.h"
void
isc_ossl_wrap_rsa_components_cleanup(isc_ossl_wrap_rsa_components_t *c) {
REQUIRE(c != NULL);
if (!c->needs_cleanup) {
return;
}
/*
* NOTE: BN_free() frees the components of the BIGNUM, and if it was
* created by BN_new(), also the structure itself. BN_clear_free()
* additionally overwrites the data before the memory is returned to the
* system. If a is NULL, nothing is done.
*/
BN_free(c->e);
BN_free(c->n);
BN_clear_free(c->d);
BN_clear_free(c->p);
BN_clear_free(c->q);
BN_clear_free(c->dmp1);
BN_clear_free(c->dmq1);
BN_clear_free(c->iqmp);
c->needs_cleanup = false;
}
isc_result_t
isc_ossl_wrap_toresult(isc_result_t fallback) {
isc_result_t result = fallback;
unsigned long err = ERR_peek_error();
#ifdef ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED
int lib = ERR_GET_LIB(err);
#endif /* ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED */
int reason = ERR_GET_REASON(err);
switch (reason) {
/*
* ERR_* errors are globally unique; others
* are unique per sublibrary
*/
case ERR_R_MALLOC_FAILURE:
result = ISC_R_NOMEMORY;
break;
default:
#ifdef ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED
if (lib == ERR_R_ECDSA_LIB &&
reason == ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED)
{
result = ISC_R_NOENTROPY;
break;
}
#endif /* ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED */
break;
}
return result;
}
isc_result_t
isc__ossl_wrap_logged_toresult(isc_logcategory_t category,
isc_logmodule_t module, const char *funcname,
isc_result_t fallback, const char *file,
int line) {
isc_result_t result = isc_ossl_wrap_toresult(fallback);
/*
* This is an exception - normally, we don't allow this, but the
* compatibility shims in dst_openssl.h needs a call that just
* translates the error code and don't do any logging.
*/
if (category == ISC_LOGCATEGORY_INVALID) {
goto done;
}
isc_log_write(category, module, ISC_LOG_WARNING,
"%s (%s:%d) failed (%s)", funcname, file, line,
isc_result_totext(result));
if (result == ISC_R_NOMEMORY) {
goto done;
}
for (;;) {
const char *func, *data;
int flags;
unsigned long err = ERR_get_error_all(&file, &line, &func,
&data, &flags);
if (err == 0U) {
break;
}
char buf[256];
ERR_error_string_n(err, buf, sizeof(buf));
isc_log_write(category, module, ISC_LOG_INFO, "%s:%s:%d:%s",
buf, file, line,
((flags & ERR_TXT_STRING) != 0) ? data : "");
}
done:
ERR_clear_error();
return result;
}

View file

@ -38,9 +38,11 @@
#include <isc/ht.h>
#include <isc/log.h>
#include <isc/magic.h>
#include <isc/md.h>
#include <isc/mem.h>
#include <isc/mutex.h>
#include <isc/once.h>
#include <isc/ossl_wrap.h>
#include <isc/random.h>
#include <isc/refcount.h>
#include <isc/rwlock.h>
@ -179,12 +181,6 @@ isc_tlsctx_createserver(const char *keyfile, const char *certfile,
X509 *cert = NULL;
EVP_PKEY *pkey = NULL;
SSL_CTX *ctx = NULL;
#if OPENSSL_VERSION_NUMBER < 0x30000000L
EC_KEY *eckey = NULL;
#else
EVP_PKEY_CTX *pkey_ctx = NULL;
EVP_PKEY *params_pkey = NULL;
#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
char errbuf[256];
const SSL_METHOD *method = NULL;
@ -206,79 +202,10 @@ isc_tlsctx_createserver(const char *keyfile, const char *certfile,
SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
if (ephemeral) {
const int group_nid = NID_X9_62_prime256v1;
#if OPENSSL_VERSION_NUMBER < 0x30000000L
eckey = EC_KEY_new_by_curve_name(group_nid);
if (eckey == NULL) {
if (isc_ossl_wrap_generate_p256_key(&pkey) != ISC_R_SUCCESS) {
goto ssl_error;
}
/* Generate the key. */
rv = EC_KEY_generate_key(eckey);
if (rv != 1) {
goto ssl_error;
}
pkey = EVP_PKEY_new();
if (pkey == NULL) {
goto ssl_error;
}
rv = EVP_PKEY_set1_EC_KEY(pkey, eckey);
if (rv != 1) {
goto ssl_error;
}
/* Use a named curve and uncompressed point conversion form. */
EC_KEY_set_asn1_flag(EVP_PKEY_get0_EC_KEY(pkey),
OPENSSL_EC_NAMED_CURVE);
EC_KEY_set_conv_form(EVP_PKEY_get0_EC_KEY(pkey),
POINT_CONVERSION_UNCOMPRESSED);
/* Cleanup */
EC_KEY_free(eckey);
eckey = NULL;
#else
/* Generate the key's parameters. */
pkey_ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
if (pkey_ctx == NULL) {
goto ssl_error;
}
rv = EVP_PKEY_paramgen_init(pkey_ctx);
if (rv != 1) {
goto ssl_error;
}
rv = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pkey_ctx,
group_nid);
if (rv != 1) {
goto ssl_error;
}
rv = EVP_PKEY_paramgen(pkey_ctx, &params_pkey);
if (rv != 1 || params_pkey == NULL) {
goto ssl_error;
}
EVP_PKEY_CTX_free(pkey_ctx);
/* Generate the key. */
pkey_ctx = EVP_PKEY_CTX_new(params_pkey, NULL);
if (pkey_ctx == NULL) {
goto ssl_error;
}
rv = EVP_PKEY_keygen_init(pkey_ctx);
if (rv != 1) {
goto ssl_error;
}
rv = EVP_PKEY_keygen(pkey_ctx, &pkey);
if (rv != 1 || pkey == NULL) {
goto ssl_error;
}
/* Cleanup */
EVP_PKEY_free(params_pkey);
params_pkey = NULL;
EVP_PKEY_CTX_free(pkey_ctx);
pkey_ctx = NULL;
#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
cert = X509_new();
if (cert == NULL) {
goto ssl_error;
@ -315,7 +242,7 @@ isc_tlsctx_createserver(const char *keyfile, const char *certfile,
-1, -1, 0);
X509_set_issuer_name(cert, name);
X509_sign(cert, pkey, isc__crypto_sha256);
X509_sign(cert, pkey, isc__crypto_md[ISC_MD_SHA256]);
rv = SSL_CTX_use_certificate(ctx, cert);
if (rv != 1) {
goto ssl_error;
@ -356,18 +283,6 @@ ssl_error:
if (pkey != NULL) {
EVP_PKEY_free(pkey);
}
#if OPENSSL_VERSION_NUMBER < 0x30000000L
if (eckey != NULL) {
EC_KEY_free(eckey);
}
#else
if (params_pkey != NULL) {
EVP_PKEY_free(params_pkey);
}
if (pkey_ctx != NULL) {
EVP_PKEY_CTX_free(pkey_ctx);
}
#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
return ISC_R_TLSERROR;
}
@ -1546,80 +1461,3 @@ isc_tls_valid_sni_hostname(const char *hostname) {
return true;
}
static isc_result_t
isc__tls_toresult(isc_result_t fallback) {
isc_result_t result = fallback;
unsigned long err = ERR_peek_error();
#if defined(ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED)
int lib = ERR_GET_LIB(err);
#endif /* if defined(ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED) */
int reason = ERR_GET_REASON(err);
switch (reason) {
/*
* ERR_* errors are globally unique; others
* are unique per sublibrary
*/
case ERR_R_MALLOC_FAILURE:
result = ISC_R_NOMEMORY;
break;
default:
#if defined(ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED)
if (lib == ERR_R_ECDSA_LIB &&
reason == ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED)
{
result = ISC_R_NOENTROPY;
break;
}
#endif /* if defined(ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED) */
break;
}
return result;
}
isc_result_t
isc__tlserr2result(isc_logcategory_t category, isc_logmodule_t module,
const char *funcname, isc_result_t fallback,
const char *file, int line) {
isc_result_t result = isc__tls_toresult(fallback);
/*
* This is an exception - normally, we don't allow this, but the
* compatibility shims in dst_openssl.h needs a call that just
* translates the error code and don't do any logging.
*/
if (category == ISC_LOGCATEGORY_INVALID) {
goto done;
}
isc_log_write(category, module, ISC_LOG_WARNING,
"%s (%s:%d) failed (%s)", funcname, file, line,
isc_result_totext(result));
if (result == ISC_R_NOMEMORY) {
goto done;
}
for (;;) {
const char *func, *data;
int flags;
unsigned long err = ERR_get_error_all(&file, &line, &func,
&data, &flags);
if (err == 0U) {
break;
}
char buf[256];
ERR_error_string_n(err, buf, sizeof(buf));
isc_log_write(category, module, ISC_LOG_INFO, "%s:%s:%d:%s",
buf, file, line,
((flags & ERR_TXT_STRING) != 0) ? data : "");
}
done:
ERR_clear_error();
return result;
}

View file

@ -242,7 +242,7 @@ list_towire(isccc_sexpr_t *list, isc_buffer_t **buffer) {
static isc_result_t
sign(unsigned char *data, unsigned int length, unsigned char *out,
uint32_t algorithm, isccc_region_t *secret) {
const isc_md_type_t *md_type;
isc_md_type_t md_type;
isccc_region_t source, target;
unsigned char digest[ISC_MAX_MD_SIZE];
unsigned int digestlen = sizeof(digest);
@ -353,7 +353,7 @@ isccc_cc_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer, uint32_t algorithm,
static isc_result_t
verify(isccc_sexpr_t *alist, unsigned char *data, unsigned int length,
uint32_t algorithm, isccc_region_t *secret) {
const isc_md_type_t *md_type;
isc_md_type_t md_type;
isccc_region_t source;
isccc_region_t target;
isccc_sexpr_t *_auth, *hmacvalue;

View file

@ -602,21 +602,37 @@ openssl_dep = [
dependency('libssl', version: '>=1.1.1'),
]
foreach fn, header : {
'EVP_default_properties_enable_fips': '#include <openssl/evp.h>',
'FIPS_mode': '#include <openssl/crypto.h>',
}
if cc.has_function(fn, prefix: header, dependencies: openssl_dep)
config.set('HAVE_OPENSSL_FIPS_TOGGLE', 1)
config.set('HAVE_@0@'.format(fn.to_upper()), 1)
endif
endforeach
fips_opt.require(
config.has('HAVE_OPENSSL_FIPS_TOGGLE'),
error_message: 'OpenSSL FIPS mode requested but not available',
# OpenSSL-forks usually set their own version number which can be greater than 3.0.0.
# To understand if the library is pre or post-3.0, use the OPENSSL_VERSION_NUMBER value.
openssl_version_number = cc.get_define(
'OPENSSL_VERSION_NUMBER',
dependencies: openssl_dep,
prefix: '#include <openssl/opensslv.h>',
)
config.set('OPENSSL_NO_DEPRECATED', true)
if openssl_version_number.version_compare('>=3.0.0')
config.set('HAVE_OPENSSL_3', true)
config.set('OPENSSL_API_COMPAT', 30000)
else
config.set('HAVE_OPENSSL_3', false)
config.set('OPENSSL_API_COMPAT', 10100)
config.set(
'HAVE_FIPS_MODE',
cc.has_function(
'FIPS_mode',
prefix: '#include <openssl/crypto.h>',
dependencies: openssl_dep,
),
)
fips_opt.require(
config.get('HAVE_FIPS_MODE'),
error_message: 'OpenSSL FIPS mode requested but not available',
)
endif
# Hash and curve probe
if cc.has_header_symbol('openssl/evp.h', 'NID_ED448', dependencies: openssl_dep)
config.set('HAVE_OPENSSL_ED448', 1)
@ -626,7 +642,6 @@ foreach fn, header : {
'ERR_get_error_all': '#include <openssl/err.h>',
'BIO_read_ex': '#include <openssl/bio.h>',
'BIO_write_ex': '#include <openssl/bio.h>',
'EVP_MD_CTX_get0_md': '#include <openssl/evp.h>',
'EVP_PKEY_eq': '#include <openssl/evp.h>',
'SSL_CTX_set1_cert_store': '#include <openssl/ssl.h>',
}

View file

@ -21,13 +21,6 @@
#include <stdlib.h>
#include <string.h>
/*
* As a workaround, include an OpenSSL header file before including cmocka.h,
* because OpenSSL 3.1.0 uses __attribute__(malloc), conflicting with a
* redefined malloc in cmocka.h.
*/
#include <openssl/err.h>
#define UNIT_TESTING
#include <cmocka.h>
@ -39,14 +32,11 @@
#include <isc/region.h>
#include <isc/result.h>
#include "hmac.c"
#include <tests/isc.h>
#define TEST_INPUT(x) (x), sizeof(x) - 1
static int
_setup(void **state) {
ISC_SETUP_TEST_IMPL(hmac_state) {
isc_hmac_t *hmac_st = isc_hmac_new();
if (hmac_st == NULL) {
return -1;
@ -69,9 +59,7 @@ _reset(void **state) {
if (*state == NULL) {
return -1;
}
if (isc_hmac_reset(*state) != ISC_R_SUCCESS) {
return -1;
}
return 0;
}
@ -94,13 +82,18 @@ ISC_RUN_TEST_IMPL(isc_hmac_free) {
static void
isc_hmac_test(isc_hmac_t *hmac_st, const void *key, size_t keylen,
const isc_md_type_t *type, const char *buf, size_t buflen,
isc_md_type_t type, const char *buf, size_t buflen,
const char *result, const size_t repeats) {
isc_hmac_key_t *hkey = NULL;
isc_result_t res;
assert_int_equal(
isc_hmac_key_create(type, key, keylen, isc_g_mctx, &hkey),
ISC_R_SUCCESS);
assert_non_null(hmac_st);
assert_int_equal(isc_hmac_init(hmac_st, key, keylen, type),
ISC_R_SUCCESS);
res = isc_hmac_init(hmac_st, hkey);
assert_return_code(res, ISC_R_SUCCESS);
for (size_t i = 0; i < repeats; i++) {
assert_int_equal(isc_hmac_update(hmac_st,
@ -109,14 +102,16 @@ isc_hmac_test(isc_hmac_t *hmac_st, const void *key, size_t keylen,
ISC_R_SUCCESS);
}
unsigned char digest[ISC_MAX_MD_SIZE];
unsigned int digestlen = sizeof(digest);
assert_int_equal(isc_hmac_final(hmac_st, digest, &digestlen),
ISC_R_SUCCESS);
unsigned char raw_digest[ISC_MAX_MD_SIZE];
isc_buffer_t digest;
isc_buffer_init(&digest, raw_digest, sizeof(raw_digest));
assert_int_equal(isc_hmac_final(hmac_st, &digest), ISC_R_SUCCESS);
char hexdigest[ISC_MAX_MD_SIZE * 2 + 3];
isc_region_t r = { .base = digest, .length = digestlen };
isc_region_t r;
isc_buffer_t b;
isc_buffer_usedregion(&digest, &r);
isc_buffer_init(&b, hexdigest, sizeof(hexdigest));
res = isc_hex_totext(&r, 0, "", &b);
@ -124,106 +119,86 @@ isc_hmac_test(isc_hmac_t *hmac_st, const void *key, size_t keylen,
assert_return_code(res, ISC_R_SUCCESS);
assert_memory_equal(hexdigest, result, result ? strlen(result) : 0);
assert_int_equal(isc_hmac_reset(hmac_st), ISC_R_SUCCESS);
isc_hmac_key_destroy(&hkey);
}
ISC_RUN_TEST_IMPL(isc_hmac_init) {
isc_hmac_t *hmac_st = *state;
assert_non_null(hmac_st);
ISC_RUN_TEST_IMPL(isc_hmac_key_create) {
isc_hmac_key_t *key = NULL;
assert_int_equal(isc_hmac_init(hmac_st, "", 0, NULL),
ISC_R_NOTIMPLEMENTED);
assert_int_equal(
isc_hmac_key_create(ISC_MD_UNKNOWN, "", 0, isc_g_mctx, &key),
ISC_R_NOTIMPLEMENTED);
if (!isc_crypto_fips_mode()) {
expect_assert_failure(isc_hmac_init(NULL, "", 0, ISC_MD_MD5));
/*
expect_assert_failure(isc_hmac_key_create(ISC_MD_MD5, NULL, 0,
isc_g_mctx, &key));
*/
expect_assert_failure(
isc_hmac_init(hmac_st, NULL, 0, ISC_MD_MD5));
assert_int_equal(isc_hmac_init(hmac_st, "", 0, ISC_MD_MD5),
assert_int_equal(isc_hmac_key_create(ISC_MD_MD5, "", 0,
isc_g_mctx, &key),
ISC_R_SUCCESS);
assert_int_equal(isc_hmac_reset(hmac_st), ISC_R_SUCCESS);
isc_hmac_key_destroy(&key);
}
assert_int_equal(isc_hmac_init(hmac_st, "", 0, ISC_MD_SHA1),
ISC_R_SUCCESS);
assert_int_equal(isc_hmac_reset(hmac_st), ISC_R_SUCCESS);
assert_int_equal(
isc_hmac_key_create(ISC_MD_SHA1, "", 0, isc_g_mctx, &key),
ISC_R_SUCCESS);
isc_hmac_key_destroy(&key);
assert_int_equal(isc_hmac_init(hmac_st, "", 0, ISC_MD_SHA224),
ISC_R_SUCCESS);
assert_int_equal(isc_hmac_reset(hmac_st), ISC_R_SUCCESS);
assert_int_equal(
isc_hmac_key_create(ISC_MD_SHA224, "", 0, isc_g_mctx, &key),
ISC_R_SUCCESS);
isc_hmac_key_destroy(&key);
assert_int_equal(isc_hmac_init(hmac_st, "", 0, ISC_MD_SHA256),
ISC_R_SUCCESS);
assert_int_equal(isc_hmac_reset(hmac_st), ISC_R_SUCCESS);
assert_int_equal(
isc_hmac_key_create(ISC_MD_SHA256, "", 0, isc_g_mctx, &key),
ISC_R_SUCCESS);
isc_hmac_key_destroy(&key);
assert_int_equal(isc_hmac_init(hmac_st, "", 0, ISC_MD_SHA384),
ISC_R_SUCCESS);
assert_int_equal(isc_hmac_reset(hmac_st), ISC_R_SUCCESS);
assert_int_equal(
isc_hmac_key_create(ISC_MD_SHA384, "", 0, isc_g_mctx, &key),
ISC_R_SUCCESS);
isc_hmac_key_destroy(&key);
assert_int_equal(isc_hmac_init(hmac_st, "", 0, ISC_MD_SHA512),
ISC_R_SUCCESS);
assert_int_equal(isc_hmac_reset(hmac_st), ISC_R_SUCCESS);
assert_int_equal(
isc_hmac_key_create(ISC_MD_SHA512, "", 0, isc_g_mctx, &key),
ISC_R_SUCCESS);
isc_hmac_key_destroy(&key);
}
ISC_RUN_TEST_IMPL(isc_hmac_update) {
isc_hmac_t *hmac_st = *state;
assert_non_null(hmac_st);
/* Uses message digest context initialized in isc_hmac_init_test() */
expect_assert_failure(isc_hmac_update(NULL, NULL, 0));
assert_int_equal(isc_hmac_update(hmac_st, NULL, 100), ISC_R_SUCCESS);
assert_int_equal(isc_hmac_update(hmac_st, (const unsigned char *)"", 0),
ISC_R_SUCCESS);
}
ISC_RUN_TEST_IMPL(isc_hmac_reset) {
isc_hmac_t *hmac_st = *state;
#if 0
unsigned char digest[ISC_MAX_MD_SIZE] ISC_ATTR_UNUSED;
unsigned int digestlen ISC_ATTR_UNUSED;
#endif /* if 0 */
assert_non_null(hmac_st);
assert_int_equal(isc_hmac_init(hmac_st, "", 0, ISC_MD_SHA512),
ISC_R_SUCCESS);
assert_int_equal(
isc_hmac_update(hmac_st, (const unsigned char *)"a", 1),
ISC_R_SUCCESS);
assert_int_equal(
isc_hmac_update(hmac_st, (const unsigned char *)"b", 1),
ISC_R_SUCCESS);
assert_int_equal(isc_hmac_reset(hmac_st), ISC_R_SUCCESS);
#if 0
/*
* This test would require OpenSSL compiled with mock_assert(),
* so this could be only manually checked that the test will
* segfault when called by hand
*/
expect_assert_failure(isc_hmac_final(hmac_st, digest, &digestlen));
#endif /* if 0 */
}
ISC_RUN_TEST_IMPL(isc_hmac_final) {
isc_hmac_key_t *key = NULL;
isc_hmac_t *hmac_st = *state;
assert_non_null(hmac_st);
assert_int_equal(
isc_hmac_key_create(ISC_MD_SHA512, "", 0, isc_g_mctx, &key),
ISC_R_SUCCESS);
unsigned char digest[ISC_MAX_MD_SIZE];
unsigned int digestlen = sizeof(digest);
isc_buffer_t digestbuf;
/* Fail when message digest context is empty */
expect_assert_failure(isc_hmac_final(NULL, digest, &digestlen));
/* Fail when output buffer is empty */
expect_assert_failure(isc_hmac_final(hmac_st, NULL, &digestlen));
isc_buffer_init(&digestbuf, NULL, 0);
assert_int_equal(isc_hmac_final(hmac_st, &digestbuf), ISC_R_NOSPACE);
assert_int_equal(isc_hmac_init(hmac_st, "", 0, ISC_MD_SHA512),
ISC_R_SUCCESS);
/* Fail when the digest length pointer is empty */
expect_assert_failure(isc_hmac_final(hmac_st, digest, NULL));
/* Fail when the digest length is empty */
assert_int_equal(isc_hmac_init(hmac_st, key), ISC_R_SUCCESS);
isc_buffer_init(&digestbuf, digest, 0);
assert_int_equal(isc_hmac_final(hmac_st, &digestbuf), ISC_R_NOSPACE);
isc_hmac_key_destroy(&key);
}
ISC_RUN_TEST_IMPL(isc_hmac_md5) {
@ -919,10 +894,7 @@ ISC_RUN_TEST_IMPL(isc_hmac_sha512) {
ISC_TEST_LIST_START
ISC_TEST_ENTRY(isc_hmac_new)
ISC_TEST_ENTRY_CUSTOM(isc_hmac_init, _reset, _reset)
ISC_TEST_ENTRY_CUSTOM(isc_hmac_reset, _reset, _reset)
ISC_TEST_ENTRY(isc_hmac_key_create)
ISC_TEST_ENTRY(isc_hmac_md5)
ISC_TEST_ENTRY(isc_hmac_sha1)
ISC_TEST_ENTRY(isc_hmac_sha224)
@ -937,4 +909,4 @@ ISC_TEST_ENTRY(isc_hmac_free)
ISC_TEST_LIST_END
ISC_TEST_MAIN_CUSTOM(_setup, _teardown)
ISC_TEST_MAIN_CUSTOM(setup_test_hmac_state, _teardown)

View file

@ -82,8 +82,8 @@ ISC_RUN_TEST_IMPL(isc_md_free) {
}
static void
isc_md_test(isc_md_t *md, const isc_md_type_t *type, const char *buf,
size_t buflen, const char *result, const size_t repeats) {
isc_md_test(isc_md_t *md, isc_md_type_t type, const char *buf, size_t buflen,
const char *result, const size_t repeats) {
isc_result_t res;
assert_non_null(md);
@ -118,7 +118,7 @@ ISC_RUN_TEST_IMPL(isc_md_init) {
expect_assert_failure(isc_md_init(NULL, ISC_MD_MD5));
assert_int_equal(isc_md_init(md, NULL), ISC_R_NOTIMPLEMENTED);
assert_int_equal(isc_md_init(md, ISC_MD_UNKNOWN), ISC_R_NOTIMPLEMENTED);
if (isc_crypto_fips_mode()) {
assert_int_equal(isc_md_init(md, ISC_MD_MD5),