diff --git a/src/pk.c b/src/pk.c index bfc039e5d0f..d666650832d 100644 --- a/src/pk.c +++ b/src/pk.c @@ -5518,8 +5518,8 @@ int wolfSSL_ED25519_verify(const unsigned char *msg, unsigned int msgSz, #endif /* OPENSSL_EXTRA && HAVE_ED25519 */ -#if (defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL)) && \ - defined(HAVE_ED25519) +#if (defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL) || \ + defined(OPENSSL_EXTRA_X509_SMALL)) && defined(HAVE_ED25519) /* Allocate and initialize a new ed25519_key. * * @param [in] heap Heap hint for memory allocation. @@ -5570,7 +5570,8 @@ void wolfSSL_ED25519_free(ed25519_key* key) #endif } } -#endif /* (OPENSSL_EXTRA || WOLFSSL_WPAS_SMALL) && HAVE_ED25519 */ +#endif /* (OPENSSL_EXTRA || WOLFSSL_WPAS_SMALL || OPENSSL_EXTRA_X509_SMALL) && + * HAVE_ED25519 */ /******************************************************************************* * END OF ED25519 API @@ -7373,6 +7374,27 @@ int pkcs8_encode(WOLFSSL_EVP_PKEY* pkey, byte* key, word32* keySz) curveOid = NULL; oidSz = 0; } +#endif +#if defined(HAVE_ED25519) + else if (pkey->type == WC_EVP_PKEY_ED25519) { + /* The cached DER is already a PKCS#8 PrivateKeyInfo (set when the + * key was generated or decoded), so return it as-is (same as the + * DH special case above). */ + if (keySz == NULL) + return BAD_FUNC_ARG; + /* The cached DER must be present; e.g. an Ed25519 EVP_PKEY built + * from wolfSSL_X509_get_pubkey() holds only a raw public key, not a + * PKCS#8 private key, so reject rather than emit bogus output. */ + if (pkey->pkey.ptr == NULL || pkey->pkey_sz <= 0) + return BAD_FUNC_ARG; + + *keySz = (word32)pkey->pkey_sz; + if (key == NULL) + return LENGTH_ONLY_E; + + XMEMCPY(key, pkey->pkey.ptr, (size_t)pkey->pkey_sz); + return pkey->pkey_sz; + } #endif else { ret = NOT_COMPILED_IN; diff --git a/src/ssl.c b/src/ssl.c index c2a5827c9dd..efbff03e74e 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -63,6 +63,9 @@ || defined(OPENSSL_EXTRA_X509_SMALL) \ || defined(HAVE_WEBSERVER) || defined(WOLFSSL_KEY_GEN)) #include + /* ed25519 compat helpers (wolfSSL_ED25519_new/free) are used by the X509 + * Ed25519 paths, which also build under OPENSSL_EXTRA_X509_SMALL. */ + #include /* openssl headers end, wolfssl internal headers next */ #endif diff --git a/src/x509.c b/src/x509.c index fd87b1da4aa..975692c2a70 100644 --- a/src/x509.c +++ b/src/x509.c @@ -6481,6 +6481,11 @@ WOLFSSL_EVP_PKEY* wolfSSL_X509_get_pubkey(WOLFSSL_X509* x509) x509->pubKeyOID == ML_DSA_87k) { key->type = WC_EVP_PKEY_DILITHIUM; } + #endif + #if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_IMPORT) + else if (x509->pubKeyOID == ED25519k) { + key->type = WC_EVP_PKEY_ED25519; + } #endif else { key->type = WC_EVP_PKEY_EC; @@ -6572,6 +6577,29 @@ WOLFSSL_EVP_PKEY* wolfSSL_X509_get_pubkey(WOLFSSL_X509* x509) } } #endif /* NO_DSA */ + + /* decode Ed25519 key */ + #if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_IMPORT) + if (key->type == WC_EVP_PKEY_ED25519) { + key->ed25519 = wolfSSL_ED25519_new(x509->heap, INVALID_DEVID); + if (key->ed25519 == NULL) { + wolfSSL_EVP_PKEY_free(key); + return NULL; + } + key->ownEd25519 = 1; + + /* The X.509 public key buffer holds the raw Ed25519 key + * (CopyDecodedToX509 / StoreKey store the BIT STRING + * contents), so import it directly. */ + if (wc_ed25519_import_public( + (const unsigned char*)key->pkey.ptr, + (word32)key->pkey_sz, key->ed25519) != 0) { + WOLFSSL_MSG("wc_ed25519_import_public failed"); + wolfSSL_EVP_PKEY_free(key); + return NULL; + } + } + #endif /* HAVE_ED25519 */ } } return key; @@ -9010,6 +9038,12 @@ static int verifyX509orX509REQ(WOLFSSL_X509* x509, WOLFSSL_EVP_PKEY* pkey, const byte* der; int derSz = 0; int type; + const byte* pubKey; + int pubKeySz; +#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT) + byte edPubKey[ED25519_PUB_KEY_SIZE]; + word32 edPubKeySz = (word32)sizeof(edPubKey); +#endif (void)req; @@ -9023,6 +9057,10 @@ static int verifyX509orX509REQ(WOLFSSL_X509* x509, WOLFSSL_EVP_PKEY* pkey, return WOLFSSL_FATAL_ERROR; } + /* Most key types verify against the cached public-key DER. */ + pubKey = (const byte*)pkey->pkey.ptr; + pubKeySz = pkey->pkey_sz; + switch (pkey->type) { case WC_EVP_PKEY_RSA: type = RSAk; @@ -9036,6 +9074,23 @@ static int verifyX509orX509REQ(WOLFSSL_X509* x509, WOLFSSL_EVP_PKEY* pkey, type = DSAk; break; + #if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT) + case WC_EVP_PKEY_ED25519: + /* The signature check needs the raw public key; for Ed25519 + * pkey->pkey.ptr holds a PKCS#8 private blob (or nothing), so + * export the public key from the ed25519_key instead. */ + if (pkey->ed25519 == NULL || + wc_ed25519_export_public(pkey->ed25519, edPubKey, + &edPubKeySz) != 0) { + WOLFSSL_MSG("Unable to export Ed25519 public key"); + return WOLFSSL_FATAL_ERROR; + } + pubKey = edPubKey; + pubKeySz = (int)edPubKeySz; + type = ED25519k; + break; + #endif + default: WOLFSSL_MSG("Unknown pkey key type"); return WOLFSSL_FATAL_ERROR; @@ -9044,11 +9099,11 @@ static int verifyX509orX509REQ(WOLFSSL_X509* x509, WOLFSSL_EVP_PKEY* pkey, #ifdef WOLFSSL_CERT_REQ if (req) ret = CheckCSRSignaturePubKey(der, (word32)derSz, x509->heap, - (unsigned char*)pkey->pkey.ptr, pkey->pkey_sz, type); + (unsigned char*)pubKey, pubKeySz, type); else #endif ret = CheckCertSignaturePubKey(der, (word32)derSz, x509->heap, - (unsigned char*)pkey->pkey.ptr, pkey->pkey_sz, type); + (unsigned char*)pubKey, pubKeySz, type); if (ret == 0) { return WOLFSSL_SUCCESS; } @@ -12225,6 +12280,14 @@ static int CertFromX509(Cert* cert, WOLFSSL_X509* x509) int hashType; int sigType = WOLFSSL_FAILURE; + #if defined(HAVE_ED25519) + /* Ed25519 carries its own hash, so md is unused (and may be NULL). + * Resolve it before touching md. */ + if (pkey->type == WC_EVP_PKEY_ED25519) { + return CTC_ED25519; + } + #endif + /* Convert key type and hash algorithm to a signature algorithm */ if (wolfSSL_EVP_get_hashinfo(md, &hashType, NULL) == WC_NO_ERR_TRACE(WOLFSSL_FAILURE)) @@ -12337,6 +12400,9 @@ static int CertFromX509(Cert* cert, WOLFSSL_X509* x509) #ifndef NO_DSA DsaKey* dsa = NULL; #endif + #if defined(HAVE_ED25519) + ed25519_key* ed25519 = NULL; + #endif #if defined(HAVE_FALCON) falcon_key* falcon = NULL; #endif @@ -12472,6 +12538,28 @@ static int CertFromX509(Cert* cert, WOLFSSL_X509* x509) key = (void*)dsa; } #endif + #if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_IMPORT) + if (x509->pubKeyOID == ED25519k) { + ed25519 = wolfSSL_ED25519_new(NULL, INVALID_DEVID); + if (ed25519 == NULL) { + WOLFSSL_MSG("Failed to allocate memory for ed25519_key"); + XFREE(cert, NULL, DYNAMIC_TYPE_CERT); + return WOLFSSL_FAILURE; + } + + type = ED25519_TYPE; + /* The X.509 public key buffer holds the raw Ed25519 key. */ + ret = wc_ed25519_import_public(x509->pubKey.buffer, + x509->pubKey.length, ed25519); + if (ret != 0) { + WOLFSSL_ERROR_VERBOSE(ret); + wolfSSL_ED25519_free(ed25519); + XFREE(cert, NULL, DYNAMIC_TYPE_CERT); + return ret; + } + key = (void*)ed25519; + } + #endif #if defined(HAVE_FALCON) if ((x509->pubKeyOID == FALCON_LEVEL1k) || (x509->pubKeyOID == FALCON_LEVEL5k)) { @@ -12723,6 +12811,11 @@ static int CertFromX509(Cert* cert, WOLFSSL_X509* x509) XFREE(ecc, NULL, DYNAMIC_TYPE_ECC); } #endif + #if defined(HAVE_ED25519) + if (x509->pubKeyOID == ED25519k) { + wolfSSL_ED25519_free(ed25519); + } + #endif #ifndef NO_DSA if (x509->pubKeyOID == DSAk) { wc_FreeDsaKey(dsa); @@ -12807,6 +12900,12 @@ static int CertFromX509(Cert* cert, WOLFSSL_X509* x509) key = pkey->ecc->internal; } #endif + #if defined(HAVE_ED25519) + if (pkey->type == WC_EVP_PKEY_ED25519) { + type = ED25519_TYPE; + key = pkey->ed25519; + } + #endif /* Sign the certificate (request) body. */ ret = wc_InitRng(&rng); @@ -12895,11 +12994,24 @@ int wolfSSL_X509_sign(WOLFSSL_X509* x509, WOLFSSL_EVP_PKEY* pkey, WOLFSSL_ENTER("wolfSSL_X509_sign"); - if (x509 == NULL || pkey == NULL || md == NULL) { + if (x509 == NULL || pkey == NULL) { ret = WOLFSSL_FAILURE; goto out; } + /* Most key types require an explicit digest. Ed25519 is the exception: + * it signs with a NULL digest (the key has a built-in hash). Kept as a + * dedicated block since other algorithms may grow similar edge cases. */ + if (md == NULL) { + #if defined(HAVE_ED25519) + if (pkey->type != WC_EVP_PKEY_ED25519) + #endif + { + ret = WOLFSSL_FAILURE; + goto out; + } + } + x509->sigOID = wolfSSL_sigTypeFromPKEY((WOLFSSL_EVP_MD*)md, pkey); if ((ret = wolfssl_x509_make_der(x509, 0, der, &derSz, 0)) != WOLFSSL_SUCCESS) { @@ -16408,6 +16520,30 @@ int wolfSSL_X509_set_pubkey(WOLFSSL_X509 *cert, WOLFSSL_EVP_PKEY *pkey) cert->pubKeyOID = ECDSAk; } break; +#endif +#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT) + case WC_EVP_PKEY_ED25519: + { + word32 rawLen = ED25519_PUB_KEY_SIZE; + + if (pkey->ed25519 == NULL) + return WOLFSSL_FAILURE; + + /* Store the RAW public key: wolfSSL keeps an X.509 Ed25519 + * public key as the bare key bytes (see StoreKey / + * CopyDecodedToX509), not a SubjectPublicKeyInfo. */ + p = (byte*)XMALLOC(rawLen, cert->heap, DYNAMIC_TYPE_PUBLIC_KEY); + if (p == NULL) + return WOLFSSL_FAILURE; + + if (wc_ed25519_export_public(pkey->ed25519, p, &rawLen) != 0) { + XFREE(p, cert->heap, DYNAMIC_TYPE_PUBLIC_KEY); + return WOLFSSL_FAILURE; + } + derSz = (int)rawLen; + cert->pubKeyOID = ED25519k; + } + break; #endif default: return WOLFSSL_FAILURE; diff --git a/tests/api.c b/tests/api.c index 32b90b9a079..893e49cb6b4 100644 --- a/tests/api.c +++ b/tests/api.c @@ -10282,6 +10282,182 @@ static int test_wolfSSL_PKCS8(void) return EXPECT_RESULT(); } +/* Exercise Ed25519 through the OpenSSL-compatibility EVP/X509 surface the + * way an application does: generate a key, serialise the public + * (SubjectPublicKeyInfo) and private (PKCS#8) parts, build and sign an + * in-memory self-signed certificate with a NULL digest, read the public key + * back out, and load the key + cert into an SSL_CTX. */ +static int test_wolfSSL_EVP_PKEY_ED25519_openssl(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && defined(HAVE_ED25519) && \ + defined(HAVE_ED25519_KEY_EXPORT) && defined(HAVE_ED25519_KEY_IMPORT) && \ + defined(HAVE_ED25519_MAKE_KEY) && \ + defined(WOLFSSL_CERT_GEN) && !defined(NO_CERTS) + WOLFSSL_EVP_PKEY_CTX* ctx = NULL; + WOLFSSL_EVP_PKEY* pkey = NULL; + WOLFSSL_EVP_PKEY* certPub = NULL; + WOLFSSL_X509* x509 = NULL; + WOLFSSL_X509_NAME* name = NULL; + WOLFSSL_ASN1_TIME* notBefore = NULL; + WOLFSSL_ASN1_TIME* notAfter = NULL; + unsigned char* spki = NULL; + unsigned char* spki2 = NULL; + int spkiSz = 0; + int spki2Sz = 0; + time_t t = 0; + + /* (1) Generate an Ed25519 key purely through the EVP API. */ + ExpectNotNull(ctx = wolfSSL_EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519, NULL)); + ExpectIntEQ(wolfSSL_EVP_PKEY_keygen_init(ctx), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_EVP_PKEY_keygen(ctx, &pkey), WOLFSSL_SUCCESS); + ExpectNotNull(pkey); + + /* (2) Encode the public key as SubjectPublicKeyInfo. */ + ExpectIntGT((spkiSz = wolfSSL_i2d_PUBKEY(pkey, &spki)), 0); + + /* (2b) Raw-key constructors populate the EVP_PKEY differently from + * keygen/decode (no cached PKCS#8 DER), so exercise them directly: a + * public-only raw key must serialize to the same SPKI, and a raw private + * seed must import as an Ed25519 key. */ + { + /* RFC 8032 Ed25519 test-vector 1 secret seed. */ + static const unsigned char ed25519Seed[ED25519_KEY_SIZE] = { + 0x9d, 0x61, 0xb1, 0x9d, 0xef, 0xfd, 0x5a, 0x60, + 0xba, 0x84, 0x4a, 0xf4, 0x92, 0xec, 0x2c, 0xc4, + 0x44, 0x49, 0xc5, 0x69, 0x7b, 0x32, 0x69, 0x19, + 0x70, 0x3b, 0xac, 0x03, 0x1c, 0xae, 0x7f, 0x60 + }; + WOLFSSL_EVP_PKEY* rawPub = NULL; + WOLFSSL_EVP_PKEY* rawPriv = NULL; + unsigned char* rawSpki = NULL; + int rawSpkiSz = 0; + + /* An Ed25519 SPKI is a 12-byte header followed by the raw public key, + * which is what new_raw_public_key() wants. */ + ExpectIntEQ(spkiSz, (int)(ED25519_PUB_KEY_SIZE + 12)); + if ((spki != NULL) && (spkiSz == (int)(ED25519_PUB_KEY_SIZE + 12))) { + ExpectNotNull(rawPub = wolfSSL_EVP_PKEY_new_raw_public_key( + EVP_PKEY_ED25519, NULL, spki + 12, ED25519_PUB_KEY_SIZE)); + ExpectIntGT((rawSpkiSz = wolfSSL_i2d_PUBKEY(rawPub, &rawSpki)), 0); + ExpectIntEQ(rawSpkiSz, spkiSz); + if ((rawSpki != NULL) && (rawSpkiSz == spkiSz)) { + ExpectIntEQ(XMEMCMP(spki, rawSpki, (size_t)spkiSz), 0); + } + } + + ExpectNotNull(rawPriv = wolfSSL_EVP_PKEY_new_raw_private_key( + EVP_PKEY_ED25519, NULL, ed25519Seed, ED25519_KEY_SIZE)); + + XFREE(rawSpki, NULL, DYNAMIC_TYPE_PUBLIC_KEY); + wolfSSL_EVP_PKEY_free(rawPub); + wolfSSL_EVP_PKEY_free(rawPriv); + } + + /* (3) Encode the private key as PKCS#8 PrivateKeyInfo and (7) decode it + * back. These compat helpers (EVP_PKEY2PKCS8, i2d_PKCS8_PKEY, + * d2i_AutoPrivateKey) are only built with OPENSSL_ALL. */ +#if defined(OPENSSL_ALL) && !defined(NO_AES) + { + WOLFSSL_PKCS8_PRIV_KEY_INFO* p8 = NULL; + WOLFSSL_EVP_PKEY* decPriv = NULL; + unsigned char* p8der = NULL; + const unsigned char* tmp = NULL; + int p8Sz = 0; + + ExpectNotNull(p8 = wolfSSL_EVP_PKEY2PKCS8(pkey)); + ExpectIntGT((p8Sz = wolfSSL_i2d_PKCS8_PKEY(p8, &p8der)), 0); + tmp = p8der; + ExpectNotNull(decPriv = wolfSSL_d2i_AutoPrivateKey(NULL, &tmp, p8Sz)); + + /* The PKCS#8-decoded private key must re-export the same public key / + * SPKI, not merely decode to non-NULL. */ + { + unsigned char* decSpki = NULL; + int decSpkiSz = 0; + + ExpectIntGT((decSpkiSz = wolfSSL_i2d_PUBKEY(decPriv, &decSpki)), 0); + ExpectIntEQ(decSpkiSz, spkiSz); + if ((spki != NULL) && (decSpki != NULL) && (decSpkiSz == spkiSz)) { + ExpectIntEQ(XMEMCMP(spki, decSpki, (size_t)spkiSz), 0); + } + XFREE(decSpki, NULL, DYNAMIC_TYPE_PUBLIC_KEY); + } + + XFREE(p8der, NULL, DYNAMIC_TYPE_ASN1); + wolfSSL_EVP_PKEY_free(decPriv); + wolfSSL_EVP_PKEY_free((WOLFSSL_EVP_PKEY*)p8); + } +#endif + + /* (4)(5) Build an in-memory self-signed cert; Ed25519 signs with a NULL + * digest (it carries its own hash). */ + ExpectNotNull(x509 = wolfSSL_X509_new()); + ExpectIntEQ(wolfSSL_X509_set_version(x509, 2), WOLFSSL_SUCCESS); + ExpectNotNull(name = wolfSSL_X509_NAME_new()); + ExpectIntEQ(wolfSSL_X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, + (const byte*)"ed25519 test", -1, -1, 0), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_X509_set_subject_name(x509, name), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_X509_set_issuer_name(x509, name), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_X509_set_pubkey(x509, pkey), WOLFSSL_SUCCESS); + t = XTIME(NULL); + ExpectNotNull(notBefore = wolfSSL_ASN1_TIME_adj(NULL, t, 0, 0)); + ExpectNotNull(notAfter = wolfSSL_ASN1_TIME_adj(NULL, t, 365, 0)); + ExpectTrue(wolfSSL_X509_set_notBefore(x509, notBefore)); + ExpectTrue(wolfSSL_X509_set_notAfter(x509, notAfter)); + ExpectIntGT(wolfSSL_X509_sign(x509, pkey, NULL), 0); + + /* (5b) The self-signed certificate verifies under its own key, proving + * the generated Ed25519 signature, OID and public-key encoding are + * usable. */ + ExpectIntEQ(wolfSSL_X509_verify(x509, pkey), WOLFSSL_SUCCESS); + + /* (8) The certificate's public key round-trips to the same SPKI. */ + ExpectNotNull(certPub = wolfSSL_X509_get_pubkey(x509)); + ExpectIntGT((spki2Sz = wolfSSL_i2d_PUBKEY(certPub, &spki2)), 0); + ExpectIntEQ(spki2Sz, spkiSz); + if ((spki != NULL) && (spki2 != NULL) && (spkiSz == spki2Sz)) { + ExpectIntEQ(XMEMCMP(spki, spki2, (size_t)spkiSz), 0); + } + + /* (8b) PKCS#8 private-key export from a public-only key (certPub holds + * only the raw public key) must fail cleanly rather than emit the raw + * public bytes or cached non-PKCS#8 data as if they were a private key. */ +#if defined(OPENSSL_ALL) || defined(WOLFSSL_WPAS_SMALL) + ExpectNull(wolfSSL_EVP_PKEY2PKCS8(certPub)); +#endif + + /* (6) Load the generated key and cert into an SSL_CTX. */ +#if !defined(NO_TLS) && \ + (!defined(NO_WOLFSSL_CLIENT) || !defined(NO_WOLFSSL_SERVER)) + { + WOLFSSL_CTX* sslCtx = NULL; +#ifndef NO_WOLFSSL_SERVER + ExpectNotNull(sslCtx = wolfSSL_CTX_new(wolfSSLv23_server_method())); +#else + ExpectNotNull(sslCtx = wolfSSL_CTX_new(wolfSSLv23_client_method())); +#endif + ExpectIntEQ(wolfSSL_CTX_use_certificate(sslCtx, x509), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey(sslCtx, pkey), + WOLFSSL_SUCCESS); + wolfSSL_CTX_free(sslCtx); + } +#endif + + XFREE(spki, NULL, DYNAMIC_TYPE_PUBLIC_KEY); + XFREE(spki2, NULL, DYNAMIC_TYPE_PUBLIC_KEY); + wolfSSL_ASN1_TIME_free(notBefore); + wolfSSL_ASN1_TIME_free(notAfter); + wolfSSL_X509_NAME_free(name); + wolfSSL_X509_free(x509); + wolfSSL_EVP_PKEY_free(certPub); + wolfSSL_EVP_PKEY_free(pkey); + wolfSSL_EVP_PKEY_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + static int test_wolfSSL_PKCS8_ED25519(void) { EXPECT_DECLS; @@ -35302,6 +35478,7 @@ TEST_CASE testCases[] = { /* PKCS8 testing */ TEST_DECL(test_wolfSSL_no_password_cb), TEST_DECL(test_wolfSSL_PKCS8), + TEST_DECL(test_wolfSSL_EVP_PKEY_ED25519_openssl), TEST_DECL(test_wolfSSL_PKCS8_ED25519), TEST_DECL(test_wolfSSL_PKCS8_ED448), diff --git a/wolfcrypt/src/evp.c b/wolfcrypt/src/evp.c index 50012883380..b41b8052c1d 100644 --- a/wolfcrypt/src/evp.c +++ b/wolfcrypt/src/evp.c @@ -3820,6 +3820,10 @@ int wolfSSL_EVP_PKEY_keygen(WOLFSSL_EVP_PKEY_CTX *ctx, #endif #ifdef HAVE_CURVE448 ctx->pkey->type != WC_EVP_PKEY_X448 && + #endif + #if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT) && \ + defined(HAVE_ED25519_MAKE_KEY) + ctx->pkey->type != WC_EVP_PKEY_ED25519 && #endif ctx->pkey->type != WC_EVP_PKEY_DH)) { WOLFSSL_MSG("Key not set or key type not supported"); @@ -3932,6 +3936,53 @@ int wolfSSL_EVP_PKEY_keygen(WOLFSSL_EVP_PKEY_CTX *ctx, ret = WOLFSSL_SUCCESS; } break; +#endif +#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT) && \ + defined(HAVE_ED25519_MAKE_KEY) + case WC_EVP_PKEY_ED25519: + if (pkey->ed25519 == NULL) { + pkey->ed25519 = wolfSSL_ED25519_new(pkey->heap, INVALID_DEVID); + if (pkey->ed25519 == NULL) { + ret = MEMORY_E; + break; + } + pkey->ownEd25519 = 1; + } + /* Reuse the RNG already initialized on the EVP_PKEY. */ + if (wc_ed25519_make_key(&pkey->rng, ED25519_KEY_SIZE, + pkey->ed25519) == 0) { + /* Cache the PKCS#8 PrivateKeyInfo DER so the EVP/SSL paths + * (use_PrivateKey, EVP_PKEY2PKCS8) can load and serialize the + * key, mirroring the state the decode path produces. */ + int edDerSz = wc_Ed25519PrivateKeyToDer(pkey->ed25519, NULL, 0); + if (edDerSz > 0) { + byte* edDer = (byte*)XMALLOC((size_t)edDerSz, pkey->heap, + DYNAMIC_TYPE_OPENSSL); + if (edDer != NULL) { + if (wc_Ed25519PrivateKeyToDer(pkey->ed25519, edDer, + (word32)edDerSz) == edDerSz) { + XFREE(pkey->pkey.ptr, pkey->heap, + DYNAMIC_TYPE_OPENSSL); + pkey->pkey.ptr = (char*)edDer; + pkey->pkey_sz = edDerSz; + ret = WOLFSSL_SUCCESS; + } + else { + XFREE(edDer, pkey->heap, DYNAMIC_TYPE_OPENSSL); + } + } + } + } + /* If the key was generated but could not be fully cached (DER + * export failed), do not leave a half-initialized key behind on a + * FAILURE return: the cleanup below only runs when we own pkey, so + * free the key we allocated here. */ + if (ret != WOLFSSL_SUCCESS && pkey->ownEd25519) { + wolfSSL_ED25519_free(pkey->ed25519); + pkey->ed25519 = NULL; + pkey->ownEd25519 = 0; + } + break; #endif default: break; diff --git a/wolfcrypt/src/evp_pk.c b/wolfcrypt/src/evp_pk.c index 777f012e3c4..4de441bfecb 100644 --- a/wolfcrypt/src/evp_pk.c +++ b/wolfcrypt/src/evp_pk.c @@ -283,6 +283,21 @@ static int d2iTryEd25519Key(WOLFSSL_EVP_PKEY** out, const unsigned char* mem, return WOLFSSL_FATAL_ERROR; } +#ifdef HAVE_ED25519_MAKE_KEY + /* A PKCS#8 v1 PrivateKeyInfo carries only the private seed, so the + * decoded key has no public part. Derive it (deterministic from the + * seed; wc_ed25519_make_public sets pubKeySet on success) so the + * resulting EVP_PKEY is complete and callers can later export/embed the + * public key. Best-effort: on failure the key is left private-only. */ + if (priv && !edKey->pubKeySet) { + if (wc_ed25519_make_public(edKey, edKey->p, ED25519_PUB_KEY_SIZE) + != 0) { + WOLFSSL_MSG("wc_ed25519_make_public failed; " + "EVP_PKEY has no public part"); + } + } +#endif /* HAVE_ED25519_MAKE_KEY */ + /* 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. */ @@ -1601,6 +1616,16 @@ WOLFSSL_EVP_PKEY* wolfSSL_d2i_AutoPrivateKey(WOLFSSL_EVP_PKEY** pkey, /* Take off PKCS#8 wrapper if found. */ if ((len = ToTraditionalInline_ex(der, &idx, keyLen, &algId)) >= 0) { + #if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_IMPORT) + if (algId == ED25519k) { + /* Ed25519's inner key is an OCTET STRING, not a SEQUENCE, so the + * RSA/ECC element-count heuristic below cannot classify it. + * Decode the full PKCS#8 PrivateKeyInfo directly (keeps the + * cached DER complete so the key can be re-loaded later). */ + return wolfSSL_d2i_PrivateKey(WC_EVP_PKEY_ED25519, pkey, pp, + length); + } + #endif der += idx; keyLen = (word32)len; } @@ -2378,6 +2403,54 @@ static int wolfssl_i_i2d_ecpublickey(const WOLFSSL_EVP_PKEY* key, } #endif +#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT) +/* Encode an Ed25519 public key as DER SubjectPublicKeyInfo. Follows the + * i2d output convention: der == NULL returns the size only; *der == NULL + * allocates the buffer (caller frees); otherwise writes into *der and + * advances it. Returns the DER size or WOLFSSL_FATAL_ERROR. */ +static int wolfssl_i_i2d_ed25519_pubkey(const ed25519_key* key, + unsigned char **der) +{ + int derSz; + unsigned char* buf; + + if (key == NULL) { + return WOLFSSL_FATAL_ERROR; + } + + /* withAlg = 1 -> wrap the raw key in a SubjectPublicKeyInfo. */ + derSz = wc_Ed25519PublicKeyToDer((ed25519_key*)key, NULL, 0, 1); + if (derSz <= 0) { + return WOLFSSL_FATAL_ERROR; + } + if (der == NULL) { + return derSz; + } + + buf = (unsigned char*)XMALLOC((size_t)derSz, NULL, DYNAMIC_TYPE_PUBLIC_KEY); + if (buf == NULL) { + return WOLFSSL_FATAL_ERROR; + } + if (wc_Ed25519PublicKeyToDer((ed25519_key*)key, buf, (word32)derSz, 1) + != derSz) { + XFREE(buf, NULL, DYNAMIC_TYPE_PUBLIC_KEY); + return WOLFSSL_FATAL_ERROR; + } + + if (*der == NULL) { + /* Hand the buffer to the caller. */ + *der = buf; + } + else { + XMEMCPY(*der, buf, (size_t)derSz); + *der += derSz; + XFREE(buf, NULL, DYNAMIC_TYPE_PUBLIC_KEY); + } + + return derSz; +} +#endif /* HAVE_ED25519 && HAVE_ED25519_KEY_EXPORT */ + /* Encode the WOLFSSL_EVP_PKEY object as public key DER. * * @param [in] key WOLFSLS_EVP_PKEY object to encode. @@ -2405,6 +2478,13 @@ int wolfSSL_i2d_PublicKey(const WOLFSSL_EVP_PKEY *key, unsigned char **der) #ifdef HAVE_ECC case WC_EVP_PKEY_EC: return wolfssl_i_i2d_ecpublickey(key, key->ecc, der); + #endif + #if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT) + /* Emit a SubjectPublicKeyInfo (withAlg=1): wolfSSL_i2d_PUBKEY aliases + * to this function (below), and the SPKI form is what the + * PEM_write_bio_PUBKEY / d2i_PUBKEY round-trip expects for Ed25519. */ + case WC_EVP_PKEY_ED25519: + return wolfssl_i_i2d_ed25519_pubkey(key->ed25519, der); #endif default: ret = WOLFSSL_FATAL_ERROR; diff --git a/wolfssl/openssl/ed25519.h b/wolfssl/openssl/ed25519.h index 1c95e71eec2..00e9f4e7a03 100644 --- a/wolfssl/openssl/ed25519.h +++ b/wolfssl/openssl/ed25519.h @@ -42,7 +42,8 @@ int wolfSSL_ED25519_verify(const unsigned char *msg, unsigned int msgSz, const unsigned char *pub, unsigned int pubSz, const unsigned char *sig, unsigned int sigSz); -#if defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL) +#if defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL) || \ + defined(OPENSSL_EXTRA_X509_SMALL) #ifndef WC_ED25519KEY_TYPE_DEFINED typedef struct ed25519_key ed25519_key; #define WC_ED25519KEY_TYPE_DEFINED