add Ed25519 and Ed448 support to the EVP_PKEY layer

- Add WC_EVP_PKEY_ED25519 / WC_EVP_PKEY_ED448 type constants and
   matching EVP_PKEY_ED25519 / EVP_PKEY_ED448 OpenSSL aliases.
  - Extend WOLFSSL_EVP_PKEY with ed25519/ed448 fields and ownership
   bits, and free them in wolfSSL_EVP_PKEY_free().
  - Add d2i probe functions that accept both SubjectPublicKeyInfo /
   PKCS#8 PrivateKeyInfo encodings and raw 32/57-byte key material,
   and hook them into the d2i_evp_pkey_try() chain.
  - Map the Ed25519/Ed448 signature OIDs in the relevant lookups and
   teach the PEM key-format dispatch and SSL_CTX_use_PrivateKey
   switch about the new types.
This commit is contained in:
Lealem Amedie
2026-04-06 12:32:25 -06:00
parent 89dac98b95
commit d385ae9c29
7 changed files with 353 additions and 1 deletions
+20
View File
@@ -6262,6 +6262,16 @@ WOLFSSL_EVP_PKEY* wolfSSL_PEM_read_bio_PrivateKey(WOLFSSL_BIO* bio,
case DHk:
type = WC_EVP_PKEY_DH;
break;
#ifdef HAVE_ED25519
case ED25519k:
type = WC_EVP_PKEY_ED25519;
break;
#endif
#ifdef HAVE_ED448
case ED448k:
type = WC_EVP_PKEY_ED448;
break;
#endif
default:
type = WOLFSSL_FATAL_ERROR;
break;
@@ -6409,6 +6419,16 @@ WOLFSSL_EVP_PKEY* wolfSSL_PEM_read_PrivateKey(XFILE fp, WOLFSSL_EVP_PKEY **key,
case DHk:
type = WC_EVP_PKEY_DH;
break;
#ifdef HAVE_ED25519
case ED25519k:
type = WC_EVP_PKEY_ED25519;
break;
#endif
#ifdef HAVE_ED448
case ED448k:
type = WC_EVP_PKEY_ED448;
break;
#endif
default:
type = WOLFSSL_FATAL_ERROR;
break;
+16
View File
@@ -17747,6 +17747,14 @@ word32 nid2oid(int nid, int grp)
return CTC_SHA3_512wECDSA;
#endif
#endif /* HAVE_ECC */
#ifdef HAVE_ED25519
case WC_NID_ED25519:
return CTC_ED25519;
#endif /* HAVE_ED25519 */
#ifdef HAVE_ED448
case WC_NID_ED448:
return CTC_ED448;
#endif /* HAVE_ED448 */
}
break;
@@ -18131,6 +18139,14 @@ int oid2nid(word32 oid, int grp)
return WC_NID_ecdsa_with_SHA3_512;
#endif
#endif /* HAVE_ECC */
#ifdef HAVE_ED25519
case CTC_ED25519:
return WC_NID_ED25519;
#endif /* HAVE_ED25519 */
#ifdef HAVE_ED448
case CTC_ED448:
return WC_NID_ED448;
#endif /* HAVE_ED448 */
}
break;
+12
View File
@@ -5256,6 +5256,18 @@ int wolfSSL_CTX_use_PrivateKey(WOLFSSL_CTX *ctx, WOLFSSL_EVP_PKEY *pkey)
WOLFSSL_MSG("populating ECC key");
ret = ECC_populate_EVP_PKEY(pkey, pkey->ecc);
break;
#endif
#ifdef HAVE_ED25519
case WC_EVP_PKEY_ED25519:
/* DER is already stored in pkey->pkey.ptr by d2i_evp_pkey. */
WOLFSSL_MSG("populating Ed25519 key");
break;
#endif
#ifdef HAVE_ED448
case WC_EVP_PKEY_ED448:
/* DER is already stored in pkey->pkey.ptr by d2i_evp_pkey. */
WOLFSSL_MSG("populating Ed448 key");
break;
#endif
default:
ret = 0;
+26
View File
@@ -41,6 +41,12 @@
#include <wolfssl/openssl/evp.h>
#include <wolfssl/openssl/kdf.h>
#include <wolfssl/wolfcrypt/wolfmath.h>
#ifdef HAVE_ED25519
#include <wolfssl/wolfcrypt/ed25519.h>
#endif
#ifdef HAVE_ED448
#include <wolfssl/wolfcrypt/ed448.h>
#endif
static const struct s_ent {
const enum wc_HashType macType;
@@ -11679,6 +11685,26 @@ void wolfSSL_EVP_PKEY_free(WOLFSSL_EVP_PKEY* key)
break;
#endif /* ! NO_DH ... */
#ifdef HAVE_ED25519
case WC_EVP_PKEY_ED25519:
if (key->ed25519 != NULL && key->ownEd25519 == 1) {
wc_ed25519_free(key->ed25519);
XFREE(key->ed25519, key->heap, DYNAMIC_TYPE_ED25519);
key->ed25519 = NULL;
}
break;
#endif /* HAVE_ED25519 */
#ifdef HAVE_ED448
case WC_EVP_PKEY_ED448:
if (key->ed448 != NULL && key->ownEd448 == 1) {
wc_ed448_free(key->ed448);
XFREE(key->ed448, key->heap, DYNAMIC_TYPE_ED448);
key->ed448 = NULL;
}
break;
#endif /* HAVE_ED448 */
#ifdef HAVE_HKDF
case WC_EVP_PKEY_HKDF:
XFREE(key->hkdfSalt, NULL, DYNAMIC_TYPE_SALT);
+261 -1
View File
@@ -231,6 +231,157 @@ static int d2iTryEccKey(WOLFSSL_EVP_PKEY** out, const unsigned char* mem,
}
#endif /* HAVE_ECC && OPENSSL_EXTRA */
#ifdef HAVE_ED25519
/**
* Try to make an Ed25519 EVP PKEY from data.
*
* @param [in, out] out On in, an EVP PKEY or NULL.
* On out, an EVP PKEY or NULL.
* @param [in] mem Memory containing key data.
* @param [in] memSz Size of key data in bytes.
* @param [in] priv 1 means private key, 0 means public key.
* @return 1 on success.
* @return 0 otherwise.
*/
static int d2iTryEd25519Key(WOLFSSL_EVP_PKEY** out, const unsigned char* mem,
long memSz, int priv)
{
ed25519_key* edKey = NULL;
word32 keyIdx = 0;
int isEdKey;
int ret = 1;
edKey = (ed25519_key*)XMALLOC(sizeof(ed25519_key), NULL,
DYNAMIC_TYPE_ED25519);
if (edKey == NULL) {
return 0;
}
if (wc_ed25519_init(edKey) != 0) {
XFREE(edKey, NULL, DYNAMIC_TYPE_ED25519);
return 0;
}
/* Try decoding data as an Ed25519 private/public key. The input may be
* either a DER-encoded SubjectPublicKeyInfo / PKCS#8 PrivateKeyInfo
* (the normal d2i_PUBKEY input) or a raw key (as passed from
* CopyDecodedToX509 where dCert->publicKey is the raw bytes from the
* SPKI BIT STRING). Try the structured form first, then fall back to
* the raw import. */
if (priv) {
isEdKey = (wc_Ed25519PrivateKeyDecode(mem, &keyIdx, edKey,
(word32)memSz) == 0);
if (!isEdKey && memSz == ED25519_KEY_SIZE) {
keyIdx = (word32)memSz;
isEdKey = (wc_ed25519_import_private_only(mem, (word32)memSz,
edKey) == 0);
}
}
else {
isEdKey = (wc_Ed25519PublicKeyDecode(mem, &keyIdx, edKey,
(word32)memSz) == 0);
if (!isEdKey && memSz == ED25519_PUB_KEY_SIZE) {
keyIdx = (word32)memSz;
isEdKey = (wc_ed25519_import_public(mem, (word32)memSz, edKey)
== 0);
}
}
if (!isEdKey) {
wc_ed25519_free(edKey);
XFREE(edKey, NULL, DYNAMIC_TYPE_ED25519);
return WOLFSSL_FATAL_ERROR;
}
/* Create an EVP PKEY object holding the DER bytes. */
ret = d2i_make_pkey(out, mem, keyIdx, priv, WC_EVP_PKEY_ED25519);
if (ret == 1) {
(*out)->ownEd25519 = 1;
(*out)->ed25519 = edKey;
}
else {
wc_ed25519_free(edKey);
XFREE(edKey, NULL, DYNAMIC_TYPE_ED25519);
}
return ret;
}
#endif /* HAVE_ED25519 */
#ifdef HAVE_ED448
/**
* Try to make an Ed448 EVP PKEY from data.
*
* @param [in, out] out On in, an EVP PKEY or NULL.
* On out, an EVP PKEY or NULL.
* @param [in] mem Memory containing key data.
* @param [in] memSz Size of key data in bytes.
* @param [in] priv 1 means private key, 0 means public key.
* @return 1 on success.
* @return 0 otherwise.
*/
static int d2iTryEd448Key(WOLFSSL_EVP_PKEY** out, const unsigned char* mem,
long memSz, int priv)
{
ed448_key* edKey = NULL;
word32 keyIdx = 0;
int isEdKey;
int ret = 1;
edKey = (ed448_key*)XMALLOC(sizeof(ed448_key), NULL, DYNAMIC_TYPE_ED448);
if (edKey == NULL) {
return 0;
}
if (wc_ed448_init(edKey) != 0) {
XFREE(edKey, NULL, DYNAMIC_TYPE_ED448);
return 0;
}
/* Try decoding data as an Ed448 private/public key. The input may be
* either a DER-encoded SubjectPublicKeyInfo / PKCS#8 PrivateKeyInfo
* (the normal d2i_PUBKEY input) or a raw key (as passed from
* CopyDecodedToX509 where dCert->publicKey is the raw bytes from the
* SPKI BIT STRING). Try the structured form first, then fall back to
* the raw import. */
if (priv) {
isEdKey = (wc_Ed448PrivateKeyDecode(mem, &keyIdx, edKey,
(word32)memSz) == 0);
if (!isEdKey && memSz == ED448_KEY_SIZE) {
keyIdx = (word32)memSz;
isEdKey = (wc_ed448_import_private_only(mem, (word32)memSz,
edKey) == 0);
}
}
else {
isEdKey = (wc_Ed448PublicKeyDecode(mem, &keyIdx, edKey,
(word32)memSz) == 0);
if (!isEdKey && memSz == ED448_PUB_KEY_SIZE) {
keyIdx = (word32)memSz;
isEdKey = (wc_ed448_import_public(mem, (word32)memSz, edKey)
== 0);
}
}
if (!isEdKey) {
wc_ed448_free(edKey);
XFREE(edKey, NULL, DYNAMIC_TYPE_ED448);
return WOLFSSL_FATAL_ERROR;
}
/* Create an EVP PKEY object holding the DER bytes. */
ret = d2i_make_pkey(out, mem, keyIdx, priv, WC_EVP_PKEY_ED448);
if (ret == 1) {
(*out)->ownEd448 = 1;
(*out)->ed448 = edKey;
}
else {
wc_ed448_free(edKey);
XFREE(edKey, NULL, DYNAMIC_TYPE_ED448);
}
return ret;
}
#endif /* HAVE_ED448 */
#if !defined(NO_DSA)
/**
* Try to make a DSA EVP PKEY from data.
@@ -686,6 +837,18 @@ static WOLFSSL_EVP_PKEY* d2i_evp_pkey_try(WOLFSSL_EVP_PKEY** out,
#endif /* !HAVE_FIPS || HAVE_FIPS_VERSION > 2 */
#endif /* !NO_DH && OPENSSL_EXTRA && WOLFSSL_DH_EXTRA */
#ifdef HAVE_ED25519
if (d2iTryEd25519Key(&pkey, *in, inSz, priv) >= 0) {
;
}
else
#endif /* HAVE_ED25519 */
#ifdef HAVE_ED448
if (d2iTryEd448Key(&pkey, *in, inSz, priv) >= 0) {
;
}
else
#endif /* HAVE_ED448 */
#ifdef HAVE_FALCON
if (d2iTryFalconKey(&pkey, *in, inSz, priv) >= 0) {
;
@@ -917,7 +1080,14 @@ static WOLFSSL_EVP_PKEY* d2i_evp_pkey(int type, WOLFSSL_EVP_PKEY** out,
) ||
(type == WC_EVP_PKEY_EC && algId != ECDSAk) ||
(type == WC_EVP_PKEY_DSA && algId != DSAk) ||
(type == WC_EVP_PKEY_DH && algId != DHk)) {
(type == WC_EVP_PKEY_DH && algId != DHk)
#ifdef HAVE_ED25519
|| (type == WC_EVP_PKEY_ED25519 && algId != ED25519k)
#endif
#ifdef HAVE_ED448
|| (type == WC_EVP_PKEY_ED448 && algId != ED448k)
#endif
) {
WOLFSSL_MSG("PKCS8 does not match EVP key type");
return NULL;
}
@@ -1022,6 +1192,96 @@ static WOLFSSL_EVP_PKEY* d2i_evp_pkey(int type, WOLFSSL_EVP_PKEY** out,
#endif /* !HAVE_FIPS || HAVE_FIPS_VERSION > 2 */
#endif /* HAVE_DH */
#endif /* WOLFSSL_QT || OPENSSL_ALL || WOLFSSL_OPENSSH */
#ifdef HAVE_ED25519
case WC_EVP_PKEY_ED25519:
{
ed25519_key* edKey;
word32 keyIdx = 0;
int isEdKey;
edKey = (ed25519_key*)XMALLOC(sizeof(ed25519_key), NULL,
DYNAMIC_TYPE_ED25519);
if (edKey == NULL) {
wolfSSL_EVP_PKEY_free(local);
return NULL;
}
if (wc_ed25519_init(edKey) != 0) {
XFREE(edKey, NULL, DYNAMIC_TYPE_ED25519);
wolfSSL_EVP_PKEY_free(local);
return NULL;
}
if (priv) {
isEdKey = (wc_Ed25519PrivateKeyDecode(p, &keyIdx, edKey,
(word32)local->pkey_sz) == 0);
if (!isEdKey && local->pkey_sz == ED25519_KEY_SIZE) {
isEdKey = (wc_ed25519_import_private_only(p,
(word32)local->pkey_sz, edKey) == 0);
}
}
else {
isEdKey = (wc_Ed25519PublicKeyDecode(p, &keyIdx, edKey,
(word32)local->pkey_sz) == 0);
if (!isEdKey && local->pkey_sz == ED25519_PUB_KEY_SIZE) {
isEdKey = (wc_ed25519_import_public(p,
(word32)local->pkey_sz, edKey) == 0);
}
}
if (!isEdKey) {
wc_ed25519_free(edKey);
XFREE(edKey, NULL, DYNAMIC_TYPE_ED25519);
wolfSSL_EVP_PKEY_free(local);
return NULL;
}
local->ownEd25519 = 1;
local->ed25519 = edKey;
break;
}
#endif /* HAVE_ED25519 */
#ifdef HAVE_ED448
case WC_EVP_PKEY_ED448:
{
ed448_key* edKey;
word32 keyIdx = 0;
int isEdKey;
edKey = (ed448_key*)XMALLOC(sizeof(ed448_key), NULL,
DYNAMIC_TYPE_ED448);
if (edKey == NULL) {
wolfSSL_EVP_PKEY_free(local);
return NULL;
}
if (wc_ed448_init(edKey) != 0) {
XFREE(edKey, NULL, DYNAMIC_TYPE_ED448);
wolfSSL_EVP_PKEY_free(local);
return NULL;
}
if (priv) {
isEdKey = (wc_Ed448PrivateKeyDecode(p, &keyIdx, edKey,
(word32)local->pkey_sz) == 0);
if (!isEdKey && local->pkey_sz == ED448_KEY_SIZE) {
isEdKey = (wc_ed448_import_private_only(p,
(word32)local->pkey_sz, edKey) == 0);
}
}
else {
isEdKey = (wc_Ed448PublicKeyDecode(p, &keyIdx, edKey,
(word32)local->pkey_sz) == 0);
if (!isEdKey && local->pkey_sz == ED448_PUB_KEY_SIZE) {
isEdKey = (wc_ed448_import_public(p,
(word32)local->pkey_sz, edKey) == 0);
}
}
if (!isEdKey) {
wc_ed448_free(edKey);
XFREE(edKey, NULL, DYNAMIC_TYPE_ED448);
wolfSSL_EVP_PKEY_free(local);
return NULL;
}
local->ownEd448 = 1;
local->ed448 = edKey;
break;
}
#endif /* HAVE_ED448 */
default:
WOLFSSL_MSG("Unsupported key type");
wolfSSL_EVP_PKEY_free(local);
+6
View File
@@ -450,6 +450,12 @@ enum {
WC_EVP_PKEY_HKDF = WC_NID_hkdf,
WC_EVP_PKEY_FALCON = 300, /* Randomly picked value. */
WC_EVP_PKEY_DILITHIUM = 301, /* Randomly picked value. */
#ifdef HAVE_ED25519
WC_EVP_PKEY_ED25519 = WC_NID_ED25519,
#endif
#ifdef HAVE_ED448
WC_EVP_PKEY_ED448 = WC_NID_ED448,
#endif
WC_AES_128_CFB1_TYPE = 24,
WC_AES_192_CFB1_TYPE = 25,
WC_AES_256_CFB1_TYPE = 26,
+12
View File
@@ -602,6 +602,12 @@ struct WOLFSSL_EVP_PKEY {
#ifndef NO_DH
WOLFSSL_DH* dh;
#endif
#ifdef HAVE_ED25519
struct ed25519_key* ed25519;
#endif
#ifdef HAVE_ED448
struct ed448_key* ed448;
#endif
WC_RNG rng;
#ifdef HAVE_HKDF
const WOLFSSL_EVP_MD* hkdfMd;
@@ -627,6 +633,12 @@ struct WOLFSSL_EVP_PKEY {
WC_BITFIELD ownEcc:1; /* if struct owns ECC and should free it */
WC_BITFIELD ownDsa:1; /* if struct owns DSA and should free it */
WC_BITFIELD ownRsa:1; /* if struct owns RSA and should free it */
#ifdef HAVE_ED25519
WC_BITFIELD ownEd25519:1; /* if struct owns Ed25519 and should free it */
#endif
#ifdef HAVE_ED448
WC_BITFIELD ownEd448:1; /* if struct owns Ed448 and should free it */
#endif
};