Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/cryptocb-only.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
name: cryptocb-only Tests

# START OF COMMON SECTION
Expand Down Expand Up @@ -105,9 +105,12 @@
{"name": "aes-gcm-via-ecb",
"comment": "Same as aes but tells swdev to refuse AES-GCM (SWDEV_AES_ONLYECB). That forces the parent's CB_ONLY_AES host-side GCM software path: GHASH runs on the host while AES-CTR blocks dispatch back through cryptocb ECB. The aes entry instead has swdev handle GCM end-to-end, so the host-side GCM path is otherwise uncovered.",
"configure": ["CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_AES -DSWDEV_AES_ONLYECB"]},
{"name": "ed25519",
"comment": "WOLF_CRYPTO_CB_ONLY_ED25519: strips software Ed25519 (keygen/sign/verify/make-pub/check-key) including the ge/fe curve math and tables; swdev provides the software path via cryptocb. Streaming verify has no callback path and is left disabled.",
"configure": ["CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_ED25519"]},
{"name": "all",
"comment": "All five ONLY_* macros at once: every supported software primitive is stripped and dispatched through cryptocb. Catches any cross-algorithm call that a single-strip entry would still resolve via the remaining software paths.",
"configure": ["CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_ECC -DWOLF_CRYPTO_CB_ONLY_RSA -DWOLF_CRYPTO_CB_ONLY_SHA256 -DWOLF_CRYPTO_CB_ONLY_SHA512 -DWOLF_CRYPTO_CB_ONLY_AES"]}
"comment": "All six ONLY_* macros at once: every supported software primitive is stripped and dispatched through cryptocb. Catches any cross-algorithm call that a single-strip entry would still resolve via the remaining software paths.",
"configure": ["CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_ECC -DWOLF_CRYPTO_CB_ONLY_RSA -DWOLF_CRYPTO_CB_ONLY_SHA256 -DWOLF_CRYPTO_CB_ONLY_SHA512 -DWOLF_CRYPTO_CB_ONLY_AES -DWOLF_CRYPTO_CB_ONLY_ED25519"]}
]}
EOF
.github/scripts/parallel-make-check.py \
Expand Down
52 changes: 52 additions & 0 deletions doc/dox_comments/header_files/cryptocb.h
Original file line number Diff line number Diff line change
Expand Up @@ -341,3 +341,55 @@ int wc_CryptoCb_EccMakePub(ecc_key* key, ecc_point* pubOut);
\sa wc_ecc_check_key
*/
int wc_CryptoCb_EccCheckPubKey(ecc_key* key, int checkOrder, int checkPriv);

/*!
\ingroup CryptoCb

\brief Offload deriving an Ed25519 public key from its private key to a
CryptoCB device.

Used by wc_ed25519_make_public (and so by key generation and private-key
import). The boundary is byte-oriented: the device writes the compressed
public key into wc_CryptoInfo.pk.ed25519makepub (\c pubOut /
\c pubOutSz, always ED25519_PUB_KEY_SIZE). The private key is taken from
the ed25519_key (resident in a secure element, or key->k). On success the
caller marks the public key set.

\param key Ed25519 key providing the device id, heap hint and the
private key
\param pubKey [out] resulting compressed public key
\param pubKeySz size of pubKey buffer, must be ED25519_PUB_KEY_SIZE

\return 0 on success
\return CRYPTOCB_UNAVAILABLE if no device handles the operation, or key or
pubKey is NULL or pubKeySz is wrong (wolfCrypt falls back to
software, which reports the argument error)

\sa wc_CryptoCb_RegisterDevice
\sa wc_ed25519_make_public
*/
int wc_CryptoCb_Ed25519MakePub(ed25519_key* key, byte* pubKey,
word32 pubKeySz);

