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.
pull/11209/head
night1rider 2026-08-20 03:19:14 -06:00
parent 3d7f7ad4d6
commit eb1cde51ab
4 changed files with 122 additions and 10 deletions

View File

@ -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

View File

@ -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),

View File

@ -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;

View File

@ -80424,6 +80424,17 @@ exit_onlycb:
#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)
@ -80451,11 +80462,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) */
@ -80477,18 +80491,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) */
@ -81186,7 +81206,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
@ -81229,6 +81263,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