diff --git a/src/wp_ecx_kmgmt.c b/src/wp_ecx_kmgmt.c index 16813f3a..93a32b5f 100644 --- a/src/wp_ecx_kmgmt.c +++ b/src/wp_ecx_kmgmt.c @@ -1508,6 +1508,10 @@ static int wp_ed25519_export_public(ed25519_key* key, const byte* out, if (!key->pubKeySet) { ret = wc_ed25519_make_public(key, (byte*)out, *outLen); + if (ret == 0) { + /* Store the generated public key in the key object for future use. */ + ret = wc_ed25519_import_public((byte*)out, *outLen, key); + } } else { ret = wc_ed25519_export_public(key, (byte*)out, outLen); @@ -1638,6 +1642,10 @@ static int wp_ed448_export_public(ed448_key* key, const byte* out, if (!key->pubKeySet) { ret = wc_ed448_make_public(key, (byte*)out, *outLen); + if (ret == 0) { + /* Store the generated public key in the key object for future use. */ + ret = wc_ed448_import_public((byte*)out, *outLen, key); + } } else { ret = wc_ed448_export_public(key, (byte*)out, outLen); diff --git a/test/include.am b/test/include.am index 75b90760..bd6798d1 100644 --- a/test/include.am +++ b/test/include.am @@ -18,6 +18,7 @@ test_unit_test_SOURCES = \ test/test_dh.c \ test/test_digest.c \ test/test_ecc.c \ + test/test_ecx.c \ test/test_gmac.c \ test/test_hkdf.c \ test/test_hmac.c \ diff --git a/test/test_ecx.c b/test/test_ecx.c new file mode 100644 index 00000000..a64c5bbd --- /dev/null +++ b/test/test_ecx.c @@ -0,0 +1,415 @@ +/* test_ecc.c + * + * Copyright (C) 2025 wolfSSL Inc. + * + * This file is part of wolfProvider. + * + * wolfProvider is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfProvider is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with wolfProvider. If not, see . + */ + +#include "unit.h" + +#include +#include +#include + +#if defined(WP_HAVE_ED25519) || defined(WP_HAVE_ECD448) + +#ifndef ARRAY_SIZE + #define ARRAY_SIZE(a) ((sizeof(a)/sizeof(a[0]))) +#endif + +#ifndef ED25519_SIGSIZE + #define ED25519_SIGSIZE 64 +#endif + +#ifndef ED448_SIGSIZE + #define ED448_SIGSIZE 114 +#endif + +#ifdef WP_HAVE_ED25519 +// Generated with OpenSSL: +// openssl genpkey -algorithm ed25519 -outform der -out ed25519.der +static const unsigned char ed25519_key_der[] = { + 0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, + 0x04, 0x22, 0x04, 0x20, 0x55, 0xba, 0xe1, 0x23, 0x18, 0x24, 0xea, 0x90, + 0x5f, 0x29, 0xe2, 0x8c, 0xe7, 0x6d, 0x99, 0x99, 0x8c, 0x15, 0x06, 0x1e, + 0x53, 0x61, 0x1a, 0x94, 0xd0, 0x4e, 0x83, 0xef, 0x04, 0xf1, 0x77, 0xdd, +}; +// Isolated private key for the keypair above. +// > openssl pkey -in ed25519.der +static const unsigned char ed25519_priv_key_raw[] = { + 0x55, 0xBA, 0xE1, 0x23, 0x18, 0x24, 0xEA, 0x90, 0x5F, 0x29, 0xE2, 0x8C, + 0xE7, 0x6D, 0x99, 0x99, 0x8C, 0x15, 0x06, 0x1E, 0x53, 0x61, 0x1A, 0x94, + 0xD0, 0x4E, 0x83, 0xEF, 0x04, 0xF1, 0x77, 0xDD +}; +// Isolated public key for the keypair above. +// > openssl pkey -in ed25519.der -pubout +static const unsigned char ed25519_pub_key_raw[] = { + 0x19, 0x31, 0xC8, 0xA8, 0x5F, 0x8F, 0x5C, 0x50, 0xEF, 0xD9, 0xB4, 0x97, + 0x4B, 0xDE, 0xBC, 0xF5, 0x0E, 0x13, 0x1B, 0xDC, 0x51, 0x91, 0x8C, 0x62, + 0xF1, 0x9C, 0x36, 0x15, 0x5C, 0x9A, 0x5F, 0x69 +}; +#endif /* WP_HAVE_ED25519 */ + +#ifdef WP_HAVE_ED448 +// Generated with OpenSSL: +// > openssl genpkey -algorithm ed448 -outform der -out ed448.der +static const unsigned char ed448_key_der[] = { + 0x30, 0x47, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x71, + 0x04, 0x3b, 0x04, 0x39, 0xe9, 0x6c, 0x21, 0x55, 0x78, 0x4d, 0x3b, 0x4e, + 0x61, 0xb2, 0xd1, 0xa8, 0x8a, 0x2a, 0xcd, 0xd7, 0xa3, 0x5f, 0xb4, 0x49, + 0x9d, 0xa8, 0x4e, 0x0c, 0xdd, 0x7b, 0x24, 0xd9, 0x79, 0x89, 0x03, 0x64, + 0x93, 0x01, 0xff, 0xc9, 0x4f, 0x72, 0x57, 0x28, 0x36, 0x81, 0x06, 0x52, + 0x38, 0xa3, 0x5f, 0x45, 0xea, 0x6f, 0xdf, 0xef, 0xc4, 0x67, 0xfc, 0xb4, + 0x53, +}; +// Isolated private key for the keypair above. +// > openssl pkey -in ed448.der +static const unsigned char ed448_priv_key_raw[] = { + 0xE9, 0x6C, 0x21, 0x55, 0x78, 0x4D, 0x3B, 0x4E, 0x61, 0xB2, 0xD1, 0xA8, + 0x8A, 0x2A, 0xCD, 0xD7, 0xA3, 0x5F, 0xB4, 0x49, 0x9D, 0xA8, 0x4E, 0x0C, + 0xDD, 0x7B, 0x24, 0xD9, 0x79, 0x89, 0x03, 0x64, 0x93, 0x01, 0xFF, 0xC9, + 0x4F, 0x72, 0x57, 0x28, 0x36, 0x81, 0x06, 0x52, 0x38, 0xA3, 0x5F, 0x45, + 0xEA, 0x6F, 0xDF, 0xEF, 0xC4, 0x67, 0xFC, 0xB4, 0x53 +}; +// Isolated public key for the keypair above. +// > openssl pkey -in ed448.der -pubout +static const unsigned char ed448_pub_key_raw[] = { + 0x3D, 0x05, 0x53, 0x9D, 0x4B, 0x89, 0xA2, 0xD1, 0xED, 0x2A, 0x2F, 0xB5, + 0xF2, 0x90, 0x38, 0x95, 0x70, 0x0D, 0xA1, 0xBB, 0x84, 0x2B, 0x52, 0x56, + 0x4E, 0xAB, 0x55, 0xF3, 0xB5, 0x9E, 0x05, 0x1F, 0x08, 0xB0, 0xBE, 0xB6, + 0x29, 0xC8, 0x68, 0x21, 0xBE, 0x21, 0xE4, 0x51, 0xB2, 0x20, 0x79, 0xB1, + 0x19, 0x6A, 0x80, 0xE7, 0x9A, 0x51, 0xF5, 0xAC, 0x00 +}; +#endif /* WP_HAVE_ED448 */ + +static int sign_verify(unsigned char* sig, size_t sigLen, + EVP_PKEY *pkey, const char* name) +{ + int err = 0; + static unsigned char buf[128]; + static size_t bufLen = 0; + + if (bufLen == 0) { + err = RAND_bytes(buf, sizeof(buf)) == 0; + bufLen = sizeof(buf); + } + + if (err == 0) { + PRINT_MSG("Sign with OpenSSL (%s)", name); + err = test_digest_sign(pkey, osslLibCtx, buf, bufLen, NULL, + sig, &sigLen, 0); + } + if (err == 0) { + PRINT_MSG("Verify with WolfProvider (%s)", name); + err = test_digest_verify(pkey, wpLibCtx, buf, bufLen, NULL, + sig, sigLen, 0); + } + if (err == 0) { + PRINT_MSG("Verify bad signature with WolfProvider (%s)", name); + sig[1] ^= 0x80; + err = test_digest_verify(pkey, wpLibCtx, buf, bufLen, NULL, + sig, sigLen, 0) != 1; + } + if (err == 0) { + PRINT_MSG("Sign with WolfProvider (%s)", name); + err = test_digest_sign(pkey, wpLibCtx, buf, bufLen, NULL, + sig, &sigLen, 0); + } + if (err == 0) { + PRINT_MSG("Verify with OpenSSL (%s)", name); + err = test_digest_verify(pkey, osslLibCtx, buf, bufLen, NULL, + sig, sigLen, 0); + } + + return err; +} + +int test_ecx_sign_verify(void *data) +{ + int err = 0; + EVP_PKEY *pkey = NULL; + unsigned char buf[128]; + const unsigned char *p; + + #ifdef WP_HAVE_ED25519 + unsigned char sig_ed25519[ED25519_SIGSIZE]; + #endif + #ifdef WP_HAVE_ED448 + unsigned char sig_ed448[ED448_SIGSIZE]; + #endif + + (void)data; + + struct { + int type; + size_t keyLen; + const unsigned char* key; + size_t sigLen; + unsigned char* sig; + const char* name; + } types[] = { + #ifdef WP_HAVE_ED25519 + { EVP_PKEY_ED25519, sizeof(ed25519_key_der), ed25519_key_der, + sizeof(sig_ed25519), sig_ed25519, "ed25519" }, + #endif + #ifdef WP_HAVE_ED448 + { EVP_PKEY_ED448, sizeof(ed448_key_der), ed448_key_der, + sizeof(sig_ed448), sig_ed448, "ed448" }, + #endif + }; + + for (unsigned i = 0; i < ARRAY_SIZE(types) && err == 0; i++) { + PRINT_MSG("Testing %s", types[i].name); + PRINT_MSG("Testing ECX sign/verify with DER keys (%s)", + types[i].name); + err = RAND_bytes(buf, sizeof(buf)) == 0; + p = types[i].key; + + if (err == 0) { + pkey = d2i_PrivateKey(types[i].type, NULL, &p, types[i].keyLen); + err = pkey == NULL; + if (err) { + PRINT_MSG("could not create key"); + } + } + + if (err == 0) { + err = sign_verify(types[i].sig, types[i].sigLen, pkey, types[i].name); + } + + EVP_PKEY_free(pkey); + } + + return err; +} + +int test_ecx_sign_verify_raw_priv(void *data) +{ + int err = 0; + (void)data; + + EVP_PKEY *pkey_ossl = NULL; + EVP_PKEY *pkey_wolf = NULL; + + #ifdef WP_HAVE_ED25519 + unsigned char sig_ed25519[ED25519_SIGSIZE]; + #endif + #ifdef WP_HAVE_ED448 + unsigned char sig_ed448[ED448_SIGSIZE]; + #endif + + struct { + int type; + size_t keyLen; + const unsigned char* key; + size_t sigLen; + unsigned char* sig; + const char* name; + } types[] = { + #ifdef WP_HAVE_ED25519 + { EVP_PKEY_ED25519, sizeof(ed25519_priv_key_raw), ed25519_priv_key_raw, + sizeof(sig_ed25519), sig_ed25519, "ED25519" }, + #endif + #ifdef WP_HAVE_ED448 + { EVP_PKEY_ED448, sizeof(ed448_priv_key_raw), ed448_priv_key_raw, + sizeof(sig_ed448), sig_ed448, "ED448" }, + #endif + }; + + for (unsigned i = 0; i < ARRAY_SIZE(types) && err == 0; i++) { + PRINT_MSG("Testing ECX sign/verify with raw keys (%s)", + types[i].name); + + if (err == 0) { + pkey_ossl = EVP_PKEY_new_raw_private_key_ex(osslLibCtx, + types[i].name, NULL, types[i].key, types[i].keyLen); + err = pkey_ossl == NULL; + if (err) { + PRINT_MSG("could not create key (OpenSSL)"); + } + } + + if (err == 0) { + pkey_wolf = EVP_PKEY_new_raw_private_key_ex(wpLibCtx, types[i].name, + NULL, types[i].key, types[i].keyLen); + err = pkey_wolf == NULL; + if (err) { + PRINT_MSG("could not create key (wolfSSL)"); + } + } + + if (err == 0) { + if (EVP_PKEY_cmp(pkey_wolf, pkey_ossl) != 1) { + PRINT_MSG("EVP_PKEY_cmp failed"); + err = 1; + } + if (EVP_PKEY_cmp_parameters(pkey_wolf, pkey_ossl) != 1) { + PRINT_MSG("EVP_PKEY_cmp_parameters failed"); + err = 1; + } + } + + if (err == 0) { + err = sign_verify(types[i].sig, types[i].sigLen, pkey_ossl, + types[i].name); + } + + if (err == 0) { + err = sign_verify(types[i].sig, types[i].sigLen, pkey_wolf, + types[i].name); + } + + EVP_PKEY_free(pkey_ossl); + EVP_PKEY_free(pkey_wolf); + } + + return err; +} + +int test_ecx_sign_verify_raw_pub(void *data) +{ + int err = 0; + (void)data; + + EVP_PKEY *pkey_der = NULL; + EVP_PKEY *pkey_ossl = NULL; + EVP_PKEY *pkey_wolf = NULL; + const unsigned char *p = NULL; + unsigned char buf[128]; + size_t bufLen = 0; + + #ifdef WP_HAVE_ED25519 + unsigned char sig_ed25519[ED25519_SIGSIZE]; + #endif + #ifdef WP_HAVE_ED448 + unsigned char sig_ed448[ED448_SIGSIZE]; + #endif + + struct { + int type; + size_t keyLen; + const unsigned char* key; + size_t pubKeyLen; + const unsigned char* pubKey; + size_t sigLen; + unsigned char* sig; + const char* name; + } types[] = { + #ifdef WP_HAVE_ED25519 + { EVP_PKEY_ED25519, + sizeof(ed25519_key_der), ed25519_key_der, + sizeof(ed25519_pub_key_raw), ed25519_pub_key_raw, + sizeof(sig_ed25519), sig_ed25519, "ED25519" }, + #endif + #ifdef WP_HAVE_ED448 + { EVP_PKEY_ED448, + sizeof(ed448_key_der), ed448_key_der, + sizeof(ed448_pub_key_raw), ed448_pub_key_raw, + sizeof(sig_ed448), sig_ed448, "ED448" }, + #endif + }; + + if (err == 0) { + err = RAND_bytes(buf, sizeof(buf)) == 0; + bufLen = sizeof(buf); + } + + for (unsigned i = 0; i < ARRAY_SIZE(types) && err == 0; i++) { + PRINT_MSG("Testing ECX sign/verify with raw public keys (%s)", + types[i].name); + + /* Use OpenSSL to get the key from the DER */ + if (err == 0) { + p = types[i].key; + pkey_der = d2i_PrivateKey(types[i].type, NULL, &p, types[i].keyLen); + err = pkey_der == NULL; + if (err) { + PRINT_MSG("could not create key"); + } + } + + /* Use OpenSSL to sign the block of random bytes */ + if (err == 0) { + PRINT_MSG("Sign with OpenSSL (%s)", types[i].name); + err = test_digest_sign(pkey_der, osslLibCtx, buf, bufLen, NULL, + types[i].sig, &types[i].sigLen, 0); + } + + /* Create keys from the public byte arrays */ + if (err == 0) { + pkey_ossl = EVP_PKEY_new_raw_public_key_ex(osslLibCtx, + types[i].name, NULL, types[i].pubKey, types[i].pubKeyLen); + err = pkey_ossl == NULL; + if (err) { + PRINT_MSG("could not create key (OpenSSL)"); + } + } + + if (err == 0) { + pkey_wolf = EVP_PKEY_new_raw_public_key_ex(wpLibCtx, types[i].name, + NULL, types[i].pubKey, types[i].pubKeyLen); + err = pkey_wolf == NULL; + if (err) { + PRINT_MSG("could not create key (wolfSSL)"); + } + } + + /* Compare keys */ + if (err == 0) { + if (EVP_PKEY_cmp(pkey_wolf, pkey_ossl) != 1) { + PRINT_MSG("EVP_PKEY_cmp failed"); + err = 1; + } + if (EVP_PKEY_cmp_parameters(pkey_wolf, pkey_ossl) != 1) { + PRINT_MSG("EVP_PKEY_cmp_parameters failed"); + err = 1; + } + } + + /* Verify the signature with the public keys */ + if (err == 0) { + PRINT_MSG("Verify with OpenSSL (%s)", types[i].name); + err = test_digest_verify(pkey_ossl, osslLibCtx, buf, bufLen, NULL, + types[i].sig, types[i].sigLen, 0); + } + if (err == 0) { + PRINT_MSG("Verify with WolfProvider (%s)", types[i].name); + err = test_digest_verify(pkey_wolf, wpLibCtx, buf, bufLen, NULL, + types[i].sig, types[i].sigLen, 0); + } + + /* Verify bad signature with the public keys */ + types[i].sig[1] ^= 0x80; + if (err == 0) { + PRINT_MSG("Verify bad signature with OpenSSL (%s)", types[i].name); + err = test_digest_verify(pkey_ossl, osslLibCtx, buf, bufLen, NULL, + types[i].sig, types[i].sigLen, 0) != 1; + } + if (err == 0) { + PRINT_MSG("Verify bad signature with WolfProvider (%s)", types[i].name); + err = test_digest_verify(pkey_wolf, wpLibCtx, buf, bufLen, NULL, + types[i].sig, types[i].sigLen, 0) != 1; + } + + EVP_PKEY_free(pkey_der); + EVP_PKEY_free(pkey_ossl); + EVP_PKEY_free(pkey_wolf); + } + + return err; +} + +#endif /* defined(WP_HAVE_ED25519) || defined(WP_HAVE_ECD444) */ diff --git a/test/unit.c b/test/unit.c index 71349e9a..36e06dff 100644 --- a/test/unit.c +++ b/test/unit.c @@ -276,6 +276,12 @@ TEST_CASE test_case[] = { #ifdef WP_HAVE_PBE TEST_DECL(test_pbe, NULL), #endif + +#if defined(WP_HAVE_ED25519) || defined(WP_HAVE_ED448) + TEST_DECL(test_ecx_sign_verify, NULL), + TEST_DECL(test_ecx_sign_verify_raw_priv, NULL), + TEST_DECL(test_ecx_sign_verify_raw_pub, NULL), +#endif }; #define TEST_CASE_CNT (int)(sizeof(test_case) / sizeof(*test_case)) diff --git a/test/unit.h b/test/unit.h index 9db64f51..aea10cad 100644 --- a/test/unit.h +++ b/test/unit.h @@ -377,4 +377,10 @@ int test_ec_import(void* data); int test_pbe(void *data); #endif /* WP_HAVE_PBE */ +#if defined(WP_HAVE_ED25519) || defined(WP_HAVE_ED448) +int test_ecx_sign_verify(void *data); +int test_ecx_sign_verify_raw_priv(void *data); +int test_ecx_sign_verify_raw_pub(void *data); +#endif + #endif /* UNIT_H */