fix:usr: Enable Edwards curves with PKCS#11

Ed25519 and Ed448 curves did not work in PKCS#11. This has been fixed.

Closes isc-projects/bind9#5762

Merge branch 'mnowak/pkcs11-enable-edwards-curves' into 'main'

See merge request isc-projects/bind9!11591
This commit is contained in:
Michal Nowak 2026-05-26 15:35:03 +02:00
commit ebb4e5e5b7
7 changed files with 172 additions and 16 deletions

View file

@ -49,8 +49,8 @@ mkdir ns1/keys
dir="ns1"
infile="${dir}/template.db.in"
for algtypebits in rsasha256:rsa:2048 rsasha512:rsa:2048 \
ecdsap256sha256:EC:prime256v1 ecdsap384sha384:EC:prime384v1; do # Edwards curves are not yet supported by OpenSC
# ed25519:EC:edwards25519 ed448:EC:edwards448
ecdsap256sha256:EC:prime256v1 ecdsap384sha384:EC:prime384v1 \
ed25519:EC:Ed25519 ed448:EC:Ed448; do
alg=$(echo "$algtypebits" | cut -f 1 -d :)
type=$(echo "$algtypebits" | cut -f 2 -d :)
bits=$(echo "$algtypebits" | cut -f 3 -d :)

View file

@ -50,11 +50,17 @@ check_keys() {
cd ns1
for algtypebits in rsasha256:rsa:2048 rsasha512:rsa:2048 \
ecdsap256sha256:EC:prime256v1 ecdsap384sha384:EC:prime384v1; do # Edwards curves are not yet supported by OpenSC
# ed25519:EC:edwards25519 ed448:EC:edwards448
ecdsap256sha256:EC:prime256v1 ecdsap384sha384:EC:prime384v1 \
ed25519:EC:Ed25519 ed448:EC:Ed448; do
alg=$(echo "$algtypebits" | cut -f 1 -d :)
type=$(echo "$algtypebits" | cut -f 2 -d :)
bits=$(echo "$algtypebits" | cut -f 3 -d :)
alg_upper=$(echo "$alg" | tr '[:lower:]' '[:upper:]')
supported=$(eval "echo \$${alg_upper}_SUPPORTED")
if [ "${supported}" != 1 ]; then
echo_i "skipping test for ${alg}:${type}:${bits}, not supported by this build"
continue
fi
zone="${alg}.example"
zonefile="zone.${zone}.db.signed"

View file

@ -17,6 +17,8 @@ import shutil
import pytest
from isctest.util import param
import isctest.mark
pytestmark = [
@ -93,9 +95,24 @@ def token_init_and_cleanup():
("rsasha512", "rsa", "2048"),
("ecdsap256sha256", "EC", "prime256v1"),
("ecdsap384sha384", "EC", "prime384v1"),
# Edwards curves are not yet supported by OpenSC
# ("ed25519","EC","edwards25519"),
# ("ed448","EC","edwards448")
param(
"ed25519",
"EC",
"Ed25519",
marks=pytest.mark.skipif(
os.environ.get("ED25519_SUPPORTED") != "1",
reason="Ed25519 not supported by this build",
),
),
param(
"ed448",
"EC",
"Ed448",
marks=pytest.mark.skipif(
os.environ.get("ED448_SUPPORTED") != "1",
reason="Ed448 not supported by this build",
),
),
],
)
def test_keyfromlabel(alg_name, alg_type, alg_bits):

View file

