Tolerate non-extractable Ed25519/Ed448 private keys in tofile

openssleddsa_tofile() called EVP_PKEY_get_raw_private_key()
unconditionally whenever the dst_key_t had a private EVP_PKEY
attached and aborted with ISC_R_FAILURE on any error.  That is
wrong for keys whose private material lives in a hardware token
(PKCS#11): the provider deliberately refuses to export the raw
bytes, but the keypair is still valid and the .private file
should be written containing only the PKCS#11 label, with no raw
key material.  Without this, "dnssec-keyfromlabel -a ed25519 -l
pkcs11:..." fails with "failed to write key ...: failure" even
though pkcs11-tool has generated a valid Ed25519 key in SoftHSM.

Mirror the behaviour already implemented in opensslecdsa_tofile():
if the raw private key cannot be retrieved AND the key has a
PKCS#11 label to fall back on, clear the OpenSSL error queue and
fall through to writing just the Label element.

If extraction fails and there is no label to fall back on, return
the OpenSSL failure rather than silently producing a .private
file with neither raw key material nor a label, which would be
unusable on the next load.

Consolidate buffer cleanup into a single cleanup: path, freeing
with the original allocation size (alginfo->key_size) rather than
the potentially-modified len output parameter.

Assisted-by: Claude:claude-opus-4-7
This commit is contained in:
Michal Nowak 2026-05-14 14:50:18 +00:00
parent 6811a8490a
commit 945838e621

View file

@ -371,14 +371,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 +401,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;
}