/*!
\ingroup CryptoCb

\brief Offload validating an Ed25519 key to a CryptoCB device.

Used by wc_ed25519_check_key and the key import validation paths. The
public key crosses the callback boundary as its compressed wire bytes in
wc_CryptoInfo.pk.ed25519checkkey (\c pubKey / \c pubKeySz), so a device
handler only deals with byte arrays. The dispatch only runs when a public
key is present; \c checkPriv is 1 when a private key is also set and the
device should additionally validate priv/pub consistency.

\param key Ed25519 key to validate

\return 0 if the key is valid
\return CRYPTOCB_UNAVAILABLE if no device handles the operation (wolfCrypt
falls back to software)

\sa wc_CryptoCb_RegisterDevice
\sa wc_ed25519_check_key
*/
int wc_CryptoCb_Ed25519CheckKey(ed25519_key* key);
9 changes: 6 additions & 3 deletions tests/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -29521,7 +29521,8 @@ static int test_SSL_CIPHER_get_current_kx(void)
#if defined(WOLF_CRYPTO_CB) && defined(HAVE_IO_TESTS_DEPENDENCIES) && \
(!defined(WOLF_CRYPTO_CB_ONLY_SHA256) && !defined(WOLF_CRYPTO_CB_ONLY_AES) && \
!defined(WOLF_CRYPTO_CB_ONLY_ECC) && !defined(WOLF_CRYPTO_CB_ONLY_RSA) && \
!defined(WOLF_CRYPTO_CB_ONLY_SHA512))
!defined(WOLF_CRYPTO_CB_ONLY_SHA512) && \
!defined(WOLF_CRYPTO_CB_ONLY_ED25519))

static int load_pem_key_file_as_der(const char* privKeyFile, DerBuffer** pDer,
int* keyFormat)
Expand Down Expand Up @@ -30625,7 +30626,8 @@ static int test_wc_CryptoCb(void)
#if defined(WOLF_CRYPTO_CB) && \
(!defined(WOLF_CRYPTO_CB_ONLY_SHA256) && !defined(WOLF_CRYPTO_CB_ONLY_AES) && \
!defined(WOLF_CRYPTO_CB_ONLY_ECC) && !defined(WOLF_CRYPTO_CB_ONLY_RSA) && \
!defined(WOLF_CRYPTO_CB_ONLY_SHA512))
!defined(WOLF_CRYPTO_CB_ONLY_SHA512) && \
!defined(WOLF_CRYPTO_CB_ONLY_ED25519))
#if defined(HAVE_IO_TESTS_DEPENDENCIES) && \
(!defined(NO_RSA) || defined(HAVE_ECC) || defined(HAVE_ED25519))
int tlsVer;
Expand Down Expand Up @@ -37002,7 +37004,8 @@ TEST_CASE testCases[] = {
#if defined(WOLF_CRYPTO_CB) && defined(HAVE_IO_TESTS_DEPENDENCIES) && \
(!defined(WOLF_CRYPTO_CB_ONLY_SHA256) && !defined(WOLF_CRYPTO_CB_ONLY_AES) && \
!defined(WOLF_CRYPTO_CB_ONLY_ECC) && !defined(WOLF_CRYPTO_CB_ONLY_RSA) && \
!defined(WOLF_CRYPTO_CB_ONLY_SHA512))
!defined(WOLF_CRYPTO_CB_ONLY_SHA512) && \
!defined(WOLF_CRYPTO_CB_ONLY_ED25519))
/* Can't memory test as client/server hangs. */
TEST_DECL(test_wc_CryptoCb_registry),
#endif
Expand Down
12 changes: 7 additions & 5 deletions tests/swdev/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ compiled separately from the main library, linked into the test
programs only, and exposes exactly two C symbols. **It is not a
production component and must not be linked into shipping binaries.**

The four switches it supports are:
The switches it supports are:

| Macro | Strips | Test target |
|--------------------------------|----------------|--------------------|
| `WOLF_CRYPTO_CB_ONLY_RSA` | software RSA | RSA via CryptoCb |
| `WOLF_CRYPTO_CB_ONLY_ECC` | software ECC | ECC via CryptoCb |
| `WOLF_CRYPTO_CB_ONLY_SHA256` | software SHA-256 | SHA-256 via CryptoCb |
| `WOLF_CRYPTO_CB_ONLY_SHA512` | software SHA-512 | SHA-512 via CryptoCb |
| `WOLF_CRYPTO_CB_ONLY_AES` | software AES | AES via CryptoCb |
| `WOLF_CRYPTO_CB_ONLY_ED25519` | software Ed25519 | Ed25519 via CryptoCb |

