Merge pull request #10552 from julek-wolfssl/evp-x25519-x448

Add NID_X25519 and NID_X448 support to the EVP layer
This commit is contained in:
JacobBarthelmeh
2026-05-28 15:57:50 -06:00
committed by GitHub
6 changed files with 451 additions and 2 deletions
+159
View File
@@ -2702,3 +2702,162 @@ int test_wolfSSL_EVP_PKEY_ed448(void)
return EXPECT_RESULT();
}
int test_wolfSSL_EVP_PKEY_x25519(void)
{
EXPECT_DECLS;
#if defined(OPENSSL_EXTRA) && defined(HAVE_CURVE25519)
EVP_PKEY* pkey = NULL;
EVP_PKEY* peer = NULL;
EVP_PKEY_CTX* genCtx = NULL;
EVP_PKEY_CTX* ctx = NULL;
unsigned char rawPriv[32];
unsigned char rawPub[32];
unsigned char secretA[32];
unsigned char secretB[32];
size_t secretLen;
int i;
for (i = 0; i < 32; i++) {
rawPriv[i] = (unsigned char)i;
rawPub[i] = (unsigned char)(0x40 + i);
}
/* Raw import with the correct length reports the X25519 type. */
ExpectNotNull(pkey = EVP_PKEY_new_raw_public_key(
EVP_PKEY_X25519, NULL, rawPub, sizeof(rawPub)));
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_X25519);
EVP_PKEY_free(pkey);
pkey = NULL;
ExpectNotNull(pkey = EVP_PKEY_new_raw_private_key(
EVP_PKEY_X25519, NULL, rawPriv, sizeof(rawPriv)));
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_X25519);
/* X25519 is key-agreement only: signing must be rejected. */
ExpectNotNull(ctx = EVP_PKEY_CTX_new(pkey, NULL));
ExpectIntNE(EVP_PKEY_sign_init(ctx), WOLFSSL_SUCCESS);
EVP_PKEY_CTX_free(ctx);
ctx = NULL;
EVP_PKEY_free(pkey);
pkey = NULL;
/* Wrong raw lengths are rejected. */
ExpectNull(EVP_PKEY_new_raw_public_key(
EVP_PKEY_X25519, NULL, rawPub, 16));
ExpectNull(EVP_PKEY_new_raw_private_key(
EVP_PKEY_X25519, NULL, rawPriv, 16));
/* Generate two key pairs and confirm ECDH agreement is symmetric. This
* also exercises the little-endian convention used on import/derive. */
ExpectNotNull(genCtx = EVP_PKEY_CTX_new_id(EVP_PKEY_X25519, NULL));
ExpectIntEQ(EVP_PKEY_keygen_init(genCtx), WOLFSSL_SUCCESS);
ExpectIntEQ(EVP_PKEY_keygen(genCtx, &pkey), WOLFSSL_SUCCESS);
ExpectIntEQ(EVP_PKEY_keygen(genCtx, &peer), WOLFSSL_SUCCESS);
EVP_PKEY_CTX_free(genCtx);
genCtx = NULL;
ExpectNotNull(ctx = EVP_PKEY_CTX_new(pkey, NULL));
ExpectIntEQ(EVP_PKEY_derive_init(ctx), WOLFSSL_SUCCESS);
ExpectIntEQ(EVP_PKEY_derive_set_peer(ctx, peer), WOLFSSL_SUCCESS);
secretLen = sizeof(secretA);
ExpectIntEQ(EVP_PKEY_derive(ctx, secretA, &secretLen), WOLFSSL_SUCCESS);
ExpectIntEQ((int)secretLen, 32);
EVP_PKEY_CTX_free(ctx);
ctx = NULL;
ExpectNotNull(ctx = EVP_PKEY_CTX_new(peer, NULL));
ExpectIntEQ(EVP_PKEY_derive_init(ctx), WOLFSSL_SUCCESS);
ExpectIntEQ(EVP_PKEY_derive_set_peer(ctx, pkey), WOLFSSL_SUCCESS);
secretLen = sizeof(secretB);
ExpectIntEQ(EVP_PKEY_derive(ctx, secretB, &secretLen), WOLFSSL_SUCCESS);
ExpectIntEQ((int)secretLen, 32);
EVP_PKEY_CTX_free(ctx);
ctx = NULL;
ExpectIntEQ(XMEMCMP(secretA, secretB, 32), 0);
EVP_PKEY_free(peer);
EVP_PKEY_free(pkey);
#endif
return EXPECT_RESULT();
}
int test_wolfSSL_EVP_PKEY_x448(void)
{
EXPECT_DECLS;
#if defined(OPENSSL_EXTRA) && defined(HAVE_CURVE448)
EVP_PKEY* pkey = NULL;
EVP_PKEY* peer = NULL;
EVP_PKEY_CTX* genCtx = NULL;
EVP_PKEY_CTX* ctx = NULL;
unsigned char rawPriv[56];
unsigned char rawPub[56];
unsigned char secretA[56];
unsigned char secretB[56];
size_t secretLen;
int i;
for (i = 0; i < 56; i++) {
rawPriv[i] = (unsigned char)i;
rawPub[i] = (unsigned char)(0x40 + i);
}
/* Raw import with the correct length reports the X448 type. */
ExpectNotNull(pkey = EVP_PKEY_new_raw_public_key(
EVP_PKEY_X448, NULL, rawPub, sizeof(rawPub)));
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_X448);
EVP_PKEY_free(pkey);
pkey = NULL;
ExpectNotNull(pkey = EVP_PKEY_new_raw_private_key(
EVP_PKEY_X448, NULL, rawPriv, sizeof(rawPriv)));
ExpectIntEQ(EVP_PKEY_id(pkey), EVP_PKEY_X448);
/* X448 is key-agreement only: signing must be rejected. */
ExpectNotNull(ctx = EVP_PKEY_CTX_new(pkey, NULL));
ExpectIntNE(EVP_PKEY_sign_init(ctx), WOLFSSL_SUCCESS);
EVP_PKEY_CTX_free(ctx);
ctx = NULL;
EVP_PKEY_free(pkey);
pkey = NULL;
/* Wrong raw lengths are rejected. */
ExpectNull(EVP_PKEY_new_raw_public_key(
EVP_PKEY_X448, NULL, rawPub, 16));
ExpectNull(EVP_PKEY_new_raw_private_key(
EVP_PKEY_X448, NULL, rawPriv, 16));
/* Generate two key pairs and confirm ECDH agreement is symmetric. */
ExpectNotNull(genCtx = EVP_PKEY_CTX_new_id(EVP_PKEY_X448, NULL));
ExpectIntEQ(EVP_PKEY_keygen_init(genCtx), WOLFSSL_SUCCESS);
ExpectIntEQ(EVP_PKEY_keygen(genCtx, &pkey), WOLFSSL_SUCCESS);
ExpectIntEQ(EVP_PKEY_keygen(genCtx, &peer), WOLFSSL_SUCCESS);
EVP_PKEY_CTX_free(genCtx);
genCtx = NULL;
ExpectNotNull(ctx = EVP_PKEY_CTX_new(pkey, NULL));
ExpectIntEQ(EVP_PKEY_derive_init(ctx), WOLFSSL_SUCCESS);
ExpectIntEQ(EVP_PKEY_derive_set_peer(ctx, peer), WOLFSSL_SUCCESS);
secretLen = sizeof(secretA);
ExpectIntEQ(EVP_PKEY_derive(ctx, secretA, &secretLen), WOLFSSL_SUCCESS);
ExpectIntEQ((int)secretLen, 56);
EVP_PKEY_CTX_free(ctx);
ctx = NULL;
ExpectNotNull(ctx = EVP_PKEY_CTX_new(peer, NULL));
ExpectIntEQ(EVP_PKEY_derive_init(ctx), WOLFSSL_SUCCESS);
ExpectIntEQ(EVP_PKEY_derive_set_peer(ctx, pkey), WOLFSSL_SUCCESS);
secretLen = sizeof(secretB);
ExpectIntEQ(EVP_PKEY_derive(ctx, secretB, &secretLen), WOLFSSL_SUCCESS);
ExpectIntEQ((int)secretLen, 56);
EVP_PKEY_CTX_free(ctx);
ctx = NULL;
ExpectIntEQ(XMEMCMP(secretA, secretB, 56), 0);
EVP_PKEY_free(peer);
EVP_PKEY_free(pkey);
#endif
return EXPECT_RESULT();
}
+5 -1
View File
@@ -66,6 +66,8 @@ int test_wolfSSL_EVP_PKEY_print_public(void);
int test_wolfSSL_EVP_PKEY_ed25519(void);
int test_wolfSSL_CTX_use_PrivateKey_ed25519(void);
int test_wolfSSL_EVP_PKEY_ed448(void);
int test_wolfSSL_EVP_PKEY_x25519(void);
int test_wolfSSL_EVP_PKEY_x448(void);
#define TEST_EVP_PKEY_DECLS \
TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_CTX_new_id), \
@@ -110,6 +112,8 @@ int test_wolfSSL_EVP_PKEY_ed448(void);
TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_print_public), \
TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_ed25519), \
TEST_DECL_GROUP("evp_pkey", test_wolfSSL_CTX_use_PrivateKey_ed25519), \
TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_ed448)
TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_ed448), \
TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_x25519), \
TEST_DECL_GROUP("evp_pkey", test_wolfSSL_EVP_PKEY_x448)
#endif /* WOLFCRYPT_TEST_EVP_PKEY_H */
+143 -1
View File
@@ -47,6 +47,12 @@
#ifdef HAVE_ED448
#include <wolfssl/wolfcrypt/ed448.h>
#endif
#ifdef HAVE_CURVE25519
#include <wolfssl/wolfcrypt/curve25519.h>
#endif
#ifdef HAVE_CURVE448
#include <wolfssl/wolfcrypt/curve448.h>
#endif
static const struct s_ent {
const enum wc_HashType macType;
@@ -2767,7 +2773,7 @@ int wolfSSL_EVP_PKEY_CTX_ctrl_str(WOLFSSL_EVP_PKEY_CTX *ctx,
#endif /* NO_WOLFSSL_STUB */
#if (!defined(NO_DH) && defined(WOLFSSL_DH_EXTRA)) || defined(HAVE_ECC) || \
defined(HAVE_HKDF)
defined(HAVE_HKDF) || defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)
int wolfSSL_EVP_PKEY_derive(WOLFSSL_EVP_PKEY_CTX *ctx, unsigned char *key, size_t *keylen)
{
int len;
@@ -2884,6 +2890,54 @@ int wolfSSL_EVP_PKEY_derive(WOLFSSL_EVP_PKEY_CTX *ctx, unsigned char *key, size_
*keylen = (size_t)len;
break;
#endif
#ifdef HAVE_CURVE25519
case WC_EVP_PKEY_X25519:
if (!ctx->pkey->curve25519 || !ctx->peerKey->curve25519) {
return WOLFSSL_FAILURE;
}
len = CURVE25519_KEYSIZE;
if (key) {
word32 len32 = (word32)*keylen;
if (*keylen < (size_t)len) {
WOLFSSL_MSG("buffer too short");
return WOLFSSL_FAILURE;
}
/* X25519 shared secret is little-endian (RFC 7748). */
if (wc_curve25519_shared_secret_ex(ctx->pkey->curve25519,
ctx->peerKey->curve25519, key, &len32,
EC25519_LITTLE_ENDIAN) != 0) {
WOLFSSL_MSG("wc_curve25519_shared_secret_ex failed");
return WOLFSSL_FAILURE;
}
len = (int)len32;
}
*keylen = (size_t)len;
break;
#endif
#ifdef HAVE_CURVE448
case WC_EVP_PKEY_X448:
if (!ctx->pkey->curve448 || !ctx->peerKey->curve448) {
return WOLFSSL_FAILURE;
}
len = CURVE448_KEY_SIZE;
if (key) {
word32 len32 = (word32)*keylen;
if (*keylen < (size_t)len) {
WOLFSSL_MSG("buffer too short");
return WOLFSSL_FAILURE;
}
/* X448 shared secret is little-endian (RFC 7748). */
if (wc_curve448_shared_secret_ex(ctx->pkey->curve448,
ctx->peerKey->curve448, key, &len32,
EC448_LITTLE_ENDIAN) != 0) {
WOLFSSL_MSG("wc_curve448_shared_secret_ex failed");
return WOLFSSL_FAILURE;
}
len = (int)len32;
}
*keylen = (size_t)len;
break;
#endif
#ifdef HAVE_HKDF
case WC_EVP_PKEY_HKDF:
(void)len;
@@ -3761,6 +3815,12 @@ int wolfSSL_EVP_PKEY_keygen(WOLFSSL_EVP_PKEY_CTX *ctx,
if (ctx->pkey == NULL ||
(ctx->pkey->type != WC_EVP_PKEY_EC &&
ctx->pkey->type != WC_EVP_PKEY_RSA &&
#ifdef HAVE_CURVE25519
ctx->pkey->type != WC_EVP_PKEY_X25519 &&
#endif
#ifdef HAVE_CURVE448
ctx->pkey->type != WC_EVP_PKEY_X448 &&
#endif
ctx->pkey->type != WC_EVP_PKEY_DH)) {
WOLFSSL_MSG("Key not set or key type not supported");
return WOLFSSL_FAILURE;
@@ -3821,6 +3881,57 @@ int wolfSSL_EVP_PKEY_keygen(WOLFSSL_EVP_PKEY_CTX *ctx,
}
}
break;
#endif
#ifdef HAVE_CURVE25519
case WC_EVP_PKEY_X25519:
if (pkey->curve25519 == NULL) {
pkey->curve25519 = (curve25519_key*)XMALLOC(
sizeof(curve25519_key), pkey->heap, DYNAMIC_TYPE_CURVE25519);
if (pkey->curve25519 == NULL) {
ret = MEMORY_E;
break;
}
if (wc_curve25519_init_ex(pkey->curve25519, pkey->heap,
INVALID_DEVID) != 0) {
XFREE(pkey->curve25519, pkey->heap, DYNAMIC_TYPE_CURVE25519);
pkey->curve25519 = NULL;
break;
}
#ifdef WOLFSSL_CURVE25519_BLINDING
/* Use the EVP_PKEY's RNG for scalar blinding on derive. */
(void)wc_curve25519_set_rng(pkey->curve25519, &pkey->rng);
#endif
pkey->ownCurve25519 = 1;
}
/* Reuse the RNG already initialized on the EVP_PKEY. */
if (wc_curve25519_make_key(&pkey->rng, CURVE25519_KEYSIZE,
pkey->curve25519) == 0) {
ret = WOLFSSL_SUCCESS;
}
break;
#endif
#ifdef HAVE_CURVE448
case WC_EVP_PKEY_X448:
if (pkey->curve448 == NULL) {
pkey->curve448 = (curve448_key*)XMALLOC(sizeof(curve448_key),
pkey->heap, DYNAMIC_TYPE_CURVE448);
if (pkey->curve448 == NULL) {
ret = MEMORY_E;
break;
}
if (wc_curve448_init(pkey->curve448) != 0) {
XFREE(pkey->curve448, pkey->heap, DYNAMIC_TYPE_CURVE448);
pkey->curve448 = NULL;
break;
}
pkey->ownCurve448 = 1;
}
/* Reuse the RNG already initialized on the EVP_PKEY. */
if (wc_curve448_make_key(&pkey->rng, CURVE448_KEY_SIZE,
pkey->curve448) == 0) {
ret = WOLFSSL_SUCCESS;
}
break;
#endif
default:
break;
@@ -3871,6 +3982,16 @@ int wolfSSL_EVP_PKEY_size(WOLFSSL_EVP_PKEY *pkey)
return wc_ecc_sig_size((ecc_key*)(pkey->ecc->internal));
#endif /* HAVE_ECC */
#ifdef HAVE_CURVE25519
case WC_EVP_PKEY_X25519:
return CURVE25519_KEYSIZE;
#endif
#ifdef HAVE_CURVE448
case WC_EVP_PKEY_X448:
return CURVE448_KEY_SIZE;
#endif
default:
break;
}
@@ -11822,6 +11943,27 @@ void wolfSSL_EVP_PKEY_free(WOLFSSL_EVP_PKEY* key)
break;
#endif /* HAVE_ED448 */
#ifdef HAVE_CURVE25519
case WC_EVP_PKEY_X25519:
if (key->curve25519 != NULL && key->ownCurve25519 == 1) {
wc_curve25519_free(key->curve25519);
XFREE(key->curve25519, key->heap,
DYNAMIC_TYPE_CURVE25519);
key->curve25519 = NULL;
}
break;
#endif /* HAVE_CURVE25519 */
#ifdef HAVE_CURVE448
case WC_EVP_PKEY_X448:
if (key->curve448 != NULL && key->ownCurve448 == 1) {
wc_curve448_free(key->curve448);
XFREE(key->curve448, key->heap, DYNAMIC_TYPE_CURVE448);
key->curve448 = NULL;
}
break;
#endif /* HAVE_CURVE448 */
#ifdef HAVE_HKDF
case WC_EVP_PKEY_HKDF:
XFREE(key->hkdfSalt, NULL, DYNAMIC_TYPE_SALT);
+120
View File
@@ -439,6 +439,64 @@ WOLFSSL_EVP_PKEY* wolfSSL_EVP_PKEY_new_raw_public_key(int type,
ok = 1;
break;
}
#endif
#ifdef HAVE_CURVE25519
case WC_EVP_PKEY_X25519: {
curve25519_key* cKey;
if (len != CURVE25519_PUB_KEY_SIZE) {
break;
}
cKey = (curve25519_key*)XMALLOC(sizeof(curve25519_key), pkey->heap,
DYNAMIC_TYPE_CURVE25519);
if (cKey == NULL) {
break;
}
if (wc_curve25519_init_ex(cKey, pkey->heap, INVALID_DEVID) != 0) {
XFREE(cKey, pkey->heap, DYNAMIC_TYPE_CURVE25519);
break;
}
/* Raw X25519 keys are little-endian (RFC 7748). */
if (wc_curve25519_import_public_ex(pub, (word32)len, cKey,
EC25519_LITTLE_ENDIAN) != 0) {
wc_curve25519_free(cKey);
XFREE(cKey, pkey->heap, DYNAMIC_TYPE_CURVE25519);
break;
}
pkey->type = WC_EVP_PKEY_X25519;
pkey->curve25519 = cKey;
pkey->ownCurve25519 = 1;
ok = 1;
break;
}
#endif
#ifdef HAVE_CURVE448
case WC_EVP_PKEY_X448: {
curve448_key* cKey;
if (len != CURVE448_PUB_KEY_SIZE) {
break;
}
cKey = (curve448_key*)XMALLOC(sizeof(curve448_key), pkey->heap,
DYNAMIC_TYPE_CURVE448);
if (cKey == NULL) {
break;
}
if (wc_curve448_init(cKey) != 0) {
XFREE(cKey, pkey->heap, DYNAMIC_TYPE_CURVE448);
break;
}
/* Raw X448 keys are little-endian (RFC 7748). */
if (wc_curve448_import_public_ex(pub, (word32)len, cKey,
EC448_LITTLE_ENDIAN) != 0) {
wc_curve448_free(cKey);
XFREE(cKey, pkey->heap, DYNAMIC_TYPE_CURVE448);
break;
}
pkey->type = WC_EVP_PKEY_X448;
pkey->curve448 = cKey;
pkey->ownCurve448 = 1;
ok = 1;
break;
}
#endif
default:
break;
@@ -526,6 +584,68 @@ WOLFSSL_EVP_PKEY* wolfSSL_EVP_PKEY_new_raw_private_key(int type,
ok = 1;
break;
}
#endif
#ifdef HAVE_CURVE25519
case WC_EVP_PKEY_X25519: {
curve25519_key* cKey;
if (len != CURVE25519_KEYSIZE) {
break;
}
cKey = (curve25519_key*)XMALLOC(sizeof(curve25519_key), pkey->heap,
DYNAMIC_TYPE_CURVE25519);
if (cKey == NULL) {
break;
}
if (wc_curve25519_init_ex(cKey, pkey->heap, INVALID_DEVID) != 0) {
XFREE(cKey, pkey->heap, DYNAMIC_TYPE_CURVE25519);
break;
}
#ifdef WOLFSSL_CURVE25519_BLINDING
/* Use the EVP_PKEY's RNG for scalar blinding on shared-secret. */
(void)wc_curve25519_set_rng(cKey, &pkey->rng);
#endif
/* Raw X25519 keys are little-endian (RFC 7748). */
if (wc_curve25519_import_private_ex(priv, (word32)len, cKey,
EC25519_LITTLE_ENDIAN) != 0) {
wc_curve25519_free(cKey);
XFREE(cKey, pkey->heap, DYNAMIC_TYPE_CURVE25519);
break;
}
pkey->type = WC_EVP_PKEY_X25519;
pkey->curve25519 = cKey;
pkey->ownCurve25519 = 1;
ok = 1;
break;
}
#endif
#ifdef HAVE_CURVE448
case WC_EVP_PKEY_X448: {
curve448_key* cKey;
if (len != CURVE448_KEY_SIZE) {
break;
}
cKey = (curve448_key*)XMALLOC(sizeof(curve448_key), pkey->heap,
DYNAMIC_TYPE_CURVE448);
if (cKey == NULL) {
break;
}
if (wc_curve448_init(cKey) != 0) {
XFREE(cKey, pkey->heap, DYNAMIC_TYPE_CURVE448);
break;
}
/* Raw X448 keys are little-endian (RFC 7748). */
if (wc_curve448_import_private_ex(priv, (word32)len, cKey,
EC448_LITTLE_ENDIAN) != 0) {
wc_curve448_free(cKey);
XFREE(cKey, pkey->heap, DYNAMIC_TYPE_CURVE448);
break;
}
pkey->type = WC_EVP_PKEY_X448;
pkey->curve448 = cKey;
pkey->ownCurve448 = 1;
ok = 1;
break;
}
#endif
default:
break;
+12
View File
@@ -455,6 +455,12 @@ enum {
#endif
#ifdef HAVE_ED448
WC_EVP_PKEY_ED448 = WC_NID_ED448,
#endif
#ifdef HAVE_CURVE25519
WC_EVP_PKEY_X25519 = WC_NID_X25519,
#endif
#ifdef HAVE_CURVE448
WC_EVP_PKEY_X448 = WC_NID_X448,
#endif
WC_AES_128_CFB1_TYPE = 24,
WC_AES_192_CFB1_TYPE = 25,
@@ -529,6 +535,12 @@ enum {
#ifdef HAVE_ED448
#define EVP_PKEY_ED448 WC_EVP_PKEY_ED448
#endif
#ifdef HAVE_CURVE25519
#define EVP_PKEY_X25519 WC_EVP_PKEY_X25519
#endif
#ifdef HAVE_CURVE448
#define EVP_PKEY_X448 WC_EVP_PKEY_X448
#endif
#define AES_128_CFB1_TYPE WC_AES_128_CFB1_TYPE
#define AES_192_CFB1_TYPE WC_AES_192_CFB1_TYPE
#define AES_256_CFB1_TYPE WC_AES_256_CFB1_TYPE
+12
View File
@@ -609,6 +609,12 @@ struct WOLFSSL_EVP_PKEY {
#ifdef HAVE_ED448
struct ed448_key* ed448;
#endif
#ifdef HAVE_CURVE25519
struct curve25519_key* curve25519;
#endif
#ifdef HAVE_CURVE448
struct curve448_key* curve448;
#endif
WC_RNG rng;
#ifdef HAVE_HKDF
const WOLFSSL_EVP_MD* hkdfMd;
@@ -640,6 +646,12 @@ struct WOLFSSL_EVP_PKEY {
#ifdef HAVE_ED448
WC_BITFIELD ownEd448:1; /* if struct owns Ed448 and should free it */
#endif
#ifdef HAVE_CURVE25519
WC_BITFIELD ownCurve25519:1; /* if struct owns X25519 and should free it */
#endif
#ifdef HAVE_CURVE448
WC_BITFIELD ownCurve448:1; /* if struct owns X448 and should free it */
#endif
};