@ -22,6 +22,7 @@
#include <isc/crypto.h>
#include <isc/mem.h>
#include <isc/ossl_wrap.h>
#include <isc/result.h>
#include <isc/safe.h>
#include <isc/string.h>
@ -270,6 +271,27 @@ openssleddsa_generate(dst_key_t *key, int unused, void (*callback)(int)) {
UNUSED(unused);
UNUSED(callback);
if (key->label != NULL) {
switch (key->key_alg) {
case DST_ALG_ED25519:
RETERR(isc_ossl_wrap_generate_pkcs11_ed25519_key(
key->label, &pkey));
break;
#if HAVE_OPENSSL_ED448
case DST_ALG_ED448:
RETERR(isc_ossl_wrap_generate_pkcs11_ed448_key(
key->label, &pkey));
break;
#endif /* HAVE_OPENSSL_ED448 */
default:
UNREACHABLE();
}
key->key_size = alginfo->key_size * 8;
key->keydata.pkeypair.priv = pkey;
key->keydata.pkeypair.pub = pkey;
return ISC_R_SUCCESS;
}
ctx = EVP_PKEY_CTX_new_id(alginfo->nid, NULL);
if (ctx == NULL) {
return dst__openssl_toresult2("EVP_PKEY_CTX_new_id",
@ -371,14 +393,22 @@ openssleddsa_tofile(const dst_key_t *key, const char *directory) {
len = alginfo->key_size;
buf = isc_mem_get(key->mctx, len);
if (EVP_PKEY_get_raw_private_key(key->keydata.pkeypair.priv,
buf, &len) != 1)
buf, &len) == 1)
{
CLEANUP(dst__openssl_toresult(ISC_R_FAILURE));
priv.elements[i].tag = TAG_EDDSA_PRIVATEKEY;
priv.elements[i].length = len;
priv.elements[i].data = buf;
i++;
} else if (key->label != NULL) {
/*
* The raw private key is not extractable
* (e.g. HSM-backed via PKCS#11); fall through to
* writing only the label.
*/
ERR_clear_error();
} else {
CLEANUP(dst__openssl_toresult(DST_R_OPENSSLFAILURE));
}
priv.elements[i].tag = TAG_EDDSA_PRIVATEKEY;
priv.elements[i].length = len;
priv.elements[i].data = buf;
i++;
}
if (key->label != NULL) {
priv.elements[i].tag = TAG_EDDSA_LABEL;
@ -393,7 +423,7 @@ openssleddsa_tofile(const dst_key_t *key, const char *directory) {
cleanup:
if (buf != NULL) {
isc_mem_put(key->mctx, buf, len);
isc_mem_put(key->mctx, buf, alginfo->key_size);
}
return result;
}

View file

@ -131,6 +131,28 @@ isc_ossl_wrap_generate_pkcs11_p384_key(char *uri, EVP_PKEY **pkeyp);
* \li `uri != NULL` and is a NUL-terminated string
*/
isc_result_t
isc_ossl_wrap_generate_pkcs11_ed25519_key(char *uri, EVP_PKEY **pkeyp);
/*%
* Generates an Ed25519 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_generate_pkcs11_ed448_key(char *uri, EVP_PKEY **pkeyp);
/*%
* Generates an Ed448 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);

View file

@ -410,9 +410,33 @@ cleanup:
isc_result_t
isc_ossl_wrap_generate_pkcs11_rsa_key(char *uri, size_t bit_size,
EVP_PKEY **pkeyp) {
UNUSED(uri);
REQUIRE(uri != NULL);
REQUIRE(pkeyp != NULL && *pkeyp == NULL);
return isc_ossl_wrap_generate_rsa_key(NULL, bit_size, pkeyp);
UNUSED(uri);
UNUSED(bit_size);
UNUSED(pkeyp);
return ISC_R_NOTIMPLEMENTED;
}
isc_result_t
isc_ossl_wrap_generate_pkcs11_ed25519_key(char *uri, EVP_PKEY **pkeyp) {
REQUIRE(uri != NULL);
REQUIRE(pkeyp != NULL && *pkeyp == NULL);
UNUSED(uri);
UNUSED(pkeyp);
return ISC_R_NOTIMPLEMENTED;
}
isc_result_t
isc_ossl_wrap_generate_pkcs11_ed448_key(char *uri, EVP_PKEY **pkeyp) {
REQUIRE(uri != NULL);
REQUIRE(pkeyp != NULL && *pkeyp == NULL);
UNUSED(uri);
UNUSED(pkeyp);
return ISC_R_NOTIMPLEMENTED;
}
bool

View file

@ -239,6 +239,60 @@ cleanup:
return result;
}
static isc_result_t
generate_pkcs11_eddsa_key(char *uri, EVP_PKEY **pkeyp, const char *keytype) {
isc_result_t result;
EVP_PKEY_CTX *pctx = NULL;
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, keytype, "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"));
}
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;
}
isc_result_t
isc_ossl_wrap_generate_pkcs11_ed25519_key(char *uri, EVP_PKEY **pkeyp) {
REQUIRE(pkeyp != NULL && *pkeyp == NULL);
REQUIRE(uri != NULL);
return generate_pkcs11_eddsa_key(uri, pkeyp, "ED25519");
}
isc_result_t
isc_ossl_wrap_generate_pkcs11_ed448_key(char *uri, EVP_PKEY **pkeyp) {
REQUIRE(pkeyp != NULL && *pkeyp == NULL);
REQUIRE(uri != NULL);
return generate_pkcs11_eddsa_key(uri, pkeyp, "ED448");
}
static isc_result_t
validate_ec_pkey(EVP_PKEY *pkey, const OSSL_PARAM *const curve_params) {
isc_result_t result;
@ -523,6 +577,9 @@ isc_ossl_wrap_generate_pkcs11_rsa_key(char *uri, size_t bit_size,
int status;
size_t len;
REQUIRE(uri != NULL);
REQUIRE(pkeyp != NULL && *pkeyp == NULL);
len = strlen(uri);
INSIST(len != 0);