When a test program calls e.g. `wc_AesCbcEncrypt()` against a libwolfssl
built with `-DWOLF_CRYPTO_CB_ONLY_AES`, the software AES path is gone;
Expand Down Expand Up @@ -55,7 +57,7 @@ internal copy of the AES code, and returns the result.
| wc_SwDev_Callback(devId, info, ctx) |
| - swdev_ensure_init() lazy wolfCrypt_Init |
| - switch (info->algo_type): |
| PK -> RSA / ECC software impl |
| PK -> RSA / ECC / Ed25519 software impl |
| HASH -> SHA-256 software impl |
| CIPHER -> AES (CBC/CTR/ECB/GCM/CCM) software impl |
| |
Expand All @@ -70,10 +72,10 @@ internal copy of the AES code, and returns the result.
The whole mechanism rests on compiling the wolfcrypt sources twice:

1. **libwolfssl** is built normally with the user's `_ONLY_*` flags
set, so its software RSA/ECC/SHA-256/AES paths are gone.
set, so its software RSA/ECC/SHA-256/SHA-512/AES/Ed25519 paths are gone.
2. **swdev** recompiles the same source set under
`tests/swdev/user_settings.h`, which `#undef`s all four `_ONLY_*`
macros. swdev therefore contains the full software implementations.
`tests/swdev/user_settings.h`, which `#undef`s every `_ONLY_*`
macro. swdev therefore contains the full software implementations.

To prevent symbol collisions when both are linked into the same test
binary, `tests/swdev/Makefile` does the following:
Expand Down
102 changes: 101 additions & 1 deletion tests/swdev/swdev.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@
#ifndef NO_AES
#include <wolfssl/wolfcrypt/aes.h>
#endif
#ifdef HAVE_ED25519
#include <wolfssl/wolfcrypt/ed25519.h>
#endif

static int swdev_initialized = 0;

Expand Down Expand Up @@ -238,6 +241,83 @@ static int swdev_ecc_check_pub(wc_CryptoInfo* info)
#endif /* HAVE_ECC_CHECK_KEY */
#endif /* HAVE_ECC */

#ifdef HAVE_ED25519
#ifdef HAVE_ED25519_MAKE_KEY
static int swdev_ed25519_keygen(wc_CryptoInfo* info)
{
return wc_ed25519_make_key(info->pk.ed25519kg.rng,
info->pk.ed25519kg.size, info->pk.ed25519kg.key);
}
#endif

#ifdef HAVE_ED25519_SIGN
static int swdev_ed25519_sign(wc_CryptoInfo* info)
{
return wc_ed25519_sign_msg_ex(info->pk.ed25519sign.in,
info->pk.ed25519sign.inLen, info->pk.ed25519sign.out,
info->pk.ed25519sign.outLen, info->pk.ed25519sign.key,
info->pk.ed25519sign.type, info->pk.ed25519sign.context,
info->pk.ed25519sign.contextLen);
}
#endif

#ifdef HAVE_ED25519_VERIFY
static int swdev_ed25519_verify(wc_CryptoInfo* info)
{
return wc_ed25519_verify_msg_ex(info->pk.ed25519verify.sig,
info->pk.ed25519verify.sigLen, info->pk.ed25519verify.msg,
info->pk.ed25519verify.msgLen, info->pk.ed25519verify.res,
info->pk.ed25519verify.key, info->pk.ed25519verify.type,
info->pk.ed25519verify.context, info->pk.ed25519verify.contextLen);
}
#endif

#ifdef HAVE_ED25519_MAKE_KEY
static int swdev_ed25519_make_pub(wc_CryptoInfo* info)
{
if (info->pk.ed25519makepub.pubOutSz != ED25519_PUB_KEY_SIZE)
return BUFFER_E;
return wc_ed25519_make_public(info->pk.ed25519makepub.key,
info->pk.ed25519makepub.pubOut, info->pk.ed25519makepub.pubOutSz);
}
#endif

