From 220833e362db5860cf1b9a30a2b12b390d05c5dc Mon Sep 17 00:00:00 2001 From: night1rider Date: Thu, 20 Aug 2026 01:22:57 -0600 Subject: [PATCH 1/7] Add curve448 crypto callback support Mirror curve25519: keygen, shared secret, make pub and generic callbacks, wc_curve448_init_ex/new/delete, new wc_curve448_generic API with scalar clamp checks, WOLF_CRYPTO_CB_ONLY_CURVE448 mode, TLS devId plumbing, tests (test.c, api unit tests, swdev), benchmark devId, CI entries. --- .github/workflows/cryptocb-only.yml | 13 +- doc/dox_comments/header_files/curve448.h | 145 ++++++++++++ src/internal.c | 6 +- src/sniffer.c | 2 +- src/ssl_api_pk.c | 2 +- src/tls.c | 3 +- tests/api/test_curve448.c | 277 +++++++++++++++++++++++ tests/api/test_curve448.h | 6 +- tests/swdev/README.md | 1 + tests/swdev/swdev.c | 52 ++++- tests/swdev/user_settings.h | 1 + wolfcrypt/benchmark/benchmark.c | 41 +++- wolfcrypt/benchmark/benchmark.h | 4 +- wolfcrypt/src/cryptocb.c | 121 ++++++++++ wolfcrypt/src/curve448.c | 215 ++++++++++++++++-- wolfcrypt/src/evp.c | 5 +- wolfcrypt/src/evp_pk.c | 4 +- wolfcrypt/src/fe_448.c | 7 +- wolfcrypt/test/test.c | 236 ++++++++++++++++++- wolfssl/wolfcrypt/cryptocb.h | 44 ++++ wolfssl/wolfcrypt/curve448.h | 20 ++ wolfssl/wolfcrypt/settings.h | 10 + wolfssl/wolfcrypt/types.h | 6 +- 23 files changed, 1172 insertions(+), 49 deletions(-) diff --git a/.github/workflows/cryptocb-only.yml b/.github/workflows/cryptocb-only.yml index d8883f81acd..d74b3ea02f1 100644 --- a/.github/workflows/cryptocb-only.yml +++ b/.github/workflows/cryptocb-only.yml @@ -117,9 +117,18 @@ jobs: {"name": "curve25519", "comment": "WOLF_CRYPTO_CB_ONLY_CURVE25519: strips software X25519 (keygen/shared-secret); swdev provides the software path via cryptocb. Nonblock and async X25519 have no callback path and are left disabled.", "configure": ["CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_CURVE25519"]}, + {"name": "curve448", + "comment": "WOLF_CRYPTO_CB_ONLY_CURVE448: strips software X448 (keygen/shared-secret/make-pub/generic) including the fe448 field math; swdev provides the software path via cryptocb.", + "configure": ["CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_CURVE448"]}, + {"name": "curve448-no-ed448", + "comment": "Same as curve448 but with ed448 off. ed448 is the only other user of the fe448 math, so without it the strip is complete and nothing must pull the math back in. The curve448 entry above keeps ed448 enabled, which leaves fe_448.c compiled and would hide such a regression at link time.", + "configure": ["--disable-ed448", "CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_CURVE448"]}, + {"name": "curve448-onlycb-no-swdev", + "comment": "CB_ONLY_CURVE448 without swdev: runs curve448_onlycb_test, the only coverage of the cb-handled/cb-delegated (exampleVar 99/1) dispatch incl. make_pub/generic. cryptonly drops TLS so make check needs no software X448 provider.", + "configure": ["--disable-swdev", "--enable-cryptonly", "CPPFLAGS=-DWOLF_CRYPTO_CB_ONLY_CURVE448"]}, {"name": "all", - "comment": "All seven 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 -DWOLF_CRYPTO_CB_ONLY_CURVE25519"]} + "comment": "All eight 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 -DWOLF_CRYPTO_CB_ONLY_CURVE25519 -DWOLF_CRYPTO_CB_ONLY_CURVE448"]} ]} EOF .github/scripts/parallel-make-check.py \ diff --git a/doc/dox_comments/header_files/curve448.h b/doc/dox_comments/header_files/curve448.h index aaa984a356b..1677e01aaf5 100644 --- a/doc/dox_comments/header_files/curve448.h +++ b/doc/dox_comments/header_files/curve448.h @@ -158,6 +158,108 @@ int wc_curve448_shared_secret_ex(curve448_key* private_key, int wc_curve448_init(curve448_key* key); +/*! + \ingroup Curve448 + \brief This function initializes a Curve448 key with extended + parameters, allowing specification of custom heap and device ID + for hardware acceleration. + + \return 0 On successfully initializing the key + \return BAD_FUNC_ARG If key is NULL + + \param [in,out] key Pointer to the curve448_key structure to initialize. + \param [in] heap Pointer to heap hint for memory allocation (can be + NULL) + \param [in] devId Device ID for hardware acceleration (use + INVALID_DEVID for software only) + + _Example_ + \code + curve448_key key; + void* heap = NULL; + int devId = INVALID_DEVID; + + int ret = wc_curve448_init_ex(&key, heap, devId); + if (ret != 0) { + // error initializing key + } + \endcode + + \sa wc_curve448_init + \sa wc_curve448_free +*/ +int wc_curve448_init_ex(curve448_key* key, void* heap, int devId); + +/*! + \ingroup Curve448 + \brief This function allocates and initializes a new Curve448 + key structure with extended parameters. The caller is responsible + for freeing the key with wc_curve448_delete. These New/Delete + functions are exposed to support allocation of the structure using + dynamic memory to provide better ABI compatibility. + + \note This API is only available when WC_NO_CONSTRUCTORS is not defined. + WC_NO_CONSTRUCTORS is automatically defined when WOLFSSL_NO_MALLOC is + defined. + + \return Pointer to newly allocated curve448_key on success + \return NULL on failure + + \param [in] heap Pointer to heap hint for memory allocation (can be + NULL) + \param [in] devId Device ID for hardware acceleration (use + INVALID_DEVID for software only) + \param [out] result_code Pointer to store result code (0 on success) + + _Example_ + \code + int ret; + curve448_key* key; + + key = wc_curve448_new(NULL, INVALID_DEVID, &ret); + if (key == NULL || ret != 0) { + // error allocating key + } + // use key + wc_curve448_delete(key, &key); + \endcode + + \sa wc_curve448_delete + \sa wc_curve448_init_ex +*/ +curve448_key* wc_curve448_new(void* heap, int devId, int* result_code); + +/*! + \ingroup Curve448 + \brief This function frees a Curve448 key structure that was + allocated with wc_curve448_new and sets the pointer to NULL. + These New/Delete functions are exposed to support allocation of the + structure using dynamic memory to provide better ABI compatibility. + + \note This API is only available when WC_NO_CONSTRUCTORS is not defined. + WC_NO_CONSTRUCTORS is automatically defined when WOLFSSL_NO_MALLOC is + defined. + + \return 0 On successfully freeing the key + \return BAD_FUNC_ARG If key is NULL + + \param [in,out] key Pointer to the curve448_key structure to free. + \param [out] key_p Pointer to the key pointer to set to NULL (can be + NULL) + + _Example_ + \code + int ret; + curve448_key* key = wc_curve448_new(NULL, INVALID_DEVID, &ret); + // use key + ret = wc_curve448_delete(key, &key); + \endcode + + \sa wc_curve448_new + \sa wc_curve448_free +*/ +int wc_curve448_delete(curve448_key* key, curve448_key** key_p); + /*! \ingroup Curve448 @@ -802,3 +904,46 @@ int wc_curve448_size(curve448_key* key); */ int wc_curve448_make_pub(int public_size, byte* pub, int private_size, const byte* priv); + +/*! + \ingroup Curve448 + \brief This function performs a generic Curve448 scalar + multiplication with a custom basepoint. This allows computing + scalar * basepoint for any basepoint, not just the standard + generator. This is a raw primitive: unlike + wc_curve448_shared_secret_ex it does not reject an all-zero + result. Callers doing key agreement with a peer-supplied point + must reject an all-zero output themselves (RFC 7748 section 6.2). + + \return 0 On successfully computing the result + \return ECC_BAD_ARG_E If any input parameter is NULL or a size is + invalid + + \param public_size Size of the output buffer (must be 56) + \param pub Pointer to buffer to store the result + \param private_size Size of the scalar (must be 56) + \param priv Pointer to buffer containing the scalar + \param basepoint_size Size of the basepoint (must be 56) + \param basepoint Pointer to buffer containing the basepoint + + _Example_ + \code + byte scalar[CURVE448_KEY_SIZE]; + byte basepoint[CURVE448_KEY_SIZE]; + byte result[CURVE448_PUB_KEY_SIZE]; + + // initialize scalar and basepoint + int ret = wc_curve448_generic(sizeof(result), result, + sizeof(scalar), scalar, + sizeof(basepoint), basepoint); + if (ret != 0) { + // error computing result + } + \endcode + + \sa wc_curve448_make_pub + \sa wc_curve448_shared_secret +*/ +int wc_curve448_generic(int public_size, byte* pub, int private_size, + const byte* priv, int basepoint_size, + const byte* basepoint); diff --git a/src/internal.c b/src/internal.c index 89384b30cfc..041e41c024d 100644 --- a/src/internal.c +++ b/src/internal.c @@ -9275,7 +9275,8 @@ int AllocKey(WOLFSSL* ssl, int type, void** pKey) #endif /* WOLFSSL_HAVE_SLHDSA */ #ifdef HAVE_CURVE448 case DYNAMIC_TYPE_CURVE448: - ret = wc_curve448_init((curve448_key*)*pKey); + ret = wc_curve448_init_ex((curve448_key*)*pKey, ssl->heap, + ssl->devId); if (ret == 0) key_inited = 1; break; @@ -9373,7 +9374,8 @@ static int ReuseKey(WOLFSSL* ssl, int type, void* pKey) #ifdef HAVE_CURVE448 case DYNAMIC_TYPE_CURVE448: wc_curve448_free((curve448_key*)pKey); - ret = wc_curve448_init((curve448_key*)pKey); + ret = wc_curve448_init_ex((curve448_key*)pKey, ssl->heap, + ssl->devId); break; #endif /* HAVE_CURVE448 */ #if defined(HAVE_FALCON) diff --git a/src/sniffer.c b/src/sniffer.c index a45e2f0c9a0..71db47dce09 100644 --- a/src/sniffer.c +++ b/src/sniffer.c @@ -3006,7 +3006,7 @@ static int SetupKeys(const byte* input, int* sslBytes, SnifferSession* session, ret = BUFFER_E; } if (ret == 0) { - ret = wc_curve448_init(&args->key->priv.x448); + ret = wc_curve448_init_ex(&args->key->priv.x448, NULL, devId); if (ret == 0) { args->key->type = WC_PK_TYPE_CURVE448; args->key->initPriv = 1; diff --git a/src/ssl_api_pk.c b/src/ssl_api_pk.c index 876629a6b44..d5ac839f561 100644 --- a/src/ssl_api_pk.c +++ b/src/ssl_api_pk.c @@ -2429,7 +2429,7 @@ static int DetectStaticEphemeralKeyType(const byte* keyBuf, unsigned int keySz, WC_ALLOC_VAR_EX(x448Key, curve448_key, 1, heap, DYNAMIC_TYPE_CURVE448, ret = MEMORY_E); if (ret == 0) { - ret = wc_curve448_init(x448Key); + ret = wc_curve448_init_ex(x448Key, heap, INVALID_DEVID); } if (ret == 0) { ret = wc_Curve448PrivateKeyDecode(keyBuf, &idx, x448Key, diff --git a/src/tls.c b/src/tls.c index 7171fcfeb26..8c9b56ccdbf 100644 --- a/src/tls.c +++ b/src/tls.c @@ -8509,7 +8509,8 @@ static int TLSX_KeyShare_GenX448Key(WOLFSSL *ssl, KeyShareEntry* kse) } /* Make an Curve448 key. */ - ret = wc_curve448_init((curve448_key*)kse->key); + ret = wc_curve448_init_ex((curve448_key*)kse->key, ssl->heap, + ssl->devId); if (ret == 0) { key = (curve448_key*)kse->key; kse->keyLen = CURVE448_KEY_SIZE; diff --git a/tests/api/test_curve448.c b/tests/api/test_curve448.c index 38a4be46713..f3933e0e4cb 100644 --- a/tests/api/test_curve448.c +++ b/tests/api/test_curve448.c @@ -30,6 +30,9 @@ #include #include +#ifdef WOLF_CRYPTO_CB + #include +#endif #include #include @@ -828,3 +831,277 @@ int test_wc_curve448_export_import_endian(void) return EXPECT_RESULT(); } /* END test_wc_curve448_export_import_endian */ +/* Cross-check make_pub, generic and keygen: public keys must match and a + * shared secret must round trip (runs via cryptocb under CB_ONLY_CURVE448). */ +int test_wc_curve448_make_pub_generic(void) +{ + EXPECT_DECLS; +#if defined(HAVE_CURVE448) && defined(HAVE_CURVE448_SHARED_SECRET) + curve448_key keyA; + curve448_key keyB; + WC_RNG rng; + byte pubM[CURVE448_PUB_KEY_SIZE]; + byte pubG[CURVE448_PUB_KEY_SIZE]; + const byte base5[CURVE448_KEY_SIZE] = { 5 }; + byte genAB[CURVE448_PUB_KEY_SIZE]; + byte ssAB[CURVE448_PUB_KEY_SIZE]; + byte ssBA[CURVE448_PUB_KEY_SIZE]; + word32 ssABLen = (word32)sizeof(ssAB); + word32 ssBALen = (word32)sizeof(ssBA); + + XMEMSET(&rng, 0, sizeof(WC_RNG)); + + ExpectIntEQ(wc_curve448_init(&keyA), 0); + ExpectIntEQ(wc_curve448_init(&keyB), 0); + ExpectIntEQ(wc_InitRng(&rng), 0); + + ExpectIntEQ(wc_curve448_make_key(&rng, CURVE448_KEY_SIZE, &keyA), 0); + ExpectIntEQ(wc_curve448_make_key(&rng, CURVE448_KEY_SIZE, &keyB), 0); + + /* make_pub from the private scalar must match the keygen public point */ + ExpectIntEQ(wc_curve448_make_pub((int)sizeof(pubM), pubM, + (int)sizeof(keyA.k), keyA.k), 0); + ExpectBufEQ(pubM, keyA.p, CURVE448_PUB_KEY_SIZE); + + /* generic against base point 5 is the same operation as make_pub */ + ExpectIntEQ(wc_curve448_generic((int)sizeof(pubG), pubG, + (int)sizeof(keyA.k), keyA.k, (int)sizeof(base5), base5), 0); + ExpectBufEQ(pubG, pubM, CURVE448_PUB_KEY_SIZE); + + /* generic against B's public point must equal the A-B shared secret, + * proving generic actually uses the supplied base point */ + ExpectIntEQ(wc_curve448_generic((int)sizeof(genAB), genAB, + (int)sizeof(keyA.k), keyA.k, (int)sizeof(keyB.p), keyB.p), 0); + ExpectIntEQ(wc_curve448_shared_secret_ex(&keyA, &keyB, ssAB, &ssABLen, + EC448_LITTLE_ENDIAN), 0); + ExpectBufEQ(genAB, ssAB, CURVE448_PUB_KEY_SIZE); + + /* shared secret must agree both ways, proving the generated keys are + * mutually consistent (a degenerate result is rejected by shared_secret) */ + ExpectIntEQ(wc_curve448_shared_secret_ex(&keyB, &keyA, ssBA, &ssBALen, + EC448_LITTLE_ENDIAN), 0); + ExpectBufEQ(ssBA, ssAB, CURVE448_PUB_KEY_SIZE); + + /* argument checks on the new generic API */ + ExpectIntEQ(wc_curve448_generic((int)sizeof(pubG), NULL, + (int)sizeof(keyA.k), keyA.k, (int)sizeof(base5), base5), + WC_NO_ERR_TRACE(ECC_BAD_ARG_E)); + ExpectIntEQ(wc_curve448_generic((int)sizeof(pubG), pubG, + (int)sizeof(keyA.k), NULL, (int)sizeof(base5), base5), + WC_NO_ERR_TRACE(ECC_BAD_ARG_E)); + ExpectIntEQ(wc_curve448_generic((int)sizeof(pubG), pubG, + (int)sizeof(keyA.k), keyA.k, (int)sizeof(base5), NULL), + WC_NO_ERR_TRACE(ECC_BAD_ARG_E)); + ExpectIntEQ(wc_curve448_generic((int)sizeof(pubG) - 1, pubG, + (int)sizeof(keyA.k), keyA.k, (int)sizeof(base5), base5), + WC_NO_ERR_TRACE(ECC_BAD_ARG_E)); + ExpectIntEQ(wc_curve448_generic((int)sizeof(pubG), pubG, + (int)sizeof(keyA.k) - 1, keyA.k, (int)sizeof(base5), base5), + WC_NO_ERR_TRACE(ECC_BAD_ARG_E)); + ExpectIntEQ(wc_curve448_generic((int)sizeof(pubG), pubG, + (int)sizeof(keyA.k), keyA.k, (int)sizeof(base5) - 1, base5), + WC_NO_ERR_TRACE(ECC_BAD_ARG_E)); + + /* unclamped scalars must be rejected: low bits set, then top bit clear */ + keyA.k[0] |= 0x01; + ExpectIntEQ(wc_curve448_make_pub((int)sizeof(pubM), pubM, + (int)sizeof(keyA.k), keyA.k), WC_NO_ERR_TRACE(ECC_BAD_ARG_E)); + ExpectIntEQ(wc_curve448_generic((int)sizeof(pubG), pubG, + (int)sizeof(keyA.k), keyA.k, (int)sizeof(base5), base5), + WC_NO_ERR_TRACE(ECC_BAD_ARG_E)); + keyA.k[0] &= 0xfc; + keyA.k[CURVE448_KEY_SIZE-1] &= 0x7f; + ExpectIntEQ(wc_curve448_make_pub((int)sizeof(pubM), pubM, + (int)sizeof(keyA.k), keyA.k), WC_NO_ERR_TRACE(ECC_BAD_ARG_E)); + ExpectIntEQ(wc_curve448_generic((int)sizeof(pubG), pubG, + (int)sizeof(keyA.k), keyA.k, (int)sizeof(base5), base5), + WC_NO_ERR_TRACE(ECC_BAD_ARG_E)); + keyA.k[CURVE448_KEY_SIZE-1] |= 0x80; + /* restored key must work again */ + ExpectIntEQ(wc_curve448_make_pub((int)sizeof(pubM), pubM, + (int)sizeof(keyA.k), keyA.k), 0); + + DoExpectIntEQ(wc_FreeRng(&rng), 0); + wc_curve448_free(&keyA); + wc_curve448_free(&keyB); +#endif + return EXPECT_RESULT(); +} /* END test_wc_curve448_make_pub_generic */ + +/* Test Curve448 keygen/shared secret routed through a crypto callback + * (CryptoCb) device. */ +/* The spy services ops by re-entering the software API, which is stripped + * under CB-only unless WOLF_CRYPTO_CB_FIND can route back to a device. */ +#if defined(WOLF_CRYPTO_CB) && defined(HAVE_CURVE448) && \ + defined(HAVE_CURVE448_SHARED_SECRET) && !defined(WC_NO_RNG) && \ + (!defined(WOLF_CRYPTO_CB_ONLY_CURVE448) || defined(WOLF_CRYPTO_CB_FIND)) +typedef struct curve448SpyCtx { + int kgSeen; + int ssSeen; + int decline; + int forceErr; + int zeroSecret; +} curve448SpyCtx; + +/* Spy device: services Curve448 keygen/shared secret in software (devId + * cleared) and counts each; declines everything else. */ +static int curve448_test_crypto_cb(int devIdArg, wc_CryptoInfo* info, void* ctx) +{ + curve448SpyCtx* spy = (curve448SpyCtx*)ctx; + int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + + (void)devIdArg; + + if (info == NULL || spy == NULL) { + return BAD_FUNC_ARG; + } + + if (info->algo_type == WC_ALGO_TYPE_PK) { + if (info->pk.type == WC_PK_TYPE_CURVE448_KEYGEN) { + int save = info->pk.curve448kg.key->devId; + spy->kgSeen++; + if (spy->decline) + return ret; + if (spy->forceErr) + return WC_NO_ERR_TRACE(WC_HW_E); + info->pk.curve448kg.key->devId = INVALID_DEVID; + ret = wc_curve448_make_key(info->pk.curve448kg.rng, + info->pk.curve448kg.size, info->pk.curve448kg.key); + info->pk.curve448kg.key->devId = save; + } + if (info->pk.type == WC_PK_TYPE_CURVE448) { + int save = info->pk.curve448.private_key->devId; + spy->ssSeen++; + if (spy->decline) + return ret; + if (spy->forceErr) + return WC_NO_ERR_TRACE(WC_HW_E); + if (spy->zeroSecret) { + /* misbehaving device: all-zero secret with success rc */ + XMEMSET(info->pk.curve448.out, 0, CURVE448_PUB_KEY_SIZE); + *info->pk.curve448.outlen = CURVE448_PUB_KEY_SIZE; + return 0; + } + info->pk.curve448.private_key->devId = INVALID_DEVID; + ret = wc_curve448_shared_secret_ex( + info->pk.curve448.private_key, info->pk.curve448.public_key, + info->pk.curve448.out, info->pk.curve448.outlen, + info->pk.curve448.endian); + info->pk.curve448.private_key->devId = save; + } + } + + return ret; +} +#endif + +int test_wc_curve448_cryptocb(void) +{ + EXPECT_DECLS; +#if defined(WOLF_CRYPTO_CB) && defined(HAVE_CURVE448) && \ + defined(HAVE_CURVE448_SHARED_SECRET) && !defined(WC_NO_RNG) && \ + (!defined(WOLF_CRYPTO_CB_ONLY_CURVE448) || defined(WOLF_CRYPTO_CB_FIND)) + int devId = 4485; + curve448SpyCtx spy; + WC_RNG rng; + curve448_key keyB; + byte ssAB[CURVE448_PUB_KEY_SIZE]; + byte ssBA[CURVE448_PUB_KEY_SIZE]; + word32 ssABLen = (word32)sizeof(ssAB); + word32 ssBALen = (word32)sizeof(ssBA); +#ifndef WC_NO_CONSTRUCTORS + int ret = 0; + curve448_key* keyA = NULL; +#else + curve448_key keyA_stack; + curve448_key* keyA = &keyA_stack; +#endif + + XMEMSET(&rng, 0, sizeof(rng)); + XMEMSET(&spy, 0, sizeof(spy)); + + ExpectIntEQ(wc_CryptoCb_RegisterDevice(devId, curve448_test_crypto_cb, + &spy), 0); + ExpectIntEQ(wc_InitRng(&rng), 0); +#ifndef WC_NO_CONSTRUCTORS + /* exercise the new constructor path */ + ExpectNotNull(keyA = wc_curve448_new(HEAP_HINT, devId, &ret)); + ExpectIntEQ(ret, 0); +#else + ExpectIntEQ(wc_curve448_init_ex(keyA, HEAP_HINT, devId), 0); +#endif + ExpectIntEQ(wc_curve448_init_ex(&keyB, HEAP_HINT, devId), 0); + + /* keygen routes through the device callback */ + if (keyA != NULL) { + ExpectIntEQ(wc_curve448_make_key(&rng, CURVE448_KEY_SIZE, keyA), 0); + } + ExpectIntEQ(wc_curve448_make_key(&rng, CURVE448_KEY_SIZE, &keyB), 0); + ExpectIntGE(spy.kgSeen, 2); + + /* shared secret routes through the device callback both ways */ + if (keyA != NULL) { + ExpectIntEQ(wc_curve448_shared_secret_ex(keyA, &keyB, ssAB, &ssABLen, + EC448_LITTLE_ENDIAN), 0); + ExpectIntEQ(wc_curve448_shared_secret_ex(&keyB, keyA, ssBA, &ssBALen, + EC448_LITTLE_ENDIAN), 0); + ExpectBufEQ(ssAB, ssBA, CURVE448_PUB_KEY_SIZE); + } + ExpectIntGE(spy.ssSeen, 2); + +#ifndef WOLF_CRYPTO_CB_ONLY_CURVE448 + /* device declines: dispatch is seen, software fallback must succeed */ + spy.decline = 1; + ExpectIntEQ(wc_curve448_make_key(&rng, CURVE448_KEY_SIZE, &keyB), 0); + ExpectIntGE(spy.kgSeen, 3); + if (keyA != NULL) { + ssABLen = (word32)sizeof(ssAB); + ExpectIntEQ(wc_curve448_shared_secret_ex(keyA, &keyB, ssAB, &ssABLen, + EC448_LITTLE_ENDIAN), 0); + } + ExpectIntGE(spy.ssSeen, 3); + spy.decline = 0; +#endif + + /* device errors: the error must propagate, not fall back to software */ + spy.forceErr = 1; + ExpectIntEQ(wc_curve448_make_key(&rng, CURVE448_KEY_SIZE, &keyB), + WC_NO_ERR_TRACE(WC_HW_E)); + if (keyA != NULL) { + ssABLen = (word32)sizeof(ssAB); + ExpectIntEQ(wc_curve448_shared_secret_ex(keyA, &keyB, ssAB, &ssABLen, + EC448_LITTLE_ENDIAN), WC_NO_ERR_TRACE(WC_HW_E)); + } + spy.forceErr = 0; + +#ifndef WOLFSSL_NO_ECDHX_SHARED_ZERO_CHECK + /* device returns an all-zero secret: wrapper must reject it */ + spy.zeroSecret = 1; + if (keyA != NULL) { + ssABLen = (word32)sizeof(ssAB); + ExpectIntEQ(wc_curve448_shared_secret_ex(keyA, &keyB, ssAB, &ssABLen, + EC448_LITTLE_ENDIAN), WC_NO_ERR_TRACE(ECC_OUT_OF_RANGE_E)); + } + spy.zeroSecret = 0; +#endif + + /* constructor arg checks */ + ExpectIntEQ(wc_curve448_init_ex(NULL, HEAP_HINT, devId), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + wc_curve448_free(&keyB); +#ifndef WC_NO_CONSTRUCTORS + if (keyA != NULL) { + DoExpectIntEQ(wc_curve448_delete(keyA, &keyA), 0); + } + ExpectIntEQ(wc_curve448_delete(NULL, NULL), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); +#else + wc_curve448_free(keyA); +#endif + DoExpectIntEQ(wc_FreeRng(&rng), 0); + wc_CryptoCb_UnRegisterDevice(devId); +#endif + return EXPECT_RESULT(); +} /* END test_wc_curve448_cryptocb */ + diff --git a/tests/api/test_curve448.h b/tests/api/test_curve448.h index 830e3ec7751..2c378ad2adc 100644 --- a/tests/api/test_curve448.h +++ b/tests/api/test_curve448.h @@ -42,6 +42,8 @@ int test_wc_curve448_check_public_be(void); int test_wc_curve448_shared_secret_keyset_checks(void); int test_wc_curve448_import_public_ex_argchecks(void); int test_wc_curve448_export_import_endian(void); +int test_wc_curve448_make_pub_generic(void); +int test_wc_curve448_cryptocb(void); #define TEST_CURVE448_DECLS \ TEST_DECL_GROUP("curve448", test_wc_curve448_make_key), \ @@ -61,6 +63,8 @@ int test_wc_curve448_export_import_endian(void); TEST_DECL_GROUP("curve448", test_wc_curve448_check_public_be), \ TEST_DECL_GROUP("curve448", test_wc_curve448_shared_secret_keyset_checks), \ TEST_DECL_GROUP("curve448", test_wc_curve448_import_public_ex_argchecks), \ - TEST_DECL_GROUP("curve448", test_wc_curve448_export_import_endian) + TEST_DECL_GROUP("curve448", test_wc_curve448_export_import_endian), \ + TEST_DECL_GROUP("curve448", test_wc_curve448_make_pub_generic), \ + TEST_DECL_GROUP("curve448", test_wc_curve448_cryptocb) #endif /* WOLFCRYPT_TEST_CURVE448_H */ diff --git a/tests/swdev/README.md b/tests/swdev/README.md index 1d62208a57c..7f7e3ab4efe 100644 --- a/tests/swdev/README.md +++ b/tests/swdev/README.md @@ -17,6 +17,7 @@ The switches it supports are: | `WOLF_CRYPTO_CB_ONLY_AES` | software AES | AES via CryptoCb | | `WOLF_CRYPTO_CB_ONLY_ED25519` | software Ed25519 | Ed25519 via CryptoCb | | `WOLF_CRYPTO_CB_ONLY_CURVE25519` | software X25519 | X25519 via CryptoCb | +| `WOLF_CRYPTO_CB_ONLY_CURVE448` | software X448 | X448 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; diff --git a/tests/swdev/swdev.c b/tests/swdev/swdev.c index 0c5862c227d..cb527366d2e 100644 --- a/tests/swdev/swdev.c +++ b/tests/swdev/swdev.c @@ -46,6 +46,9 @@ #ifdef HAVE_CURVE25519 #include #endif +#ifdef HAVE_CURVE448 +#include +#endif static int swdev_initialized = 0; @@ -356,6 +359,41 @@ static int swdev_curve25519_generic(wc_CryptoInfo* info) } #endif /* HAVE_CURVE25519 */ +#ifdef HAVE_CURVE448 +static int swdev_curve448_keygen(wc_CryptoInfo* info) +{ + return wc_curve448_make_key(info->pk.curve448kg.rng, + info->pk.curve448kg.size, info->pk.curve448kg.key); +} + +#ifdef HAVE_CURVE448_SHARED_SECRET +static int swdev_curve448(wc_CryptoInfo* info) +{ + return wc_curve448_shared_secret_ex(info->pk.curve448.private_key, + info->pk.curve448.public_key, info->pk.curve448.out, + info->pk.curve448.outlen, info->pk.curve448.endian); +} +#endif /* HAVE_CURVE448_SHARED_SECRET */ + +static int swdev_curve448_make_pub(wc_CryptoInfo* info) +{ + return wc_curve448_make_pub((int)info->pk.curve448makepub.pubSz, + info->pk.curve448makepub.pub, + (int)info->pk.curve448makepub.privSz, + info->pk.curve448makepub.priv); +} + +static int swdev_curve448_generic(wc_CryptoInfo* info) +{ + return wc_curve448_generic((int)info->pk.curve448generic.pubSz, + info->pk.curve448generic.pub, + (int)info->pk.curve448generic.privSz, + info->pk.curve448generic.priv, + (int)info->pk.curve448generic.basepointSz, + info->pk.curve448generic.basepoint); +} +#endif /* HAVE_CURVE448 */ + #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. */ @@ -1008,7 +1046,7 @@ WC_SWDEV_EXPORT int wc_SwDev_Callback(int devId, wc_CryptoInfo* info, switch (info->algo_type) { #if !defined(NO_RSA) || defined(HAVE_ECC) || defined(HAVE_ED25519) || \ - defined(HAVE_CURVE25519) + defined(HAVE_CURVE25519) || defined(HAVE_CURVE448) case WC_ALGO_TYPE_PK: switch (info->pk.type) { #ifndef NO_RSA @@ -1071,6 +1109,18 @@ WC_SWDEV_EXPORT int wc_SwDev_Callback(int devId, wc_CryptoInfo* info, case WC_PK_TYPE_CURVE25519_GENERIC: return swdev_curve25519_generic(info); #endif /* HAVE_CURVE25519 */ + #ifdef HAVE_CURVE448 + case WC_PK_TYPE_CURVE448_KEYGEN: + return swdev_curve448_keygen(info); + #ifdef HAVE_CURVE448_SHARED_SECRET + case WC_PK_TYPE_CURVE448: + return swdev_curve448(info); + #endif + case WC_PK_TYPE_CURVE448_MAKE_PUB: + return swdev_curve448_make_pub(info); + case WC_PK_TYPE_CURVE448_GENERIC: + return swdev_curve448_generic(info); + #endif /* HAVE_CURVE448 */ default: return CRYPTOCB_UNAVAILABLE; } diff --git a/tests/swdev/user_settings.h b/tests/swdev/user_settings.h index ca23306d442..e0157b19dd0 100644 --- a/tests/swdev/user_settings.h +++ b/tests/swdev/user_settings.h @@ -29,6 +29,7 @@ #undef WOLF_CRYPTO_CB_ONLY_AES #undef WOLF_CRYPTO_CB_ONLY_ED25519 #undef WOLF_CRYPTO_CB_ONLY_CURVE25519 +#undef WOLF_CRYPTO_CB_ONLY_CURVE448 #ifndef WOLF_CRYPTO_CB #error "wc_swdev requires the main build to define WOLF_CRYPTO_CB" diff --git a/wolfcrypt/benchmark/benchmark.c b/wolfcrypt/benchmark/benchmark.c index 9d96beb57c5..889830f4eaa 100644 --- a/wolfcrypt/benchmark/benchmark.c +++ b/wolfcrypt/benchmark/benchmark.c @@ -4822,11 +4822,22 @@ static void* benchmarks_do(void* args) #endif #ifdef HAVE_CURVE448 - if (bench_all || (bench_asym_algs & BENCH_CURVE448_KEYGEN)) - bench_curve448KeyGen(); + if (bench_all || (bench_asym_algs & BENCH_CURVE448_KEYGEN)) { + #ifndef NO_SW_BENCH + bench_curve448KeyGen(0); + #endif + #ifdef BENCH_DEVID + bench_curve448KeyGen(1); + #endif + } + #ifdef HAVE_CURVE448_SHARED_SECRET - if (bench_all || (bench_asym_algs & BENCH_CURVE448_KA)) - bench_curve448KeyAgree(); + if (bench_all || (bench_asym_algs & BENCH_CURVE448_KA)) { + bench_curve448KeyAgree(0); + #ifdef BENCH_DEVID + bench_curve448KeyAgree(1); + #endif + } #endif #endif @@ -15303,7 +15314,7 @@ void bench_ed25519KeySign(int useDeviceID) #endif /* HAVE_ED25519 */ #ifdef HAVE_CURVE448 -void bench_curve448KeyGen(void) +void bench_curve448KeyGen(int useDeviceID) { curve448_key genKey; double start; @@ -15317,6 +15328,12 @@ void bench_curve448KeyGen(void) bench_stats_start(&count, &start); do { for (i = 0; i < genTimes; i++) { + ret = wc_curve448_init_ex(&genKey, HEAP_HINT, + useDeviceID ? devId : INVALID_DEVID); + if (ret != 0) { + printf("wc_curve448_init_ex failed: %d\n", ret); + break; + } ret = wc_curve448_make_key(&gRng, 56, &genKey); wc_curve448_free(&genKey); if (ret != 0) { @@ -15332,14 +15349,15 @@ void bench_curve448KeyGen(void) #endif ); - bench_stats_asym_finish("CURVE", 448, desc[2], 0, count, start, ret); + bench_stats_asym_finish("CURVE", 448, desc[2], useDeviceID, count, start, + ret); #ifdef MULTI_VALUE_STATISTICS bench_multi_value_stats(max, min, sum, squareSum, runs); #endif } #ifdef HAVE_CURVE448_SHARED_SECRET -void bench_curve448KeyAgree(void) +void bench_curve448KeyAgree(int useDeviceID) { curve448_key genKey, genKey2; double start; @@ -15351,8 +15369,10 @@ void bench_curve448KeyAgree(void) bench_stats_prepare(); - wc_curve448_init(&genKey); - wc_curve448_init(&genKey2); + wc_curve448_init_ex(&genKey, HEAP_HINT, + useDeviceID ? devId : INVALID_DEVID); + wc_curve448_init_ex(&genKey2, HEAP_HINT, + useDeviceID ? devId : INVALID_DEVID); ret = wc_curve448_make_key(&gRng, 56, &genKey); if (ret != 0) { @@ -15386,7 +15406,8 @@ void bench_curve448KeyAgree(void) ); exit: - bench_stats_asym_finish("CURVE", 448, desc[3], 0, count, start, ret); + bench_stats_asym_finish("CURVE", 448, desc[3], useDeviceID, count, start, + ret); #ifdef MULTI_VALUE_STATISTICS bench_multi_value_stats(max, min, sum, squareSum, runs); #endif diff --git a/wolfcrypt/benchmark/benchmark.h b/wolfcrypt/benchmark/benchmark.h index b74bd5df0f5..adbabc63f69 100644 --- a/wolfcrypt/benchmark/benchmark.h +++ b/wolfcrypt/benchmark/benchmark.h @@ -123,8 +123,8 @@ void bench_curve25519KeyGen(int useDeviceID); void bench_curve25519KeyAgree(int useDeviceID); void bench_ed25519KeyGen(int useDeviceID); void bench_ed25519KeySign(int useDeviceID); -void bench_curve448KeyGen(void); -void bench_curve448KeyAgree(void); +void bench_curve448KeyGen(int useDeviceID); +void bench_curve448KeyAgree(int useDeviceID); void bench_ed448KeyGen(void); void bench_ed448KeySign(void); void bench_eccsiKeyGen(void); diff --git a/wolfcrypt/src/cryptocb.c b/wolfcrypt/src/cryptocb.c index 569a68e01a6..dff517d8773 100644 --- a/wolfcrypt/src/cryptocb.c +++ b/wolfcrypt/src/cryptocb.c @@ -173,6 +173,10 @@ static const char* GetPkTypeStr(int pk) case WC_PK_TYPE_ED25519_CHECK_KEY: return "ED25519 CheckKey"; case WC_PK_TYPE_CURVE25519_MAKE_PUB: return "CURVE25519 MakePub"; case WC_PK_TYPE_CURVE25519_GENERIC: return "CURVE25519 Generic"; + case WC_PK_TYPE_CURVE448: return "CURVE448"; + case WC_PK_TYPE_CURVE448_KEYGEN: return "CURVE448 KeyGen"; + case WC_PK_TYPE_CURVE448_MAKE_PUB: return "CURVE448 MakePub"; + case WC_PK_TYPE_CURVE448_GENERIC: return "CURVE448 Generic"; } return NULL; } @@ -1436,6 +1440,123 @@ int wc_CryptoCb_Ed25519CheckKey(ed25519_key* key) } #endif /* HAVE_ED25519 */ +#ifdef HAVE_CURVE448 +int wc_CryptoCb_Curve448Gen(WC_RNG* rng, int keySize, + curve448_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_CURVE448_KEYGEN; + cryptoInfo.pk.curve448kg.rng = rng; + cryptoInfo.pk.curve448kg.size = keySize; + cryptoInfo.pk.curve448kg.key = key; + + ret = dev->cb(dev->devId, &cryptoInfo, dev->ctx); + } + + return wc_CryptoCb_TranslateErrorCode(ret); +} + +int wc_CryptoCb_Curve448(curve448_key* private_key, + curve448_key* public_key, byte* out, word32* outlen, int endian) +{ + int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + CryptoCb* dev; + + if (private_key == NULL) + return ret; + + /* locate registered callback */ + dev = wc_CryptoCb_FindDevice(private_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_CURVE448; + cryptoInfo.pk.curve448.private_key = private_key; + cryptoInfo.pk.curve448.public_key = public_key; + cryptoInfo.pk.curve448.out = out; + cryptoInfo.pk.curve448.outlen = outlen; + cryptoInfo.pk.curve448.endian = endian; + + ret = dev->cb(dev->devId, &cryptoInfo, dev->ctx); + } + + return wc_CryptoCb_TranslateErrorCode(ret); +} + +int wc_CryptoCb_Curve448MakePub(int public_size, byte* pub, + int private_size, const byte* priv) +{ + int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + CryptoCb* dev; + + if (pub == NULL || priv == NULL) + return ret; + + /* try the find callback first, else grab the first registered device */ + dev = wc_CryptoCb_FindDevice(INVALID_DEVID, WC_ALGO_TYPE_PK); + if (dev == NULL || dev->cb == NULL) + dev = wc_CryptoCb_FindDeviceByIndex(0); + 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_CURVE448_MAKE_PUB; + cryptoInfo.pk.curve448makepub.pub = pub; + cryptoInfo.pk.curve448makepub.pubSz = (word32)public_size; + cryptoInfo.pk.curve448makepub.priv = priv; + cryptoInfo.pk.curve448makepub.privSz = (word32)private_size; + + ret = dev->cb(dev->devId, &cryptoInfo, dev->ctx); + } + + return wc_CryptoCb_TranslateErrorCode(ret); +} + +int wc_CryptoCb_Curve448Generic(int public_size, byte* pub, + int private_size, const byte* priv, int basepoint_size, + const byte* basepoint) +{ + int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); + CryptoCb* dev; + + if (pub == NULL || priv == NULL || basepoint == NULL) + return ret; + + /* try the find callback first, else grab the first registered device */ + dev = wc_CryptoCb_FindDevice(INVALID_DEVID, WC_ALGO_TYPE_PK); + if (dev == NULL || dev->cb == NULL) + dev = wc_CryptoCb_FindDeviceByIndex(0); + 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_CURVE448_GENERIC; + cryptoInfo.pk.curve448generic.pub = pub; + cryptoInfo.pk.curve448generic.pubSz = (word32)public_size; + cryptoInfo.pk.curve448generic.priv = priv; + cryptoInfo.pk.curve448generic.privSz = (word32)private_size; + cryptoInfo.pk.curve448generic.basepoint = basepoint; + cryptoInfo.pk.curve448generic.basepointSz = (word32)basepoint_size; + + ret = dev->cb(dev->devId, &cryptoInfo, dev->ctx); + } + + return wc_CryptoCb_TranslateErrorCode(ret); +} +#endif /* HAVE_CURVE448 */ + #ifdef HAVE_ED448 int wc_CryptoCb_Ed448Sign(const byte* in, word32 inLen, byte* out, word32 *outLen, ed448_key* key, byte type, const byte* context, diff --git a/wolfcrypt/src/curve448.c b/wolfcrypt/src/curve448.c index e2448ba423b..b803fb144d9 100644 --- a/wolfcrypt/src/curve448.c +++ b/wolfcrypt/src/curve448.c @@ -44,6 +44,9 @@ #ifdef HAVE_CURVE448 #include +#ifdef WOLF_CRYPTO_CB + #include +#endif #ifdef NO_INLINE #include #else @@ -51,11 +54,24 @@ #include #endif +/* Check the private scalar is clamped per RFC 7748 section 5: + * low two bits of byte 0 clear and top bit of byte 55 set. */ +static WC_INLINE int curve448_priv_clamp_check(const byte* priv) +{ + int ret = 0; + if ((priv[0] & 0x03) || !(priv[CURVE448_KEY_SIZE-1] & 0x80)) { + ret = ECC_BAD_ARG_E; + } + return ret; +} + int wc_curve448_make_pub(int public_size, byte* pub, int private_size, const byte* priv) { int ret; +#ifndef WOLF_CRYPTO_CB_ONLY_CURVE448 unsigned char basepoint[CURVE448_KEY_SIZE] = {5}; +#endif if ((pub == NULL) || (priv == NULL)) { return ECC_BAD_ARG_E; @@ -65,10 +81,66 @@ int wc_curve448_make_pub(int public_size, byte* pub, int private_size, return ECC_BAD_ARG_E; } + /* check clamping */ + ret = curve448_priv_clamp_check(priv); + if (ret != 0) + return ret; + +#ifdef WOLF_CRYPTO_CB + ret = wc_CryptoCb_Curve448MakePub(public_size, pub, private_size, priv); + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + return ret; + /* fall-through when unavailable */ +#endif + +#ifdef WOLF_CRYPTO_CB_ONLY_CURVE448 + ret = NO_VALID_DEVID; +#else fe448_init(); /* compute public key */ ret = curve448(pub, priv, basepoint); +#endif /* WOLF_CRYPTO_CB_ONLY_CURVE448 */ + + return ret; +} + +/* Multiply a scalar (private key) against any basepoint over curve448. */ +int wc_curve448_generic(int public_size, byte* pub, + int private_size, const byte* priv, + int basepoint_size, const byte* basepoint) +{ + int ret; + + if ((pub == NULL) || (priv == NULL) || (basepoint == NULL)) { + return ECC_BAD_ARG_E; + } + if ((public_size != CURVE448_PUB_KEY_SIZE) || + (private_size != CURVE448_KEY_SIZE) || + (basepoint_size != CURVE448_KEY_SIZE)) { + return ECC_BAD_ARG_E; + } + + /* check clamping */ + ret = curve448_priv_clamp_check(priv); + if (ret != 0) + return ret; + +#ifdef WOLF_CRYPTO_CB + ret = wc_CryptoCb_Curve448Generic(public_size, pub, private_size, priv, + basepoint_size, basepoint); + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + return ret; + /* fall-through when unavailable */ +#endif + +#ifdef WOLF_CRYPTO_CB_ONLY_CURVE448 + ret = NO_VALID_DEVID; +#else + fe448_init(); + + ret = curve448(pub, priv, basepoint); +#endif /* WOLF_CRYPTO_CB_ONLY_CURVE448 */ return ret; } @@ -96,6 +168,27 @@ int wc_curve448_make_key(WC_RNG* rng, int keysize, curve448_key* key) ret = ECC_BAD_ARG_E; } +#ifdef WOLF_CRYPTO_CB + if (ret == 0) { + #ifndef WOLF_CRYPTO_CB_FIND + if (key->devId != INVALID_DEVID) + #endif + { + ret = wc_CryptoCb_Curve448Gen(rng, keysize, key); + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + return ret; + /* fall-through when unavailable */ + ret = 0; + } + } +#endif + +#ifdef WOLF_CRYPTO_CB_ONLY_CURVE448 + /* software path stripped; callback is the only provider */ + if (ret == 0) { + ret = NO_VALID_DEVID; + } +#else if (ret == 0) { /* random number for private key */ ret = wc_RNG_GenerateBlock(rng, key->k, (word32)keysize); @@ -118,6 +211,7 @@ int wc_curve448_make_key(WC_RNG* rng, int keysize, curve448_key* key) XMEMSET(key->p, 0, sizeof(key->p)); } } +#endif /* WOLF_CRYPTO_CB_ONLY_CURVE448 */ return ret; } @@ -162,27 +256,61 @@ int wc_curve448_shared_secret_ex(curve448_key* private_key, curve448_key* public_key, byte* out, word32* outLen, int endian) { +#ifndef WOLF_CRYPTO_CB_ONLY_CURVE448 unsigned char o[CURVE448_PUB_KEY_SIZE]; - int ret = 0; int i; - -#ifdef WOLFSSL_CHECK_MEM_ZERO - /* Register the shared-secret buffer up front (no early return bypasses the - * cleanup ForceZero) so every path is checked. XMEMSET makes it defined. */ - XMEMSET(o, 0, sizeof(o)); - wc_MemZero_Add("wc_curve448_shared_secret_ex o", o, CURVE448_PUB_KEY_SIZE); #endif + int ret = 0; /* sanity check */ if ((private_key == NULL) || (public_key == NULL) || (out == NULL) || (outLen == NULL) || (*outLen < CURVE448_PUB_KEY_SIZE)) { - ret = BAD_FUNC_ARG; + return BAD_FUNC_ARG; } /* make sure we have a populated private and public key */ - if (ret == 0 && (!private_key->privSet || !public_key->pubSet)) { - ret = ECC_BAD_ARG_E; + if (!private_key->privSet || !public_key->pubSet) { + return ECC_BAD_ARG_E; } +#ifdef WOLF_CRYPTO_CB + #ifndef WOLF_CRYPTO_CB_FIND + if (private_key->devId != INVALID_DEVID) + #endif + { + ret = wc_CryptoCb_Curve448(private_key, public_key, out, outLen, + endian); + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { +#ifndef WOLFSSL_NO_ECDHX_SHARED_ZERO_CHECK + /* RFC 7748: reject an all-zero secret from the callback too */ + if (ret == 0) { + int j; + byte t = 0; + for (j = 0; j < CURVE448_PUB_KEY_SIZE; j++) { + t |= out[j]; + } + if (t == 0) { + ret = ECC_OUT_OF_RANGE_E; + } + } +#endif + return ret; + } + /* fall-through when unavailable */ + ret = 0; + } +#endif + +#ifdef WOLF_CRYPTO_CB_ONLY_CURVE448 + /* software path stripped; callback is the only provider */ + ret = NO_VALID_DEVID; +#else +#ifdef WOLFSSL_CHECK_MEM_ZERO + /* Register the buffer after the early returns so every later path + * reaches the cleanup ForceZero. XMEMSET makes it defined. */ + XMEMSET(o, 0, sizeof(o)); + wc_MemZero_Add("wc_curve448_shared_secret_ex o", o, CURVE448_PUB_KEY_SIZE); +#endif + if (ret == 0) { ret = curve448(o, private_key->k, public_key->p); } @@ -216,6 +344,7 @@ int wc_curve448_shared_secret_ex(curve448_key* private_key, #ifdef WOLFSSL_CHECK_MEM_ZERO wc_MemZero_Check(o, CURVE448_PUB_KEY_SIZE); #endif +#endif /* WOLF_CRYPTO_CB_ONLY_CURVE448 */ return ret; } @@ -697,13 +826,44 @@ int wc_curve448_import_private_ex(const byte* priv, word32 privSz, #endif /* HAVE_CURVE448_KEY_IMPORT */ -/* Initialize the curve448 key. - * - * key [in] Curve448 key object. - * returns BAD_FUNC_ARG when key is NULL, - * 0 otherwise. - */ -int wc_curve448_init(curve448_key* key) +#ifndef WC_NO_CONSTRUCTORS +curve448_key* wc_curve448_new(void* heap, int devId, int* result_code) +{ + int ret; + curve448_key* key = (curve448_key*)XMALLOC(sizeof(curve448_key), heap, + DYNAMIC_TYPE_CURVE448); + if (key == NULL) { + ret = MEMORY_E; + } + else { + ret = wc_curve448_init_ex(key, heap, devId); + if (ret != 0) { + XFREE(key, heap, DYNAMIC_TYPE_CURVE448); + key = NULL; + } + } + + if (result_code != NULL) + *result_code = ret; + + return key; +} + +int wc_curve448_delete(curve448_key* key, curve448_key** key_p) { + void* heap; + if (key == NULL) + return BAD_FUNC_ARG; + heap = key->heap; + wc_curve448_free(key); + XFREE(key, heap, DYNAMIC_TYPE_CURVE448); + if (key_p != NULL) + *key_p = NULL; + return 0; +} +#endif /* !WC_NO_CONSTRUCTORS */ + +/* Initialize the curve448 key with a heap hint and crypto callback devId. */ +int wc_curve448_init_ex(curve448_key* key, void* heap, int devId) { int ret = 0; @@ -714,7 +874,17 @@ int wc_curve448_init(curve448_key* key) if (ret == 0) { XMEMSET(key, 0, sizeof(*key)); + #ifdef WOLF_CRYPTO_CB + key->devId = devId; + #else + (void)devId; + #endif + key->heap = heap; + + /* field math is implemented in the callback in crypto cb only */ + #ifndef WOLF_CRYPTO_CB_ONLY_CURVE448 fe448_init(); + #endif #ifdef WOLFSSL_CHECK_MEM_ZERO wc_MemZero_Add("wc_curve448_init key->k", &key->k, CURVE448_KEY_SIZE); @@ -724,6 +894,17 @@ int wc_curve448_init(curve448_key* key) return ret; } +/* Initialize the curve448 key. + * + * key [in] Curve448 key object. + * returns BAD_FUNC_ARG when key is NULL, + * 0 otherwise. + */ +int wc_curve448_init(curve448_key* key) +{ + return wc_curve448_init_ex(key, NULL, INVALID_DEVID); +} + /* Clears the curve448 key data. * diff --git a/wolfcrypt/src/evp.c b/wolfcrypt/src/evp.c index a85d7603d79..a8a62436102 100644 --- a/wolfcrypt/src/evp.c +++ b/wolfcrypt/src/evp.c @@ -3950,7 +3950,8 @@ int wolfSSL_EVP_PKEY_keygen(WOLFSSL_EVP_PKEY_CTX *ctx, ret = MEMORY_E; break; } - if (wc_curve448_init(pkey->curve448) != 0) { + if (wc_curve448_init_ex(pkey->curve448, pkey->heap, + INVALID_DEVID) != 0) { XFREE(pkey->curve448, pkey->heap, DYNAMIC_TYPE_CURVE448); pkey->curve448 = NULL; break; @@ -10327,7 +10328,7 @@ int wolfSSL_EVP_PKEY_set1_encoded_public_key(WOLFSSL_EVP_PKEY* pkey, WOLFSSL_MSG("malloc failed"); break; } - if (wc_curve448_init(key) != 0) { + if (wc_curve448_init_ex(key, pkey->heap, INVALID_DEVID) != 0) { XFREE(key, pkey->heap, DYNAMIC_TYPE_CURVE448); break; } diff --git a/wolfcrypt/src/evp_pk.c b/wolfcrypt/src/evp_pk.c index d5883c2d631..6cfe173a867 100644 --- a/wolfcrypt/src/evp_pk.c +++ b/wolfcrypt/src/evp_pk.c @@ -523,7 +523,7 @@ WOLFSSL_EVP_PKEY* wolfSSL_EVP_PKEY_new_raw_public_key(int type, if (cKey == NULL) { break; } - if (wc_curve448_init(cKey) != 0) { + if (wc_curve448_init_ex(cKey, pkey->heap, INVALID_DEVID) != 0) { XFREE(cKey, pkey->heap, DYNAMIC_TYPE_CURVE448); break; } @@ -672,7 +672,7 @@ WOLFSSL_EVP_PKEY* wolfSSL_EVP_PKEY_new_raw_private_key(int type, if (cKey == NULL) { break; } - if (wc_curve448_init(cKey) != 0) { + if (wc_curve448_init_ex(cKey, pkey->heap, INVALID_DEVID) != 0) { XFREE(cKey, pkey->heap, DYNAMIC_TYPE_CURVE448); break; } diff --git a/wolfcrypt/src/fe_448.c b/wolfcrypt/src/fe_448.c index c12d8faf440..395be13d6db 100644 --- a/wolfcrypt/src/fe_448.c +++ b/wolfcrypt/src/fe_448.c @@ -26,7 +26,10 @@ #include -#if defined(HAVE_CURVE448) || defined(HAVE_ED448) +/* under WOLF_CRYPTO_CB_ONLY_CURVE448 the callback device does all the field + * math, so curve448 does not pull this file in on its own */ +#if (defined(HAVE_CURVE448) && !defined(WOLF_CRYPTO_CB_ONLY_CURVE448)) || \ + defined(HAVE_ED448) #include @@ -2509,4 +2512,4 @@ void fe448_cmov(sword32* a, const sword32* b, int c) #endif /* HAVE_ED448 */ #endif -#endif /* HAVE_CURVE448 || HAVE_ED448 */ +#endif /* (HAVE_CURVE448 && !WOLF_CRYPTO_CB_ONLY_CURVE448) || HAVE_ED448 */ diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 0556fd53513..accaf7130dc 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -3241,7 +3241,8 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\ TEST_PASS("ED25519 test passed!\n"); #endif -#ifdef HAVE_CURVE448 +#if defined(HAVE_CURVE448) && \ + (!defined(WOLF_CRYPTO_CB_ONLY_CURVE448) || defined(WOLFSSL_SWDEV)) if ( (ret = curve448_test()) != 0) TEST_FAIL("CURVE448 test failed!\n", ret); else @@ -50625,9 +50626,9 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t curve448_test(void) if (ret != 0) return WC_TEST_RET_ENC_EC(ret); - wc_curve448_init(&userA); - wc_curve448_init(&userB); - wc_curve448_init(&pubKey); + wc_curve448_init_ex(&userA, HEAP_HINT, devId); + wc_curve448_init_ex(&userB, HEAP_HINT, devId); + wc_curve448_init_ex(&pubKey, HEAP_HINT, devId); ret = curve448_keyagree_test(&rng, &userA, &userB, &pubKey); if (ret != 0) @@ -79253,6 +79254,10 @@ typedef struct { int ed448SignCount; /* Ed448 sign callback invocations */ int ed448VerifyCount; /* Ed448 verify callback invocations */ #endif +#ifdef HAVE_CURVE448 + int curve448KgCount; /* Curve448 keygen callback invocations */ + int curve448SsCount; /* Curve448 shared-secret callback invocations */ +#endif #if defined(WOLFSSL_CMAC) && defined(WOLF_CRYPTO_CB_FREE) int cmacFreeCount; /* CMAC free callback invocations */ #endif @@ -80136,6 +80141,143 @@ static wc_test_ret_t curve25519_onlycb_test(myCryptoDevCtx *ctx) } #endif /* WOLF_CRYPTO_CB_ONLY_CURVE25519 */ +#if defined(WOLF_CRYPTO_CB_ONLY_CURVE448) && !defined(WOLFSSL_SWDEV) +/* Exercise Curve448 dispatch under CB_ONLY_CURVE448: cb-handled then + * cb-delegated. */ +static wc_test_ret_t curve448_onlycb_test(myCryptoDevCtx *ctx) +{ + wc_test_ret_t ret = 0; + curve448_key key; +#if defined(HAVE_CURVE448_SHARED_SECRET) && \ + defined(HAVE_CURVE448_KEY_IMPORT) + curve448_key pubKey; + int pubInit = 0; + const byte priv[CURVE448_KEY_SIZE] = {1}; + const byte pub[CURVE448_KEY_SIZE] = {5}; + byte out[CURVE448_KEY_SIZE]; + word32 outLen = (word32)sizeof(out); +#endif + WC_RNG rng; + + ret = wc_curve448_init_ex(&key, HEAP_HINT, devId); + if (ret != 0) + return WC_TEST_RET_ENC_EC(ret); + + ret = wc_InitRng(&rng); + if (ret != 0) { + wc_curve448_free(&key); + return WC_TEST_RET_ENC_EC(ret); + } + + /* cb handles the op, expects 0(success) */ + ctx->exampleVar = 99; + ret = wc_curve448_make_key(&rng, CURVE448_KEY_SIZE, &key); + if (ret != 0) + ret = WC_TEST_RET_ENC_EC(ret); + + if (ret == 0) { + /* cb delegates to software, expects NO_VALID_DEVID(failure) */ + ctx->exampleVar = 1; + ret = wc_curve448_make_key(&rng, CURVE448_KEY_SIZE, &key); + if (ret != WC_NO_ERR_TRACE(NO_VALID_DEVID)) + ret = WC_TEST_RET_ENC_EC(ret); + else + ret = 0; + } + + if (ret == 0) { + const byte basepoint[CURVE448_KEY_SIZE] = {5}; + byte scalar[CURVE448_KEY_SIZE]; + byte pubTmp[CURVE448_PUB_KEY_SIZE]; + + /* clamped scalar: low two bits clear, top bit set */ + XMEMSET(scalar, 0, sizeof(scalar)); + scalar[0] = 4; + scalar[CURVE448_KEY_SIZE-1] = 0x80; + + /* cb handles make_pub and generic, expects 0(success) */ + ctx->exampleVar = 99; + ret = wc_curve448_make_pub((int)sizeof(pubTmp), pubTmp, + (int)sizeof(scalar), scalar); + if (ret != 0) + ret = WC_TEST_RET_ENC_EC(ret); + if (ret == 0) { + ret = wc_curve448_generic((int)sizeof(pubTmp), pubTmp, + (int)sizeof(scalar), scalar, (int)sizeof(basepoint), + basepoint); + if (ret != 0) + ret = WC_TEST_RET_ENC_EC(ret); + } + + /* cb delegates to software, expects NO_VALID_DEVID(failure) */ + if (ret == 0) { + ctx->exampleVar = 1; + ret = wc_curve448_make_pub((int)sizeof(pubTmp), pubTmp, + (int)sizeof(scalar), scalar); + if (ret != WC_NO_ERR_TRACE(NO_VALID_DEVID)) + ret = WC_TEST_RET_ENC_EC(ret); + else + ret = 0; + } + if (ret == 0) { + ret = wc_curve448_generic((int)sizeof(pubTmp), pubTmp, + (int)sizeof(scalar), scalar, (int)sizeof(basepoint), + basepoint); + if (ret != WC_NO_ERR_TRACE(NO_VALID_DEVID)) + ret = WC_TEST_RET_ENC_EC(ret); + else + ret = 0; + } + } + +#if defined(HAVE_CURVE448_SHARED_SECRET) && \ + defined(HAVE_CURVE448_KEY_IMPORT) + if (ret == 0) { + ret = wc_curve448_init_ex(&pubKey, HEAP_HINT, devId); + if (ret != 0) + ret = WC_TEST_RET_ENC_EC(ret); + else + pubInit = 1; + } + if (ret == 0) { + ret = wc_curve448_import_private(priv, sizeof(priv), &key); + if (ret != 0) + ret = WC_TEST_RET_ENC_EC(ret); + } + if (ret == 0) { + ret = wc_curve448_import_public(pub, sizeof(pub), &pubKey); + if (ret != 0) + ret = WC_TEST_RET_ENC_EC(ret); + } + if (ret == 0) { + /* cb handles the op, expects 0(success) and the stub's secret */ + ctx->exampleVar = 99; + ret = wc_curve448_shared_secret(&key, &pubKey, out, &outLen); + if (ret != 0) + ret = WC_TEST_RET_ENC_EC(ret); + else if ((outLen != CURVE448_KEY_SIZE) || (out[0] != 0xA5)) + ret = WC_TEST_RET_ENC_NC; + } + if (ret == 0) { + /* cb delegates to software, expects NO_VALID_DEVID(failure) */ + ctx->exampleVar = 1; + ret = wc_curve448_shared_secret(&key, &pubKey, out, &outLen); + if (ret != WC_NO_ERR_TRACE(NO_VALID_DEVID)) + ret = WC_TEST_RET_ENC_EC(ret); + else + ret = 0; + } + if (pubInit) + wc_curve448_free(&pubKey); +#endif /* HAVE_CURVE448_SHARED_SECRET && HAVE_CURVE448_KEY_IMPORT */ + + wc_FreeRng(&rng); + wc_curve448_free(&key); + (void)ctx; + return ret; +} +#endif /* WOLF_CRYPTO_CB_ONLY_CURVE448 && !WOLFSSL_SWDEV */ + #if defined(HAVE_ECC) && !defined(WOLFSSL_NO_MALLOC) && \ defined(HAVE_ECC_KEY_EXPORT) /* Serialize pub to X9.63 uncompressed (0x04 || X || Y) using the curve size @@ -80752,6 +80894,65 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) info->pk.ed25519checkkey.key->devId = devIdArg; } #endif /* HAVE_ED25519 */ + #ifdef HAVE_CURVE448 + if (info->pk.type == WC_PK_TYPE_CURVE448_KEYGEN) { + myCtx->curve448KgCount++; + /* set devId to invalid, so software is used */ + info->pk.curve448kg.key->devId = INVALID_DEVID; + #if defined(WOLF_CRYPTO_CB_ONLY_CURVE448) + #ifdef DEBUG_WOLFSSL + printf("CryptoDevCb: exampleVar %d\n", myCtx->exampleVar); + #endif + if (myCtx->exampleVar == 99) { + info->pk.curve448kg.key->devId = devIdArg; + return 0; + } + #endif + + ret = wc_curve448_make_key(info->pk.curve448kg.rng, + info->pk.curve448kg.size, info->pk.curve448kg.key); + + /* reset devId */ + info->pk.curve448kg.key->devId = devIdArg; + } + #ifdef HAVE_CURVE448_SHARED_SECRET + else if (info->pk.type == WC_PK_TYPE_CURVE448) { + myCtx->curve448SsCount++; + /* set devId to invalid, so software is used */ + info->pk.curve448.private_key->devId = INVALID_DEVID; + #if defined(WOLF_CRYPTO_CB_ONLY_CURVE448) + #ifdef DEBUG_WOLFSSL + printf("CryptoDevCb: exampleVar %d\n", myCtx->exampleVar); + #endif + if (myCtx->exampleVar == 99) { + info->pk.curve448.private_key->devId = devIdArg; + /* deterministic non-zero secret so the caller's RFC 7748 + * all-zero check has defined input to pass */ + XMEMSET(info->pk.curve448.out, 0xA5, CURVE448_PUB_KEY_SIZE); + *info->pk.curve448.outlen = CURVE448_PUB_KEY_SIZE; + return 0; + } + #endif + + ret = wc_curve448_shared_secret_ex( + info->pk.curve448.private_key, info->pk.curve448.public_key, + info->pk.curve448.out, info->pk.curve448.outlen, + info->pk.curve448.endian); + + /* reset devId */ + info->pk.curve448.private_key->devId = devIdArg; + } + #endif /* HAVE_CURVE448_SHARED_SECRET */ + else if (info->pk.type == WC_PK_TYPE_CURVE448_MAKE_PUB || + info->pk.type == WC_PK_TYPE_CURVE448_GENERIC) { + #if defined(WOLF_CRYPTO_CB_ONLY_CURVE448) + if (myCtx->exampleVar == 99) { + return 0; + } + #endif + /* decline: ret stays NOT_COMPILED_IN, software is used */ + } + #endif /* HAVE_CURVE448 */ #ifdef HAVE_ED448 #ifdef HAVE_ED448_SIGN if (info->pk.type == WC_PK_TYPE_ED448) { @@ -82760,6 +82961,10 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void) myCtx.ed448SignCount = 0; myCtx.ed448VerifyCount = 0; #endif +#ifdef HAVE_CURVE448 + myCtx.curve448KgCount = 0; + myCtx.curve448SsCount = 0; +#endif #if defined(WOLFSSL_CMAC) && defined(WOLF_CRYPTO_CB_FREE) myCtx.cmacFreeCount = 0; #endif @@ -83115,6 +83320,29 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t cryptocb_test(void) ret = curve25519_onlycb_test(&myCtx); PRIVATE_KEY_LOCK(); #endif +#if defined(HAVE_CURVE448) && \ + (!defined(WOLF_CRYPTO_CB_ONLY_CURVE448) || defined(WOLFSSL_SWDEV)) + myCtx.curve448KgCount = 0; + myCtx.curve448SsCount = 0; + if (ret == 0) + ret = curve448_test(); +#endif +#if defined(WOLF_CRYPTO_CB_ONLY_CURVE448) && !defined(WOLFSSL_SWDEV) + PRIVATE_KEY_UNLOCK(); + if (ret == 0) + ret = curve448_onlycb_test(&myCtx); + PRIVATE_KEY_LOCK(); +#endif +#if defined(HAVE_CURVE448) && \ + (!defined(WOLF_CRYPTO_CB_ONLY_CURVE448) || defined(WOLFSSL_SWDEV)) + /* the keys carry devId, so the callback must have been dispatched */ + if (ret == 0 && myCtx.curve448KgCount == 0) + ret = WC_TEST_RET_ENC_NC; + #ifdef HAVE_CURVE448_SHARED_SECRET + if (ret == 0 && myCtx.curve448SsCount == 0) + ret = WC_TEST_RET_ENC_NC; + #endif +#endif #if !defined(NO_AES) && !defined(WOLF_CRYPTO_CB_ONLY_AES) /* CB_ONLY_AES skips these (aes_onlycb_test covers that path). */ #ifdef HAVE_AESGCM diff --git a/wolfssl/wolfcrypt/cryptocb.h b/wolfssl/wolfcrypt/cryptocb.h index d3028e91e96..615bb210891 100644 --- a/wolfssl/wolfcrypt/cryptocb.h +++ b/wolfssl/wolfcrypt/cryptocb.h @@ -77,6 +77,9 @@ #ifdef HAVE_CURVE25519 #include #endif +#ifdef HAVE_CURVE448 + #include +#endif #if defined(WOLFSSL_SHA512) || defined(WOLFSSL_SHA384) #include #endif @@ -382,6 +385,35 @@ typedef struct wc_CryptoInfo { * priv/pub consistency */ } ed25519checkkey; #endif + #ifdef HAVE_CURVE448 + struct { + WC_RNG* rng; + int size; + curve448_key* key; + int curveId; + } curve448kg; + struct { + curve448_key* private_key; + curve448_key* public_key; + byte* out; + word32* outlen; + int endian; + } curve448; + struct { + byte* pub; + word32 pubSz; + const byte* priv; + word32 privSz; + } curve448makepub; + struct { + byte* pub; + word32 pubSz; + const byte* priv; + word32 privSz; + const byte* basepoint; + word32 basepointSz; + } curve448generic; + #endif /* HAVE_CURVE448 */ #ifdef HAVE_ED448 struct { const byte* in; @@ -952,6 +984,18 @@ WOLFSSL_LOCAL int wc_CryptoCb_Ed25519MakePub(ed25519_key* key, byte* pubKey, WOLFSSL_LOCAL int wc_CryptoCb_Ed25519CheckKey(ed25519_key* key); #endif /* HAVE_ED25519 */ +#ifdef HAVE_CURVE448 +WOLFSSL_LOCAL int wc_CryptoCb_Curve448Gen(WC_RNG* rng, int keySize, + curve448_key* key); +WOLFSSL_LOCAL int wc_CryptoCb_Curve448(curve448_key* private_key, + curve448_key* public_key, byte* out, word32* outlen, int endian); +WOLFSSL_LOCAL int wc_CryptoCb_Curve448MakePub(int public_size, byte* pub, + int private_size, const byte* priv); +WOLFSSL_LOCAL int wc_CryptoCb_Curve448Generic(int public_size, byte* pub, + int private_size, const byte* priv, int basepoint_size, + const byte* basepoint); +#endif /* HAVE_CURVE448 */ + #ifdef HAVE_ED448 WOLFSSL_LOCAL int wc_CryptoCb_Ed448Sign(const byte* in, word32 inLen, byte* out, word32 *outLen, ed448_key* key, byte type, const byte* context, diff --git a/wolfssl/wolfcrypt/curve448.h b/wolfssl/wolfcrypt/curve448.h index 9310aaaddd1..b286fb6f856 100644 --- a/wolfssl/wolfcrypt/curve448.h +++ b/wolfssl/wolfcrypt/curve448.h @@ -56,6 +56,11 @@ struct curve448_key { #ifdef WOLFSSL_ASYNC_CRYPT WC_ASYNC_DEV asyncDev; #endif +#ifdef WOLF_CRYPTO_CB + void* devCtx; + int devId; +#endif + void* heap; /* bit fields */ WC_BITFIELD pubSet:1; @@ -74,6 +79,11 @@ WOLFSSL_API int wc_curve448_make_pub(int public_size, byte* pub, int private_size, const byte* priv); +WOLFSSL_API +int wc_curve448_generic(int public_size, byte* pub, + int private_size, const byte* priv, + int basepoint_size, const byte* basepoint); + WOLFSSL_API int wc_curve448_shared_secret(curve448_key* private_key, curve448_key* public_key, @@ -87,6 +97,16 @@ int wc_curve448_shared_secret_ex(curve448_key* private_key, WOLFSSL_API int wc_curve448_init(curve448_key* key); +WOLFSSL_API +int wc_curve448_init_ex(curve448_key* key, void* heap, int devId); + +#ifndef WC_NO_CONSTRUCTORS +WOLFSSL_API +curve448_key* wc_curve448_new(void* heap, int devId, int* result_code); +WOLFSSL_API +int wc_curve448_delete(curve448_key* key, curve448_key** key_p); +#endif + WOLFSSL_API void wc_curve448_free(curve448_key* key); diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index 2bfbb4ddef4..b66ae2cac39 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -5901,6 +5901,16 @@ blinding by defining WC_BLINDING_NO_RNG_ACKNOWLEDGE_WEAKNESS." #error "WOLF_CRYPTO_CB_ONLY_CURVE25519 is incompatible with " \ "WOLFSSL_ASYNC_CRYPT" #endif +#if defined(WOLF_CRYPTO_CB_ONLY_CURVE448) && !defined(WOLF_CRYPTO_CB) + #error "WOLF_CRYPTO_CB_ONLY_CURVE448 requires WOLF_CRYPTO_CB" +#endif +#if defined(WOLF_CRYPTO_CB_ONLY_CURVE448) && !defined(HAVE_CURVE448) + #error "WOLF_CRYPTO_CB_ONLY_CURVE448 requires HAVE_CURVE448" +#endif +#if defined(WOLF_CRYPTO_CB_ONLY_CURVE448) && defined(WOLFSSL_ASYNC_CRYPT) + #error "WOLF_CRYPTO_CB_ONLY_CURVE448 is incompatible with " \ + "WOLFSSL_ASYNC_CRYPT" +#endif /* Early Data / Session Rules */ #if !defined(WOLFCRYPT_ONLY) && defined(WOLFSSL_EARLY_DATA) && \ diff --git a/wolfssl/wolfcrypt/types.h b/wolfssl/wolfcrypt/types.h index 3cce9b4c70b..48f3364813f 100644 --- a/wolfssl/wolfcrypt/types.h +++ b/wolfssl/wolfcrypt/types.h @@ -1703,8 +1703,12 @@ enum wc_PkType { WC_PK_TYPE_RSA_PSS_VERIFY = 42, /* Ed448 sign reuses WC_PK_TYPE_ED448 (12); verify needs its own type. */ WC_PK_TYPE_ED448_VERIFY = 43, + /* Curve448 shared secret reuses WC_PK_TYPE_CURVE448 (13). */ + WC_PK_TYPE_CURVE448_KEYGEN = 44, + WC_PK_TYPE_CURVE448_MAKE_PUB = 45, + WC_PK_TYPE_CURVE448_GENERIC = 46, #undef _WC_PK_TYPE_MAX - #define _WC_PK_TYPE_MAX WC_PK_TYPE_ED448_VERIFY + #define _WC_PK_TYPE_MAX WC_PK_TYPE_CURVE448_GENERIC WC_PK_TYPE_MAX = _WC_PK_TYPE_MAX }; From 141c65c71d66a527c91e9c11300023c9fc08ac75 Mon Sep 17 00:00:00 2001 From: night1rider Date: Thu, 20 Aug 2026 03:19:14 -0600 Subject: [PATCH 2/7] Address review findings on curve448 cryptocb support Assert cryptocb output in curve448_onlycb_test, reject an all-zero wc_curve448_generic result, guard the new API test for CB-only builds. --- doc/dox_comments/header_files/curve448.h | 10 ++-- tests/api/test_curve448.c | 19 ++++++- wolfcrypt/src/curve448.c | 35 +++++++++++- wolfcrypt/test/test.c | 68 +++++++++++++++++++++++- 4 files changed, 122 insertions(+), 10 deletions(-) diff --git a/doc/dox_comments/header_files/curve448.h b/doc/dox_comments/header_files/curve448.h index 1677e01aaf5..ae26adf0e8d 100644 --- a/doc/dox_comments/header_files/curve448.h +++ b/doc/dox_comments/header_files/curve448.h @@ -910,14 +910,16 @@ int wc_curve448_make_pub(int public_size, byte* pub, int private_size, \brief This function performs a generic Curve448 scalar multiplication with a custom basepoint. This allows computing scalar * basepoint for any basepoint, not just the standard - generator. This is a raw primitive: unlike - wc_curve448_shared_secret_ex it does not reject an all-zero - result. Callers doing key agreement with a peer-supplied point - must reject an all-zero output themselves (RFC 7748 section 6.2). + generator. As in wc_curve448_shared_secret_ex, an all-zero result + is rejected (RFC 7748 section 6.2); build with + WOLFSSL_NO_ECDHX_SHARED_ZERO_CHECK to get the raw scalar + multiplication result instead. \return 0 On successfully computing the result \return ECC_BAD_ARG_E If any input parameter is NULL or a size is invalid + \return ECC_OUT_OF_RANGE_E If the result is all-zero, i.e. the + basepoint was of small order \param public_size Size of the output buffer (must be 56) \param pub Pointer to buffer to store the result diff --git a/tests/api/test_curve448.c b/tests/api/test_curve448.c index f3933e0e4cb..170ac54fb35 100644 --- a/tests/api/test_curve448.c +++ b/tests/api/test_curve448.c @@ -832,11 +832,15 @@ int test_wc_curve448_export_import_endian(void) } /* END test_wc_curve448_export_import_endian */ /* Cross-check make_pub, generic and keygen: public keys must match and a - * shared secret must round trip (runs via cryptocb under CB_ONLY_CURVE448). */ + * shared secret must round trip. */ +/* The keys are built with wc_curve448_init (INVALID_DEVID), so under CB-only + * the software path is stripped and wc_curve448_make_key only dispatches when + * WOLF_CRYPTO_CB_FIND can route to a registered device. */ int test_wc_curve448_make_pub_generic(void) { EXPECT_DECLS; -#if defined(HAVE_CURVE448) && defined(HAVE_CURVE448_SHARED_SECRET) +#if defined(HAVE_CURVE448) && defined(HAVE_CURVE448_SHARED_SECRET) && \ + (!defined(WOLF_CRYPTO_CB_ONLY_CURVE448) || defined(WOLF_CRYPTO_CB_FIND)) curve448_key keyA; curve448_key keyB; WC_RNG rng; @@ -882,6 +886,17 @@ int test_wc_curve448_make_pub_generic(void) EC448_LITTLE_ENDIAN), 0); ExpectBufEQ(ssBA, ssAB, CURVE448_PUB_KEY_SIZE); + /* an all-zero result (small-order basepoint) must be rejected, matching + * wc_curve448_shared_secret_ex */ + XMEMSET(pubG, 0, sizeof(pubG)); + { + byte baseZero[CURVE448_KEY_SIZE]; + XMEMSET(baseZero, 0, sizeof(baseZero)); + ExpectIntEQ(wc_curve448_generic((int)sizeof(pubG), pubG, + (int)sizeof(keyA.k), keyA.k, (int)sizeof(baseZero), baseZero), + WC_NO_ERR_TRACE(ECC_OUT_OF_RANGE_E)); + } + /* argument checks on the new generic API */ ExpectIntEQ(wc_curve448_generic((int)sizeof(pubG), NULL, (int)sizeof(keyA.k), keyA.k, (int)sizeof(base5), base5), diff --git a/wolfcrypt/src/curve448.c b/wolfcrypt/src/curve448.c index b803fb144d9..653a1198323 100644 --- a/wolfcrypt/src/curve448.c +++ b/wolfcrypt/src/curve448.c @@ -105,7 +105,26 @@ int wc_curve448_make_pub(int public_size, byte* pub, int private_size, return ret; } -/* Multiply a scalar (private key) against any basepoint over curve448. */ +/* Is every byte of the curve448 result zero? Only reached when the caller's + * basepoint is of small order, which leaks the result to anyone watching. */ +#ifndef WOLFSSL_NO_ECDHX_SHARED_ZERO_CHECK +static WC_INLINE int curve448_result_is_zero(const byte* out) +{ + int i; + byte t = 0; + for (i = 0; i < CURVE448_PUB_KEY_SIZE; i++) { + t |= out[i]; + } + return (t == 0); +} +#endif + +/* Multiply a scalar (private key) against any basepoint over curve448. + * + * An all-zero result is rejected with ECC_OUT_OF_RANGE_E, matching + * wc_curve448_shared_secret_ex; define WOLFSSL_NO_ECDHX_SHARED_ZERO_CHECK to + * get the raw scalar multiplication result instead. + */ int wc_curve448_generic(int public_size, byte* pub, int private_size, const byte* priv, int basepoint_size, const byte* basepoint) @@ -129,8 +148,15 @@ int wc_curve448_generic(int public_size, byte* pub, #ifdef WOLF_CRYPTO_CB ret = wc_CryptoCb_Curve448Generic(public_size, pub, private_size, priv, basepoint_size, basepoint); - if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { + #ifndef WOLFSSL_NO_ECDHX_SHARED_ZERO_CHECK + /* RFC 7748: reject an all-zero result from the callback too */ + if ((ret == 0) && curve448_result_is_zero(pub)) { + ret = ECC_OUT_OF_RANGE_E; + } + #endif return ret; + } /* fall-through when unavailable */ #endif @@ -140,6 +166,11 @@ int wc_curve448_generic(int public_size, byte* pub, fe448_init(); ret = curve448(pub, priv, basepoint); +#ifndef WOLFSSL_NO_ECDHX_SHARED_ZERO_CHECK + if ((ret == 0) && curve448_result_is_zero(pub)) { + ret = ECC_OUT_OF_RANGE_E; + } +#endif #endif /* WOLF_CRYPTO_CB_ONLY_CURVE448 */ return ret; diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index accaf7130dc..1825aa1d7cd 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -80142,6 +80142,17 @@ static wc_test_ret_t curve25519_onlycb_test(myCryptoDevCtx *ctx) #endif /* WOLF_CRYPTO_CB_ONLY_CURVE25519 */ #if defined(WOLF_CRYPTO_CB_ONLY_CURVE448) && !defined(WOLFSSL_SWDEV) +/* Is every byte of buf the marker value v? */ +static int curve448_buf_is(const byte* buf, byte v, word32 len) +{ + word32 i; + for (i = 0; i < len; i++) { + if (buf[i] != v) + return 0; + } + return 1; +} + /* Exercise Curve448 dispatch under CB_ONLY_CURVE448: cb-handled then * cb-delegated. */ static wc_test_ret_t curve448_onlycb_test(myCryptoDevCtx *ctx) @@ -80169,11 +80180,14 @@ static wc_test_ret_t curve448_onlycb_test(myCryptoDevCtx *ctx) return WC_TEST_RET_ENC_EC(ret); } - /* cb handles the op, expects 0(success) */ + /* cb handles the op, expects 0(success) and the stub's key material */ ctx->exampleVar = 99; ret = wc_curve448_make_key(&rng, CURVE448_KEY_SIZE, &key); if (ret != 0) ret = WC_TEST_RET_ENC_EC(ret); + else if (!key.privSet || !key.pubSet || (key.p[0] != 0xC3) || + (key.k[CURVE448_KEY_SIZE-1] != 0xDA)) + ret = WC_TEST_RET_ENC_NC; if (ret == 0) { /* cb delegates to software, expects NO_VALID_DEVID(failure) */ @@ -80195,18 +80209,24 @@ static wc_test_ret_t curve448_onlycb_test(myCryptoDevCtx *ctx) scalar[0] = 4; scalar[CURVE448_KEY_SIZE-1] = 0x80; - /* cb handles make_pub and generic, expects 0(success) */ + /* cb handles make_pub and generic, expects 0(success) and the + * stub's per-type marker written over the caller's buffer */ ctx->exampleVar = 99; + XMEMSET(pubTmp, 0, sizeof(pubTmp)); ret = wc_curve448_make_pub((int)sizeof(pubTmp), pubTmp, (int)sizeof(scalar), scalar); if (ret != 0) ret = WC_TEST_RET_ENC_EC(ret); + else if (!curve448_buf_is(pubTmp, 0x6B, sizeof(pubTmp))) + ret = WC_TEST_RET_ENC_NC; if (ret == 0) { ret = wc_curve448_generic((int)sizeof(pubTmp), pubTmp, (int)sizeof(scalar), scalar, (int)sizeof(basepoint), basepoint); if (ret != 0) ret = WC_TEST_RET_ENC_EC(ret); + else if (!curve448_buf_is(pubTmp, 0x3C, sizeof(pubTmp))) + ret = WC_TEST_RET_ENC_NC; } /* cb delegates to software, expects NO_VALID_DEVID(failure) */ @@ -80904,7 +80924,21 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) printf("CryptoDevCb: exampleVar %d\n", myCtx->exampleVar); #endif if (myCtx->exampleVar == 99) { + /* the dispatcher must hand over a usable payload */ + if ((info->pk.curve448kg.rng == NULL) || + (info->pk.curve448kg.size != CURVE448_KEY_SIZE)) { + return BAD_FUNC_ARG; + } info->pk.curve448kg.key->devId = devIdArg; + /* deterministic key material so the caller can prove the + * callback's output actually reached it */ + XMEMSET(info->pk.curve448kg.key->k, 0x5A, CURVE448_KEY_SIZE); + info->pk.curve448kg.key->k[0] &= 0xfc; + info->pk.curve448kg.key->k[CURVE448_KEY_SIZE-1] |= 0x80; + XMEMSET(info->pk.curve448kg.key->p, 0xC3, + CURVE448_PUB_KEY_SIZE); + info->pk.curve448kg.key->privSet = 1; + info->pk.curve448kg.key->pubSet = 1; return 0; } #endif @@ -80947,6 +80981,36 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) info->pk.type == WC_PK_TYPE_CURVE448_GENERIC) { #if defined(WOLF_CRYPTO_CB_ONLY_CURVE448) if (myCtx->exampleVar == 99) { + /* check the dispatcher populated every payload field, then + * write a per-type marker so the caller can prove the + * callback's output actually reached it */ + if (info->pk.type == WC_PK_TYPE_CURVE448_MAKE_PUB) { + if ((info->pk.curve448makepub.pub == NULL) || + (info->pk.curve448makepub.priv == NULL) || + (info->pk.curve448makepub.pubSz != + CURVE448_PUB_KEY_SIZE) || + (info->pk.curve448makepub.privSz != + CURVE448_KEY_SIZE)) { + return BAD_FUNC_ARG; + } + XMEMSET(info->pk.curve448makepub.pub, 0x6B, + CURVE448_PUB_KEY_SIZE); + } + else { + if ((info->pk.curve448generic.pub == NULL) || + (info->pk.curve448generic.priv == NULL) || + (info->pk.curve448generic.basepoint == NULL) || + (info->pk.curve448generic.pubSz != + CURVE448_PUB_KEY_SIZE) || + (info->pk.curve448generic.privSz != + CURVE448_KEY_SIZE) || + (info->pk.curve448generic.basepointSz != + CURVE448_KEY_SIZE)) { + return BAD_FUNC_ARG; + } + XMEMSET(info->pk.curve448generic.pub, 0x3C, + CURVE448_PUB_KEY_SIZE); + } return 0; } #endif From 36b71693f07f19a1b2bba433b8637f0e94590889 Mon Sep 17 00:00:00 2001 From: night1rider Date: Thu, 20 Aug 2026 08:15:59 -0600 Subject: [PATCH 3/7] Address second round of review findings on curve448 cryptocb support Restore key devId on the keygen stub's error return, full-buffer-check callback output, cover the new/delete NULL out-params, drop a dead guard. --- tests/api/test_curve448.c | 9 +++++++++ wolfcrypt/src/curve448.c | 6 +++--- wolfcrypt/test/test.c | 10 +++++++--- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/tests/api/test_curve448.c b/tests/api/test_curve448.c index 170ac54fb35..83fd0f8c371 100644 --- a/tests/api/test_curve448.c +++ b/tests/api/test_curve448.c @@ -1106,8 +1106,17 @@ int test_wc_curve448_cryptocb(void) wc_curve448_free(&keyB); #ifndef WC_NO_CONSTRUCTORS + /* result_code and key_p are both optional */ + { + curve448_key* keyC = NULL; + ExpectNotNull(keyC = wc_curve448_new(HEAP_HINT, devId, NULL)); + if (keyC != NULL) { + DoExpectIntEQ(wc_curve448_delete(keyC, NULL), 0); + } + } if (keyA != NULL) { DoExpectIntEQ(wc_curve448_delete(keyA, &keyA), 0); + ExpectNull(keyA); } ExpectIntEQ(wc_curve448_delete(NULL, NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); diff --git a/wolfcrypt/src/curve448.c b/wolfcrypt/src/curve448.c index 653a1198323..058b7f44cc5 100644 --- a/wolfcrypt/src/curve448.c +++ b/wolfcrypt/src/curve448.c @@ -342,9 +342,9 @@ int wc_curve448_shared_secret_ex(curve448_key* private_key, wc_MemZero_Add("wc_curve448_shared_secret_ex o", o, CURVE448_PUB_KEY_SIZE); #endif - if (ret == 0) { - ret = curve448(o, private_key->k, public_key->p); - } + /* ret is 0 here: the argument checks return early and the cryptocb block + * either returns or resets it. */ + ret = curve448(o, private_key->k, public_key->p); #ifndef WOLFSSL_NO_ECDHX_SHARED_ZERO_CHECK if (ret == 0) { byte t = 0; diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 1825aa1d7cd..dcfce3693e8 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -80185,7 +80185,10 @@ static wc_test_ret_t curve448_onlycb_test(myCryptoDevCtx *ctx) ret = wc_curve448_make_key(&rng, CURVE448_KEY_SIZE, &key); if (ret != 0) ret = WC_TEST_RET_ENC_EC(ret); - else if (!key.privSet || !key.pubSet || (key.p[0] != 0xC3) || + else if (!key.privSet || !key.pubSet || + !curve448_buf_is(key.p, 0xC3, CURVE448_PUB_KEY_SIZE) || + (key.k[0] != 0x58) || + !curve448_buf_is(key.k + 1, 0x5A, CURVE448_KEY_SIZE - 2) || (key.k[CURVE448_KEY_SIZE-1] != 0xDA)) ret = WC_TEST_RET_ENC_NC; @@ -80275,7 +80278,8 @@ static wc_test_ret_t curve448_onlycb_test(myCryptoDevCtx *ctx) ret = wc_curve448_shared_secret(&key, &pubKey, out, &outLen); if (ret != 0) ret = WC_TEST_RET_ENC_EC(ret); - else if ((outLen != CURVE448_KEY_SIZE) || (out[0] != 0xA5)) + else if ((outLen != CURVE448_KEY_SIZE) || + !curve448_buf_is(out, 0xA5, CURVE448_PUB_KEY_SIZE)) ret = WC_TEST_RET_ENC_NC; } if (ret == 0) { @@ -80924,12 +80928,12 @@ static int myCryptoDevCb(int devIdArg, wc_CryptoInfo* info, void* ctx) printf("CryptoDevCb: exampleVar %d\n", myCtx->exampleVar); #endif if (myCtx->exampleVar == 99) { + info->pk.curve448kg.key->devId = devIdArg; /* the dispatcher must hand over a usable payload */ if ((info->pk.curve448kg.rng == NULL) || (info->pk.curve448kg.size != CURVE448_KEY_SIZE)) { return BAD_FUNC_ARG; } - info->pk.curve448kg.key->devId = devIdArg; /* deterministic key material so the caller can prove the * callback's output actually reached it */ XMEMSET(info->pk.curve448kg.key->k, 0x5A, CURVE448_KEY_SIZE); From 4feab6c6f050c5ad5e929496aabc06c107ff9e18 Mon Sep 17 00:00:00 2001 From: night1rider Date: Thu, 20 Aug 2026 08:25:56 -0600 Subject: [PATCH 4/7] Only offload a key-owned X448 private scalar to that key's own device wc_curve448_make_key and wc_curve448_export_public_ex derived the public point through the keyless wc_curve448_make_pub, which dispatches to the first registered device; route them through a devId-carrying helper. --- tests/api/test_curve448.c | 28 +++++++++++++++ wolfcrypt/src/cryptocb.c | 20 ++++++----- wolfcrypt/src/curve448.c | 66 +++++++++++++++++++++++++++++------- wolfssl/wolfcrypt/cryptocb.h | 8 ++--- 4 files changed, 98 insertions(+), 24 deletions(-) diff --git a/tests/api/test_curve448.c b/tests/api/test_curve448.c index 83fd0f8c371..1f9fde86ca3 100644 --- a/tests/api/test_curve448.c +++ b/tests/api/test_curve448.c @@ -953,6 +953,7 @@ int test_wc_curve448_make_pub_generic(void) typedef struct curve448SpyCtx { int kgSeen; int ssSeen; + int mpSeen; int decline; int forceErr; int zeroSecret; @@ -984,6 +985,10 @@ static int curve448_test_crypto_cb(int devIdArg, wc_CryptoInfo* info, void* ctx) info->pk.curve448kg.size, info->pk.curve448kg.key); info->pk.curve448kg.key->devId = save; } + if (info->pk.type == WC_PK_TYPE_CURVE448_MAKE_PUB) { + /* count, then decline so the software path produces the point */ + spy->mpSeen++; + } if (info->pk.type == WC_PK_TYPE_CURVE448) { int save = info->pk.curve448.private_key->devId; spy->ssSeen++; @@ -1022,6 +1027,7 @@ int test_wc_curve448_cryptocb(void) curve448_key keyB; byte ssAB[CURVE448_PUB_KEY_SIZE]; byte ssBA[CURVE448_PUB_KEY_SIZE]; + byte pubTmp[CURVE448_PUB_KEY_SIZE]; word32 ssABLen = (word32)sizeof(ssAB); word32 ssBALen = (word32)sizeof(ssBA); #ifndef WC_NO_CONSTRUCTORS @@ -1100,6 +1106,28 @@ int test_wc_curve448_cryptocb(void) spy.zeroSecret = 0; #endif +#if !defined(WOLF_CRYPTO_CB_FIND) && !defined(WOLF_CRYPTO_CB_ONLY_CURVE448) + /* a key bound to no device must not have its private scalar handed to + * whichever device happens to be registered: keygen derives the public + * point in software without dispatching make_pub */ + { + curve448_key unbound; + int mpBefore = spy.mpSeen; + + XMEMSET(&unbound, 0, sizeof(unbound)); + ExpectIntEQ(wc_curve448_init(&unbound), 0); + ExpectIntEQ(wc_curve448_make_key(&rng, CURVE448_KEY_SIZE, &unbound), + 0); + ExpectIntEQ(spy.mpSeen, mpBefore); + /* the keyless public API has no devId to respect, so it still + * reaches the device */ + ExpectIntEQ(wc_curve448_make_pub((int)sizeof(pubTmp), pubTmp, + (int)sizeof(unbound.k), unbound.k), 0); + ExpectIntGT(spy.mpSeen, mpBefore); + wc_curve448_free(&unbound); + } +#endif + /* constructor arg checks */ ExpectIntEQ(wc_curve448_init_ex(NULL, HEAP_HINT, devId), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); diff --git a/wolfcrypt/src/cryptocb.c b/wolfcrypt/src/cryptocb.c index dff517d8773..7bbd8494196 100644 --- a/wolfcrypt/src/cryptocb.c +++ b/wolfcrypt/src/cryptocb.c @@ -1495,7 +1495,7 @@ int wc_CryptoCb_Curve448(curve448_key* private_key, return wc_CryptoCb_TranslateErrorCode(ret); } -int wc_CryptoCb_Curve448MakePub(int public_size, byte* pub, +int wc_CryptoCb_Curve448MakePub(int devId, int public_size, byte* pub, int private_size, const byte* priv) { int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE); @@ -1504,9 +1504,11 @@ int wc_CryptoCb_Curve448MakePub(int public_size, byte* pub, if (pub == NULL || priv == NULL) return ret; - /* try the find callback first, else grab the first registered device */ - dev = wc_CryptoCb_FindDevice(INVALID_DEVID, WC_ALGO_TYPE_PK); - if (dev == NULL || dev->cb == NULL) + /* locate registered callback */ + dev = wc_CryptoCb_FindDevice(devId, WC_ALGO_TYPE_PK); + /* only a caller that selected no device settles for the first registered + * one; a devId names the single device allowed to see the scalar */ + if ((dev == NULL || dev->cb == NULL) && (devId == INVALID_DEVID)) dev = wc_CryptoCb_FindDeviceByIndex(0); if (dev && dev->cb) { wc_CryptoInfo cryptoInfo; @@ -1524,7 +1526,7 @@ int wc_CryptoCb_Curve448MakePub(int public_size, byte* pub, return wc_CryptoCb_TranslateErrorCode(ret); } -int wc_CryptoCb_Curve448Generic(int public_size, byte* pub, +int wc_CryptoCb_Curve448Generic(int devId, int public_size, byte* pub, int private_size, const byte* priv, int basepoint_size, const byte* basepoint) { @@ -1534,9 +1536,11 @@ int wc_CryptoCb_Curve448Generic(int public_size, byte* pub, if (pub == NULL || priv == NULL || basepoint == NULL) return ret; - /* try the find callback first, else grab the first registered device */ - dev = wc_CryptoCb_FindDevice(INVALID_DEVID, WC_ALGO_TYPE_PK); - if (dev == NULL || dev->cb == NULL) + /* locate registered callback */ + dev = wc_CryptoCb_FindDevice(devId, WC_ALGO_TYPE_PK); + /* only a caller that selected no device settles for the first registered + * one; a devId names the single device allowed to see the scalar */ + if ((dev == NULL || dev->cb == NULL) && (devId == INVALID_DEVID)) dev = wc_CryptoCb_FindDeviceByIndex(0); if (dev && dev->cb) { wc_CryptoInfo cryptoInfo; diff --git a/wolfcrypt/src/curve448.c b/wolfcrypt/src/curve448.c index 058b7f44cc5..35890f262f8 100644 --- a/wolfcrypt/src/curve448.c +++ b/wolfcrypt/src/curve448.c @@ -65,8 +65,18 @@ static WC_INLINE int curve448_priv_clamp_check(const byte* priv) return ret; } -int wc_curve448_make_pub(int public_size, byte* pub, int private_size, - const byte* priv) +/* Compute pub = priv * basepoint(5). + * + * devId [in] Device to offload to, INVALID_DEVID for the caller's choice. + * cbOk [in] Whether the private scalar may be offered to a crypto + * callback at all. The keyless public API sets this, since it + * has no key to take a devId from; a key-owned scalar only sets + * it when the key is actually bound to a device, so an unbound + * key is never offloaded to whichever device happens to be + * registered first. + */ +static int curve448_make_pub_ex(int public_size, byte* pub, int private_size, + const byte* priv, int devId, int cbOk) { int ret; #ifndef WOLF_CRYPTO_CB_ONLY_CURVE448 @@ -87,10 +97,16 @@ int wc_curve448_make_pub(int public_size, byte* pub, int private_size, return ret; #ifdef WOLF_CRYPTO_CB - ret = wc_CryptoCb_Curve448MakePub(public_size, pub, private_size, priv); - if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) - return ret; - /* fall-through when unavailable */ + if (cbOk) { + ret = wc_CryptoCb_Curve448MakePub(devId, public_size, pub, + private_size, priv); + if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) + return ret; + /* fall-through when unavailable */ + } +#else + (void)devId; + (void)cbOk; #endif #ifdef WOLF_CRYPTO_CB_ONLY_CURVE448 @@ -105,6 +121,33 @@ int wc_curve448_make_pub(int public_size, byte* pub, int private_size, return ret; } +/* Derive a key's public point from its own private scalar. */ +static int curve448_key_make_pub(curve448_key* key) +{ +#ifdef WOLF_CRYPTO_CB + #ifdef WOLF_CRYPTO_CB_FIND + /* the find callback gets to route unbound keys */ + const int cbOk = 1; + #else + const int cbOk = (key->devId != INVALID_DEVID); + #endif + + return curve448_make_pub_ex((int)sizeof(key->p), key->p, + (int)sizeof(key->k), key->k, key->devId, cbOk); +#else + return curve448_make_pub_ex((int)sizeof(key->p), key->p, + (int)sizeof(key->k), key->k, INVALID_DEVID, 0); +#endif +} + +int wc_curve448_make_pub(int public_size, byte* pub, int private_size, + const byte* priv) +{ + /* no key, so no device was selected: any registered one may serve it */ + return curve448_make_pub_ex(public_size, pub, private_size, priv, + INVALID_DEVID, 1); +} + /* Is every byte of the curve448 result zero? Only reached when the caller's * basepoint is of small order, which leaks the result to anyone watching. */ #ifndef WOLFSSL_NO_ECDHX_SHARED_ZERO_CHECK @@ -146,8 +189,9 @@ int wc_curve448_generic(int public_size, byte* pub, return ret; #ifdef WOLF_CRYPTO_CB - ret = wc_CryptoCb_Curve448Generic(public_size, pub, private_size, priv, - basepoint_size, basepoint); + /* no key, so no device was selected: any registered one may serve it */ + ret = wc_CryptoCb_Curve448Generic(INVALID_DEVID, public_size, pub, + private_size, priv, basepoint_size, basepoint); if (ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) { #ifndef WOLFSSL_NO_ECDHX_SHARED_ZERO_CHECK /* RFC 7748: reject an all-zero result from the callback too */ @@ -232,8 +276,7 @@ int wc_curve448_make_key(WC_RNG* rng, int keysize, curve448_key* key) key->k[CURVE448_KEY_SIZE-1] |= 0x80; /* compute public */ - ret = wc_curve448_make_pub((int)sizeof(key->p), key->p, - (int)sizeof(key->k), key->k); + ret = curve448_key_make_pub(key); if (ret == 0) { key->pubSet = 1; } @@ -435,8 +478,7 @@ int wc_curve448_export_public_ex(curve448_key* key, byte* out, word32* outLen, if (ret == 0) { /* calculate public if missing */ if (!key->pubSet) { - ret = wc_curve448_make_pub((int)sizeof(key->p), key->p, - (int)sizeof(key->k), key->k); + ret = curve448_key_make_pub(key); key->pubSet = (ret == 0); } } diff --git a/wolfssl/wolfcrypt/cryptocb.h b/wolfssl/wolfcrypt/cryptocb.h index 615bb210891..06460584f5c 100644 --- a/wolfssl/wolfcrypt/cryptocb.h +++ b/wolfssl/wolfcrypt/cryptocb.h @@ -989,10 +989,10 @@ WOLFSSL_LOCAL int wc_CryptoCb_Curve448Gen(WC_RNG* rng, int keySize, curve448_key* key); WOLFSSL_LOCAL int wc_CryptoCb_Curve448(curve448_key* private_key, curve448_key* public_key, byte* out, word32* outlen, int endian); -WOLFSSL_LOCAL int wc_CryptoCb_Curve448MakePub(int public_size, byte* pub, - int private_size, const byte* priv); -WOLFSSL_LOCAL int wc_CryptoCb_Curve448Generic(int public_size, byte* pub, - int private_size, const byte* priv, int basepoint_size, +WOLFSSL_LOCAL int wc_CryptoCb_Curve448MakePub(int devId, int public_size, + byte* pub, int private_size, const byte* priv); +WOLFSSL_LOCAL int wc_CryptoCb_Curve448Generic(int devId, int public_size, + byte* pub, int private_size, const byte* priv, int basepoint_size, const byte* basepoint); #endif /* HAVE_CURVE448 */ From bfeebed370d1c00c3170332eee39973de44ee61e Mon Sep 17 00:00:00 2001 From: night1rider Date: Thu, 20 Aug 2026 10:29:07 -0600 Subject: [PATCH 5/7] Fix curve448_key_make_pub being unused under WOLF_CRYPTO_CB_ONLY_CURVE448 with key export disabled --- wolfcrypt/src/curve448.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/wolfcrypt/src/curve448.c b/wolfcrypt/src/curve448.c index 35890f262f8..b54530f059f 100644 --- a/wolfcrypt/src/curve448.c +++ b/wolfcrypt/src/curve448.c @@ -121,7 +121,14 @@ static int curve448_make_pub_ex(int public_size, byte* pub, int private_size, return ret; } -/* Derive a key's public point from its own private scalar. */ +/* Derive a key's public point from its own private scalar. + * + * Only wc_curve448_make_key() and the public key export use this, so it is + * unreferenced when the software key generation is stripped out and export is + * disabled. + */ +#if !defined(WOLF_CRYPTO_CB_ONLY_CURVE448) || \ + defined(HAVE_CURVE448_KEY_EXPORT) static int curve448_key_make_pub(curve448_key* key) { #ifdef WOLF_CRYPTO_CB @@ -139,6 +146,7 @@ static int curve448_key_make_pub(curve448_key* key) (int)sizeof(key->k), key->k, INVALID_DEVID, 0); #endif } +#endif /* !WOLF_CRYPTO_CB_ONLY_CURVE448 || HAVE_CURVE448_KEY_EXPORT */ int wc_curve448_make_pub(int public_size, byte* pub, int private_size, const byte* priv) From 6999515983a4d26cbec11881efa17ca85569ed9c Mon Sep 17 00:00:00 2001 From: night1rider Date: Thu, 20 Aug 2026 12:16:58 -0600 Subject: [PATCH 6/7] Fix curve448 test guards for CB_FIND and zero-check builds, cover cb zero result --- tests/api/test_curve448.c | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/tests/api/test_curve448.c b/tests/api/test_curve448.c index 1f9fde86ca3..6827732c8bd 100644 --- a/tests/api/test_curve448.c +++ b/tests/api/test_curve448.c @@ -886,6 +886,7 @@ int test_wc_curve448_make_pub_generic(void) EC448_LITTLE_ENDIAN), 0); ExpectBufEQ(ssBA, ssAB, CURVE448_PUB_KEY_SIZE); +#ifndef WOLFSSL_NO_ECDHX_SHARED_ZERO_CHECK /* an all-zero result (small-order basepoint) must be rejected, matching * wc_curve448_shared_secret_ex */ XMEMSET(pubG, 0, sizeof(pubG)); @@ -896,6 +897,7 @@ int test_wc_curve448_make_pub_generic(void) (int)sizeof(keyA.k), keyA.k, (int)sizeof(baseZero), baseZero), WC_NO_ERR_TRACE(ECC_OUT_OF_RANGE_E)); } +#endif /* argument checks on the new generic API */ ExpectIntEQ(wc_curve448_generic((int)sizeof(pubG), NULL, @@ -954,6 +956,7 @@ typedef struct curve448SpyCtx { int kgSeen; int ssSeen; int mpSeen; + int genSeen; int decline; int forceErr; int zeroSecret; @@ -989,6 +992,16 @@ static int curve448_test_crypto_cb(int devIdArg, wc_CryptoInfo* info, void* ctx) /* count, then decline so the software path produces the point */ spy->mpSeen++; } + if (info->pk.type == WC_PK_TYPE_CURVE448_GENERIC) { + spy->genSeen++; + if (spy->zeroSecret) { + /* misbehaving device: all-zero point with success rc */ + XMEMSET(info->pk.curve448generic.pub, 0, + CURVE448_PUB_KEY_SIZE); + return 0; + } + /* otherwise decline so the software path produces the point */ + } if (info->pk.type == WC_PK_TYPE_CURVE448) { int save = info->pk.curve448.private_key->devId; spy->ssSeen++; @@ -1027,7 +1040,6 @@ int test_wc_curve448_cryptocb(void) curve448_key keyB; byte ssAB[CURVE448_PUB_KEY_SIZE]; byte ssBA[CURVE448_PUB_KEY_SIZE]; - byte pubTmp[CURVE448_PUB_KEY_SIZE]; word32 ssABLen = (word32)sizeof(ssAB); word32 ssBALen = (word32)sizeof(ssBA); #ifndef WC_NO_CONSTRUCTORS @@ -1112,6 +1124,7 @@ int test_wc_curve448_cryptocb(void) * point in software without dispatching make_pub */ { curve448_key unbound; + byte pubTmp[CURVE448_PUB_KEY_SIZE]; int mpBefore = spy.mpSeen; XMEMSET(&unbound, 0, sizeof(unbound)); @@ -1124,6 +1137,26 @@ int test_wc_curve448_cryptocb(void) ExpectIntEQ(wc_curve448_make_pub((int)sizeof(pubTmp), pubTmp, (int)sizeof(unbound.k), unbound.k), 0); ExpectIntGT(spy.mpSeen, mpBefore); + + /* wc_curve448_generic also has no devId to respect, so it reaches + * the device; a device that answers with an all-zero point must be + * rejected just like the software path's small-order result */ + { + const byte base5[CURVE448_KEY_SIZE] = { 5 }; + int genBefore = spy.genSeen; + + ExpectIntEQ(wc_curve448_generic((int)sizeof(pubTmp), pubTmp, + (int)sizeof(unbound.k), unbound.k, (int)sizeof(base5), + base5), 0); + ExpectIntGT(spy.genSeen, genBefore); +#ifndef WOLFSSL_NO_ECDHX_SHARED_ZERO_CHECK + spy.zeroSecret = 1; + ExpectIntEQ(wc_curve448_generic((int)sizeof(pubTmp), pubTmp, + (int)sizeof(unbound.k), unbound.k, (int)sizeof(base5), + base5), WC_NO_ERR_TRACE(ECC_OUT_OF_RANGE_E)); + spy.zeroSecret = 0; +#endif + } wc_curve448_free(&unbound); } #endif From 3fd41c3d734c8219cfbd15374eff2ccd0b7a1f27 Mon Sep 17 00:00:00 2001 From: night1rider Date: Thu, 20 Aug 2026 13:53:20 -0600 Subject: [PATCH 7/7] Guard curve448 shared secret software path on ret so the cryptocb reset is read --- wolfcrypt/src/curve448.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/wolfcrypt/src/curve448.c b/wolfcrypt/src/curve448.c index b54530f059f..c318935ef95 100644 --- a/wolfcrypt/src/curve448.c +++ b/wolfcrypt/src/curve448.c @@ -384,7 +384,9 @@ int wc_curve448_shared_secret_ex(curve448_key* private_key, #ifdef WOLF_CRYPTO_CB_ONLY_CURVE448 /* software path stripped; callback is the only provider */ - ret = NO_VALID_DEVID; + if (ret == 0) { + ret = NO_VALID_DEVID; + } #else #ifdef WOLFSSL_CHECK_MEM_ZERO /* Register the buffer after the early returns so every later path @@ -393,9 +395,9 @@ int wc_curve448_shared_secret_ex(curve448_key* private_key, wc_MemZero_Add("wc_curve448_shared_secret_ex o", o, CURVE448_PUB_KEY_SIZE); #endif - /* ret is 0 here: the argument checks return early and the cryptocb block - * either returns or resets it. */ - ret = curve448(o, private_key->k, public_key->p); + if (ret == 0) { + ret = curve448(o, private_key->k, public_key->p); + } #ifndef WOLFSSL_NO_ECDHX_SHARED_ZERO_CHECK if (ret == 0) { byte t = 0;