From 8b762301ef22bd78bad30a9d303043d5837b3d56 Mon Sep 17 00:00:00 2001 From: Lealem Amedie Date: Mon, 20 Apr 2026 09:31:42 -0600 Subject: [PATCH] Add EVP_PKEY_new_raw_public_key and EVP_PKEY_new_raw_private_key --- src/internal.c | 27 ++++- tests/api/test_evp_pkey.c | 13 ++- wolfcrypt/src/evp_pk.c | 222 ++++++++++++++++++++++++++++++++------ wolfssl/openssl/evp.h | 6 ++ 4 files changed, 226 insertions(+), 42 deletions(-) diff --git a/src/internal.c b/src/internal.c index 067b7a6c08..8662a9bbaf 100644 --- a/src/internal.c +++ b/src/internal.c @@ -13894,9 +13894,30 @@ int CopyDecodedToX509(WOLFSSL_X509* x509, DecodedCert* dCert) } wolfSSL_EVP_PKEY_free(x509->key.pkey); - if (!(x509->key.pkey = wolfSSL_d2i_PUBKEY(NULL, - &dCert->publicKey, - dCert->pubKeySize))) { + x509->key.pkey = NULL; + + switch (dCert->keyOID) { + #ifdef HAVE_ED25519 + case ED25519k: + x509->key.pkey = wolfSSL_EVP_PKEY_new_raw_public_key( + WC_EVP_PKEY_ED25519, NULL, dCert->publicKey, + dCert->pubKeySize); + break; + #endif + #ifdef HAVE_ED448 + case ED448k: + x509->key.pkey = wolfSSL_EVP_PKEY_new_raw_public_key( + WC_EVP_PKEY_ED448, NULL, dCert->publicKey, + dCert->pubKeySize); + break; + #endif + default: + x509->key.pkey = wolfSSL_d2i_PUBKEY(NULL, + &dCert->publicKey, dCert->pubKeySize); + break; + } + + if (x509->key.pkey == NULL) { ret = PUBLIC_KEY_E; WOLFSSL_ERROR_VERBOSE(ret); } diff --git a/tests/api/test_evp_pkey.c b/tests/api/test_evp_pkey.c index eee753125e..ec46779a71 100644 --- a/tests/api/test_evp_pkey.c +++ b/tests/api/test_evp_pkey.c @@ -2400,10 +2400,9 @@ int test_wolfSSL_EVP_PKEY_ed25519(void) wolfSSL_EVP_PKEY_free(pkey); pkey = NULL; - /* Exercise d2iTryEd25519Key's raw fallback where the caller passes - * the raw bytes from the SPKI BIT STRING rather than a wrapped SPKI. */ - p = rawPub; - ExpectNotNull(pkey = wolfSSL_d2i_PUBKEY(NULL, &p, (long)sizeof(rawPub))); + /* Exercise EVP_PKEY_new_raw_public_key to parse 32 raw BIT STRING bytes */ + ExpectNotNull(pkey = wolfSSL_EVP_PKEY_new_raw_public_key( + WC_EVP_PKEY_ED25519, NULL, rawPub, sizeof(rawPub))); ExpectIntEQ(wolfSSL_EVP_PKEY_id(pkey), EVP_PKEY_ED25519); wolfSSL_EVP_PKEY_free(pkey); pkey = NULL; @@ -2490,9 +2489,9 @@ int test_wolfSSL_EVP_PKEY_ed448(void) wolfSSL_EVP_PKEY_free(pkey); pkey = NULL; - /* Raw 57-byte fallback path. */ - p = rawPub; - ExpectNotNull(pkey = wolfSSL_d2i_PUBKEY(NULL, &p, (long)sizeof(rawPub))); + /* Parse raw bytes */ + ExpectNotNull(pkey = wolfSSL_EVP_PKEY_new_raw_public_key( + WC_EVP_PKEY_ED448, NULL, rawPub, sizeof(rawPub))); ExpectIntEQ(wolfSSL_EVP_PKEY_id(pkey), EVP_PKEY_ED448); wolfSSL_EVP_PKEY_free(pkey); pkey = NULL; diff --git a/wolfcrypt/src/evp_pk.c b/wolfcrypt/src/evp_pk.c index 4a82d4931f..68fecb8fda 100644 --- a/wolfcrypt/src/evp_pk.c +++ b/wolfcrypt/src/evp_pk.c @@ -262,13 +262,8 @@ static int d2iTryEd25519Key(WOLFSSL_EVP_PKEY** out, const unsigned char* mem, return 0; } - /* Try decoding data as an Ed25519 private/public key. Private keys must - * be PKCS#8 PrivateKeyInfo DER (matching OpenSSL d2i_PrivateKey - * semantics). Public keys may be DER-encoded SubjectPublicKeyInfo (the - * normal d2i_PUBKEY input) or raw 32-byte key material (as passed from - * CopyDecodedToX509 where dCert->publicKey is the raw bytes from the - * SPKI BIT STRING); the structured form is tried first, then the raw - * import as fallback. */ + /* Decode data as an Ed25519 key in DER form (SubjectPublicKeyInfo for + * public keys, PKCS#8 PrivateKeyInfo for private keys). */ if (priv) { isEdKey = (wc_Ed25519PrivateKeyDecode(mem, &keyIdx, edKey, (word32)memSz) == 0); @@ -276,11 +271,6 @@ static int d2iTryEd25519Key(WOLFSSL_EVP_PKEY** out, const unsigned char* mem, 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) { @@ -288,10 +278,9 @@ static int d2iTryEd25519Key(WOLFSSL_EVP_PKEY** out, const unsigned char* mem, return WOLFSSL_FATAL_ERROR; } - /* Create an EVP PKEY object holding the input bytes. These are the - * SPKI / PKCS#8 DER on the structured path, or the raw 32-byte key - * on the raw fallback path. If the caller already populated the EVP - * PKEY with the input bytes (pkey.ptr set), skip the allocate/copy. */ + /* Create an EVP PKEY object holding the input DER bytes. If the caller + * already populated the EVP PKEY with the input bytes (pkey.ptr set), + * skip the allocate/copy. */ if (*out == NULL || (*out)->pkey.ptr == NULL) { ret = d2i_make_pkey(out, mem, keyIdx, priv, WC_EVP_PKEY_ED25519); } @@ -338,13 +327,8 @@ static int d2iTryEd448Key(WOLFSSL_EVP_PKEY** out, const unsigned char* mem, return 0; } - /* Try decoding data as an Ed448 private/public key. Private keys must - * be PKCS#8 PrivateKeyInfo DER (matching OpenSSL d2i_PrivateKey - * semantics). Public keys may be DER-encoded SubjectPublicKeyInfo (the - * normal d2i_PUBKEY input) or raw 57-byte key material (as passed from - * CopyDecodedToX509 where dCert->publicKey is the raw bytes from the - * SPKI BIT STRING); the structured form is tried first, then the raw - * import as fallback. */ + /* Decode data as an Ed448 key in DER form (SubjectPublicKeyInfo for + * public keys, PKCS#8 PrivateKeyInfo for private keys). */ if (priv) { isEdKey = (wc_Ed448PrivateKeyDecode(mem, &keyIdx, edKey, (word32)memSz) == 0); @@ -352,11 +336,6 @@ static int d2iTryEd448Key(WOLFSSL_EVP_PKEY** out, const unsigned char* mem, 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) { @@ -364,10 +343,9 @@ static int d2iTryEd448Key(WOLFSSL_EVP_PKEY** out, const unsigned char* mem, return WOLFSSL_FATAL_ERROR; } - /* Create an EVP PKEY object holding the input bytes. These are the - * SPKI / PKCS#8 DER on the structured path, or the raw 57-byte key - * on the raw fallback path. If the caller already populated the EVP - * PKEY with the input bytes (pkey.ptr set), skip the allocate/copy. */ + /* Create an EVP PKEY object holding the input DER bytes. If the caller + * already populated the EVP PKEY with the input bytes (pkey.ptr set), + * skip the allocate/copy. */ if (*out == NULL || (*out)->pkey.ptr == NULL) { ret = d2i_make_pkey(out, mem, keyIdx, priv, WC_EVP_PKEY_ED448); } @@ -383,6 +361,186 @@ static int d2iTryEd448Key(WOLFSSL_EVP_PKEY** out, const unsigned char* mem, } #endif /* HAVE_ED448 */ +/* Create a new EVP_PKEY from raw Ed25519 or Ed448 key material. + * + * Used for for callers who already have the raw key bytes and shouldn't need + * to rewrap them in an SPKI just to decode. + * + * @param [in] type WC_EVP_PKEY_ED25519 or WC_EVP_PKEY_ED448. + * @param [in] e Engine. Ignored; accepted for OpenSSL API parity. + * @param [in] pub Raw public key bytes. + * @param [in] len Length of pub. Must match the curve's public key size + * (ED25519_PUB_KEY_SIZE or ED448_PUB_KEY_SIZE). + * @return WOLFSSL_EVP_PKEY on success, NULL on failure. + */ +WOLFSSL_EVP_PKEY* wolfSSL_EVP_PKEY_new_raw_public_key(int type, + WOLFSSL_ENGINE* e, const unsigned char* pub, size_t len) +{ + WOLFSSL_EVP_PKEY* pkey; + int ok = 0; + + (void)e; + WOLFSSL_ENTER("wolfSSL_EVP_PKEY_new_raw_public_key"); + + if (pub == NULL || len == 0) { + return NULL; + } + + pkey = wolfSSL_EVP_PKEY_new(); + if (pkey == NULL) { + return NULL; + } + + switch (type) { + #ifdef HAVE_ED25519 + case WC_EVP_PKEY_ED25519: { + ed25519_key* edKey; + if (len != ED25519_PUB_KEY_SIZE) { + break; + } + edKey = wolfSSL_ED25519_new(pkey->heap, INVALID_DEVID); + if (edKey == NULL) { + break; + } + if (wc_ed25519_import_public(pub, (word32)len, edKey) != 0) { + wolfSSL_ED25519_free(edKey); + break; + } + pkey->type = WC_EVP_PKEY_ED25519; + pkey->ed25519 = edKey; + pkey->ownEd25519 = 1; + ok = 1; + break; + } + #endif + #ifdef HAVE_ED448 + case WC_EVP_PKEY_ED448: { + ed448_key* edKey; + if (len != ED448_PUB_KEY_SIZE) { + break; + } + edKey = wolfSSL_ED448_new(pkey->heap, INVALID_DEVID); + if (edKey == NULL) { + break; + } + if (wc_ed448_import_public(pub, (word32)len, edKey) != 0) { + wolfSSL_ED448_free(edKey); + break; + } + pkey->type = WC_EVP_PKEY_ED448; + pkey->ed448 = edKey; + pkey->ownEd448 = 1; + ok = 1; + break; + } + #endif + default: + break; + } + + if (!ok) { + wolfSSL_EVP_PKEY_free(pkey); + return NULL; + } + + /* Stash the raw bytes so callers that later serialize the EVP_PKEY see + * consistent state. */ + pkey->pkey.ptr = (char*)XMALLOC(len, pkey->heap, DYNAMIC_TYPE_PUBLIC_KEY); + if (pkey->pkey.ptr == NULL) { + wolfSSL_EVP_PKEY_free(pkey); + return NULL; + } + XMEMCPY(pkey->pkey.ptr, pub, len); + pkey->pkey_sz = (int)len; + + return pkey; +} + +/* Private-key counterpart to wolfSSL_EVP_PKEY_new_raw_public_key. The raw + * input is the 32-byte seed (Ed25519) or 57-byte seed (Ed448). + */ +WOLFSSL_EVP_PKEY* wolfSSL_EVP_PKEY_new_raw_private_key(int type, + WOLFSSL_ENGINE* e, const unsigned char* priv, size_t len) +{ + WOLFSSL_EVP_PKEY* pkey; + int ok = 0; + + (void)e; + WOLFSSL_ENTER("wolfSSL_EVP_PKEY_new_raw_private_key"); + + if (priv == NULL || len == 0) { + return NULL; + } + + pkey = wolfSSL_EVP_PKEY_new(); + if (pkey == NULL) { + return NULL; + } + + switch (type) { + #ifdef HAVE_ED25519 + case WC_EVP_PKEY_ED25519: { + ed25519_key* edKey; + if (len != ED25519_KEY_SIZE) { + break; + } + edKey = wolfSSL_ED25519_new(pkey->heap, INVALID_DEVID); + if (edKey == NULL) { + break; + } + if (wc_ed25519_import_private_only(priv, (word32)len, edKey) + != 0) { + wolfSSL_ED25519_free(edKey); + break; + } + pkey->type = WC_EVP_PKEY_ED25519; + pkey->ed25519 = edKey; + pkey->ownEd25519 = 1; + ok = 1; + break; + } + #endif + #ifdef HAVE_ED448 + case WC_EVP_PKEY_ED448: { + ed448_key* edKey; + if (len != ED448_KEY_SIZE) { + break; + } + edKey = wolfSSL_ED448_new(pkey->heap, INVALID_DEVID); + if (edKey == NULL) { + break; + } + if (wc_ed448_import_private_only(priv, (word32)len, edKey) != 0) { + wolfSSL_ED448_free(edKey); + break; + } + pkey->type = WC_EVP_PKEY_ED448; + pkey->ed448 = edKey; + pkey->ownEd448 = 1; + ok = 1; + break; + } + #endif + default: + break; + } + + if (!ok) { + wolfSSL_EVP_PKEY_free(pkey); + return NULL; + } + + pkey->pkey.ptr = (char*)XMALLOC(len, pkey->heap, DYNAMIC_TYPE_PRIVATE_KEY); + if (pkey->pkey.ptr == NULL) { + wolfSSL_EVP_PKEY_free(pkey); + return NULL; + } + XMEMCPY(pkey->pkey.ptr, priv, len); + pkey->pkey_sz = (int)len; + + return pkey; +} + #if !defined(NO_DSA) /** * Try to make a DSA EVP PKEY from data. diff --git a/wolfssl/openssl/evp.h b/wolfssl/openssl/evp.h index dc41bd11a4..05cd80f314 100644 --- a/wolfssl/openssl/evp.h +++ b/wolfssl/openssl/evp.h @@ -991,6 +991,10 @@ WOLFSSL_API int wolfSSL_EVP_PKEY_encrypt(WOLFSSL_EVP_PKEY_CTX *ctx, WOLFSSL_API int wolfSSL_EVP_PKEY_encrypt_init(WOLFSSL_EVP_PKEY_CTX *ctx); WOLFSSL_API WOLFSSL_EVP_PKEY *wolfSSL_EVP_PKEY_new(void); WOLFSSL_API WOLFSSL_EVP_PKEY* wolfSSL_EVP_PKEY_new_ex(void* heap); +WOLFSSL_API WOLFSSL_EVP_PKEY* wolfSSL_EVP_PKEY_new_raw_public_key(int type, + WOLFSSL_ENGINE* e, const unsigned char* pub, size_t len); +WOLFSSL_API WOLFSSL_EVP_PKEY* wolfSSL_EVP_PKEY_new_raw_private_key(int type, + WOLFSSL_ENGINE* e, const unsigned char* priv, size_t len); WOLFSSL_API void wolfSSL_EVP_PKEY_free(WOLFSSL_EVP_PKEY* key); WOLFSSL_API int wolfSSL_EVP_PKEY_size(WOLFSSL_EVP_PKEY *pkey); WOLFSSL_API int wolfSSL_EVP_PKEY_copy_parameters(WOLFSSL_EVP_PKEY *to, const WOLFSSL_EVP_PKEY *from); @@ -1400,6 +1404,8 @@ WOLFSSL_API int wolfSSL_EVP_SignInit_ex(WOLFSSL_EVP_MD_CTX* ctx, #define EVP_PKEY_encrypt wolfSSL_EVP_PKEY_encrypt #define EVP_PKEY_encrypt_init wolfSSL_EVP_PKEY_encrypt_init #define EVP_PKEY_new wolfSSL_EVP_PKEY_new +#define EVP_PKEY_new_raw_public_key wolfSSL_EVP_PKEY_new_raw_public_key +#define EVP_PKEY_new_raw_private_key wolfSSL_EVP_PKEY_new_raw_private_key #define EVP_PKEY_free wolfSSL_EVP_PKEY_free #define EVP_PKEY_up_ref wolfSSL_EVP_PKEY_up_ref #define EVP_PKEY_size wolfSSL_EVP_PKEY_size