static int swdev_ed25519_check_key(wc_CryptoInfo* info)
{
ed25519_key* key = info->pk.ed25519checkkey.key;
int ret = 0;
int validatedFromWire = 0;

#ifdef HAVE_ED25519_KEY_IMPORT
{
WC_DECLARE_VAR(pubOnly, ed25519_key, 1, key->heap);

/* vault-style consumption: rebuild a public-only key from the wire
* bytes and validate it, proving the serialized form is sufficient.
* trusted=0 runs the full public-key validation during import. */
WC_ALLOC_VAR(pubOnly, ed25519_key, 1, key->heap);
if (!WC_VAR_OK(pubOnly))
return MEMORY_E;
ret = wc_ed25519_init_ex(pubOnly, key->heap, INVALID_DEVID);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [Low] Resource leak when wc_ed25519_init_ex fails in swdev_ed25519_check_key · Resource leaks

wc_ed25519_free(pubOnly) is inside if (ret == 0), so if wc_ed25519_init_ex fails (e.g., under WOLFSSL_ED25519_PERSISTENT_SHA where ed25519_hash_init can fail), any partially allocated SHA state is not released before WC_FREE_VAR frees the struct.

Fix: Move wc_ed25519_free(pubOnly) outside the if (ret == 0) block so it always runs before WC_FREE_VAR.

if (ret == 0) {
ret = wc_ed25519_import_public_ex(info->pk.ed25519checkkey.pubKey,
info->pk.ed25519checkkey.pubKeySz, pubOnly, 0);
wc_ed25519_free(pubOnly);
}
WC_FREE_VAR(pubOnly, key->heap);
validatedFromWire = 1;
}
#endif
/* private part (and public-only validation when the import path is
* unavailable): validate via the key handle */
if (ret == 0 && (!validatedFromWire ||
info->pk.ed25519checkkey.checkPriv)) {
ret = wc_ed25519_check_key(key);
}
return ret;
}
#endif /* HAVE_ED25519 */

#ifndef NO_SHA256
/* Copy hash state between caller's wc_Sha256 and swdev's shadow, leaving
* admin fields (heap, devId, devCtx, W, async, HW ctx) per-side. */
Expand Down Expand Up @@ -833,7 +913,7 @@ WC_SWDEV_EXPORT int wc_SwDev_Callback(int devId, wc_CryptoInfo* info,
return ret;

switch (info->algo_type) {
#if !defined(NO_RSA) || defined(HAVE_ECC)
#if !defined(NO_RSA) || defined(HAVE_ECC) || defined(HAVE_ED25519)
case WC_ALGO_TYPE_PK:
switch (info->pk.type) {
#ifndef NO_RSA
Expand Down Expand Up @@ -864,6 +944,26 @@ WC_SWDEV_EXPORT int wc_SwDev_Callback(int devId, wc_CryptoInfo* info,
return swdev_ecc_check_pub(info);
#endif
#endif /* HAVE_ECC */
#ifdef HAVE_ED25519
#ifdef HAVE_ED25519_MAKE_KEY
case WC_PK_TYPE_ED25519_KEYGEN:
return swdev_ed25519_keygen(info);
#endif
#ifdef HAVE_ED25519_SIGN
case WC_PK_TYPE_ED25519_SIGN:
return swdev_ed25519_sign(info);
#endif
#ifdef HAVE_ED25519_VERIFY
case WC_PK_TYPE_ED25519_VERIFY:
return swdev_ed25519_verify(info);
#endif
#ifdef HAVE_ED25519_MAKE_KEY
case WC_PK_TYPE_ED25519_MAKE_PUB:
return swdev_ed25519_make_pub(info);
#endif
case WC_PK_TYPE_ED25519_CHECK_KEY:
return swdev_ed25519_check_key(info);
#endif /* HAVE_ED25519 */
default:
return CRYPTOCB_UNAVAILABLE;
}
Expand Down
1 change: 1 addition & 0 deletions tests/swdev/user_settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#undef WOLF_CRYPTO_CB_ONLY_SHA256
#undef WOLF_CRYPTO_CB_ONLY_SHA512
#undef WOLF_CRYPTO_CB_ONLY_AES
#undef WOLF_CRYPTO_CB_ONLY_ED25519

#ifndef WOLF_CRYPTO_CB
#error "wc_swdev requires the main build to define WOLF_CRYPTO_CB"
Expand Down
57 changes: 57 additions & 0 deletions wolfcrypt/src/cryptocb.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ Crypto Callback Build Options:
* WOLF_CRYPTO_CB_ONLY_SHA256: Use only callbacks for SHA-256 default: off
* WOLF_CRYPTO_CB_ONLY_SHA512: Use only callbacks for SHA-512 default: off
* WOLF_CRYPTO_CB_ONLY_AES: Use only callbacks for AES default: off
* WOLF_CRYPTO_CB_ONLY_ED25519: Use only callbacks for Ed25519 default: off
*/

#include <wolfssl/wolfcrypt/libwolfssl_sources.h>
Expand Down Expand Up @@ -156,6 +157,8 @@ static const char* GetPkTypeStr(int pk)
case WC_PK_TYPE_EC_GET_SIG_SIZE: return "ECC GetSigSize";
case WC_PK_TYPE_EC_MAKE_PUB: return "ECC MakePub";
case WC_PK_TYPE_EC_CHECK_PUB_KEY: return "ECC CheckPubKey";
case WC_PK_TYPE_ED25519_MAKE_PUB: return "ED25519 MakePub";
case WC_PK_TYPE_ED25519_CHECK_KEY: return "ED25519 CheckKey";
}
return NULL;
}
Expand Down Expand Up @@ -1167,6 +1170,60 @@ int wc_CryptoCb_Ed25519Verify(const byte* sig, word32 sigLen,

return wc_CryptoCb_TranslateErrorCode(ret);
}

int wc_CryptoCb_Ed25519MakePub(ed25519_key* key, byte* pubKey, word32 pubKeySz)
{
int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
CryptoCb* dev;

if (key == NULL || pubKey == NULL || pubKeySz != ED25519_PUB_KEY_SIZE)
return ret;

/* locate registered callback */
dev = wc_CryptoCb_FindDevice(key->devId, WC_ALGO_TYPE_PK);
if (dev && dev->cb) {
wc_CryptoInfo cryptoInfo;
XMEMSET(&cryptoInfo, 0, sizeof(cryptoInfo));
cryptoInfo.algo_type = WC_ALGO_TYPE_PK;
cryptoInfo.pk.type = WC_PK_TYPE_ED25519_MAKE_PUB;
cryptoInfo.pk.ed25519makepub.key = key;
cryptoInfo.pk.ed25519makepub.pubOut = pubKey;
cryptoInfo.pk.ed25519makepub.pubOutSz = pubKeySz;

ret = dev->cb(dev->devId, &cryptoInfo, dev->ctx);
}

return wc_CryptoCb_TranslateErrorCode(ret);
}

int wc_CryptoCb_Ed25519CheckKey(ed25519_key* key)
{
int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
CryptoCb* dev;

if (key == NULL)
return ret;

/* locate registered callback */
dev = wc_CryptoCb_FindDevice(key->devId, WC_ALGO_TYPE_PK);
if (dev && dev->cb) {
wc_CryptoInfo cryptoInfo;
XMEMSET(&cryptoInfo, 0, sizeof(cryptoInfo));
cryptoInfo.algo_type = WC_ALGO_TYPE_PK;
cryptoInfo.pk.type = WC_PK_TYPE_ED25519_CHECK_KEY;
cryptoInfo.pk.ed25519checkkey.key = key;
/* key->p is already the compressed wire form; cross it as bytes so a
* device can validate the actual input without touching key internals
*/
cryptoInfo.pk.ed25519checkkey.pubKey = key->p;
cryptoInfo.pk.ed25519checkkey.pubKeySz = ED25519_PUB_KEY_SIZE;
cryptoInfo.pk.ed25519checkkey.checkPriv = key->privKeySet ? 1 : 0;

ret = dev->cb(dev->devId, &cryptoInfo, dev->ctx);
}

return wc_CryptoCb_TranslateErrorCode(ret);
}
#endif /* HAVE_ED25519 */

#if defined(WOLFSSL_HAVE_LMS) || defined(WOLFSSL_HAVE_XMSS)
Expand Down
Loading
Loading