1
0
mirror of https://github.com/wolfSSL/wolfssl.git synced 2026-10-24 04:09:07 +03:00
Files
wolfssl/tests/unit-mcdc/test_ecc_whitebox.c

3885 lines
152 KiB
C

/* test_ecc_whitebox.c
*
* White-box MC/DC supplement for wolfcrypt/src/ecc.c.
*
* The tests/api ECC suite (test_ecc.c, including its MC/DC-focused
* test_wc_EccDecisionCoverage) drives ecc.c through its *public* API. A
* handful of decision conditions live in file-static helpers whose "bad"
* operand combinations are rejected by every public caller *before* the
* helper runs (the caller hard-codes a value, or pre-validates the same
* condition), so those combinations can never be exercised from the API
* without modifying library source. This translation unit reaches them by
* compiling ecc.c directly (#include) and calling the helpers with both
* halves of each MC/DC independence pair.
*
* Coverage from this binary is unioned with the tests/api variant coverage by
* source line:col in the per-module suite:
* llvm-cov computes MC/DC independence PER BINARY, and the
* aggregate.sh ORs the "independence shown" bit across binaries by key. That
* is why every pair below is completed *within this file* rather than
* relying on the API tests to supply the other half.
*
* Build: compiled by the coverage runner's white-box step with the SAME MC/DC CFLAGS,
* -DHAVE_CONFIG_H and -I<workspace> as the instrumented library, then linked
* against that variant's libwolfssl.a with its ecc.o removed (this TU
* supplies the instrumented ecc.c). NOT part of the wolfSSL build; not
* registered in tests/api. See tests/unit-mcdc/README.md.
*
* Targeted residuals (ecc.c), by class:
* Class 1 wc_ecc_curve_load() dp/pCurve NULL guard ............. 2 conditions
* Class 2 _ecc_import_private_key_ex() key/priv NULL guard ...... 2 conditions
* Class 3 ecc_ctx_set_salt() ctx/flags==0 guard ................. 2 conditions
* Class 4 wc_ecc_ctx_get_own_salt() ctx->protocol==0 half ....... 1 condition
* Class 5 wc_ecc_ctx_set_peer_salt() ctx->protocol==0 half ...... 1 condition
* Class 6 wc_ecc_ctx_set_own_salt() ctx->protocol==0 half ....... 1 condition
* These are the only ecc.c gaps confirmed structurally unreachable through
* any public wrapper (every wrapper either hard-codes the "safe" side of the
* static helper's own re-check, or -- for the ecEncCtx cases -- there is no
* public constructor that leaves ctx->protocol == 0 on a live, non-NULL
* context). See RESIDUALS.md for everything else.
*/
/* ecc.c refuses the AES-GCM ECIES DEM in the default IV mode unless one of
* WOLFSSL_ECIES_OLD / _GEN_IV / _STATIC_GCM_NONCE is set, so without an opt-in
* every GCM call returns NOT_COMPILED_IN before reaching ecc_is_gcm's callers
* (15809 / 16157) and their operands stay unreachable in every variant. This
* TU compiles its OWN instrumented copy of ecc.c and links against the library
* with ecc.o removed, so the opt-in is local to the white-box binary: it does
* not change the library under test, and it touches nothing outside ecc.c
* (the macro appears in no header, so no shared type or layout moves). The
* suite unions MC/DC per source line, which is exactly how a white-box is
* meant to add rows the native variants cannot produce. */
#ifndef WOLFSSL_ECIES_STATIC_GCM_NONCE
#define WOLFSSL_ECIES_STATIC_GCM_NONCE
#endif
/* Pull ecc.c in verbatim so the file-static helpers below are in scope and
* instrumented in THIS binary. ecc.c includes settings.h (which picks up
* user_settings.h via -DWOLFSSL_USER_SETTINGS) and ecc.h itself. */
#include <wolfcrypt/src/ecc.c>
#include <stdio.h>
#ifndef INVALID_DEVID
#define INVALID_DEVID (-2)
#endif
static int wb_fail = 0;
#define WB_NOTE(msg) do { printf(" [wb] %s\n", (msg)); } while (0)
#if defined(HAVE_ECC) && !defined(WOLF_CRYPTO_CB_ONLY_ECC)
/* ------------------------------------------------------------------------- *
* Class 1: wc_ecc_curve_load() dp/pCurve NULL guard (line ~1772).
*
* if (dp == NULL || pCurve == NULL)
*
* Every public caller (wc_ecc_set_curve, wc_ecc_make_key_ex, ...) looks up a
* non-NULL ecc_set_type* from the static ecc_sets[] table and always passes
* the address of a live ecc_curve_spec* local, so neither operand's TRUE
* side is reachable from the API.
* ------------------------------------------------------------------------- */
static void wb_curve_load(void)
{
/* Without ECC_CACHE_CURVE (our variants don't define it), wc_ecc_
* curve_load()'s *pCurve is NOT an out-only "give me a fresh one"
* slot -- the DECLARE_CURVE_SPECS() macro (used by every real caller)
* pre-allocates a real ecc_curve_spec on the caller's stack and passes
* its address; wc_ecc_curve_load() only fills it in. A bare NULL
* ecc_curve_spec* (valid ONLY under ECC_CACHE_CURVE, where the second
* branch of DECLARE_CURVE_SPECS lets the callee allocate/cache it) hits
* "curve = *pCurve; curve->dp != dp" on a NULL curve and crashes. Match
* the real DECLARE_CURVE_SPECS(1) shape here for the all-false call.
*/
ecc_curve_spec* curveNull = NULL;
int ret;
ret = wc_ecc_curve_load(NULL, &curveNull, ECC_CURVE_FIELD_ALL);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
WB_NOTE("wc_ecc_curve_load(dp=NULL) unexpected return");
wb_fail = 1;
}
ret = wc_ecc_curve_load(&ecc_sets[0], NULL, ECC_CURVE_FIELD_ALL);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
WB_NOTE("wc_ecc_curve_load(pCurve=NULL) unexpected return");
wb_fail = 1;
}
/* all-false: real dp + a properly pre-allocated pCurve slot (already
* exercised by every public caller via DECLARE_CURVE_SPECS(), repeated
* here so the independence PAIR -- not just each TRUE half -- lives in
* this binary too). */
{
/* ECC_CURVE_FIELD_ALL loads all six fields (prime/Af/Bf/order/Gx/
* Gy), matching DECLARE_CURVE_SPECS(ECC_CURVE_FIELD_COUNT) -- an
* undersized spec_ints (e.g. count 1) makes wc_ecc_curve_load()
* overrun it while loading the later fields. */
DECLARE_CURVE_SPECS(ECC_CURVE_FIELD_COUNT);
int allocErr = MP_OKAY;
ALLOC_CURVE_SPECS(ECC_CURVE_FIELD_COUNT, allocErr);
if (allocErr == MP_OKAY) {
ret = wc_ecc_curve_load(&ecc_sets[0], &curve, ECC_CURVE_FIELD_ALL);
if (ret != 0) {
WB_NOTE("wc_ecc_curve_load(all-false) unexpected return");
wb_fail = 1;
}
wc_ecc_curve_free(curve);
}
else {
WB_NOTE("ALLOC_CURVE_SPECS failed (all-false case skipped)");
}
FREE_CURVE_SPECS();
}
WB_NOTE("wc_ecc_curve_load dp/pCurve NULL guard pairs exercised");
}
/* ------------------------------------------------------------------------- *
* Class 2: _ecc_import_private_key_ex() key/priv NULL guard (line ~11671).
*
* if (key == NULL || priv == NULL)
*
* wc_ecc_import_private_key_ex() (the public wrapper) performs the identical
* check itself before ever reaching the static, so both operands' TRUE side
* inside the static are white-box only. The all-false ("both valid") side
* is exercised elsewhere by tests/api's test_wc_ecc_import_private_key --
* but that is a DIFFERENT binary, and llvm-cov computes MC/DC independence
* per binary, so this function also drives one real (all-false) call
* itself to complete both operands' pairs within this binary.
* ------------------------------------------------------------------------- */
static void wb_import_private_key_ex(void)
{
byte priv[32];
ecc_key key;
int ret;
XMEMSET(priv, 0x5A, sizeof(priv));
XMEMSET(&key, 0, sizeof(key));
ret = _ecc_import_private_key_ex(priv, sizeof(priv), NULL, 0, NULL,
ECC_CURVE_DEF);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
WB_NOTE("_ecc_import_private_key_ex(key=NULL) unexpected return");
wb_fail = 1;
}
ret = _ecc_import_private_key_ex(NULL, 0, NULL, 0, &key, ECC_CURVE_DEF);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
WB_NOTE("_ecc_import_private_key_ex(priv=NULL) unexpected return");
wb_fail = 1;
}
/* all-false: real key + real priv (pub left NULL -- private-only
* import), a small but nonzero/in-range scalar (k=1) so wc_ecc_
* set_curve()'s size check and the rest of the import succeed. */
if (wc_ecc_init(&key) == 0) {
byte k1[32];
XMEMSET(k1, 0, sizeof(k1));
k1[sizeof(k1) - 1] = 1;
ret = _ecc_import_private_key_ex(k1, sizeof(k1), NULL, 0, &key,
ECC_SECP256R1);
if (ret != 0) {
WB_NOTE("_ecc_import_private_key_ex(all-false) unexpected return");
wb_fail = 1;
}
wc_ecc_free(&key);
}
WB_NOTE("_ecc_import_private_key_ex key/priv NULL guard pairs exercised");
}
#ifdef HAVE_ECC_ENCRYPT
/* ------------------------------------------------------------------------- *
* Class 3: ecc_ctx_set_salt() ctx/flags==0 guard (line ~14665).
*
* if (ctx == NULL || flags == 0)
*
* Both public callers (wc_ecc_ctx_set_own_salt via REQ_RESP_CLIENT/SERVER,
* ecc_ctx_init) always pass a live ctx and a hard-coded nonzero flags
* (REQ_RESP_CLIENT/REQ_RESP_SERVER), so flags==0 can never be observed from
* the API; ctx==NULL is likewise never forwarded by any caller (they all
* either early-return on their own NULL check or pass &localCtx).
*
* Classes 4-6: ecEncCtx.protocol == 0 halves of the get_own_salt /
* set_peer_salt / set_own_salt guards (lines ~14506, ~14554, ~14646). The
* only public constructor, wc_ecc_ctx_new()/wc_ecc_ctx_new_ex(), always sets
* ctx->protocol to REQ_RESP_CLIENT or REQ_RESP_SERVER (or fails and frees the
* ctx), so a live ctx with protocol==0 does not exist on any API path. Build
* one directly here since ecEncCtx's full definition is only visible inside
* this TU (it is an opaque forward-declared type in ecc.h).
* ------------------------------------------------------------------------- */
static void wb_ctx_set_salt(void)
{
ecEncCtx ctx;
WC_RNG rng;
int ret;
XMEMSET(&ctx, 0, sizeof(ctx));
if (wc_InitRng(&rng) != 0) {
WB_NOTE("wc_InitRng failed (ecc_ctx_set_salt skipped)");
wb_fail = 1;
return;
}
ctx.rng = &rng;
ret = ecc_ctx_set_salt(NULL, REQ_RESP_CLIENT);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
WB_NOTE("ecc_ctx_set_salt(ctx=NULL) unexpected return");
wb_fail = 1;
}
ret = ecc_ctx_set_salt(&ctx, 0);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
WB_NOTE("ecc_ctx_set_salt(flags=0) unexpected return");
wb_fail = 1;
}
/* all-false: real ctx + real flags value. */
ret = ecc_ctx_set_salt(&ctx, REQ_RESP_CLIENT);
if (ret != 0) {
WB_NOTE("ecc_ctx_set_salt(all-false) unexpected return");
wb_fail = 1;
}
(void)wc_FreeRng(&rng);
WB_NOTE("ecc_ctx_set_salt ctx/flags==0 guard pairs exercised");
}
/* llvm-cov computes MC/DC independence PER BINARY: this whitebox TU is the
* ONLY place wc_ecc_ctx_set_own_salt() is ever called at all (the tests/api
* suite only exercises wc_ecc_ctx_set_peer_salt()), so every operand of
* both functions' 3-operand OR guard --
* if (ctx == NULL || ctx->protocol == 0 || salt == NULL)
* -- must get its full independence pair (toggle one operand, hold the
* other two fixed at their "continue" value) from calls made HERE; a TRUE
* shown in one binary and a FALSE shown in another do not combine. */
static void wb_ctx_protocol_zero(void)
{
ecEncCtx zeroCtx; /* protocol left at 0 by XMEMSET */
ecEncCtx liveCtx;
WC_RNG rng;
byte salt[EXCHANGE_SALT_SZ];
XMEMSET(&zeroCtx, 0, sizeof(zeroCtx));
XMEMSET(salt, 0x11, sizeof(salt));
/* wc_ecc_ctx_get_own_salt: protocol==0 TRUE half (line ~14506). The
* NULL/valid halves are already shown by the tests/api suite. */
if (wc_ecc_ctx_get_own_salt(&zeroCtx) != NULL) {
WB_NOTE("wc_ecc_ctx_get_own_salt(protocol=0) unexpected non-NULL");
wb_fail = 1;
}
if (wc_InitRng(&rng) != 0) {
WB_NOTE("wc_InitRng failed (protocol guard pairs skipped)");
return;
}
ecc_ctx_init(&liveCtx, REQ_RESP_CLIENT, &rng);
/* wc_ecc_ctx_get_own_salt: protocol==0 FALSE half (live ctx, real
* protocol) -- completes the independence pair for that operand
* within this binary (the TRUE half was zeroCtx above). */
if (wc_ecc_ctx_get_own_salt(&liveCtx) == NULL) {
WB_NOTE("wc_ecc_ctx_get_own_salt(live) unexpected NULL");
wb_fail = 1;
}
/* ---- wc_ecc_ctx_set_peer_salt (line ~14554): ctx==NULL and salt==NULL
* halves are already shown by tests/api's test_wc_ecc_ctx_set_peer_salt
* (same pattern, different binary -- doesn't count here); supply ALL
* THREE operands' pairs in this one binary too so this binary's own
* MC/DC is complete independent of what the API binary shows. */
if (wc_ecc_ctx_set_peer_salt(NULL, salt) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
WB_NOTE("wc_ecc_ctx_set_peer_salt(ctx=NULL) unexpected return");
wb_fail = 1;
}
if (wc_ecc_ctx_set_peer_salt(&zeroCtx, salt) !=
WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
WB_NOTE("wc_ecc_ctx_set_peer_salt(protocol=0) unexpected return");
wb_fail = 1;
}
if (wc_ecc_ctx_set_peer_salt(&liveCtx, NULL) !=
WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
WB_NOTE("wc_ecc_ctx_set_peer_salt(salt=NULL) unexpected return");
wb_fail = 1;
}
/* all-false companion: real ctx (protocol != 0), real salt. */
(void)wc_ecc_ctx_set_peer_salt(&liveCtx, salt);
/* ---- wc_ecc_ctx_set_own_salt (line ~14646): never called anywhere
* else, so needs its complete independence set here. */
if (wc_ecc_ctx_set_own_salt(NULL, salt, sizeof(salt)) !=
WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
WB_NOTE("wc_ecc_ctx_set_own_salt(ctx=NULL) unexpected return");
wb_fail = 1;
}
if (wc_ecc_ctx_set_own_salt(&zeroCtx, salt, sizeof(salt)) !=
WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
WB_NOTE("wc_ecc_ctx_set_own_salt(protocol=0) unexpected return");
wb_fail = 1;
}
if (wc_ecc_ctx_set_own_salt(&liveCtx, NULL, 0) !=
WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
WB_NOTE("wc_ecc_ctx_set_own_salt(salt=NULL) unexpected return");
wb_fail = 1;
}
/* all-false companion: real ctx (protocol != 0), real salt. */
(void)wc_ecc_ctx_set_own_salt(&liveCtx, salt, sizeof(salt));
(void)wc_FreeRng(&rng);
WB_NOTE("ecEncCtx protocol==0 guard halves exercised");
}
#else
static void wb_ctx_set_salt(void) { WB_NOTE("HAVE_ECC_ENCRYPT off; skipped"); }
static void wb_ctx_protocol_zero(void)
{
WB_NOTE("HAVE_ECC_ENCRYPT off; skipped");
}
#endif /* HAVE_ECC_ENCRYPT */
/* ------------------------------------------------------------------------- *
* Class 7: wc_ecc_is_valid_idx() "n < x" independence (line ~4335).
*
* if ((n >= ECC_CUSTOM_IDX) && (n < x)) { return 1; }
*
* Every public caller passes either a real ecc_sets[] index (n < x, always
* true) or a value already rejected by the earlier "n >= ECC_SET_COUNT"
* guard, so the "n < x" operand's FALSE half (n >= ECC_CUSTOM_IDX true, but
* n not less than the live table size x) is never observed. n == x (the
* table's own terminator slot) is the smallest value that is still
* ECC_CUSTOM_IDX and still under ECC_SET_COUNT.
* ------------------------------------------------------------------------- */
static void wb_is_valid_idx_n_lt_x(void)
{
int x;
for (x = 0; ecc_sets[x].size != 0; x++) { }
if (wc_ecc_is_valid_idx(x) != 0) {
WB_NOTE("wc_ecc_is_valid_idx(terminator idx) unexpected valid");
wb_fail = 1;
}
WB_NOTE("wc_ecc_is_valid_idx n<x independence exercised");
}
/* ------------------------------------------------------------------------- *
* Class 8: wc_ecc_cmp_param() param/curveParam NULL guard (line ~4460).
*
* if (param == NULL || curveParam == NULL) return BAD_FUNC_ARG;
*
* Both public callers always pass live ecc_sets[] hex strings and a live
* caller-supplied buffer, so neither NULL half is reachable from the API.
* File-static: only callable because this TU #includes ecc.c.
* ------------------------------------------------------------------------- */
static void wb_cmp_param_null(void)
{
byte param[4] = { 1, 2, 3, 4 };
int ret;
ret = wc_ecc_cmp_param(NULL, param, sizeof(param), WC_TYPE_UNSIGNED_BIN);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
WB_NOTE("wc_ecc_cmp_param(curveParam=NULL) unexpected return");
wb_fail = 1;
}
ret = wc_ecc_cmp_param("01020304", NULL, 0, WC_TYPE_UNSIGNED_BIN);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
WB_NOTE("wc_ecc_cmp_param(param=NULL) unexpected return");
wb_fail = 1;
}
/* all-false companion, in this binary. */
(void)wc_ecc_cmp_param("01020304", param, sizeof(param),
WC_TYPE_UNSIGNED_BIN);
WB_NOTE("wc_ecc_cmp_param NULL guard pairs exercised");
}
/* Convert a curve hex-string field to its raw big-endian bytes, using the
* same mp_read_radix()/mp_to_unsigned_bin() the library itself uses -- so
* the bytes compare equal (via wc_ecc_cmp_param) to the source hex string
* regardless of any leading-zero-nibble width difference. */
#define WB_MAXFIELD 100 /* > any ecc_sets[] field at P-521 (66 bytes) */
static int wb_hex_to_bin(const char* hex, byte* out, word32* outLen)
{
mp_int t;
int err;
int sz;
if (mp_init(&t) != MP_OKAY)
return -1;
err = mp_read_radix(&t, hex, MP_RADIX_HEX);
if (err == MP_OKAY) {
sz = mp_unsigned_bin_size(&t);
if (sz < 0 || (word32)sz > WB_MAXFIELD) {
err = -1;
}
else {
*outLen = (word32)sz;
err = mp_to_unsigned_bin(&t, out);
}
}
mp_clear(&t);
return err;
}
/* ------------------------------------------------------------------------- *
* Class 9/10: wc_ecc_get_curve_id_from_params() (lines ~4537, ~4545).
*
* NULL OR guard: prime==NULL || Af==NULL || Bf==NULL || order==NULL ||
* Gx==NULL || Gy==NULL
* AND match chain: prime match && Af match && Bf match && order match &&
* Gx match && Gy match && cofactor match
*
* No tests/api caller ever supplies a real matching parameter set (every
* caller either passes a deliberately-wrong set to prove ECC_CURVE_INVALID,
* or does not call this function at all), so neither the NULL guard's non-
* prime operands nor any operand of the match chain past "prime" has its
* independence pair shown anywhere. Build a byte-exact copy of a real
* curve's fields (SECP256R1) to drive both.
* ------------------------------------------------------------------------- */
static void wb_get_curve_id_from_params(void)
{
int idx = wc_ecc_get_curve_idx(ECC_SECP256R1);
const ecc_set_type* cs;
byte prime[WB_MAXFIELD], Af[WB_MAXFIELD], Bf[WB_MAXFIELD];
byte order[WB_MAXFIELD], Gx[WB_MAXFIELD], Gy[WB_MAXFIELD];
byte bad[WB_MAXFIELD];
word32 primeSz, AfSz, BfSz, orderSz, GxSz, GySz;
int fieldSize, ret;
if (idx == ECC_CURVE_INVALID) {
WB_NOTE("SECP256R1 not in ecc_sets[]; skipped");
wb_fail = 1;
return;
}
cs = wc_ecc_get_curve_params(idx);
fieldSize = cs->size * 8;
if (wb_hex_to_bin(cs->prime, prime, &primeSz) != 0 ||
wb_hex_to_bin(cs->Af, Af, &AfSz) != 0 ||
wb_hex_to_bin(cs->Bf, Bf, &BfSz) != 0 ||
wb_hex_to_bin(cs->order, order, &orderSz) != 0 ||
wb_hex_to_bin(cs->Gx, Gx, &GxSz) != 0 ||
wb_hex_to_bin(cs->Gy, Gy, &GySz) != 0) {
WB_NOTE("wb_hex_to_bin failed; wb_get_curve_id_from_params skipped");
wb_fail = 1;
return;
}
/* ---- NULL OR guard: isolate each non-prime operand, rest valid ---- */
ret = wc_ecc_get_curve_id_from_params(fieldSize, prime, primeSz, NULL,
AfSz, Bf, BfSz, order, orderSz, Gx, GxSz, Gy, GySz, cs->cofactor);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) { wb_fail = 1; }
ret = wc_ecc_get_curve_id_from_params(fieldSize, prime, primeSz, Af,
AfSz, NULL, BfSz, order, orderSz, Gx, GxSz, Gy, GySz, cs->cofactor);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) { wb_fail = 1; }
ret = wc_ecc_get_curve_id_from_params(fieldSize, prime, primeSz, Af,
AfSz, Bf, BfSz, NULL, orderSz, Gx, GxSz, Gy, GySz, cs->cofactor);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) { wb_fail = 1; }
ret = wc_ecc_get_curve_id_from_params(fieldSize, prime, primeSz, Af,
AfSz, Bf, BfSz, order, orderSz, NULL, GxSz, Gy, GySz, cs->cofactor);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) { wb_fail = 1; }
ret = wc_ecc_get_curve_id_from_params(fieldSize, prime, primeSz, Af,
AfSz, Bf, BfSz, order, orderSz, Gx, GxSz, NULL, GySz, cs->cofactor);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) { wb_fail = 1; }
if (wb_fail) {
WB_NOTE("wc_ecc_get_curve_id_from_params NULL guard unexpected");
}
/* ---- all-valid baseline: full real match, needed in THIS binary ---- */
ret = wc_ecc_get_curve_id_from_params(fieldSize, prime, primeSz, Af,
AfSz, Bf, BfSz, order, orderSz, Gx, GxSz, Gy, GySz, cs->cofactor);
if (ret != cs->id) {
WB_NOTE("wc_ecc_get_curve_id_from_params(all-match) unexpected id");
wb_fail = 1;
}
/* ---- AND-chain independence: corrupt exactly one field, keep the
* rest matching, expect ECC_CURVE_INVALID (no ecc_sets[] entry can
* coincidentally match a flipped cryptographic field of the same
* length). ---- */
#define WB_CORRUPT1(buf, len) do { \
XMEMCPY(bad, (buf), (len)); \
bad[(len) - 1] = (byte)(bad[(len) - 1] ^ 0xFFu); \
} while (0)
WB_CORRUPT1(Af, AfSz);
ret = wc_ecc_get_curve_id_from_params(fieldSize, prime, primeSz, bad,
AfSz, Bf, BfSz, order, orderSz, Gx, GxSz, Gy, GySz, cs->cofactor);
if (ret != ECC_CURVE_INVALID) { wb_fail = 1; }
WB_CORRUPT1(Bf, BfSz);
ret = wc_ecc_get_curve_id_from_params(fieldSize, prime, primeSz, Af,
AfSz, bad, BfSz, order, orderSz, Gx, GxSz, Gy, GySz, cs->cofactor);
if (ret != ECC_CURVE_INVALID) { wb_fail = 1; }
WB_CORRUPT1(order, orderSz);
ret = wc_ecc_get_curve_id_from_params(fieldSize, prime, primeSz, Af,
AfSz, Bf, BfSz, bad, orderSz, Gx, GxSz, Gy, GySz, cs->cofactor);
if (ret != ECC_CURVE_INVALID) { wb_fail = 1; }
WB_CORRUPT1(Gx, GxSz);
ret = wc_ecc_get_curve_id_from_params(fieldSize, prime, primeSz, Af,
AfSz, Bf, BfSz, order, orderSz, bad, GxSz, Gy, GySz, cs->cofactor);
if (ret != ECC_CURVE_INVALID) { wb_fail = 1; }
WB_CORRUPT1(Gy, GySz);
ret = wc_ecc_get_curve_id_from_params(fieldSize, prime, primeSz, Af,
AfSz, Bf, BfSz, order, orderSz, Gx, GxSz, bad, GySz, cs->cofactor);
if (ret != ECC_CURVE_INVALID) { wb_fail = 1; }
ret = wc_ecc_get_curve_id_from_params(fieldSize, prime, primeSz, Af,
AfSz, Bf, BfSz, order, orderSz, Gx, GxSz, Gy, GySz, cs->cofactor + 1);
if (ret != ECC_CURVE_INVALID) { wb_fail = 1; }
#undef WB_CORRUPT1
WB_NOTE("wc_ecc_get_curve_id_from_params NULL+match-chain pairs done");
}
/* ------------------------------------------------------------------------- *
* Class 11: wc_ecc_get_curve_id_from_dp_params() (lines ~4580, ~4591).
*
* 7-operand OR guard: dp==NULL || dp->prime==NULL || dp->Af==NULL ||
* dp->Bf==NULL || dp->order==NULL || dp->Gx==NULL || dp->Gy==NULL
* AND match chain: same 6 hex-string fields (WC_TYPE_HEX_STR) + cofactor.
*
* No public caller ever builds an ecc_set_type with a live curve's own hex
* strings copied in field-by-field (real callers pass a whole, pre-existing
* dp), so every operand past "dp itself" is unreached both in the OR guard
* and the match chain.
* ------------------------------------------------------------------------- */
static void wb_get_curve_id_from_dp_params(void)
{
int idx = wc_ecc_get_curve_idx(ECC_SECP256R1);
const ecc_set_type* cs;
ecc_set_type dp;
char bad[WB_MAXFIELD * 2 + 4];
int ret;
if (idx == ECC_CURVE_INVALID) {
WB_NOTE("SECP256R1 not in ecc_sets[]; skipped");
wb_fail = 1;
return;
}
cs = wc_ecc_get_curve_params(idx);
ret = wc_ecc_get_curve_id_from_dp_params(NULL);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) { wb_fail = 1; }
XMEMSET(&dp, 0, sizeof(dp));
dp.size = cs->size;
dp.cofactor = cs->cofactor;
dp.Af = cs->Af; dp.Bf = cs->Bf; dp.order = cs->order;
dp.Gx = cs->Gx; dp.Gy = cs->Gy;
dp.prime = NULL;
ret = wc_ecc_get_curve_id_from_dp_params(&dp);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) { wb_fail = 1; }
dp.prime = cs->prime;
dp.Af = NULL;
ret = wc_ecc_get_curve_id_from_dp_params(&dp);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) { wb_fail = 1; }
dp.Af = cs->Af;
dp.Bf = NULL;
ret = wc_ecc_get_curve_id_from_dp_params(&dp);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) { wb_fail = 1; }
dp.Bf = cs->Bf;
dp.order = NULL;
ret = wc_ecc_get_curve_id_from_dp_params(&dp);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) { wb_fail = 1; }
dp.order = cs->order;
dp.Gx = NULL;
ret = wc_ecc_get_curve_id_from_dp_params(&dp);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) { wb_fail = 1; }
dp.Gx = cs->Gx;
dp.Gy = NULL;
ret = wc_ecc_get_curve_id_from_dp_params(&dp);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) { wb_fail = 1; }
dp.Gy = cs->Gy;
if (wb_fail) {
WB_NOTE("wc_ecc_get_curve_id_from_dp_params NULL guard unexpected");
}
/* all-valid baseline: full real match. */
ret = wc_ecc_get_curve_id_from_dp_params(&dp);
if (ret != cs->id) {
WB_NOTE("wc_ecc_get_curve_id_from_dp_params(all-match) unexpected");
wb_fail = 1;
}
/* AND-chain independence: corrupt one hex digit at a time (same
* strlen, so the "strlen mismatch" fast-reject in wc_ecc_cmp_param
* does not short-circuit before the byte compare runs), keep every
* other field matching. */
#define WB_CORRUPT_HEX(field) do { \
size_t wb_n = XSTRLEN((field)); \
XSTRNCPY(bad, (field), sizeof(bad) - 1); \
bad[sizeof(bad) - 1] = '\0'; \
bad[wb_n - 1] = (bad[wb_n - 1] == '0') ? '1' : '0'; \
} while (0)
WB_CORRUPT_HEX(cs->Af); dp.Af = bad;
ret = wc_ecc_get_curve_id_from_dp_params(&dp);
if (ret != ECC_CURVE_INVALID) { wb_fail = 1; }
dp.Af = cs->Af;
WB_CORRUPT_HEX(cs->Bf); dp.Bf = bad;
ret = wc_ecc_get_curve_id_from_dp_params(&dp);
if (ret != ECC_CURVE_INVALID) { wb_fail = 1; }
dp.Bf = cs->Bf;
WB_CORRUPT_HEX(cs->order); dp.order = bad;
ret = wc_ecc_get_curve_id_from_dp_params(&dp);
if (ret != ECC_CURVE_INVALID) { wb_fail = 1; }
dp.order = cs->order;
WB_CORRUPT_HEX(cs->Gx); dp.Gx = bad;
ret = wc_ecc_get_curve_id_from_dp_params(&dp);
if (ret != ECC_CURVE_INVALID) { wb_fail = 1; }
dp.Gx = cs->Gx;
WB_CORRUPT_HEX(cs->Gy); dp.Gy = bad;
ret = wc_ecc_get_curve_id_from_dp_params(&dp);
if (ret != ECC_CURVE_INVALID) { wb_fail = 1; }
dp.Gy = cs->Gy;
#undef WB_CORRUPT_HEX
dp.cofactor = cs->cofactor + 1;
ret = wc_ecc_get_curve_id_from_dp_params(&dp);
if (ret != ECC_CURVE_INVALID) { wb_fail = 1; }
WB_NOTE("wc_ecc_get_curve_id_from_dp_params NULL+match-chain pairs done");
}
/* ------------------------------------------------------------------------- *
* Class 12: wc_ecc_mulmod_ex2() 4-operand NULL guard, generic (!SP_MATH)
* path (line ~4009): k==NULL || G==NULL || R==NULL || modulus==NULL.
*
* No variant here defines bare WOLFSSL_SP_MATH, so this is the branch every
* variant compiles. No current test calls this entry point directly (real
* traffic goes through wc_ecc_mulmod_ex/wc_ecc_mulmod, or the FP_ECC/SP
* layers above it), so not even the all-valid baseline is shown elsewhere
* in this binary; supply the full independence set plus one real call.
* ------------------------------------------------------------------------- */
static void wb_mulmod_ex2_null_guard(void)
{
ecc_point *G = NULL, *R = NULL;
mp_int k, a, modulus, order;
int ret;
if (mp_init_multi(&k, &a, &modulus, &order, NULL, NULL) != MP_OKAY) {
WB_NOTE("mp_init_multi failed; wb_mulmod_ex2_null_guard skipped");
wb_fail = 1;
return;
}
(void)mp_set(&k, 3);
(void)mp_set(&a, 2);
(void)mp_set_int(&modulus, 1000000007uL);
(void)mp_set_int(&order, 1000000007uL);
G = wc_ecc_new_point();
R = wc_ecc_new_point();
if (G == NULL || R == NULL) {
WB_NOTE("wc_ecc_new_point failed; wb_mulmod_ex2_null_guard skipped");
wb_fail = 1;
goto out;
}
(void)mp_set(G->x, 5);
(void)mp_set(G->y, 7);
(void)mp_set(G->z, 1);
ret = wc_ecc_mulmod_ex2(NULL, G, R, &a, &modulus, &order, NULL, 1, NULL);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
ret = wc_ecc_mulmod_ex2(&k, NULL, R, &a, &modulus, &order, NULL, 1, NULL);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
ret = wc_ecc_mulmod_ex2(&k, G, NULL, &a, &modulus, &order, NULL, 1, NULL);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
ret = wc_ecc_mulmod_ex2(&k, G, R, &a, NULL, &order, NULL, 1, NULL);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
/* all-false baseline: real (if not curve-accurate) arithmetic inputs;
* the generic point-math does not require the operands to satisfy a
* real curve equation, only that modulus is odd (for montgomery). */
(void)wc_ecc_mulmod_ex2(&k, G, R, &a, &modulus, &order, NULL, 1, NULL);
if (wb_fail) {
WB_NOTE("wc_ecc_mulmod_ex2 NULL guard unexpected return");
}
WB_NOTE("wc_ecc_mulmod_ex2 4-operand NULL guard pairs exercised");
out:
wc_ecc_del_point(G);
wc_ecc_del_point(R);
mp_clear(&order);
mp_clear(&modulus);
mp_clear(&a);
mp_clear(&k);
}
/* ------------------------------------------------------------------------- *
* Class 13: ecc_map_ex() P/modulus NULL guard (line ~2791).
*
* if (P == NULL || modulus == NULL) return ECC_BAD_ARG_E;
*
* Every caller of ecc_map()/ecc_map_ex() passes a live point off the stack
* and a live curve modulus, so neither NULL half is reachable via the API.
* ------------------------------------------------------------------------- */
static void wb_ecc_map_ex_null(void)
{
ecc_point* P;
mp_int modulus;
int ret;
if (mp_init(&modulus) != MP_OKAY) {
wb_fail = 1;
return;
}
(void)mp_set_int(&modulus, 1000000007uL);
P = wc_ecc_new_point();
if (P == NULL) {
mp_clear(&modulus);
wb_fail = 1;
return;
}
(void)mp_set(P->x, 3);
(void)mp_set(P->y, 5);
(void)mp_set(P->z, 1);
ret = ecc_map_ex(NULL, &modulus, 0, 0);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
ret = ecc_map_ex(P, NULL, 0, 0);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
wc_ecc_del_point(P);
mp_clear(&modulus);
WB_NOTE("ecc_map_ex P/modulus NULL guard pairs exercised");
}
/* ------------------------------------------------------------------------- *
* Class 14: ecc_projective_add_point()/ecc_projective_dbl_point() public
* wrappers (lines ~2393/2397, ~2761/2764): NULL guard + coordinate range
* check ("mp_cmp(..) != MP_LT" over x/y/z of both operands).
*
* These wrappers are compiled unconditionally in ecc.c (always in scope for
* a same-TU #include), but only PROTOTYPED under WOLFSSL_PUBLIC_ECC_ADD_DBL
* -- no in-tree caller (all of which use the _safe() variants) reaches them
* at all, so no operand of either guard has any coverage.
* ------------------------------------------------------------------------- */
static void wb_projective_wrappers(void)
{
ecc_point *P, *Q, *R;
mp_int a, modulus;
int ret;
if (mp_init_multi(&a, &modulus, NULL, NULL, NULL, NULL) != MP_OKAY) {
wb_fail = 1;
return;
}
(void)mp_set(&a, 2);
(void)mp_set_int(&modulus, 1000000007uL);
P = wc_ecc_new_point();
Q = wc_ecc_new_point();
R = wc_ecc_new_point();
if (P == NULL || Q == NULL || R == NULL) {
wb_fail = 1;
goto out;
}
(void)mp_set(P->x, 3); (void)mp_set(P->y, 5); (void)mp_set(P->z, 1);
(void)mp_set(Q->x, 11); (void)mp_set(Q->y, 13); (void)mp_set(Q->z, 1);
/* ---- ecc_projective_add_point: NULL guard, each operand isolated ---- */
ret = ecc_projective_add_point(NULL, Q, R, &a, &modulus, 0);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
ret = ecc_projective_add_point(P, NULL, R, &a, &modulus, 0);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
ret = ecc_projective_add_point(P, Q, NULL, &a, &modulus, 0);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
ret = ecc_projective_add_point(P, Q, R, &a, NULL, 0);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
/* ---- range check: each of the 6 coordinate comparisons isolated,
* one coordinate at a time set >= modulus, rest in range. ---- */
(void)mp_set_int(P->x, 1000000007uL); /* == modulus: not MP_LT */
ret = ecc_projective_add_point(P, Q, R, &a, &modulus, 0);
if (ret != WC_NO_ERR_TRACE(ECC_OUT_OF_RANGE_E)) { wb_fail = 1; }
(void)mp_set(P->x, 3);
(void)mp_set_int(P->y, 1000000007uL);
ret = ecc_projective_add_point(P, Q, R, &a, &modulus, 0);
if (ret != WC_NO_ERR_TRACE(ECC_OUT_OF_RANGE_E)) { wb_fail = 1; }
(void)mp_set(P->y, 5);
(void)mp_set_int(P->z, 1000000007uL);
ret = ecc_projective_add_point(P, Q, R, &a, &modulus, 0);
if (ret != WC_NO_ERR_TRACE(ECC_OUT_OF_RANGE_E)) { wb_fail = 1; }
(void)mp_set(P->z, 1);
(void)mp_set_int(Q->x, 1000000007uL);
ret = ecc_projective_add_point(P, Q, R, &a, &modulus, 0);
if (ret != WC_NO_ERR_TRACE(ECC_OUT_OF_RANGE_E)) { wb_fail = 1; }
(void)mp_set(Q->x, 11);
(void)mp_set_int(Q->y, 1000000007uL);
ret = ecc_projective_add_point(P, Q, R, &a, &modulus, 0);
if (ret != WC_NO_ERR_TRACE(ECC_OUT_OF_RANGE_E)) { wb_fail = 1; }
(void)mp_set(Q->y, 13);
(void)mp_set_int(Q->z, 1000000007uL);
ret = ecc_projective_add_point(P, Q, R, &a, &modulus, 0);
if (ret != WC_NO_ERR_TRACE(ECC_OUT_OF_RANGE_E)) { wb_fail = 1; }
(void)mp_set(Q->z, 1);
/* all-in-range baseline (drives the real _ecc_projective_add_point). */
ret = ecc_projective_add_point(P, Q, R, &a, &modulus, 0);
if (ret != MP_OKAY) { wb_fail = 1; }
/* ---- ecc_projective_dbl_point: NULL guard + range check, same idea
* with 3 operands (P/R/modulus; no Q). ---- */
ret = ecc_projective_dbl_point(NULL, R, &a, &modulus, 0);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
ret = ecc_projective_dbl_point(P, NULL, &a, &modulus, 0);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
ret = ecc_projective_dbl_point(P, R, &a, NULL, 0);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
(void)mp_set_int(P->x, 1000000007uL);
ret = ecc_projective_dbl_point(P, R, &a, &modulus, 0);
if (ret != WC_NO_ERR_TRACE(ECC_OUT_OF_RANGE_E)) { wb_fail = 1; }
(void)mp_set(P->x, 3);
(void)mp_set_int(P->y, 1000000007uL);
ret = ecc_projective_dbl_point(P, R, &a, &modulus, 0);
if (ret != WC_NO_ERR_TRACE(ECC_OUT_OF_RANGE_E)) { wb_fail = 1; }
(void)mp_set(P->y, 5);
(void)mp_set_int(P->z, 1000000007uL);
ret = ecc_projective_dbl_point(P, R, &a, &modulus, 0);
if (ret != WC_NO_ERR_TRACE(ECC_OUT_OF_RANGE_E)) { wb_fail = 1; }
(void)mp_set(P->z, 1);
ret = ecc_projective_dbl_point(P, R, &a, &modulus, 0);
if (ret != MP_OKAY) { wb_fail = 1; }
if (wb_fail) {
WB_NOTE("ecc_projective_add/dbl_point wrapper unexpected return");
}
WB_NOTE("ecc_projective_add/dbl_point wrapper NULL+range pairs done");
out:
wc_ecc_del_point(P);
wc_ecc_del_point(Q);
wc_ecc_del_point(R);
mp_clear(&modulus);
mp_clear(&a);
}
/* ECC_SHAMIR is unconditional in the base config (see the module registry "ecc"
* notes): under it, ecc_mul2add() is `static normal_ecc_mul2add()` (FP_ECC
* on, the default) or the public `ecc_mul2add()` itself (no_fp_shamir, FP_ECC
* off) -- select the same symbol the source itself would use. */
#ifdef ECC_SHAMIR
#ifdef FP_ECC
#define WB_MUL2ADD_FN normal_ecc_mul2add
#else
#define WB_MUL2ADD_FN ecc_mul2add
#endif
/* ------------------------------------------------------------------------- *
* Class 15: ecc_mul2add()/normal_ecc_mul2add() (line ~8774): 6-operand NULL
* guard (A/kA/B/kB/C/modulus -- "a" is not checked) and the ECC_BUFSIZE
* scalar-length sanity check (line ~8861).
*
* No caller passes a NULL operand (every public path -- verify_hash's
* Shamir's-trick optimization -- already validated key/hash/sig upstream),
* and no caller's scalar ever exceeds ECC_BUFSIZE (257) bytes (they are all
* bounded by a curve order), so neither guard's TRUE half is reachable via
* the API. Drives real SECP256R1 generator-point arithmetic (kA=3, kB=5) so
* the all-false baseline is also completed within this binary.
* ------------------------------------------------------------------------- */
static void wb_mul2add_and_bufsize(void)
{
int idx = wc_ecc_get_curve_idx(ECC_SECP256R1);
const ecc_set_type* cs;
mp_int a, modulus, kA, kB, kBig;
ecc_point *A = NULL, *B = NULL, *C = NULL;
byte fbuf[WB_MAXFIELD];
byte bigbuf[300];
word32 sz;
int ret;
if (idx == ECC_CURVE_INVALID) {
wb_fail = 1;
return;
}
cs = wc_ecc_get_curve_params(idx);
if (mp_init_multi(&a, &modulus, &kA, &kB, &kBig, NULL) != MP_OKAY) {
wb_fail = 1;
return;
}
if (wb_hex_to_bin(cs->Af, fbuf, &sz) != 0 ||
mp_read_unsigned_bin(&a, fbuf, (int)sz) != MP_OKAY) {
wb_fail = 1; goto out;
}
if (wb_hex_to_bin(cs->prime, fbuf, &sz) != 0 ||
mp_read_unsigned_bin(&modulus, fbuf, (int)sz) != MP_OKAY) {
wb_fail = 1; goto out;
}
(void)mp_set(&kA, 3);
(void)mp_set(&kB, 5);
XMEMSET(bigbuf, 0xFF, sizeof(bigbuf)); /* > ECC_BUFSIZE (257) bytes */
if (mp_read_unsigned_bin(&kBig, bigbuf, (int)sizeof(bigbuf)) != MP_OKAY) {
wb_fail = 1; goto out;
}
A = wc_ecc_new_point();
B = wc_ecc_new_point();
C = wc_ecc_new_point();
if (A == NULL || B == NULL || C == NULL) {
wb_fail = 1; goto out;
}
if (wb_hex_to_bin(cs->Gx, fbuf, &sz) != 0 ||
mp_read_unsigned_bin(A->x, fbuf, (int)sz) != MP_OKAY) {
wb_fail = 1; goto out;
}
if (wb_hex_to_bin(cs->Gy, fbuf, &sz) != 0 ||
mp_read_unsigned_bin(A->y, fbuf, (int)sz) != MP_OKAY) {
wb_fail = 1; goto out;
}
(void)mp_set(A->z, 1);
(void)mp_copy(A->x, B->x);
(void)mp_copy(A->y, B->y);
(void)mp_copy(A->z, B->z);
/* ---- 6-operand NULL guard, each isolated ---- */
ret = WB_MUL2ADD_FN(NULL, &kA, B, &kB, C, &a, &modulus, NULL);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
ret = WB_MUL2ADD_FN(A, NULL, B, &kB, C, &a, &modulus, NULL);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
ret = WB_MUL2ADD_FN(A, &kA, NULL, &kB, C, &a, &modulus, NULL);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
ret = WB_MUL2ADD_FN(A, &kA, B, NULL, C, &a, &modulus, NULL);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
ret = WB_MUL2ADD_FN(A, &kA, B, &kB, NULL, &a, &modulus, NULL);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
ret = WB_MUL2ADD_FN(A, &kA, B, &kB, C, &a, NULL, NULL);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
/* ---- ECC_BUFSIZE check: lenA/lenB isolated ---- */
ret = WB_MUL2ADD_FN(A, &kBig, B, &kB, C, &a, &modulus, NULL);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) { wb_fail = 1; }
ret = WB_MUL2ADD_FN(A, &kA, B, &kBig, C, &a, &modulus, NULL);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) { wb_fail = 1; }
/* ---- all-false baseline: real kA*G + kB*G Shamir computation ---- */
ret = WB_MUL2ADD_FN(A, &kA, B, &kB, C, &a, &modulus, NULL);
if (ret != MP_OKAY) { wb_fail = 1; }
if (wb_fail) {
WB_NOTE("ecc_mul2add NULL/ECC_BUFSIZE guard unexpected return");
}
WB_NOTE("ecc_mul2add NULL guard + ECC_BUFSIZE pairs done");
out:
wc_ecc_del_point(A);
wc_ecc_del_point(B);
wc_ecc_del_point(C);
mp_clear(&kBig);
mp_clear(&kB);
mp_clear(&kA);
mp_clear(&modulus);
mp_clear(&a);
}
#else
static void wb_mul2add_and_bufsize(void)
{
WB_NOTE("ECC_SHAMIR off; ecc_mul2add skipped");
}
#endif /* ECC_SHAMIR */
/* ------------------------------------------------------------------------- *
* Class 16: ecc_projective_add_point_safe()/ecc_projective_dbl_point_safe()
* (lines ~8581-8656): the point-at-infinity special cases and the
* A==B / A==-B collision cases that the "safe" wrappers exist to handle.
*
* Real ECDSA/ECDH traffic essentially never presents the infinity point or
* an exact doubling/negation collision to these wrappers (a valid random
* scalar practically never produces one), so none of the mp_iszero()/
* mp_cmp() branches here have ever been shown with a live point. Uses a
* small modulus (not a real curve) since the wrappers only need montgomery-
* valid modular arithmetic, not an actual point-on-curve check.
* ------------------------------------------------------------------------- */
static void wb_projective_safe_special_cases(void)
{
mp_int a, modulus;
mp_digit mp;
ecc_point *A, *B, *R;
int ret, infinity;
if (mp_init_multi(&a, &modulus, NULL, NULL, NULL, NULL) != MP_OKAY) {
wb_fail = 1;
return;
}
(void)mp_set(&a, 2);
(void)mp_set_int(&modulus, 1000000007uL);
if (mp_montgomery_setup(&modulus, &mp) != MP_OKAY) {
wb_fail = 1;
mp_clear(&modulus); mp_clear(&a);
return;
}
A = wc_ecc_new_point();
B = wc_ecc_new_point();
R = wc_ecc_new_point();
if (A == NULL || B == NULL || R == NULL) {
wb_fail = 1;
goto out;
}
/* ---- A at infinity (x==0 && y==0): copy B into R. ---- */
(void)mp_set(A->x, 0); (void)mp_set(A->y, 0); (void)mp_set(A->z, 1);
(void)mp_set(B->x, 3); (void)mp_set(B->y, 5); (void)mp_set(B->z, 1);
infinity = 0;
ret = ecc_projective_add_point_safe(A, B, R, &a, &modulus, mp, &infinity);
if (ret != MP_OKAY) { wb_fail = 1; }
/* ---- A not infinity (x==0,y!=0: isolates the "&&"), B at infinity:
* copy A into R. ---- */
(void)mp_set(A->x, 0); (void)mp_set(A->y, 5); (void)mp_set(A->z, 1);
(void)mp_set(B->x, 0); (void)mp_set(B->y, 0); (void)mp_set(B->z, 1);
infinity = 0;
ret = ecc_projective_add_point_safe(A, B, R, &a, &modulus, mp, &infinity);
if (ret != MP_OKAY) { wb_fail = 1; }
/* ---- neither infinite (y==0,x!=0 isolates A's "&&" other side;
* x==0,y!=0 isolates B's), same x/z, same y: A == B -> double. ---- */
(void)mp_set(A->x, 7); (void)mp_set(A->y, 0); (void)mp_set(A->z, 1);
(void)mp_set(B->x, 0); (void)mp_set(B->y, 5); (void)mp_set(B->z, 1);
infinity = 0;
ret = ecc_projective_add_point_safe(A, B, R, &a, &modulus, mp, &infinity);
if (ret != MP_OKAY) { wb_fail = 1; }
(void)mp_set(A->x, 11); (void)mp_set(A->y, 13); (void)mp_set(A->z, 1);
(void)mp_set(B->x, 11); (void)mp_set(B->y, 13); (void)mp_set(B->z, 1);
infinity = 0;
ret = ecc_projective_add_point_safe(A, B, R, &a, &modulus, mp, &infinity);
if (ret != MP_OKAY) { wb_fail = 1; }
/* ---- same x/z, y differs: A == -B -> result set to infinity,
* *infinity set (infinity!=NULL true half). ---- */
(void)mp_set(A->x, 11); (void)mp_set(A->y, 13); (void)mp_set(A->z, 1);
(void)mp_set(B->x, 11); (void)mp_set(B->y, 17); (void)mp_set(B->z, 1);
infinity = 0;
ret = ecc_projective_add_point_safe(A, B, R, &a, &modulus, mp, &infinity);
if (ret != MP_OKAY || !infinity) { wb_fail = 1; }
/* same call, infinity==NULL: isolates that operand's other half. */
ret = ecc_projective_add_point_safe(A, B, R, &a, &modulus, mp, NULL);
if (ret != MP_OKAY) { wb_fail = 1; }
/* ---- general add, neither special case (the common path). ---- */
(void)mp_set(A->x, 3); (void)mp_set(A->y, 5); (void)mp_set(A->z, 1);
(void)mp_set(B->x, 11); (void)mp_set(B->y, 13); (void)mp_set(B->z, 1);
infinity = 0;
ret = ecc_projective_add_point_safe(A, B, R, &a, &modulus, mp, &infinity);
if (ret != MP_OKAY) { wb_fail = 1; }
/* ---- ecc_projective_dbl_point_safe: P at infinity vs not. ---- */
(void)mp_set(A->x, 0); (void)mp_set(A->y, 0); (void)mp_set(A->z, 1);
ret = ecc_projective_dbl_point_safe(A, R, &a, &modulus, mp);
if (ret != MP_OKAY) { wb_fail = 1; }
(void)mp_set(A->x, 0); (void)mp_set(A->y, 5); (void)mp_set(A->z, 1);
ret = ecc_projective_dbl_point_safe(A, R, &a, &modulus, mp);
if (ret != MP_OKAY) { wb_fail = 1; }
(void)mp_set(A->x, 3); (void)mp_set(A->y, 5); (void)mp_set(A->z, 1);
ret = ecc_projective_dbl_point_safe(A, R, &a, &modulus, mp);
if (ret != MP_OKAY) { wb_fail = 1; }
if (wb_fail) {
WB_NOTE("ecc_projective_*_safe special-case unexpected return");
}
WB_NOTE("ecc_projective_add/dbl_point_safe special cases exercised");
out:
wc_ecc_del_point(A);
wc_ecc_del_point(B);
wc_ecc_del_point(R);
mp_clear(&modulus);
mp_clear(&a);
}
/* find_hole()/find_base()/add_entry() and fp_cache[] itself only exist
* inside ecc.c's own #ifdef FP_ECC (+ !WOLFSSL_SP_MATH) block. */
#if defined(FP_ECC) && !defined(WOLFSSL_SP_MATH)
/* ------------------------------------------------------------------------- *
* Class 17: FP_ECC fixed-point cache internals: find_base() (line ~13585),
* find_hole() (line ~13548). Both are file-static and process-global
* (fp_cache[FP_ENTRIES]), reset via wc_ecc_fp_free() so each shot starts
* from deterministic empty-cache state.
*
* Real traffic keeps the cache warm across many identical-curve operations,
* so a fresh process practically never observes find_base() miss on an
* OCCUPIED-but-different-point entry, nor find_hole() choosing between a
* locked low-lru entry and an empty one, nor its "evict a live entry"
* cleanup path -- none of those have a public-API trigger this precise.
* add_entry() itself only needs mp_copy (no curve math), so it is safe to
* call directly; the mp_init() of fp_cache[idx].mu below stands in for what
* build_lut() would normally have set up right after add_entry(), so
* find_hole()'s later mp_clear(&fp_cache[idx].mu) operates on a live value.
* ------------------------------------------------------------------------- */
static void wb_fp_cache_internals(void)
{
ecc_point *g0, *g1;
int idx0, ret;
unsigned x;
wc_ecc_fp_free(); /* deterministic empty-cache start */
g0 = wc_ecc_new_point();
g1 = wc_ecc_new_point();
if (g0 == NULL || g1 == NULL) {
wb_fail = 1;
goto out;
}
(void)mp_set(g0->x, 3); (void)mp_set(g0->y, 5); (void)mp_set(g0->z, 1);
(void)mp_set(g1->x, 11); (void)mp_set(g1->y, 13); (void)mp_set(g1->z, 1);
/* find_base on an empty cache: every fp_cache[x].g == NULL -> -1. */
if (find_base(g0) != -1) { wb_fail = 1; }
ret = add_entry(0, g0);
if (ret != MP_OKAY) { wb_fail = 1; }
(void)mp_init(&fp_cache[0].mu); /* stand-in for build_lut()'s own init */
/* find_base: entry 0 occupied and matches (all 3 mp_cmp true) -> 0. */
if (find_base(g0) != 0) { wb_fail = 1; }
/* find_base: entry 0 occupied but a DIFFERENT point -> falls through
* to -1 (isolates the mp_cmp x/y/z operands' FALSE side). */
if (find_base(g1) != -1) { wb_fail = 1; }
/* find_hole: lock entry 0 so it is excluded (lock==0 FALSE) even
* though it has the lowest lru_count; some other (empty, g==NULL)
* slot must be chosen -- exercises the "z>=0 && g" FALSE half. */
fp_cache[0].lock = 1;
idx0 = find_hole();
if (idx0 < 0 || idx0 == 0 || fp_cache[idx0].g != NULL) { wb_fail = 1; }
fp_cache[0].lock = 0;
/* find_hole: bump every OTHER entry's lru_count above entry 0's, so
* entry 0 (lowest lru_count, unlocked) is chosen and its live g/LUT
* get freed -- exercises the "z>=0 && g" TRUE half and the eviction
* cleanup block, plus the "lru_count>3" TRUE half on entries 1..N. */
for (x = 1; x < FP_ENTRIES; x++) {
fp_cache[x].lru_count = 5;
}
idx0 = find_hole();
if (idx0 != 0) { wb_fail = 1; }
if (wb_fail) {
WB_NOTE("fp_cache find_base/find_hole unexpected state");
}
WB_NOTE("fp_cache find_base/find_hole/add_entry internals exercised");
out:
wc_ecc_del_point(g0);
wc_ecc_del_point(g1);
for (x = 0; x < FP_ENTRIES; x++) {
fp_cache[x].lru_count = 0;
fp_cache[x].lock = 0;
}
wc_ecc_fp_free();
}
#else
static void wb_fp_cache_internals(void)
{
WB_NOTE("FP_ECC off (or WOLFSSL_SP_MATH); fp_cache internals skipped");
}
#endif /* FP_ECC && !WOLFSSL_SP_MATH */
#ifdef HAVE_ECC_KEY_EXPORT
/* ------------------------------------------------------------------------- *
* Class 18: wc_ecc_export_point_der()/wc_ecc_export_point_der_compressed()
* (lines ~10330-10351, ~10396-10418): curve_idx range/valid-idx guard,
* length-query idiom, NULL guard, coordinate-size sanity check.
*
* No tests/api caller drives curve_idx with an in-range-but-invalid index,
* nor a point whose x/y encodes larger than the curve's own byte width, so
* those halves are white-box only.
* ------------------------------------------------------------------------- */
static void wb_export_point_der(void)
{
int idx = wc_ecc_get_curve_idx(ECC_SECP256R1);
ecc_point* point;
byte out[300];
byte big[400];
word32 outLen;
int ret;
if (idx == ECC_CURVE_INVALID) {
wb_fail = 1;
return;
}
point = wc_ecc_new_point();
if (point == NULL) {
wb_fail = 1;
return;
}
(void)mp_set(point->x, 3);
(void)mp_set(point->y, 5);
(void)mp_set(point->z, 1);
XMEMSET(big, 0xFF, sizeof(big));
/* ---- curve_idx guard: both operands isolated ---- */
outLen = sizeof(out);
ret = wc_ecc_export_point_der(-1, point, out, &outLen);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
outLen = sizeof(out);
ret = wc_ecc_export_point_der(9999, point, out, &outLen);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
/* ---- length-query (point!=NULL && out==NULL && outLen!=NULL): isolate
* point and outLen operands (out==NULL held true throughout). ---- */
outLen = sizeof(out);
ret = wc_ecc_export_point_der(idx, point, NULL, &outLen);
if (ret != WC_NO_ERR_TRACE(LENGTH_ONLY_E)) { wb_fail = 1; }
ret = wc_ecc_export_point_der(idx, point, NULL, NULL);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
outLen = sizeof(out);
ret = wc_ecc_export_point_der(idx, NULL, NULL, &outLen);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
/* ---- coordinate-size check: x/y isolated ---- */
(void)mp_read_unsigned_bin(point->x, big, (int)sizeof(big));
outLen = sizeof(out);
ret = wc_ecc_export_point_der(idx, point, out, &outLen);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
(void)mp_set(point->x, 3);
(void)mp_read_unsigned_bin(point->y, big, (int)sizeof(big));
outLen = sizeof(out);
ret = wc_ecc_export_point_der(idx, point, out, &outLen);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
(void)mp_set(point->y, 5);
/* baseline real export. */
outLen = sizeof(out);
ret = wc_ecc_export_point_der(idx, point, out, &outLen);
if (ret != MP_OKAY) { wb_fail = 1; }
#ifdef HAVE_COMP_KEY
outLen = sizeof(out);
ret = wc_ecc_export_point_der_compressed(-1, point, out, &outLen);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
outLen = sizeof(out);
ret = wc_ecc_export_point_der_compressed(9999, point, out, &outLen);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
outLen = sizeof(out);
ret = wc_ecc_export_point_der_compressed(idx, point, NULL, &outLen);
if (ret != WC_NO_ERR_TRACE(LENGTH_ONLY_E)) { wb_fail = 1; }
ret = wc_ecc_export_point_der_compressed(idx, point, NULL, NULL);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
outLen = sizeof(out);
ret = wc_ecc_export_point_der_compressed(idx, NULL, NULL, &outLen);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
(void)mp_read_unsigned_bin(point->x, big, (int)sizeof(big));
outLen = sizeof(out);
ret = wc_ecc_export_point_der_compressed(idx, point, out, &outLen);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
(void)mp_set(point->x, 3);
outLen = sizeof(out);
ret = wc_ecc_export_point_der_compressed(idx, point, out, &outLen);
if (ret != MP_OKAY) { wb_fail = 1; }
#endif /* HAVE_COMP_KEY */
if (wb_fail) {
WB_NOTE("wc_ecc_export_point_der[_compressed] unexpected return");
}
WB_NOTE("wc_ecc_export_point_der[_compressed] guard+range pairs done");
wc_ecc_del_point(point);
}
/* ------------------------------------------------------------------------- *
* Class 19: _ecc_export_x963() (lines ~10456-10503): length-query idiom,
* NULL guard, key->type/idx/dp validity guard, pubkey x/y size check.
* File-static (only callable because this TU #includes ecc.c).
* ------------------------------------------------------------------------- */
static void wb_export_x963_internal(void)
{
int idx = wc_ecc_get_curve_idx(ECC_SECP256R1);
const ecc_set_type* cs;
ecc_key key, k2;
byte out[200];
byte big[400];
word32 outLen;
int ret;
if (idx == ECC_CURVE_INVALID) {
wb_fail = 1;
return;
}
cs = wc_ecc_get_curve_params(idx);
if (wc_ecc_init(&key) != 0) {
wb_fail = 1;
return;
}
(void)mp_set(key.pubkey.x, 3);
(void)mp_set(key.pubkey.y, 5);
(void)mp_set(key.pubkey.z, 1);
key.type = ECC_PUBLICKEY;
key.idx = idx;
key.dp = cs;
XMEMSET(big, 0xFF, sizeof(big));
/* ---- length-query (key!=NULL && out==NULL && outLen!=NULL) ---- */
outLen = sizeof(out);
ret = _ecc_export_x963(&key, NULL, &outLen);
if (ret != WC_NO_ERR_TRACE(LENGTH_ONLY_E)) { wb_fail = 1; }
ret = _ecc_export_x963(&key, NULL, NULL);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
outLen = sizeof(out);
ret = _ecc_export_x963(NULL, NULL, &outLen);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
/* ---- NULL guard (key==NULL || out==NULL || outLen==NULL) ---- */
ret = _ecc_export_x963(NULL, out, &outLen);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
ret = _ecc_export_x963(&key, out, NULL);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
/* ---- key->type==0 || is_valid_idx==0 || dp==NULL (shallow copies:
* only .type/.dp differ, never freed, never dereferences pubkey). ---- */
k2 = key;
k2.type = 0;
outLen = sizeof(out);
ret = _ecc_export_x963(&k2, out, &outLen);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
k2 = key;
k2.dp = NULL;
outLen = sizeof(out);
ret = _ecc_export_x963(&k2, out, &outLen);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
/* ---- pubxlen>numlen || pubylen>numlen: isolated ---- */
(void)mp_read_unsigned_bin(key.pubkey.x, big, (int)sizeof(big));
outLen = sizeof(out);
ret = _ecc_export_x963(&key, out, &outLen);
if (ret != WC_NO_ERR_TRACE(BUFFER_E)) { wb_fail = 1; }
(void)mp_set(key.pubkey.x, 3);
(void)mp_read_unsigned_bin(key.pubkey.y, big, (int)sizeof(big));
outLen = sizeof(out);
ret = _ecc_export_x963(&key, out, &outLen);
if (ret != WC_NO_ERR_TRACE(BUFFER_E)) { wb_fail = 1; }
(void)mp_set(key.pubkey.y, 5);
/* baseline real export. */
outLen = sizeof(out);
ret = _ecc_export_x963(&key, out, &outLen);
if (ret != MP_OKAY) { wb_fail = 1; }
if (wb_fail) {
WB_NOTE("_ecc_export_x963 unexpected return");
}
WB_NOTE("_ecc_export_x963 NULL/type-idx-dp/size pairs done");
wc_ecc_free(&key);
}
/* ------------------------------------------------------------------------- *
* Class 20: repeated "wc_ecc_is_valid_idx(key->idx) == 0 || key->dp == NULL"
* guard, isolated at each of its easy-to-drive call sites:
* _ecc_export_ex() (line ~11843, static)
* wc_ecc_export_public_raw() (line ~12005, qx/qxLen/qy/qyLen NULL
* guard -- a different, public, guard on
* the same call path)
* wc_ecc_export_x963_compressed() (line ~16779, static, 3-operand chain
* with key->type==0 in front)
* Every public caller of these already has a real dp/idx by construction
* (wc_ecc_init/make_key/import always set both together), so an
* idx-invalid-but-dp-set or idx-valid-but-dp-NULL key never reaches them
* via the API.
* ------------------------------------------------------------------------- */
static void wb_idx_dp_guard_export_paths(void)
{
int idx = wc_ecc_get_curve_idx(ECC_SECP256R1);
const ecc_set_type* cs;
ecc_key key;
byte qx[64], qy[64];
word32 qxLen, qyLen;
byte out[200];
word32 outLen;
int ret;
if (idx == ECC_CURVE_INVALID) {
wb_fail = 1;
return;
}
cs = wc_ecc_get_curve_params(idx);
if (wc_ecc_init(&key) != 0) {
wb_fail = 1;
return;
}
(void)mp_set(key.pubkey.x, 3);
(void)mp_set(key.pubkey.y, 5);
(void)mp_set(key.pubkey.z, 1);
key.type = ECC_PUBLICKEY;
/* ---- _ecc_export_ex: idx invalid/dp valid, then idx valid/dp NULL ---- */
key.idx = 9999; key.dp = cs;
qxLen = sizeof(qx); qyLen = sizeof(qy);
ret = _ecc_export_ex(&key, qx, &qxLen, qy, &qyLen, NULL, NULL,
WC_TYPE_UNSIGNED_BIN);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
key.idx = idx; key.dp = NULL;
qxLen = sizeof(qx); qyLen = sizeof(qy);
ret = _ecc_export_ex(&key, qx, &qxLen, qy, &qyLen, NULL, NULL,
WC_TYPE_UNSIGNED_BIN);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
key.idx = idx; key.dp = cs; /* baseline: real export */
qxLen = sizeof(qx); qyLen = sizeof(qy);
ret = _ecc_export_ex(&key, qx, &qxLen, qy, &qyLen, NULL, NULL,
WC_TYPE_UNSIGNED_BIN);
if (ret != MP_OKAY) { wb_fail = 1; }
/* ---- wc_ecc_export_public_raw: qx/qxLen/qy/qyLen NULL guard,
* each operand isolated (rest valid). ---- */
qxLen = sizeof(qx); qyLen = sizeof(qy);
ret = wc_ecc_export_public_raw(&key, NULL, &qxLen, qy, &qyLen);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) { wb_fail = 1; }
ret = wc_ecc_export_public_raw(&key, qx, NULL, qy, &qyLen);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) { wb_fail = 1; }
ret = wc_ecc_export_public_raw(&key, qx, &qxLen, NULL, &qyLen);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) { wb_fail = 1; }
ret = wc_ecc_export_public_raw(&key, qx, &qxLen, qy, NULL);
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) { wb_fail = 1; }
qxLen = sizeof(qx); qyLen = sizeof(qy);
ret = wc_ecc_export_public_raw(&key, qx, &qxLen, qy, &qyLen);
if (ret != MP_OKAY) { wb_fail = 1; }
/* ---- wc_ecc_export_x963_compressed: type==0 / idx-invalid / dp==NULL,
* each isolated (needs HAVE_COMP_KEY; the function itself is only
* compiled under it). ---- */
#ifdef HAVE_COMP_KEY
key.idx = idx; key.dp = cs; key.type = 0;
outLen = sizeof(out);
ret = wc_ecc_export_x963_compressed(&key, out, &outLen);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
key.type = ECC_PUBLICKEY; key.idx = 9999; key.dp = cs;
outLen = sizeof(out);
ret = wc_ecc_export_x963_compressed(&key, out, &outLen);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
key.idx = idx; key.dp = NULL;
outLen = sizeof(out);
ret = wc_ecc_export_x963_compressed(&key, out, &outLen);
if (ret != WC_NO_ERR_TRACE(ECC_BAD_ARG_E)) { wb_fail = 1; }
key.dp = cs; /* baseline: real compressed export */
outLen = sizeof(out);
ret = wc_ecc_export_x963_compressed(&key, out, &outLen);
if (ret != MP_OKAY) { wb_fail = 1; }
#endif
if (wb_fail) {
WB_NOTE("idx/dp guard export paths unexpected return");
}
WB_NOTE("_ecc_export_ex/export_public_raw/export_x963_compressed "
"idx/dp guard pairs done");
wc_ecc_free(&key);
}
#else
static void wb_export_point_der(void)
{
WB_NOTE("HAVE_ECC_KEY_EXPORT off; skipped");
}
static void wb_export_x963_internal(void)
{
WB_NOTE("HAVE_ECC_KEY_EXPORT off; skipped");
}
static void wb_idx_dp_guard_export_paths(void)
{
WB_NOTE("HAVE_ECC_KEY_EXPORT off; skipped");
}
#endif /* HAVE_ECC_KEY_EXPORT */
#if defined(HAVE_ECC_MAKE_PUB) || !defined(WOLFSSL_ATECC508A)
/* ------------------------------------------------------------------------- *
* Class 21: wc_ecc_make_pub_ex() "key->type == ECC_PRIVATEKEY_ONLY &&
* pubOut == NULL" (line ~5730): on a private-only key, a NULL pubOut means
* "cache the recomputed public part on the key" -- promotes key->type to
* ECC_PRIVATEKEY. No caller in the API ever calls wc_ecc_make_pub() on a key
* whose type is already ECC_PRIVATEKEY_ONLY (make_key always leaves type ==
* ECC_PRIVATEKEY), so this promotion path is unreached. A real key is needed
* (wc_ecc_make_pub_ex does the actual k*G scalar multiply), so build one via
* the real wc_ecc_make_key(), then force the private-only state by hand.
* ------------------------------------------------------------------------- */
static void wb_make_pub_privatekey_only(void)
{
WC_RNG rng;
ecc_key key;
int ret;
if (wc_InitRng(&rng) != 0) {
wb_fail = 1;
return;
}
if (wc_ecc_init(&key) != 0) {
wc_FreeRng(&rng);
wb_fail = 1;
return;
}
ret = wc_ecc_make_key_ex(&rng, 0, &key, ECC_SECP256R1);
if (ret != 0) {
WB_NOTE("wc_ecc_make_key_ex failed; wb_make_pub_privatekey_only "
"skipped");
wb_fail = 1;
goto out;
}
key.type = ECC_PRIVATEKEY_ONLY;
ret = wc_ecc_make_pub(&key, NULL);
if (ret != 0 || key.type != ECC_PRIVATEKEY) {
WB_NOTE("wc_ecc_make_pub(PRIVATEKEY_ONLY, pubOut=NULL) unexpected");
wb_fail = 1;
}
else {
WB_NOTE("wc_ecc_make_pub PRIVATEKEY_ONLY promotion exercised");
}
out:
wc_ecc_free(&key);
wc_FreeRng(&rng);
}
#else
static void wb_make_pub_privatekey_only(void)
{
WB_NOTE("wc_ecc_make_pub not available; skipped");
}
#endif
#endif /* HAVE_ECC && !WOLF_CRYPTO_CB_ONLY_ECC */
/* Argument-guard vectors that ordinary use never produces. Each guard needs
* BOTH halves inside THIS binary: llvm-cov derives MC/DC per binary, so a
* rejection vector on its own proves nothing without the accepting vector of
* the same decision to pair it against. */
static void wb_arg_guards(void)
{
WC_RNG rng;
ecc_key key;
ecc_point* pt = NULL;
mp_int m1;
word32 sz = 0;
byte buf[256];
word32 bufLen = (word32)sizeof(buf);
int haveKey = 0;
int haveM1 = 0;
XMEMSET(&key, 0, sizeof(key));
XMEMSET(&m1, 0, sizeof(m1));
XMEMSET(buf, 0, sizeof(buf));
if (wc_InitRng(&rng) != 0) {
WB_NOTE("wc_InitRng failed; wb_arg_guards skipped");
wb_fail = 1;
return;
}
if (wc_ecc_init(&key) == 0) {
haveKey = (wc_ecc_make_key_ex(&rng, 0, &key, ECC_SECP256R1) == 0);
}
if (!haveKey) {
WB_NOTE("key setup failed; wb_arg_guards skipped");
wb_fail = 1;
wc_ecc_free(&key);
wc_FreeRng(&rng);
return;
}
pt = wc_ecc_new_point();
haveM1 = (mp_init(&m1) == MP_OKAY);
/* The import guards run against a scratch key: their accepting vectors
* overwrite whatever they are handed, and the operations further down need
* `key` to still hold a valid point. */
{
ecc_key imp;
word32 xLen = (word32)sizeof(buf);
int haveImp = (wc_ecc_init(&imp) == 0);
/* key == NULL || qx == NULL || qy == NULL */
(void)_ecc_import_raw_private(NULL, "1", "1", "1", ECC_SECP256R1,
WC_TYPE_HEX_STR);
if (haveImp) {
(void)_ecc_import_raw_private(&imp, NULL, "1", "1", ECC_SECP256R1,
WC_TYPE_HEX_STR);
(void)_ecc_import_raw_private(&imp, "1", NULL, "1", ECC_SECP256R1,
WC_TYPE_HEX_STR);
(void)_ecc_import_raw_private(&imp, "1", "1", "1", ECC_SECP256R1,
WC_TYPE_HEX_STR);
}
/* in == NULL || key == NULL */
(void)_ecc_import_x963_ex2(NULL, 1, &imp, ECC_SECP256R1, 0);
(void)_ecc_import_x963_ex2(buf, 1, NULL, ECC_SECP256R1, 0);
if (haveImp && wc_ecc_export_x963(&key, buf, &xLen) == 0) {
wc_ecc_free(&imp);
if (wc_ecc_init(&imp) == 0) {
(void)_ecc_import_x963_ex2(buf, xLen, &imp, ECC_SECP256R1, 0);
}
}
if (haveImp) {
wc_ecc_free(&imp);
}
}
/* ecc_public_key_size: key == NULL || key->dp == NULL */
{
const ecc_set_type* savedDp = key.dp;
(void)ecc_public_key_size(NULL, &sz);
key.dp = NULL;
(void)ecc_public_key_size(&key, &sz);
key.dp = savedDp;
(void)ecc_public_key_size(&key, &sz);
}
/* The accepting vectors below need the real curve constants: a zero
* modulus/order would make wc_ecc_gen_deterministic_k's RFC 6979 retry
* loop spin, and the harness kills the variant on TEST_TIMEOUT. */
{
mp_int prime;
mp_int order;
mp_int af;
int haveCurve = 0;
XMEMSET(&prime, 0, sizeof(prime));
XMEMSET(&order, 0, sizeof(order));
XMEMSET(&af, 0, sizeof(af));
if (key.dp != NULL && mp_init(&prime) == MP_OKAY) {
if (mp_init(&order) == MP_OKAY) {
if (mp_init(&af) == MP_OKAY) {
haveCurve =
(mp_read_radix(&prime, key.dp->prime, 16) == MP_OKAY) &&
(mp_read_radix(&order, key.dp->order, 16) == MP_OKAY) &&
(mp_read_radix(&af, key.dp->Af, 16) == MP_OKAY);
}
}
}
/* hash == NULL || k == NULL || order == NULL */
if (haveM1 && haveCurve) {
(void)wc_ecc_gen_deterministic_k(NULL, 32, WC_HASH_TYPE_SHA256,
key.k, &m1, &order, key.heap);
(void)wc_ecc_gen_deterministic_k(buf, 32, WC_HASH_TYPE_SHA256,
key.k, NULL, &order, key.heap);
(void)wc_ecc_gen_deterministic_k(buf, 32, WC_HASH_TYPE_SHA256,
key.k, &m1, NULL, key.heap);
(void)wc_ecc_gen_deterministic_k(buf, 32, WC_HASH_TYPE_SHA256,
key.k, &m1, &order, key.heap);
}
/* wc_ecc_mulmod_ex / _ex2 NULL chains, then the accepting vector. */
if (pt != NULL && haveCurve &&
wc_ecc_copy_point(&key.pubkey, pt) == MP_OKAY) {
ecc_point* out = wc_ecc_new_point();
#ifdef WOLFSSL_SP_MATH
/* Only the SP build rejects these: the generic implementation has
* no such guard and dereferences both operands. */
(void)wc_ecc_mulmod_ex(key.k, pt, pt, NULL, &prime, 1, key.heap);
(void)wc_ecc_mulmod_ex2(key.k, pt, pt, NULL, &prime, &order, &rng,
1, key.heap);
(void)wc_ecc_mulmod_ex2(key.k, pt, pt, &af, &prime, NULL, &rng, 1,
key.heap);
#endif
if (out != NULL) {
(void)wc_ecc_mulmod_ex(key.k, pt, out, &af, &prime, 1,
key.heap);
(void)wc_ecc_mulmod_ex2(key.k, pt, out, &af, &prime, &order,
&rng, 1, key.heap);
wc_ecc_del_point(out);
}
/* point == NULL || out == NULL || outLen == NULL */
sz = (word32)sizeof(buf);
(void)wc_ecc_export_point_der_compressed(key.idx, pt, buf, NULL);
(void)wc_ecc_export_point_der_compressed(key.idx, pt, buf, &sz);
}
mp_free(&af);
mp_free(&order);
mp_free(&prime);
}
#ifdef WC_ECC_NONBLOCK
/* wc_ecc_set_nonblock: key->nb_ctx != NULL && key->nb_ctx != ctx */
{
ecc_nb_ctx_t nb;
XMEMSET(&nb, 0, sizeof(nb));
(void)wc_ecc_set_nonblock(&key, &nb);
(void)wc_ecc_set_nonblock(&key, &nb);
(void)wc_ecc_set_nonblock(&key, NULL);
}
#endif
/* The is_valid_idx/dp family: an invalid idx fires the first operand, a
* valid idx with dp cleared fires the second, an untouched key neither. */
{
const ecc_set_type* savedDp = key.dp;
int savedIdx = key.idx;
mp_int r;
mp_int s;
int stat = 0;
key.idx = ECC_CUSTOM_IDX - 1;
bufLen = (word32)sizeof(buf);
(void)wc_ecc_shared_secret(&key, &key, buf, &bufLen);
(void)wc_ecc_shared_secret_ex(&key, &key.pubkey, buf, &bufLen);
key.idx = savedIdx;
key.dp = NULL;
bufLen = (word32)sizeof(buf);
(void)wc_ecc_shared_secret(&key, &key, buf, &bufLen);
(void)wc_ecc_shared_secret_ex(&key, &key.pubkey, buf, &bufLen);
key.dp = savedDp;
PRIVATE_KEY_UNLOCK();
bufLen = (word32)sizeof(buf);
(void)wc_ecc_shared_secret(&key, &key, buf, &bufLen);
bufLen = (word32)sizeof(buf);
(void)wc_ecc_shared_secret_ex(&key, &key.pubkey, buf, &bufLen);
PRIVATE_KEY_LOCK();
if (mp_init(&r) == MP_OKAY) {
if (mp_init(&s) == MP_OKAY) {
key.idx = ECC_CUSTOM_IDX - 1;
(void)wc_ecc_sign_hash_ex(buf, 32, &rng, &key, &r, &s);
(void)wc_ecc_verify_hash_ex(&r, &s, buf, 32, &stat, &key);
key.idx = savedIdx;
key.dp = NULL;
(void)wc_ecc_sign_hash_ex(buf, 32, &rng, &key, &r, &s);
(void)wc_ecc_verify_hash_ex(&r, &s, buf, 32, &stat, &key);
key.dp = savedDp;
if (wc_ecc_sign_hash_ex(buf, 32, &rng, &key, &r, &s) == 0) {
(void)wc_ecc_verify_hash_ex(&r, &s, buf, 32, &stat, &key);
}
mp_free(&s);
}
mp_free(&r);
}
}
if (pt != NULL) {
wc_ecc_del_point(pt);
}
if (haveM1) {
mp_free(&m1);
}
wc_ecc_free(&key);
wc_FreeRng(&rng);
/* wc_ecc_free: key->deallocSet && key->dp != NULL */
{
ecc_key k2;
if (wc_ecc_init(&k2) == 0) {
k2.dp = NULL;
(void)wc_ecc_free(&k2);
}
}
}
/* ------------------------------------------------------------------------- *
* Pass 2: argument/state shapes that no ordinary caller produces.
*
* Same rule as everywhere else in this file -- MC/DC is judged per BINARY, so
* each decision's accepting vector is issued here next to its rejecting one
* even when the tests/api lane already covers the accepting side.
* ------------------------------------------------------------------------- */
static void wb_gap_pass2(void)
{
WC_RNG rng;
ecc_key key;
ecc_key peer;
byte buf[256];
word32 bufLen;
int haveKey = 0, havePeer = 0;
XMEMSET(&rng, 0, sizeof(rng));
XMEMSET(&key, 0, sizeof(key));
XMEMSET(&peer, 0, sizeof(peer));
/* NOT all-zero: ecc.c rejects an all-zero digest (WC_ALLOW_ECC_ZERO_HASH
* is off) several lines BEFORE the key-type guard at 7672, so a zeroed
* buffer never reaches it. */
XMEMSET(buf, 0xa7, sizeof(buf));
if (wc_InitRng(&rng) != 0) {
WB_NOTE("wc_InitRng failed; wb_gap_pass2 skipped");
wb_fail = 1;
return;
}
if (wc_ecc_init(&key) == 0)
haveKey = (wc_ecc_make_key_ex(&rng, 0, &key, ECC_SECP256R1) == 0);
if (wc_ecc_init(&peer) == 0)
havePeer = (wc_ecc_make_key_ex(&rng, 0, &peer, ECC_SECP256R1) == 0);
if (!haveKey || !havePeer) {
WB_NOTE("key setup failed; wb_gap_pass2 skipped");
wb_fail = 1;
wc_ecc_free(&peer);
wc_ecc_free(&key);
wc_FreeRng(&rng);
return;
}
/* ---- wc_ecc_sign_hash_ex key-type guard (7672)
* if (key->type != ECC_PRIVATEKEY && key->type != ECC_PRIVATEKEY_ONLY)
* A signing key is by construction one of the two accepted types, so the
* rejecting rows only exist if the type is set by hand. */
{
int savedType = key.type;
mp_int r, s;
XMEMSET(&r, 0, sizeof(r));
XMEMSET(&s, 0, sizeof(s));
#if defined(HAVE_ECC_SIGN)
if ((mp_init(&r) == MP_OKAY) && (mp_init(&s) == MP_OKAY)) {
key.type = ECC_PUBLICKEY; /* (T,T) -> rejected */
(void)wc_ecc_sign_hash_ex(buf, 32, &rng, &key, &r, &s);
key.type = ECC_PRIVATEKEY_ONLY; /* (T,F) -> accepted */
(void)wc_ecc_sign_hash_ex(buf, 32, &rng, &key, &r, &s);
key.type = ECC_PRIVATEKEY; /* (F,-) -> accepted */
(void)wc_ecc_sign_hash_ex(buf, 32, &rng, &key, &r, &s);
mp_free(&s);
mp_free(&r);
}
#endif /* HAVE_ECC_SIGN */
key.type = savedType;
}
/* ---- wc_ecc_shared_secret private-key-type guard (4792) and the
* PUBLIC half of the idx/dp chain (4806 idx2/idx3). wb_arg_guards above
* already drives idx0/idx1 by corrupting the key it passes as BOTH
* arguments; a separate peer key is needed to leave the private side
* valid and break only the public one. */
#ifdef HAVE_ECC_DHE
{
int savedType = key.type;
int savedIdx = peer.idx;
const ecc_set_type* savedDp = peer.dp;
PRIVATE_KEY_UNLOCK();
key.type = ECC_PRIVATEKEY; /* 4792 (T,-) */
bufLen = (word32)sizeof(buf);
(void)wc_ecc_shared_secret(&key, &peer, buf, &bufLen);
key.type = ECC_PRIVATEKEY_ONLY; /* 4792 (F,T) */
bufLen = (word32)sizeof(buf);
(void)wc_ecc_shared_secret(&key, &peer, buf, &bufLen);
key.type = ECC_PUBLICKEY; /* 4792 (F,F) -> reject */
bufLen = (word32)sizeof(buf);
(void)wc_ecc_shared_secret(&key, &peer, buf, &bufLen);
key.type = savedType;
peer.idx = ECC_CUSTOM_IDX - 1; /* 4806 (F,F,T,-) */
bufLen = (word32)sizeof(buf);
(void)wc_ecc_shared_secret(&key, &peer, buf, &bufLen);
peer.idx = savedIdx;
peer.dp = NULL; /* 4806 (F,F,F,T) */
bufLen = (word32)sizeof(buf);
(void)wc_ecc_shared_secret(&key, &peer, buf, &bufLen);
peer.dp = savedDp;
bufLen = (word32)sizeof(buf); /* 4806 (F,F,F,F) */
(void)wc_ecc_shared_secret(&key, &peer, buf, &bufLen);
PRIVATE_KEY_LOCK();
}
#endif /* HAVE_ECC_DHE */
/* ---- wc_ecc_make_pub state promotion (5768)
* if (key->type == ECC_PRIVATEKEY_ONLY && pubOut == NULL)
* wb_make_pub_privatekey_only supplies (T,T); the (F,-) row needs a key
* that already holds its public point, i.e. a plain ECC_PRIVATEKEY. */
#if defined(HAVE_ECC_MAKE_PUB) || !defined(WOLFSSL_ATECC508A)
{
int savedType = key.type;
ecc_point* pubPt = wc_ecc_new_point();
/* (T,T): pubOut == NULL. Note ecc_make_pub_ex FORCES
* key->type = ECC_PRIVATEKEY_ONLY on its way in whenever pubOut is
* NULL, so this row cannot be varied -- with a NULL pubOut the first
* operand is always TRUE. */
(void)wc_ecc_make_pub(&key, NULL);
/* (F,-): the only way to reach the guard with the first operand
* FALSE is a NON-NULL pubOut (which leaves key->type alone) on a key
* that is a full ECC_PRIVATEKEY. Every API caller passes NULL, which
* is why this row is missing from the harness. */
if (pubPt != NULL) {
key.type = ECC_PRIVATEKEY;
(void)wc_ecc_make_pub(&key, pubPt);
wc_ecc_del_point(pubPt);
}
key.type = savedType;
}
#endif
/* ---- _ecc_import_x963_ex2 point-type guard (11485)
* if (pointType != 4 && pointType != 2 && pointType != 3)
* Every real X9.63 blob starts 0x04 (or 0x02/0x03 when compressed), so
* the all-TRUE row needs a hand-made prefix. One call per operand:
* 0x05 -> (T,T,T) reject, 0x04 -> (F,-), 0x02 -> (T,F,-),
* 0x03 -> (T,T,F). */
#ifdef HAVE_ECC_KEY_EXPORT
{
word32 xLen = (word32)sizeof(buf);
if (wc_ecc_export_x963(&key, buf, &xLen) == 0) {
static const byte prefixes[4] = { 0x05, 0x04, 0x02, 0x03 };
int i;
for (i = 0; i < 4; i++) {
ecc_key imp;
XMEMSET(&imp, 0, sizeof(imp));
if (wc_ecc_init(&imp) != 0)
break;
buf[0] = prefixes[i];
(void)_ecc_import_x963_ex2(buf, xLen, &imp, ECC_SECP256R1, 0);
wc_ecc_free(&imp);
}
}
else {
WB_NOTE("wc_ecc_export_x963 failed; 11485 vectors skipped");
}
}
#endif /* HAVE_ECC_KEY_EXPORT */
/* ---- wc_X963_KDF X9.63 hash whitelist (17023). Five operands, so five
* accepted algorithms plus one rejected one; only the SHA224 and SHA384
* rows are missing from ordinary use (the KDF is always driven with
* SHA-256 in ECIES). */
#ifdef HAVE_X963_KDF
{
static const byte secret[32] = { 0 };
byte out[32];
(void)wc_X963_KDF(WC_HASH_TYPE_SHA, secret, sizeof(secret), NULL, 0,
out, sizeof(out));
#ifdef WOLFSSL_SHA224
(void)wc_X963_KDF(WC_HASH_TYPE_SHA224, secret, sizeof(secret), NULL, 0,
out, sizeof(out));
#endif
(void)wc_X963_KDF(WC_HASH_TYPE_SHA256, secret, sizeof(secret), NULL, 0,
out, sizeof(out));
#ifdef WOLFSSL_SHA384
(void)wc_X963_KDF(WC_HASH_TYPE_SHA384, secret, sizeof(secret), NULL, 0,
out, sizeof(out));
#endif
#ifdef WOLFSSL_SHA512
(void)wc_X963_KDF(WC_HASH_TYPE_SHA512, secret, sizeof(secret), NULL, 0,
out, sizeof(out));
#endif
/* all five operands TRUE */
(void)wc_X963_KDF(WC_HASH_TYPE_MD5, secret, sizeof(secret), NULL, 0,
out, sizeof(out));
}
#endif /* HAVE_X963_KDF */
/* ---- wc_ecc_is_point coordinate range guards (10836 / 10843)
* if ((mp_cmp(ecp->x, prime) != MP_LT) || mp_isneg(ecp->x))
* The public import paths reduce/reject out-of-range coordinates before
* building a point, so only a hand-built point reaches these with a
* coordinate >= p. (The mp_isneg operand of each is a separate matter --
* see the residual note in the harness report.) */
{
mp_int prime, af, bf;
ecc_point* pt = wc_ecc_new_point();
XMEMSET(&prime, 0, sizeof(prime));
XMEMSET(&af, 0, sizeof(af));
XMEMSET(&bf, 0, sizeof(bf));
if ((pt != NULL) && (key.dp != NULL) &&
(mp_init(&prime) == MP_OKAY) && (mp_init(&af) == MP_OKAY) &&
(mp_init(&bf) == MP_OKAY) &&
(mp_read_radix(&prime, key.dp->prime, 16) == MP_OKAY) &&
(mp_read_radix(&af, key.dp->Af, 16) == MP_OKAY) &&
(mp_read_radix(&bf, key.dp->Bf, 16) == MP_OKAY) &&
(wc_ecc_copy_point(&key.pubkey, pt) == MP_OKAY)) {
/* all-false row: the key's own (valid, in-range, affine) point */
(void)wc_ecc_is_point(pt, &af, &bf, &prime);
/* x = p -> 10836 (T,-) */
if (mp_copy(&prime, pt->x) == MP_OKAY)
(void)wc_ecc_is_point(pt, &af, &bf, &prime);
/* restore x, then y = p -> 10843 (T,-) */
if ((wc_ecc_copy_point(&key.pubkey, pt) == MP_OKAY) &&
(mp_copy(&prime, pt->y) == MP_OKAY))
(void)wc_ecc_is_point(pt, &af, &bf, &prime);
}
else {
WB_NOTE("wc_ecc_is_point range vectors skipped (setup)");
}
mp_free(&bf);
mp_free(&af);
mp_free(&prime);
if (pt != NULL)
wc_ecc_del_point(pt);
}
/* ---- wc_ecc_free custom-curve teardown (8611)
* if (key->deallocSet && key->dp != NULL)
* deallocSet is only ever set by wc_ecc_set_custom_curve, which sets dp in
* the same breath, so the (T,F) row cannot arise from the API. Setting the
* flag by hand on a dp-less key is safe precisely because the guard is
* what stops the free. */
{
ecc_key k3;
/* (T,F): flag set, dp already NULL. Safe precisely because the guard
* is what stops wc_ecc_free_curve from running. */
XMEMSET(&k3, 0, sizeof(k3));
if (wc_ecc_init(&k3) == 0) {
k3.deallocSet = 1;
k3.dp = NULL;
wc_ecc_free(&k3);
}
#ifdef WOLFSSL_CUSTOM_CURVES
/* (T,T): the shape asn.c builds when it decodes a certificate with an
* EXPLICIT (specified) curve -- a heap ecc_set_type owned by the key.
* wc_ecc_free_curve XFREEs each string field only when non-NULL and
* then the struct itself, so an all-zero record allocated from the
* same pool is exactly what it expects. No public API produces this
* state, which is why the row is missing everywhere else. */
XMEMSET(&k3, 0, sizeof(k3));
if (wc_ecc_init(&k3) == 0) {
ecc_set_type* dyn = (ecc_set_type*)XMALLOC(sizeof(*dyn), NULL,
DYNAMIC_TYPE_ECC_BUFFER);
if (dyn != NULL) {
XMEMSET(dyn, 0, sizeof(*dyn));
k3.dp = dyn;
k3.deallocSet = 1;
}
wc_ecc_free(&k3);
}
#endif
}
wc_ecc_free(&peer);
wc_ecc_free(&key);
wc_FreeRng(&rng);
WB_NOTE("pass-2 argument/state gap vectors done");
}
/* ------------------------------------------------------------------------- *
* Custom-curve dispatch guards.
*
* ecc.c dispatches to the accelerated single-precision routines with
* if (key->idx != ECC_CUSTOM_IDX && ecc_sets[key->idx].id == <curve>)
* repeated for each SP-supported curve (make_pub 5599, uncompress 10221 /
* 10242, order check 11144 / 11165, on-curve check 11728, plus the pubkey
* copy at 7314). Nothing in the API test set builds a CUSTOM-curve key, so
* the first operand is never FALSE; and where only one SP curve is compiled
* in, the second operand is never FALSE either unless a key on a DIFFERENT
* standard curve is put through the same call.
*
* This runs each of those entry points three times: on a custom curve (idx ==
* ECC_CUSTOM_IDX), on SECP256R1, and on the largest other standard curve
* available. The custom curve is a verbatim copy of a standard curve's
* parameters with oidSz zeroed so wc_ecc_get_curve_idx_from_params cannot
* fold it back onto the table entry.
* ------------------------------------------------------------------------- */
#ifdef WOLFSSL_CUSTOM_CURVES
static void wb_custom_curve_dispatch(void)
{
WC_RNG rng;
ecc_key ck;
ecc_key sk;
ecc_set_type custom;
const ecc_set_type* base = NULL;
int idx;
byte buf[256];
word32 bufLen;
XMEMSET(&rng, 0, sizeof(rng));
XMEMSET(&ck, 0, sizeof(ck));
XMEMSET(&sk, 0, sizeof(sk));
XMEMSET(&custom, 0, sizeof(custom));
XMEMSET(buf, 0, sizeof(buf));
idx = wc_ecc_get_curve_idx(ECC_SECP256R1);
if (idx < 0) {
WB_NOTE("SECP256R1 not in ecc_sets[]; custom-curve pass skipped");
return;
}
base = &ecc_sets[idx];
/* A verbatim copy of a real curve's parameters is enough: what makes this
* a "custom" key is wc_ecc_set_custom_curve setting key->idx to
* ECC_CUSTOM_IDX, and wc_ecc_set_curve deliberately leaves that alone on
* every later call. Every dispatch guard below then short-circuits on its
* FIRST operand, which is exactly the row that is missing -- and it is
* also what keeps `ecc_sets[key->idx]` from being indexed with
* ECC_CUSTOM_IDX. Copying real parameters (rather than inventing them)
* keeps the key usable, so the calls run to completion instead of
* bailing out early. */
custom = *base;
if (wc_InitRng(&rng) != 0) {
WB_NOTE("wc_InitRng failed; custom-curve pass skipped");
return;
}
if ((wc_ecc_init(&ck) == 0) && (wc_ecc_init(&sk) == 0) &&
(wc_ecc_set_custom_curve(&ck, &custom) == 0) &&
(wc_ecc_make_key_ex(&rng, 0, &sk, ECC_SECP256R1) == 0)) {
/* wc_ecc_make_key_ex on the custom curve keeps idx == ECC_CUSTOM_IDX
* and walks 5599 / 7314 with the first operand FALSE. */
int madeCustom = (wc_ecc_make_key_ex(&rng, base->size, &ck,
ECC_CURVE_DEF) == 0);
if (madeCustom) {
/* 11144 / 11165 / 11728: full public-key validation. */
(void)wc_ecc_check_key(&ck);
/* 7314: the ECDH/keygen path that copies custom params onto a
* freshly made pubkey. */
#ifdef HAVE_ECC_DHE
PRIVATE_KEY_UNLOCK();
bufLen = (word32)sizeof(buf);
(void)wc_ecc_shared_secret(&ck, &ck, buf, &bufLen);
PRIVATE_KEY_LOCK();
#endif
#ifdef HAVE_ECC_SIGN
/* 7314: ecc_sign_hash_sw copies the custom parameters onto the
* ephemeral pubkey it builds for the nonce point. Only a SIGN with
* a custom-curve key reaches it. */
{
byte dig[32];
byte sig[ECC_MAX_SIG_SIZE];
word32 sigSz = (word32)sizeof(sig);
XMEMSET(dig, 0x9c, sizeof(dig));
XMEMSET(sig, 0, sizeof(sig));
(void)wc_ecc_sign_hash(dig, (word32)sizeof(dig), sig, &sigSz,
&rng, &ck);
}
#endif
#if defined(HAVE_COMP_KEY) && defined(HAVE_ECC_KEY_EXPORT)
/* 10221 / 10242: the compressed-point uncompress dispatch. */
bufLen = (word32)sizeof(buf);
if (wc_ecc_export_x963_compressed(&ck, buf, &bufLen) == 0) {
ecc_point* pt = wc_ecc_new_point();
if (pt != NULL) {
(void)wc_ecc_import_point_der(buf, bufLen, ck.idx, pt);
wc_ecc_del_point(pt);
}
}
#endif
}
else {
WB_NOTE("custom-curve key generation failed");
}
/* Same entry points on a STANDARD curve so each decision's (T,T)/(T,F)
* rows live in this binary next to the (F,-) rows. */
(void)wc_ecc_check_key(&sk);
#ifdef HAVE_ECC_SIGN
{
byte dig[32];
byte sig[ECC_MAX_SIG_SIZE];
word32 sigSz = (word32)sizeof(sig);
XMEMSET(dig, 0x9c, sizeof(dig));
XMEMSET(sig, 0, sizeof(sig));
(void)wc_ecc_sign_hash(dig, (word32)sizeof(dig), sig, &sigSz, &rng,
&sk);
}
#endif
#ifdef HAVE_ECC_DHE
PRIVATE_KEY_UNLOCK();
bufLen = (word32)sizeof(buf);
(void)wc_ecc_shared_secret(&sk, &sk, buf, &bufLen);
PRIVATE_KEY_LOCK();
#endif
#if defined(HAVE_COMP_KEY) && defined(HAVE_ECC_KEY_EXPORT)
bufLen = (word32)sizeof(buf);
if (wc_ecc_export_x963_compressed(&sk, buf, &bufLen) == 0) {
ecc_point* pt = wc_ecc_new_point();
if (pt != NULL) {
(void)wc_ecc_import_point_der(buf, bufLen, sk.idx, pt);
wc_ecc_del_point(pt);
}
}
#endif
/* And on a non-SECP256R1 standard curve, which is what makes each
* dispatch's `ecc_sets[idx].id == <curve>` operand FALSE with the
* first operand still TRUE. Prefer the largest curve compiled in so
* the SP_384/SP_521 arms of the same chain are reached too. */
{
static const int others[] = {
#ifdef HAVE_ECC521
ECC_SECP521R1,
#endif
#ifdef HAVE_ECC384
ECC_SECP384R1,
#endif
#ifdef HAVE_ECC224
ECC_SECP224R1,
#endif
ECC_SECP256R1 /* fallback: never absent, adds nothing new */
};
size_t i;
for (i = 0; i < sizeof(others) / sizeof(others[0]); i++) {
ecc_key ok;
XMEMSET(&ok, 0, sizeof(ok));
if (wc_ecc_init(&ok) != 0)
break;
if (wc_ecc_make_key_ex(&rng, 0, &ok, (int)others[i]) == 0) {
(void)wc_ecc_check_key(&ok);
#ifdef HAVE_ECC_DHE
PRIVATE_KEY_UNLOCK();
bufLen = (word32)sizeof(buf);
(void)wc_ecc_shared_secret(&ok, &ok, buf, &bufLen);
PRIVATE_KEY_LOCK();
#endif
#if defined(HAVE_COMP_KEY) && defined(HAVE_ECC_KEY_EXPORT)
bufLen = (word32)sizeof(buf);
if (wc_ecc_export_x963_compressed(&ok, buf, &bufLen) == 0) {
ecc_point* pt = wc_ecc_new_point();
if (pt != NULL) {
(void)wc_ecc_import_point_der(buf, bufLen, ok.idx,
pt);
wc_ecc_del_point(pt);
}
}
#endif
}
wc_ecc_free(&ok);
}
}
}
else {
WB_NOTE("custom-curve setup failed; pass skipped");
}
wc_ecc_free(&sk);
wc_ecc_free(&ck);
wc_FreeRng(&rng);
WB_NOTE("custom-curve dispatch vectors done");
}
#else
static void wb_custom_curve_dispatch(void)
{
WB_NOTE("WOLFSSL_CUSTOM_CURVES off; custom-curve dispatch skipped");
}
#endif /* WOLFSSL_CUSTOM_CURVES */
/* ------------------------------------------------------------------------- *
* ECIES AES-GCM cluster (15269 / 15809 / 16157 / 16221).
*
* ecc_is_gcm() and the two `ret == 0 && [!]ecc_is_gcm(ctx->encAlgo)` guards
* only separate once the SAME binary runs both a GCM and a non-GCM ECIES
* exchange; the API suite runs the default AES-128-CBC + HMAC-SHA256 suite
* only. 16221's ConstantCompare operand additionally needs a decrypt whose
* MAC does not match, i.e. a deliberately corrupted ciphertext.
* ------------------------------------------------------------------------- */
#if defined(HAVE_ECC_ENCRYPT) && defined(HAVE_HKDF) && !defined(NO_AES) && \
!defined(NO_HMAC)
static void wb_ecies_algos(void)
{
static const byte encAlgos[3] = { ecAES_128_CBC, ecAES_128_GCM,
ecAES_256_GCM };
WC_RNG rng;
ecc_key a;
ecc_key b;
byte plain[32];
byte enc[256];
byte dec[256];
size_t i;
XMEMSET(&rng, 0, sizeof(rng));
XMEMSET(&a, 0, sizeof(a));
XMEMSET(&b, 0, sizeof(b));
XMEMSET(plain, 0x5a, sizeof(plain));
if (wc_InitRng(&rng) != 0) {
WB_NOTE("wc_InitRng failed; ECIES pass skipped");
return;
}
/* One full exchange per (algorithm, corrupt?) pair. Fresh contexts every
* time: wc_ecc_ctx_set_peer_salt MIXES the two salts in place and advances
* a state machine that rejects a second call, so a context cannot be
* reused and the two own-salts must be COPIED OUT before either side is
* mixed. */
for (i = 0; i < (sizeof(encAlgos) / sizeof(encAlgos[0])) * 2; i++) {
byte algo = encAlgos[i / 2];
int corrupt = (int)(i % 2);
ecEncCtx* ec = wc_ecc_ctx_new(REQ_RESP_CLIENT, &rng);
ecEncCtx* dc = wc_ecc_ctx_new(REQ_RESP_SERVER, &rng);
word32 encSz = (word32)sizeof(enc);
word32 decSz = (word32)sizeof(dec);
byte saltCli[EXCHANGE_SALT_SZ];
byte saltSrv[EXCHANGE_SALT_SZ];
const byte* sp;
int ok = 0;
/* A FRESH key pair every time: in the non-WOLFSSL_ECIES_OLD message
* format wc_ecc_decrypt imports the sender's ephemeral point into the
* ecc_key passed as its pubKey argument, which leaves that key a
* public-only key and makes the next wc_ecc_encrypt reject it. */
XMEMSET(&a, 0, sizeof(a));
XMEMSET(&b, 0, sizeof(b));
if ((wc_ecc_init(&a) != 0) || (wc_ecc_init(&b) != 0) ||
(wc_ecc_make_key_ex(&rng, 0, &a, ECC_SECP256R1) != 0) ||
(wc_ecc_make_key_ex(&rng, 0, &b, ECC_SECP256R1) != 0)) {
WB_NOTE("ECIES key setup failed");
wc_ecc_free(&b);
wc_ecc_free(&a);
wc_ecc_ctx_free(dc);
wc_ecc_ctx_free(ec);
break;
}
if ((ec != NULL) && (dc != NULL)) {
sp = wc_ecc_ctx_get_own_salt(ec);
if (sp != NULL) {
XMEMCPY(saltCli, sp, EXCHANGE_SALT_SZ);
sp = wc_ecc_ctx_get_own_salt(dc);
if (sp != NULL) {
XMEMCPY(saltSrv, sp, EXCHANGE_SALT_SZ);
ok = (wc_ecc_ctx_set_peer_salt(ec, saltSrv) == 0) &&
(wc_ecc_ctx_set_peer_salt(dc, saltCli) == 0) &&
(wc_ecc_ctx_set_algo(ec, algo, ecHKDF_SHA256,
ecHMAC_SHA256) == 0) &&
(wc_ecc_ctx_set_algo(dc, algo, ecHKDF_SHA256,
ecHMAC_SHA256) == 0);
}
}
}
if (ok) {
ok = (wc_ecc_encrypt(&a, &b, plain, (word32)sizeof(plain), enc,
&encSz, ec) == 0) && (encSz > 0);
}
if (ok) {
if (corrupt) {
/* 16221: break the authenticator so ConstantCompare (HMAC
* suites) / the GCM tag check reports a mismatch. */
enc[encSz - 1] ^= 0xff;
}
(void)wc_ecc_decrypt(&b, &a, enc, encSz, dec, &decSz, dc);
}
wc_ecc_ctx_free(dc);
wc_ecc_ctx_free(ec);
wc_ecc_free(&b);
wc_ecc_free(&a);
}
/* The trailing one-byte-message vector needs a live pair of its own. */
XMEMSET(&a, 0, sizeof(a));
XMEMSET(&b, 0, sizeof(b));
if ((wc_ecc_init(&a) != 0) || (wc_ecc_init(&b) != 0) ||
(wc_ecc_make_key_ex(&rng, 0, &a, ECC_SECP256R1) != 0) ||
(wc_ecc_make_key_ex(&rng, 0, &b, ECC_SECP256R1) != 0)) {
WB_NOTE("ECIES tail key setup failed");
wc_ecc_free(&b);
wc_ecc_free(&a);
wc_FreeRng(&rng);
return;
}
/* 15978 idx0: `(msgSz > 1) && ((msg[0] == 0x02) || (msg[0] == 0x03))`
* -- a one-byte message is shorter than any real ECIES blob, so only a
* direct call supplies the FALSE row of the length operand. */
{
ecEncCtx* dc = wc_ecc_ctx_new(REQ_RESP_SERVER, &rng);
word32 decSz = (word32)sizeof(dec);
byte one = 0x02;
if (dc != NULL) {
(void)wc_ecc_decrypt(&b, &a, &one, 1, dec, &decSz, dc);
wc_ecc_ctx_free(dc);
}
/* (T,T,-) and (T,F,T): longer than one byte AND a compressed-point
* prefix, once for each parity tag. Only an ECIES sender that chose
* point compression emits these, and nothing in the harness does --
* which also made the 0x03 operand a COIN FLIP before this vector
* existed: it was only covered when a random ephemeral key happened to
* have an odd y, so the module's number moved between runs of an
* unchanged tree. Issuing both tags explicitly pins it. The buffer does
* not need to decrypt: the decision is reached before any of it is
* parsed. */
{
static const byte tags[2] = { 0x02, 0x03 };
size_t t;
for (t = 0; t < sizeof(tags) / sizeof(tags[0]); t++) {
dc = wc_ecc_ctx_new(REQ_RESP_SERVER, &rng);
if (dc != NULL) {
byte comp[80];
XMEMSET(comp, 0, sizeof(comp));
comp[0] = tags[t];
decSz = (word32)sizeof(dec);
(void)wc_ecc_decrypt(&b, &a, comp, (word32)sizeof(comp),
dec, &decSz, dc);
wc_ecc_ctx_free(dc);
}
}
}
}
wc_ecc_free(&b);
wc_ecc_free(&a);
wc_FreeRng(&rng);
WB_NOTE("ECIES CBC/GCM + corrupted-MAC vectors done");
}
#else
static void wb_ecies_algos(void)
{
WB_NOTE("HAVE_ECC_ENCRYPT/HKDF/AES/HMAC off; ECIES pass skipped");
}
#endif
/* ========================================================================= *
* Pass 3 -- forged keys and points.
*
* What is left after pass 2 is dominated by validation guards that only fire
* on keys and points that are deliberately WRONG: an ordinate wider than the
* field prime (or negative modulo it), a point that is not on the curve, a
* private scalar of 0 / of n / negative, a curve index that disagrees with the
* key's own parameters. No correct operation can produce those, so rather than
* hunting for one, the vectors below BUILD the exact shape each guard rejects
* and hand it straight to the file-static helper that owns the guard -- which
* this TU can do because it #includes ecc.c.
*
* Calling the helpers directly is also what makes the SP-dispatch rows
* reachable at all: every public entry point (wc_ecc_check_key,
* wc_ecc_sign_hash, wc_ecc_import_point_der) either returns from an SP fast
* path before the generic helper runs, or rejects the odd shape earlier, so
* from the API only ONE side of each dispatch decision is ever taken.
*
* Determinism: every value comes from ecc_sets[] -- the curve's own generator
* and its published prime/order -- or from a small fixed integer. Nothing here
* depends on live entropy, so the pass contributes identical MC/DC rows on
* every run.
*
* Crash-safety: each forged shape is rejected by the very guard it targets, or
* (the off-curve / wrong-modulus vectors) is processed as plain modular
* arithmetic whose result is never dereferenced. Buffers are sized from
* ecc_sets[].size exactly as the real callers size them.
* ========================================================================= */
/* Curves the pass-3 vectors run over: P-256 (the curve every SP build
* accelerates) plus the larger and the smaller standard curves, so each
* `ecc_sets[idx].id == <curve>` dispatch operand gets both a matching and a
* non-matching key inside one binary. */
static const int wbCurveIds[] = {
ECC_SECP256R1,
ECC_SECP384R1,
ECC_SECP521R1,
ECC_SECP224R1
};
#define WB_CURVE_COUNT ((int)(sizeof(wbCurveIds) / sizeof(wbCurveIds[0])))
/* Curve availability is a RUNTIME question here, not a compile-time one: the
* per-curve HAVE_ECCnnn macros are only set when a build hand-picks curves,
* and this suite's configs take the HAVE_ALL_CURVES default instead -- so
* guarding on them would silently reduce every sweep below to P-256 and leave
* each `ecc_sets[idx].id == <curve>` operand permanently TRUE. Asking the
* table is correct for both kinds of build. */
static int wb_curve_present(int curveId)
{
return wc_ecc_get_curve_idx(curveId) >= 0;
}
/* Make *a hold -1, but only when the math backend can represent a negative
* mp_int; returns 1 when it did.
*
* Under SP math without WOLFSSL_SP_INT_NEGATIVE, mp_isneg() expands to the
* literal (0) and no mp_int is ever negative, so the mp_isneg operand of every
* range guard is dead there and each caller simply skips its negative vector.
* The fastmath variant links tfm, where sign is a real field, and that is the
* build in which these rows are produced. The value is built in a scratch
* mp_int and only copied over on success, so a backend that refuses the
* subtraction leaves the caller's operand untouched. */
static int wb_set_neg_one(mp_int* a)
{
int ok = 0;
mp_int t[2];
XMEMSET(t, 0, sizeof(t));
if (mp_init_multi(&t[0], &t[1], NULL, NULL, NULL, NULL) != MP_OKAY)
return 0;
if ((mp_set(&t[0], 0) == MP_OKAY) && (mp_set(&t[1], 1) == MP_OKAY) &&
(mp_sub(&t[0], &t[1], &t[0]) == MP_OKAY) &&
(mp_isneg(&t[0]) != 0) && (mp_copy(&t[0], a) == MP_OKAY)) {
ok = 1;
}
mp_free(&t[1]);
mp_free(&t[0]);
return ok;
}
/* Reset pt to the curve's own generator: a valid, in-range, affine point of
* full order -- the "all guards pass" half of every vector below. */
static int wb_point_from_generator(ecc_point* pt, ecc_curve_spec* curve)
{
int err = mp_copy(curve->Gx, pt->x);
if (err == MP_OKAY)
err = mp_copy(curve->Gy, pt->y);
if (err == MP_OKAY)
err = mp_set(pt->z, 1);
return err;
}
/* ------------------------------------------------------------------------- *
* ecc_check_pubkey_order() -- 11131 (ordinate wider than the modulus),
* 11144 / 11165 (the SP order-multiply dispatch) and 11174 (order*Q really is
* the identity).
*
* Reached from wc_ecc_check_key, which for an SP-supported curve returns from
* sp_ecc_check_key_<n>() long before this helper, and which no test drives
* with a CUSTOM-curve key -- so from the API the `key->idx != ECC_CUSTOM_IDX`
* operand is never FALSE and the `id == <curve>` operand never separates.
* ------------------------------------------------------------------------- */
#if defined(HAVE_ECC_CHECK_PUBKEY_ORDER) && !defined(WOLFSSL_SP_MATH)
static void wb_forged_pubkey_order(void)
{
int c;
for (c = 0; c < WB_CURVE_COUNT; c++) {
DECLARE_CURVE_SPECS(ECC_CURVE_FIELD_COUNT);
ecc_key key;
ecc_point* pt = NULL;
mp_int tmp[1];
int err = MP_OKAY;
int savedIdx;
XMEMSET(&key, 0, sizeof(key));
XMEMSET(tmp, 0, sizeof(tmp));
if (!wb_curve_present(wbCurveIds[c]))
continue;
if (wc_ecc_init(&key) != 0)
continue;
if (wc_ecc_set_curve(&key, 0, wbCurveIds[c]) != 0) {
wc_ecc_free(&key);
continue;
}
ALLOC_CURVE_SPECS(ECC_CURVE_FIELD_COUNT, err);
if (err == MP_OKAY)
err = wc_ecc_curve_load(key.dp, &curve, ECC_CURVE_FIELD_ALL);
if (err == MP_OKAY)
err = mp_init(tmp);
pt = wc_ecc_new_point();
if ((err != MP_OKAY) || (pt == NULL)) {
WB_NOTE("pubkey-order setup failed; curve skipped");
wb_fail = 1;
if (pt != NULL)
wc_ecc_del_point(pt);
mp_free(tmp);
if (err == MP_OKAY)
wc_ecc_curve_free(curve);
FREE_CURVE_SPECS();
wc_ecc_free(&key);
continue;
}
/* 11131 (F,F,F) + 11144/11165 (T,T) or (T,F) + 11174 (T,F): the
* curve's own generator, whose order-multiple IS the identity. */
if (wb_point_from_generator(pt, curve) == MP_OKAY)
(void)ecc_check_pubkey_order(&key, pt, curve->Af, curve->prime,
curve->order);
/* 11131 (T,-,-) / (F,T,-) / (F,F,T): one ordinate two times the
* modulus, i.e. one bit wider than it. Rejected before any point math
* runs, so an ordinate that is not a field element is never used. */
if ((mp_copy(curve->prime, tmp) == MP_OKAY) &&
(mp_add(tmp, tmp, tmp) == MP_OKAY)) {
if ((wb_point_from_generator(pt, curve) == MP_OKAY) &&
(mp_copy(tmp, pt->x) == MP_OKAY))
(void)ecc_check_pubkey_order(&key, pt, curve->Af, curve->prime,
curve->order);
if ((wb_point_from_generator(pt, curve) == MP_OKAY) &&
(mp_copy(tmp, pt->y) == MP_OKAY))
(void)ecc_check_pubkey_order(&key, pt, curve->Af, curve->prime,
curve->order);
if ((wb_point_from_generator(pt, curve) == MP_OKAY) &&
(mp_copy(tmp, pt->z) == MP_OKAY))
(void)ecc_check_pubkey_order(&key, pt, curve->Af, curve->prime,
curve->order);
}
/* 11144 / 11165 (F,-): a key whose index says "custom" while its dp
* still points at a real curve. That is exactly the shape
* wc_ecc_set_custom_curve() leaves behind, and it is the only way the
* first operand of each SP dispatch is FALSE. */
savedIdx = key.idx;
key.idx = ECC_CUSTOM_IDX;
if (wb_point_from_generator(pt, curve) == MP_OKAY)
(void)ecc_check_pubkey_order(&key, pt, curve->Af, curve->prime,
curve->order);
/* 11174 (T,T): a point that is NOT on the curve, so order*Q is not the
* identity and the guard rejects it. Kept on the custom index so the
* generic multiply -- the one this guard follows -- is what runs. */
if ((wb_point_from_generator(pt, curve) == MP_OKAY) &&
(mp_add_d(pt->y, 1, pt->y) == MP_OKAY))
(void)ecc_check_pubkey_order(&key, pt, curve->Af, curve->prime,
curve->order);
/* 11174 (F,-): prime+1 is even, so the Montgomery setup inside the
* multiply refuses it and the guard is reached with err already set.
* Still one bit-count wider than every ordinate, so the 11131 guard
* above lets it through to here. */
if ((wb_point_from_generator(pt, curve) == MP_OKAY) &&
(mp_copy(curve->prime, tmp) == MP_OKAY) &&
(mp_add_d(tmp, 1, tmp) == MP_OKAY))
(void)ecc_check_pubkey_order(&key, pt, curve->Af, tmp,
curve->order);
key.idx = savedIdx;
wc_ecc_del_point(pt);
mp_free(tmp);
wc_ecc_curve_free(curve);
FREE_CURVE_SPECS();
wc_ecc_free(&key);
}
WB_NOTE("forged pubkey-order vectors done");
}
#else
static void wb_forged_pubkey_order(void)
{
WB_NOTE("pubkey-order check not compiled in; forged vectors skipped");
}
#endif /* HAVE_ECC_CHECK_PUBKEY_ORDER && !WOLFSSL_SP_MATH */
/* ------------------------------------------------------------------------- *
* ecc_make_pub_sw() -- 5553 (private scalar out of range) and 5599 (the
* base-point-multiply SP dispatch).
*
* Every public path into this helper generates or imports a scalar that has
* already been range-checked, so the three operands of the range guard are
* never TRUE; and the dispatch chain is only entered for keys whose curve the
* public caller already resolved.
* ------------------------------------------------------------------------- */
#if defined(HAVE_ECC_MAKE_PUB) && !defined(WOLF_CRYPTO_CB_ONLY_ECC)
static void wb_forged_make_pub_sw(void)
{
int c;
WC_RNG deadRng;
/* A never-initialized WC_RNG: its status is WC_DRBG_NOT_INIT, so every
* wc_RNG_GenerateBlock() through it fails. That is the lever for the
* "err is not MP_OKAY" halves inside ecc_mulmod()'s z-randomization and
* for the `err == MP_OKAY && map` guard that follows the multiply --
* a computation failure no allocation fault can produce. */
XMEMSET(&deadRng, 0, sizeof(deadRng));
for (c = 0; c < WB_CURVE_COUNT; c++) {
DECLARE_CURVE_SPECS(ECC_CURVE_FIELD_COUNT);
ecc_key key;
ecc_point* pub = NULL;
int err = MP_OKAY;
int savedIdx;
XMEMSET(&key, 0, sizeof(key));
if (!wb_curve_present(wbCurveIds[c]))
continue;
if (wc_ecc_init(&key) != 0)
continue;
if (wc_ecc_set_curve(&key, 0, wbCurveIds[c]) != 0) {
wc_ecc_free(&key);
continue;
}
ALLOC_CURVE_SPECS(ECC_CURVE_FIELD_COUNT, err);
if (err == MP_OKAY)
err = wc_ecc_curve_load(key.dp, &curve, ECC_CURVE_FIELD_ALL);
pub = wc_ecc_new_point();
if ((err != MP_OKAY) || (pub == NULL)) {
WB_NOTE("make-pub-sw setup failed; curve skipped");
wb_fail = 1;
if (pub != NULL)
wc_ecc_del_point(pub);
if (err == MP_OKAY)
wc_ecc_curve_free(curve);
FREE_CURVE_SPECS();
wc_ecc_free(&key);
continue;
}
savedIdx = key.idx;
/* 5553 (F,F,F) + 5599 (T,T) or (T,F): a scalar of 1, so the result is
* the generator itself. */
if (mp_set(ecc_get_k(&key), 1) == MP_OKAY)
(void)ecc_make_pub_sw(&key, curve, pub, NULL);
/* 5599 (F,-): same scalar, "custom" index -- the generic multiply. */
key.idx = ECC_CUSTOM_IDX;
if (mp_set(ecc_get_k(&key), 1) == MP_OKAY)
(void)ecc_make_pub_sw(&key, curve, pub, NULL);
/* 3296/3298/3314 and the `err == MP_OKAY && map` guards after the
* multiply: a dead RNG makes the z-randomization fail part-way, which
* is the only way those success-chain operands go FALSE. */
if (mp_set(ecc_get_k(&key), 1) == MP_OKAY)
(void)ecc_make_pub_sw(&key, curve, pub, &deadRng);
key.idx = savedIdx;
/* 5553 (T,-,-): the scalar zero. */
if (mp_set(ecc_get_k(&key), 0) == MP_OKAY)
(void)ecc_make_pub_sw(&key, curve, pub, NULL);
/* 5553 (F,F,T): the scalar n -- in range for the field, out of range
* for the group. */
if (mp_copy(curve->order, ecc_get_k(&key)) == MP_OKAY)
(void)ecc_make_pub_sw(&key, curve, pub, NULL);
/* 5553 (F,T,-): a negative scalar (fastmath/tfm only). */
if (wb_set_neg_one(ecc_get_k(&key)))
(void)ecc_make_pub_sw(&key, curve, pub, NULL);
(void)mp_set(ecc_get_k(&key), 1);
wc_ecc_del_point(pub);
wc_ecc_curve_free(curve);
FREE_CURVE_SPECS();
wc_ecc_free(&key);
}
WB_NOTE("forged make-pub-sw vectors done");
}
#else
static void wb_forged_make_pub_sw(void)
{
WB_NOTE("HAVE_ECC_MAKE_PUB off; forged make-pub-sw vectors skipped");
}
#endif /* HAVE_ECC_MAKE_PUB && !WOLF_CRYPTO_CB_ONLY_ECC */
/* ------------------------------------------------------------------------- *
* _ecc_validate_public_key() -- 11343 / 11351 (negative ordinate) and 11374
* (the five-operand private-scalar bound check).
*
* wc_ecc_check_key() hands an SP-supported curve to sp_ecc_check_key_<n>()
* and returns, so on the accelerated variants this body is only entered for a
* key whose index says "custom". Forcing that index is therefore what makes
* the whole decision reachable, and hand-set scalars/ordinates are what
* separate its operands.
* ------------------------------------------------------------------------- */
#if defined(HAVE_ECC_CHECK_PUBKEY_ORDER) && !defined(WOLFSSL_SP_MATH)
static void wb_forged_validate_public_key(void)
{
DECLARE_CURVE_SPECS(ECC_CURVE_FIELD_COUNT);
ecc_key key;
int err = MP_OKAY;
XMEMSET(&key, 0, sizeof(key));
if (wc_ecc_init(&key) != 0) {
WB_NOTE("validate-public-key init failed");
wb_fail = 1;
return;
}
if (wc_ecc_set_curve(&key, 0, ECC_SECP256R1) != 0) {
wc_ecc_free(&key);
WB_NOTE("validate-public-key set_curve failed");
wb_fail = 1;
return;
}
ALLOC_CURVE_SPECS(ECC_CURVE_FIELD_COUNT, err);
if (err == MP_OKAY)
err = wc_ecc_curve_load(key.dp, &curve, ECC_CURVE_FIELD_ALL);
if (err != MP_OKAY) {
FREE_CURVE_SPECS();
wc_ecc_free(&key);
WB_NOTE("validate-public-key curve load failed");
wb_fail = 1;
return;
}
/* A consistent private key built entirely from the curve table: Q = G and
* d = 1. dp keeps pointing at the real curve; only the index is forced to
* "custom" so the generic validation body runs on every variant. */
if ((wb_point_from_generator(&key.pubkey, curve) == MP_OKAY) &&
(mp_set(ecc_get_k(&key), 1) == MP_OKAY)) {
key.idx = ECC_CUSTOM_IDX;
key.type = ECC_PRIVATEKEY;
/* 11374 (T,T,F,F,F): everything in range. partial = 1 keeps the
* (separately covered) order multiply out of the way. */
(void)_ecc_validate_public_key(&key, 1, 1);
/* 11374 (T,F,-,-,-): a key that carries no cached public point is
* still asked for the private-side check. */
key.type = ECC_PRIVATEKEY_ONLY;
(void)_ecc_validate_public_key(&key, 1, 1);
key.type = ECC_PRIVATEKEY;
/* 11374 (T,T,T,-,-): the scalar zero. */
if (mp_set(ecc_get_k(&key), 0) == MP_OKAY)
(void)_ecc_validate_public_key(&key, 1, 1);
/* 11374 (T,T,F,F,T): the scalar n. */
if (mp_copy(curve->order, ecc_get_k(&key)) == MP_OKAY)
(void)_ecc_validate_public_key(&key, 1, 1);
/* 11374 (T,T,F,T,-): a negative scalar (fastmath/tfm only). */
if (wb_set_neg_one(ecc_get_k(&key)))
(void)_ecc_validate_public_key(&key, 1, 1);
/* 11374 (F,-,-,-,-): a public point one unit off the curve, so the
* on-curve check leaves err set before the private-side guard. */
if ((mp_set(ecc_get_k(&key), 1) == MP_OKAY) &&
(mp_add_d(key.pubkey.y, 1, key.pubkey.y) == MP_OKAY))
(void)_ecc_validate_public_key(&key, 1, 1);
/* 11343 (F,T) / 11351 (F,T): a NEGATIVE ordinate compares less-than
* the prime, so only the mp_isneg operand can reject it (fastmath). */
if (wb_point_from_generator(&key.pubkey, curve) == MP_OKAY) {
if (wb_set_neg_one(key.pubkey.x))
(void)_ecc_validate_public_key(&key, 1, 0);
}
if (wb_point_from_generator(&key.pubkey, curve) == MP_OKAY) {
if (wb_set_neg_one(key.pubkey.y))
(void)_ecc_validate_public_key(&key, 1, 0);
}
(void)wb_point_from_generator(&key.pubkey, curve);
}
else {
WB_NOTE("validate-public-key vector setup failed");
wb_fail = 1;
}
wc_ecc_curve_free(curve);
FREE_CURVE_SPECS();
key.idx = 0;
wc_ecc_free(&key);
WB_NOTE("forged validate-public-key vectors done");
}
#else
static void wb_forged_validate_public_key(void)
{
WB_NOTE("public-key validation not compiled in; forged vectors skipped");
}
#endif /* HAVE_ECC_CHECK_PUBKEY_ORDER && !WOLFSSL_SP_MATH */
/* ------------------------------------------------------------------------- *
* wc_ecc_is_point() -- 10836 / 10843 mp_isneg halves.
*
* pass 2 supplied the x == p and y == p rows (the mp_cmp operand). The second
* operand of each needs an ordinate that is LESS than the prime and still
* invalid, which only a negative value is -- unrepresentable under SP math,
* real under tfm.
* ------------------------------------------------------------------------- */
static void wb_forged_is_point_neg(void)
{
DECLARE_CURVE_SPECS(ECC_CURVE_FIELD_COUNT);
ecc_point* pt = NULL;
ecc_key key;
int err = MP_OKAY;
XMEMSET(&key, 0, sizeof(key));
if (wc_ecc_init(&key) != 0)
return;
if (wc_ecc_set_curve(&key, 0, ECC_SECP256R1) != 0) {
wc_ecc_free(&key);
return;
}
ALLOC_CURVE_SPECS(ECC_CURVE_FIELD_COUNT, err);
if (err == MP_OKAY)
err = wc_ecc_curve_load(key.dp, &curve, ECC_CURVE_FIELD_ALL);
pt = wc_ecc_new_point();
if ((err == MP_OKAY) && (pt != NULL)) {
/* (F,F) reference row, then a negative x, then a negative y. */
if (wb_point_from_generator(pt, curve) == MP_OKAY)
(void)wc_ecc_is_point(pt, curve->Af, curve->Bf, curve->prime);
if ((wb_point_from_generator(pt, curve) == MP_OKAY) &&
wb_set_neg_one(pt->x))
(void)wc_ecc_is_point(pt, curve->Af, curve->Bf, curve->prime);
if ((wb_point_from_generator(pt, curve) == MP_OKAY) &&
wb_set_neg_one(pt->y))
(void)wc_ecc_is_point(pt, curve->Af, curve->Bf, curve->prime);
}
if (pt != NULL)
wc_ecc_del_point(pt);
if (err == MP_OKAY)
wc_ecc_curve_free(curve);
FREE_CURVE_SPECS();
wc_ecc_free(&key);
WB_NOTE("forged is-point range vectors done");
}
/* ------------------------------------------------------------------------- *
* Point decompression -- 10221 / 10242 (the SP uncompress dispatch), 10325
* and 11611 (the sqrt-parity adjust).
*
* wc_ecc_import_point_der() passes shortKeySize = 1, which makes the
* compressed branch compute keysize = inLen/2 and bail with a size mismatch
* BEFORE the decompression runs -- which is why every compressed vector in the
* API suite (and in pass 2) stops short of these decisions. Calling
* wc_ecc_import_point_der_ex() with shortKeySize = 0 is the documented way to
* import a compressed point, and is what gets past it.
*
* The parity adjust needs BOTH a square root that is odd and one that is even,
* against BOTH the 0x02 and 0x03 prefixes. Rather than depend on a generated
* key's y (whose parity is a coin flip -- a genuine source of run-to-run
* variation in this file's numbers), the vectors sweep a fixed list of small
* x values: those that are on the curve yield a deterministic root, and the
* list is long enough to contain both parities on every curve.
*
* Note the third operand, `mp_isodd(t2) == MP_NO`, is the exact complement of
* the first, `mp_isodd(t2) == MP_YES`: no pair of test vectors can vary one
* while holding the other fixed, so it has no independence pair by
* construction (see RESIDUALS).
* ------------------------------------------------------------------------- */
#ifdef HAVE_COMP_KEY
static void wb_uncompress_parity(void)
{
byte blob[1 + ECC_MAXSIZE];
int c;
for (c = 0; c < WB_CURVE_COUNT; c++) {
int idx = wc_ecc_get_curve_idx(wbCurveIds[c]);
int size;
int xv;
if (idx < 0)
continue;
size = ecc_sets[idx].size;
if ((size <= 0) || (size > ECC_MAXSIZE))
continue;
for (xv = 1; xv <= 12; xv++) {
int p;
for (p = 0; p < 2; p++) {
ecc_point* pt;
ecc_key ik;
XMEMSET(blob, 0, sizeof(blob));
blob[0] = (p == 0) ? ECC_POINT_COMP_EVEN : ECC_POINT_COMP_ODD;
blob[size] = (byte)xv; /* x = xv, big-endian, size bytes */
pt = wc_ecc_new_point();
if (pt != NULL) {
(void)wc_ecc_import_point_der_ex(blob, (word32)size + 1,
idx, pt, 0);
wc_ecc_del_point(pt);
}
/* Same blob through the key importer, whose own copy of the
* parity adjust is 11611. */
XMEMSET(&ik, 0, sizeof(ik));
if (wc_ecc_init(&ik) == 0) {
(void)_ecc_import_x963_ex2(blob, (word32)size + 1, &ik,
wbCurveIds[c], 0);
wc_ecc_free(&ik);
}
}
}
}
WB_NOTE("compressed-point parity vectors done");
}
#else
static void wb_uncompress_parity(void)
{
WB_NOTE("HAVE_COMP_KEY off; compressed-point parity vectors skipped");
}
#endif /* HAVE_COMP_KEY */
/* ------------------------------------------------------------------------- *
* _ecc_import_x963_ex2() untrusted branch -- 11728.
*
* The first operand only goes FALSE when the imported point is the identity,
* because the line above sets err in exactly that case; an all-zero X9.63 blob
* is that point. The second needs a key whose index says "custom", which no
* X9.63 import produces on its own.
* ------------------------------------------------------------------------- */
#ifdef HAVE_ECC_KEY_IMPORT
static void wb_untrusted_import(void)
{
byte blob[1 + 2 * ECC_MAXSIZE];
int idx = wc_ecc_get_curve_idx(ECC_SECP256R1);
int size;
if (idx < 0) {
WB_NOTE("SECP256R1 absent; untrusted-import vectors skipped");
return;
}
size = ecc_sets[idx].size;
if ((size <= 0) || (size > ECC_MAXSIZE))
return;
/* (F,-): x = y = 0 is the point at infinity, rejected one line earlier. */
{
ecc_key ik;
XMEMSET(blob, 0, sizeof(blob));
blob[0] = ECC_POINT_UNCOMP;
XMEMSET(&ik, 0, sizeof(ik));
if (wc_ecc_init(&ik) == 0) {
(void)_ecc_import_x963_ex2(blob, (word32)(2 * size + 1), &ik,
ECC_SECP256R1, 1);
wc_ecc_free(&ik);
}
}
/* (T,T) and (T,F): the generator, imported into a plain key and into a key
* that already carries a custom curve. */
{
DECLARE_CURVE_SPECS(2);
ecc_key ik;
int err = MP_OKAY;
ALLOC_CURVE_SPECS(2, err);
if (err == MP_OKAY)
err = wc_ecc_curve_load(&ecc_sets[idx], &curve,
ECC_CURVE_FIELD_GX | ECC_CURVE_FIELD_GY);
if (err == MP_OKAY) {
XMEMSET(blob, 0, sizeof(blob));
blob[0] = ECC_POINT_UNCOMP;
if ((mp_to_unsigned_bin_len(curve->Gx, blob + 1, size)
== MP_OKAY) &&
(mp_to_unsigned_bin_len(curve->Gy, blob + 1 + size, size)
== MP_OKAY)) {
XMEMSET(&ik, 0, sizeof(ik));
if (wc_ecc_init(&ik) == 0) {
(void)_ecc_import_x963_ex2(blob, (word32)(2 * size + 1),
&ik, ECC_SECP256R1, 1);
wc_ecc_free(&ik);
}
#ifdef WOLFSSL_CUSTOM_CURVES
XMEMSET(&ik, 0, sizeof(ik));
if (wc_ecc_init(&ik) == 0) {
if (wc_ecc_set_custom_curve(&ik, &ecc_sets[idx]) == 0)
(void)_ecc_import_x963_ex2(blob,
(word32)(2 * size + 1), &ik,
ECC_CURVE_DEF, 1);
ik.dp = NULL; /* borrowed from ecc_sets[]; not ours */
wc_ecc_free(&ik);
}
#endif
}
wc_ecc_curve_free(curve);
}
FREE_CURVE_SPECS();
}
WB_NOTE("untrusted-import vectors done");
}
#else
static void wb_untrusted_import(void)
{
WB_NOTE("HAVE_ECC_KEY_IMPORT off; untrusted-import vectors skipped");
}
#endif /* HAVE_ECC_KEY_IMPORT */
/* ------------------------------------------------------------------------- *
* Negative-value guards on the public import/convert helpers -- 12426
* (wc_ecc_rs_to_sig) and 12745 (_ecc_import_raw_private).
*
* Both read their operand from a hex STRING, and tfm's mp_read_radix accepts a
* leading '-'. That is the only way a negative scalar / signature component
* enters ecc.c at all; under SP math the read simply fails and the guard is
* skipped, which is why these rows only exist in the fastmath variant.
* ------------------------------------------------------------------------- */
static void wb_forged_negative_strings(void)
{
#ifdef HAVE_ECC_SIGN
{
byte out[128];
word32 outLen;
outLen = (word32)sizeof(out);
(void)wc_ecc_rs_to_sig("1", "2", out, &outLen); /* (F,F) */
outLen = (word32)sizeof(out);
(void)wc_ecc_rs_to_sig("-1", "2", out, &outLen); /* (T,-) */
outLen = (word32)sizeof(out);
(void)wc_ecc_rs_to_sig("1", "-2", out, &outLen); /* (F,T) */
}
#endif
#if defined(HAVE_ECC_KEY_IMPORT) && !defined(WOLFSSL_ECC_CURVE_STATIC)
{
int idx = wc_ecc_get_curve_idx(ECC_SECP256R1);
if (idx >= 0) {
static const char* const privs[3] = { "1", "0", "-1" };
int i;
/* (F,F), then (T,-) via a zero scalar, then (F,T) via a negative
* one. Public point is the curve's own generator, so nothing but
* the scalar is out of the ordinary. */
for (i = 0; i < 3; i++) {
ecc_key ik;
XMEMSET(&ik, 0, sizeof(ik));
if (wc_ecc_init(&ik) != 0)
continue;
(void)wc_ecc_import_raw(&ik, ecc_sets[idx].Gx,
ecc_sets[idx].Gy, privs[i], ecc_sets[idx].name);
wc_ecc_free(&ik);
}
}
}
#endif
WB_NOTE("negative-string import vectors done");
}
/* ------------------------------------------------------------------------- *
* ecc_check_order_minus_1() -- 3941.
*
* Compiled only where the fixed-point cache is off (the no_fp_shamir variant).
* Both operands reject a curve whose order or modulus is wider than the build
* supports; no entry in ecc_sets[] is, so only a hand-built mp_int gets there.
* The guard returns before the points are touched.
* ------------------------------------------------------------------------- */
#if !defined(FP_ECC) && !defined(WOLFSSL_SP_MATH) && defined(ECC_TIMING_RESISTANT)
static void wb_forged_order_minus_1(void)
{
DECLARE_CURVE_SPECS(ECC_CURVE_FIELD_COUNT);
ecc_key key;
ecc_point* tG = NULL;
ecc_point* R = NULL;
mp_int huge[1];
int err = MP_OKAY;
XMEMSET(&key, 0, sizeof(key));
XMEMSET(huge, 0, sizeof(huge));
if (wc_ecc_init(&key) != 0)
return;
if (wc_ecc_set_curve(&key, 0, ECC_SECP256R1) != 0) {
wc_ecc_free(&key);
return;
}
ALLOC_CURVE_SPECS(ECC_CURVE_FIELD_COUNT, err);
if (err == MP_OKAY)
err = wc_ecc_curve_load(key.dp, &curve, ECC_CURVE_FIELD_ALL);
if (err == MP_OKAY)
err = mp_init(huge);
tG = wc_ecc_new_point();
R = wc_ecc_new_point();
if ((err == MP_OKAY) && (tG != NULL) && (R != NULL) &&
(wb_point_from_generator(tG, curve) == MP_OKAY) &&
(mp_set(huge, 0) == MP_OKAY) &&
(mp_set_bit(huge, MAX_ECC_BITS_USE + 64) == MP_OKAY)) {
/* (T,-): an order wider than the build's maximum. */
(void)ecc_check_order_minus_1(curve->order, tG, R, curve->prime, huge);
/* (F,T): the order is fine, the modulus is not. */
(void)ecc_check_order_minus_1(curve->order, tG, R, huge, curve->order);
}
else if (err == MP_OKAY) {
WB_NOTE("order-minus-1 vector setup failed");
}
if (R != NULL)
wc_ecc_del_point(R);
if (tG != NULL)
wc_ecc_del_point(tG);
mp_free(huge);
if (err == MP_OKAY)
wc_ecc_curve_free(curve);
FREE_CURVE_SPECS();
wc_ecc_free(&key);
WB_NOTE("forged order-minus-1 vectors done");
}
#else
static void wb_forged_order_minus_1(void)
{
WB_NOTE("ecc_check_order_minus_1 not compiled in; vectors skipped");
}
#endif
/* ------------------------------------------------------------------------- *
* ecc_sign_hash_sw() custom-curve pubkey fixup -- 7314.
*
* wc_ecc_sign_hash() returns from sp_ecc_sign_<n>() for every SP-supported
* curve, so on the accelerated variants the ONLY key that reaches this helper
* through the API is a custom-curve one -- the second operand never separates.
* Calling the helper directly with the same key on both indices supplies both
* rows. dp keeps pointing at a real curve either way, so the ephemeral key the
* signer builds from dp->id is a normal one.
* ------------------------------------------------------------------------- */
#if defined(HAVE_ECC_SIGN) && !defined(WOLFSSL_SP_MATH) && \
!defined(WOLFSSL_ATECC508A) && !defined(WOLFSSL_ATECC608A) && \
!defined(WOLFSSL_MICROCHIP_TA100) && !defined(WOLFSSL_CRYPTOCELL) && \
!defined(WOLFSSL_KCAPI_ECC) && !defined(WOLFSSL_DHUK) && \
defined(WOLFSSL_CUSTOM_CURVES)
static void wb_sign_hash_sw_indices(void)
{
DECLARE_CURVE_SPECS(ECC_CURVE_FIELD_COUNT);
WC_RNG rng;
ecc_key key;
ecc_key eph;
mp_int ers[3];
int err = MP_OKAY;
int haveRng = 0;
int i;
XMEMSET(&rng, 0, sizeof(rng));
XMEMSET(&key, 0, sizeof(key));
XMEMSET(ers, 0, sizeof(ers));
if (wc_ecc_init(&key) != 0)
return;
if (wc_ecc_set_curve(&key, 0, ECC_SECP256R1) != 0) {
wc_ecc_free(&key);
return;
}
ALLOC_CURVE_SPECS(ECC_CURVE_FIELD_COUNT, err);
if (err == MP_OKAY)
err = wc_ecc_curve_load(key.dp, &curve, ECC_CURVE_FIELD_ALL);
if (err == MP_OKAY)
err = mp_init_multi(&ers[0], &ers[1], &ers[2], NULL, NULL, NULL);
if (err == MP_OKAY)
haveRng = (wc_InitRng(&rng) == 0);
if ((err == MP_OKAY) && haveRng &&
(mp_set(ecc_get_k(&key), 3) == MP_OKAY) &&
(mp_set(&ers[0], 0x2a) == MP_OKAY)) {
key.type = ECC_PRIVATEKEY;
/* i == 0: the table index -> (T,F). i == 1: "custom" -> (T,T), which
* copies key->dp onto the ephemeral key before it is generated. */
for (i = 0; i < 2; i++) {
key.idx = (i == 0) ? wc_ecc_get_curve_idx(ECC_SECP256R1)
: ECC_CUSTOM_IDX;
XMEMSET(&eph, 0, sizeof(eph));
if (wc_ecc_init(&eph) == 0) {
(void)ecc_sign_hash_sw(&key, &eph, &rng, curve, &ers[0],
&ers[1], &ers[2]);
/* The custom-curve fixup points eph.dp at key.dp, an entry of
* the static ecc_sets[] table. wc_ecc_set_custom_curve does
* not set deallocSet, so wc_ecc_free leaves it alone. */
wc_ecc_free(&eph);
}
}
key.idx = wc_ecc_get_curve_idx(ECC_SECP256R1);
}
if (haveRng)
wc_FreeRng(&rng);
mp_free(&ers[2]);
mp_free(&ers[1]);
mp_free(&ers[0]);
if (err == MP_OKAY)
wc_ecc_curve_free(curve);
FREE_CURVE_SPECS();
wc_ecc_free(&key);
WB_NOTE("sign-hash-sw index vectors done");
}
#else
static void wb_sign_hash_sw_indices(void)
{
WB_NOTE("ecc_sign_hash_sw not compiled in; index vectors skipped");
}
#endif
/* ------------------------------------------------------------------------- *
* wc_ecc_check_r_s_range() -- 9345.
*
* A signature produced by ecc.c always has r and s reduced mod n, and the
* verify paths that reach this helper only ever see such a pair. Handing it a
* hand-built (r, s) is the only way its second range check separates, and the
* only way it is reached with err already set by the first one.
* ------------------------------------------------------------------------- */
#if !defined(WOLF_CRYPTO_CB_ONLY_ECC) && !defined(WOLFSSL_PSOC6_CRYPTO) && \
(!defined(WOLFSSL_STM32_PKA) || defined(WC_STM32_PKA_SIGN_ONLY))
static void wb_forged_rs_range(void)
{
DECLARE_CURVE_SPECS(1);
ecc_key key;
mp_int rs[2];
int err = MP_OKAY;
XMEMSET(&key, 0, sizeof(key));
XMEMSET(rs, 0, sizeof(rs));
if (wc_ecc_init(&key) != 0)
return;
if (wc_ecc_set_curve(&key, 0, ECC_SECP256R1) != 0) {
wc_ecc_free(&key);
return;
}
ALLOC_CURVE_SPECS(1, err);
if (err == MP_OKAY)
err = wc_ecc_curve_load(key.dp, &curve, ECC_CURVE_FIELD_ORDER);
if (err == MP_OKAY)
err = mp_init_multi(&rs[0], &rs[1], NULL, NULL, NULL, NULL);
if (err == MP_OKAY) {
/* (T,F): both in range. */
if ((mp_set(&rs[0], 1) == MP_OKAY) && (mp_set(&rs[1], 1) == MP_OKAY))
(void)wc_ecc_check_r_s_range(&key, &rs[0], &rs[1]);
/* (T,T): s == n, which is one past the top of the range. */
if (mp_copy(curve->order, &rs[1]) == MP_OKAY)
(void)wc_ecc_check_r_s_range(&key, &rs[0], &rs[1]);
/* (F,-): r == n, so the r check has already set err. */
if ((mp_copy(curve->order, &rs[0]) == MP_OKAY) &&
(mp_set(&rs[1], 1) == MP_OKAY))
(void)wc_ecc_check_r_s_range(&key, &rs[0], &rs[1]);
mp_free(&rs[1]);
mp_free(&rs[0]);
wc_ecc_curve_free(curve);
}
FREE_CURVE_SPECS();
wc_ecc_free(&key);
WB_NOTE("forged r/s range vectors done");
}
#else
static void wb_forged_rs_range(void)
{
WB_NOTE("r/s range helper not compiled in; vectors skipped");
}
#endif
/* ------------------------------------------------------------------------- *
* RFC 6979 deterministic-k helpers -- 7939 (_HMAC_K) and 8205 / 8213
* (wc_ecc_gen_deterministic_k's retry loop).
*
* The retry loop only turns over when the candidate k falls outside [1, n-1].
* Against a real curve order that is a ~2^-128 event, so the API can never
* show it. Against a deliberately TINY order it happens on most iterations,
* and the whole chain stays deterministic because every input is fixed.
*
* _HMAC_K's `ret == 0` operand needs the HMAC itself to fail, which a hash
* type the build does not implement does -- inside wc_HmacSetKey, before any
* buffer is touched.
* ------------------------------------------------------------------------- */
#if defined(WOLFSSL_ECDSA_DETERMINISTIC_K) || \
defined(WOLFSSL_ECDSA_DETERMINISTIC_K_VARIANT)
static void wb_deterministic_k_retry(void)
{
byte K[WC_MAX_DIGEST_SIZE];
byte V[WC_MAX_DIGEST_SIZE];
byte x[8];
byte h1[8];
byte out[WC_MAX_DIGEST_SIZE];
byte intOct = 0x00;
mp_int nums[3];
XMEMSET(K, 0x00, sizeof(K));
XMEMSET(V, 0x01, sizeof(V));
XMEMSET(x, 0x11, sizeof(x));
XMEMSET(h1, 0x22, sizeof(h1));
XMEMSET(out, 0, sizeof(out));
XMEMSET(nums, 0, sizeof(nums));
/* 7939 (T,T) / (T,F): a working HMAC with and without the octet. */
(void)_HMAC_K(K, WC_SHA256_DIGEST_SIZE, V, WC_SHA256_DIGEST_SIZE,
h1, (word32)sizeof(h1), x, (word32)sizeof(x), &intOct, out,
WC_HASH_TYPE_SHA256, NULL);
(void)_HMAC_K(K, WC_SHA256_DIGEST_SIZE, V, WC_SHA256_DIGEST_SIZE,
NULL, 0, NULL, 0, NULL, out, WC_HASH_TYPE_SHA256, NULL);
/* 7939 (F,-): wc_HmacSetKey rejects the type, so ret is already set when
* the octet operand would otherwise be looked at. */
(void)_HMAC_K(K, WC_SHA256_DIGEST_SIZE, V, WC_SHA256_DIGEST_SIZE,
h1, (word32)sizeof(h1), x, (word32)sizeof(x), &intOct, out,
WC_HASH_TYPE_NONE, NULL);
/* 8205 / 8213: a one-byte order of 2 leaves exactly one acceptable
* candidate out of the four values a two-bit k can take, so the loop
* rejects and re-derives several times before it settles -- both halves
* of both `ret == 0 && err != 0` guards, from fixed inputs. */
if (mp_init_multi(&nums[0], &nums[1], &nums[2], NULL, NULL, NULL)
== MP_OKAY) {
byte hash[WC_SHA256_DIGEST_SIZE];
XMEMSET(hash, 0x5c, sizeof(hash));
if ((mp_set(&nums[0], 1) == MP_OKAY) && /* priv */
(mp_set(&nums[2], 2) == MP_OKAY)) { /* order */
(void)wc_ecc_gen_deterministic_k(hash, (word32)sizeof(hash),
WC_HASH_TYPE_SHA256, &nums[0], &nums[1], &nums[2], NULL);
}
mp_free(&nums[2]);
mp_free(&nums[1]);
mp_free(&nums[0]);
}
#ifndef USE_FAST_MATH
/* 8186 / 8205 / 8213 (F,-): the same loop, but with a destination k that
* has room for a single digit. The candidate is read back as qLen bytes,
* so the read fails and every guard downstream of it is reached with ret
* already set -- something no caller can arrange, because every caller
* passes a full-width mp_int. tfm has no mp_init_size, so this row comes
* from the SP-math variants. */
{
mp_int small[3];
byte hash[WC_SHA256_DIGEST_SIZE];
XMEMSET(small, 0, sizeof(small));
XMEMSET(hash, 0x5c, sizeof(hash));
if ((mp_init(&small[0]) == MP_OKAY) &&
(mp_init(&small[2]) == MP_OKAY) &&
(mp_init_size(&small[1], 1) == MP_OKAY) &&
(mp_set(&small[0], 1) == MP_OKAY) && /* priv */
(mp_set(&small[2], 0) == MP_OKAY) &&
(mp_set_bit(&small[2], 127) == MP_OKAY)) { /* 16-byte order */
(void)wc_ecc_gen_deterministic_k(hash, (word32)sizeof(hash),
WC_HASH_TYPE_SHA256, &small[0], &small[1], &small[2],
NULL);
}
mp_free(&small[2]);
mp_free(&small[1]);
mp_free(&small[0]);
}
#endif
WB_NOTE("deterministic-k retry vectors done");
}
#else
static void wb_deterministic_k_retry(void)
{
WB_NOTE("deterministic k not compiled in; retry vectors skipped");
}
#endif
/* ------------------------------------------------------------------------- *
* ECIES with a KDF the build does not implement -- 15809 / 16157.
*
* Both guards are `ret == 0 && [!]ecc_is_gcm(...)`, and their first operand
* only goes FALSE when something upstream already failed. ecc_get_key_sizes()
* validates the CIPHER and the MAC, but not the KDF, so a context carrying an
* unimplemented kdfAlgo passes every size check and then falls into the KDF
* switch's default -- reaching both guards with ret set, which no correctly
* configured exchange does.
* ------------------------------------------------------------------------- */
#if defined(HAVE_ECC_ENCRYPT) && defined(HAVE_HKDF) && !defined(NO_AES) && \
!defined(NO_HMAC)
static void wb_ecies_bad_kdf(void)
{
WC_RNG rng;
ecc_key a;
ecc_key b;
byte plain[32];
byte enc[256];
byte dec[256];
byte good[256];
word32 goodSz = 0;
XMEMSET(&rng, 0, sizeof(rng));
XMEMSET(plain, 0x5a, sizeof(plain));
XMEMSET(good, 0, sizeof(good));
if (wc_InitRng(&rng) != 0) {
WB_NOTE("wc_InitRng failed; ECIES bad-KDF pass skipped");
return;
}
/* Pass 0 encrypts with a broken KDF (15809). Pass 1 builds a good message
* first and then decrypts it with a broken KDF (16157). Pass 2 decrypts a
* good message with a MAC salt whose pointer is NULL while its length is
* not, so the HMAC that authenticates the message fails and 16221 is
* reached with ret already set -- the only way that guard's first operand
* goes FALSE, since the salt is otherwise always a real buffer. */
{
int pass;
for (pass = 0; pass < 3; pass++) {
ecEncCtx* ec = wc_ecc_ctx_new(REQ_RESP_CLIENT, &rng);
ecEncCtx* dc = wc_ecc_ctx_new(REQ_RESP_SERVER, &rng);
word32 encSz = (word32)sizeof(enc);
word32 decSz = (word32)sizeof(dec);
byte saltCli[EXCHANGE_SALT_SZ];
byte saltSrv[EXCHANGE_SALT_SZ];
const byte* sp;
int ok = 0;
/* A fresh pair each pass: wc_ecc_decrypt overwrites its pubKey
* argument with the sender's ephemeral point. */
XMEMSET(&a, 0, sizeof(a));
XMEMSET(&b, 0, sizeof(b));
if ((wc_ecc_init(&a) != 0) || (wc_ecc_init(&b) != 0) ||
(wc_ecc_make_key_ex(&rng, 0, &a, ECC_SECP256R1) != 0) ||
(wc_ecc_make_key_ex(&rng, 0, &b, ECC_SECP256R1) != 0)) {
WB_NOTE("ECIES bad-KDF key setup failed");
wc_ecc_free(&b);
wc_ecc_free(&a);
wc_ecc_ctx_free(dc);
wc_ecc_ctx_free(ec);
break;
}
if ((ec != NULL) && (dc != NULL)) {
sp = wc_ecc_ctx_get_own_salt(ec);
if (sp != NULL) {
XMEMCPY(saltCli, sp, EXCHANGE_SALT_SZ);
sp = wc_ecc_ctx_get_own_salt(dc);
if (sp != NULL) {
XMEMCPY(saltSrv, sp, EXCHANGE_SALT_SZ);
ok = (wc_ecc_ctx_set_peer_salt(ec, saltSrv) == 0) &&
(wc_ecc_ctx_set_peer_salt(dc, saltCli) == 0) &&
(wc_ecc_ctx_set_algo(ec, ecAES_128_CBC,
ecHKDF_SHA256, ecHMAC_SHA256) == 0) &&
(wc_ecc_ctx_set_algo(dc, ecAES_128_CBC,
ecHKDF_SHA256, ecHMAC_SHA256) == 0);
}
}
}
if (ok && (pass == 0)) {
/* Set the field directly: wc_ecc_ctx_set_algo screens the
* value, and screening it is exactly what we need to skip. */
ec->kdfAlgo = 0x7f;
(void)wc_ecc_encrypt(&a, &b, plain, (word32)sizeof(plain), enc,
&encSz, ec);
}
else if (ok) {
if ((wc_ecc_encrypt(&a, &b, plain, (word32)sizeof(plain), good,
&goodSz, ec) == 0) && (goodSz > 0)) {
if (pass == 1) {
dc->kdfAlgo = 0x7f;
}
else {
dc->macSalt = NULL;
dc->macSaltSz = 8;
}
(void)wc_ecc_decrypt(&b, &a, good, goodSz, dec, &decSz,
dc);
}
}
wc_ecc_ctx_free(dc);
wc_ecc_ctx_free(ec);
wc_ecc_free(&b);
wc_ecc_free(&a);
goodSz = (word32)sizeof(good);
}
}
wc_FreeRng(&rng);
WB_NOTE("ECIES bad-KDF vectors done");
}
#else
static void wb_ecies_bad_kdf(void)
{
WB_NOTE("ECIES not compiled in; bad-KDF vectors skipped");
}
#endif
/* ------------------------------------------------------------------------- *
* RESIDUAL, recorded here so it is not re-attempted: 8684's second operand,
* `mp_iszero(R->y)` in
*
* if ((err == MP_OKAY) && mp_iszero(R->z)) {
* if (mp_iszero(R->x) && mp_iszero(R->y)) {
*
* has no independence pair. The adder computes
* Z3 = -Z*Z'*H, X3 = r^2 - (U1+U2)*H^2,
* Y3 = (-r^3 + 3*r*X3 + (S1+S2)*H^3) / 2
* over a prime modulus, so Z3 == 0 forces H == 0, which collapses those to
* X3 = r^2 and Y3 = -r*X3/2: X3 == 0 therefore forces Y3 == 0 and the
* (X3 == 0, Y3 != 0) row does not exist.
*
* Feeding the helper a COMPOSITE modulus directly (the only other way Z*Z' can
* vanish with H != 0) does not help either, and was tried: for every way of
* splitting the modulus so that Z*Z' == 0, one side's own U and S collapse to
* zero in that prime component, which restores a Y3 = c*X3 relation there.
* Exhaustive sweeps over the moduli 9, 15, 21 and 35 with the Z ordinates set
* to the factors produced no such row.
* ------------------------------------------------------------------------- */
/* ========================================================================= *
* RESIDUALS after pass 3 -- conditions with no independence pair, and why.
* Recorded here so they are not re-attempted. Line numbers are ecc.c's.
*
* 4394:0 `ecc_sets[curve_idx].name &&`
* 4666:0 `ecc_sets[curve_idx].oid &&`
* ecc_sets[] is a static const table; every entry before the
* size == 0 sentinel (which stops both loops first) initializes name
* and oid from a string literal, so neither operand is ever FALSE.
* For the oid loop there is a second, independent reason: this build
* has HAVE_OID_ENCODING without HAVE_OID_DECODING, so the iteration
* calls EncodeObjectId(ecc_sets[i].oid, ...) BEFORE the guard, and
* EncodeObjectId rejects a NULL input and makes the loop `continue`
* without ever evaluating the guard.
*
* 5082:0 `if (checkInf && wc_ecc_point_is_at_infinity(result))`
* 5110:1 `if ((err == MP_OKAY) && checkInf)`
* checkInf is a local initialized to the constant 1 and never
* assigned again outside WOLFSSL_SE050 builds, so it cannot be FALSE
* in any variant this suite compiles.
*
* 5088:1 `x < mp_unsigned_bin_size(result->x)`
* x is mp_unsigned_bin_size(curve->prime) and the guard only runs
* after ecc_map_ex() succeeded, which leaves result->x reduced mod
* that same prime. A reduced value can never need more bytes than
* the modulus, so this operand is FALSE on every path that reaches
* it -- including when the caller supplies its own curve, because
* the multiply and the size both come from the one loaded prime.
*
* 5737:0 `if ((err == MP_OKAY) && !doneInCb)`
* 5737:1 doneInCb is only ever set inside the WOLF_CRYPTO_CB offload block,
* which no variant of this module compiles, so it is a constant 0;
* and with it absent nothing between the function's entry and this
* guard can fail (the mp_init_multi above it cannot fail for the
* fixed-size mp_ints of a live ecc_point), so err is a constant
* MP_OKAY. Reaching either row needs a registered crypto callback,
* i.e. a different build, not a different test.
*
* 8684:1 See the dedicated note above: over a prime field Z3 == 0 forces
* H == 0, which makes Y3 a multiple of X3, so X3 == 0 drags Y3 to
* zero with it. Composite moduli were tried too and do not help.
*
* 10221:0 `curve_idx != ECC_CUSTOM_IDX &&` (x2, uncompress dispatch)
* 10242:0 ECC_CUSTOM_IDX is -1 and wc_ecc_import_point_der_ex() returns
* ECC_BAD_ARG_E for any curve_idx < 0 in its first guard, so the
* custom index can never reach these decisions. The operand is a
* defensive re-check of something already rejected.
*
* 10325:2 `mp_isodd(t2) == MP_NO` (and 11611:2, the same code in the key
* 11611:2 importer). This is the exact complement of the decision's FIRST
* operand, `mp_isodd(t2) == MP_YES`, computed from the same value:
* no pair of test vectors can vary one while holding the other
* fixed, so the condition has no independence pair by construction.
* The other three operands of each decision are covered.
*
* 10769:0 `while (err == MP_OKAY && mp_isneg(t1))`
* 10769:1
* 10772:0 `while (err == MP_OKAY && mp_cmp(t1, prime) != MP_LT)`
* 10772:1 Both loops are defensive range fixups running immediately after
* the step that already normalized t1: whichever arm of the "is a
* equal to -3" test was taken, t1 was last written by mp_mod() or
* mp_addmod() against prime, and both backends return a result with
* the sign of the modulus and strictly below it. So t1 is never
* negative and never >= prime, neither loop body ever runs, and
* neither the loop-entry operand nor the err operand can form a
* pair. (A negative prime WOULD make the first test TRUE, but the
* body then adds that negative prime and never terminates, so it is
* not a legitimate input.)
*
* 14289:1 `} else if (zB && first == 1)`
* first is declared int, initialized to 1, and only ever assigned 0
* here or 1 through the `infinity` out-parameter of
* ecc_projective_add_point_safe(). The immediately preceding arm
* tested `(zB && first == 0)`, so reaching this one with zB true
* means first != 0, i.e. first == 1. Always TRUE where evaluated.
*
* 16564:0 `if ((mp_init_multi(t1, C, Q, S, Z, M) != MP_OKAY) ||`
* 16564:1 ` (mp_init_multi(T, R, N, two, NULL, NULL) != MP_OKAY))`
* Both backends this module builds return MP_OKAY unconditionally
* and skip NULL arguments: sp_init_multi() (sp_int.c) and
* mp_init_multi() (tfm.c) are `if (x) init(x);` six times followed
* by `return MP_OKAY;`. The storage is caller-owned and fixed-size,
* so neither call can report anything else and the decision is
* never TRUE.
*
* 14180:0 `if ((mp_unsigned_bin_size(tka) > (int)(KB_SIZE - 2)) ||`
* 14180:1 ` (mp_unsigned_bin_size(tkb) > (int)(KB_SIZE - 2)))`
* NOT a residual -- COVERED by Class 33 below. The argument that
* used to be filed here (MAX_ECC_BITS caps the scalars at 521 bits,
* 66 bytes, well under KB_SIZE - 2 = 126) is false for this
* suite's configs: they #define WOLFCRYPT_HAVE_SAKKE, which
* raises MAX_ECC_BITS to 1024 and puts the 128-byte ECC_SAKKE_1
* curve in ecc_sets[], while accel_fp_mul2add() uses a flat
* `#define KB_SIZE 128` (accel_fp_mul() is 256 under the same
* macro). A scalar the width of that curve's modulus is 128 bytes
* and is not reduced on the way in, so the guard fires.
* ========================================================================= */
/* ------------------------------------------------------------------------- *
* Class 33: accel_fp_mul2add()'s KB_SIZE scalar-length guard (ecc.c:14180),
* if ((mp_unsigned_bin_size(tka) > (int)(KB_SIZE - 2)) ||
* (mp_unsigned_bin_size(tkb) > (int)(KB_SIZE - 2)) )
*
* accel_fp_mul2add() declares a flat `#define KB_SIZE 128` -- unlike
* accel_fp_mul(), which is 256 under WOLFCRYPT_HAVE_SAKKE -- so the bound is
* 126 bytes. The base configs #define WOLFCRYPT_HAVE_SAKKE, which puts the
* 128-byte ECC_SAKKE_1 curve in ecc_sets[] and raises MAX_ECC_BITS to 1024,
* so a scalar sized to that curve is 128 bytes and the guard fires.
*
* Each operand is isolated: the scalars are only reduced when they are LARGER
* than the modulus, so a 128-byte scalar against a 128-byte modulus is
* carried through unchanged and reaches the test at full width, while a
* one-digit scalar stays short. The all-false row is the same call with both
* scalars short. Reaching accel_fp_mul2add() at all needs both points in the
* fixed-point cache with their LUTs built, which ecc_mul2add() does once an
* entry's lru_count reaches 2 -- A and B are the same point here, so a single
* warm-up call gets there.
* ------------------------------------------------------------------------- */
#if defined(FP_ECC) && defined(ECC_SHAMIR) && !defined(WOLFSSL_SP_MATH) && \
defined(WOLFCRYPT_HAVE_SAKKE)
static void wb_fp_mul2add_kb_size(void)
{
int idx = wc_ecc_get_curve_idx(ECC_SAKKE_1);
const ecc_set_type* cs;
mp_int a, modulus, kShort, kWide;
ecc_point *A = NULL, *B = NULL, *C = NULL;
byte wide[128];
int ret;
if (idx == ECC_CURVE_INVALID) {
WB_NOTE("SAKKE1 curve absent; KB_SIZE guard skipped");
return;
}
cs = wc_ecc_get_curve_params(idx);
if (cs == NULL || cs->size != 128) {
WB_NOTE("SAKKE1 curve is not 128 bytes; KB_SIZE guard skipped");
return;
}
if (mp_init_multi(&a, &modulus, &kShort, &kWide, NULL, NULL) != MP_OKAY) {
WB_NOTE("KB_SIZE: mp_init_multi failed");
wb_fail = 1;
return;
}
wc_ecc_fp_free(); /* deterministic empty-cache start */
/* Read the curve fields straight into mp_ints: the file's shared
* wb_hex_to_bin() caps a field at WB_MAXFIELD (100) bytes, which the
* 128-byte SAKKE1 fields exceed. */
if ((mp_read_radix(&a, cs->Af, MP_RADIX_HEX) != MP_OKAY) ||
(mp_read_radix(&modulus, cs->prime, MP_RADIX_HEX) != MP_OKAY)) {
WB_NOTE("KB_SIZE: SAKKE1 a/prime would not parse");
wb_fail = 1; goto out;
}
if (mp_unsigned_bin_size(&modulus) != 128) {
WB_NOTE("KB_SIZE: SAKKE1 prime is not 128 bytes");
wb_fail = 1; goto out;
}
(void)mp_set(&kShort, 3);
/* 128 bytes: the same width as the modulus, so accel_fp_mul2add's
* "smaller than modulus" test does not reduce it and it reaches the
* KB_SIZE test at full width. */
XMEMSET(wide, 0xFF, sizeof(wide));
if (mp_read_unsigned_bin(&kWide, wide, (word32)sizeof(wide)) != MP_OKAY) {
WB_NOTE("KB_SIZE: 128-byte scalar would not load");
wb_fail = 1; goto out;
}
A = wc_ecc_new_point();
B = wc_ecc_new_point();
C = wc_ecc_new_point();
if (A == NULL || B == NULL || C == NULL) {
WB_NOTE("KB_SIZE: point allocation failed");
wb_fail = 1; goto out;
}
if ((mp_read_radix(A->x, cs->Gx, MP_RADIX_HEX) != MP_OKAY) ||
(mp_read_radix(A->y, cs->Gy, MP_RADIX_HEX) != MP_OKAY)) {
WB_NOTE("KB_SIZE: SAKKE1 generator would not parse");
wb_fail = 1; goto out;
}
(void)mp_set(A->z, 1);
(void)mp_copy(A->x, B->x);
(void)mp_copy(A->y, B->y);
(void)mp_copy(A->z, B->z);
/* Warm-up: caches the base point and builds its LUT, then dispatches to
* accel_fp_mul2add with both scalars short -- the (FALSE,FALSE) row. */
ret = ecc_mul2add(A, &kShort, B, &kShort, C, &a, &modulus, NULL);
if (ret != MP_OKAY) {
WB_NOTE("KB_SIZE: SAKKE1 fp mul2add warm-up did not complete");
wb_fail = 1; goto out;
}
ret = ecc_mul2add(A, &kShort, B, &kShort, C, &a, &modulus, NULL);
if (ret != MP_OKAY) {
WB_NOTE("KB_SIZE: second warm-up call failed");
wb_fail = 1; goto out;
}
/* operand 0 TRUE (short-circuits) */
ret = ecc_mul2add(A, &kWide, B, &kShort, C, &a, &modulus, NULL);
if (ret != WC_NO_ERR_TRACE(BUFFER_E)) {
WB_NOTE("KB_SIZE: wide kA did not give BUFFER_E");
wb_fail = 1;
}
/* operand 0 FALSE, operand 1 TRUE */
ret = ecc_mul2add(A, &kShort, B, &kWide, C, &a, &modulus, NULL);
if (ret != WC_NO_ERR_TRACE(BUFFER_E)) {
WB_NOTE("KB_SIZE: wide kB did not give BUFFER_E");
wb_fail = 1;
}
WB_NOTE("accel_fp_mul2add KB_SIZE scalar-width pairs done");
out:
wc_ecc_del_point(A);
wc_ecc_del_point(B);
wc_ecc_del_point(C);
mp_clear(&kWide);
mp_clear(&kShort);
mp_clear(&modulus);
mp_clear(&a);
wc_ecc_fp_free();
}
#else
static void wb_fp_mul2add_kb_size(void)
{
WB_NOTE("FP_ECC/ECC_SHAMIR/SAKKE off; accel_fp_mul2add KB_SIZE skipped");
}
#endif
int main(void)
{
/* Unbuffered: on a timeout or a fault the process is killed and anything
* still buffered is lost, which reads as an empty log. */
setvbuf(stdout, NULL, _IONBF, 0);
printf("ecc.c white-box MC/DC supplement\n");
#if !defined(HAVE_ECC) || defined(WOLF_CRYPTO_CB_ONLY_ECC)
printf(" HAVE_ECC off (or crypto-cb-only build); nothing to exercise\n");
return 0;
#else
wb_curve_load();
wb_import_private_key_ex();
wb_ctx_set_salt();
wb_ctx_protocol_zero();
wb_is_valid_idx_n_lt_x();
wb_cmp_param_null();
wb_get_curve_id_from_params();
wb_get_curve_id_from_dp_params();
wb_mulmod_ex2_null_guard();
wb_ecc_map_ex_null();
wb_projective_wrappers();
wb_mul2add_and_bufsize();
wb_projective_safe_special_cases();
wb_fp_cache_internals();
wb_export_point_der();
wb_export_x963_internal();
wb_idx_dp_guard_export_paths();
wb_make_pub_privatekey_only();
wb_arg_guards();
wb_gap_pass2();
wb_custom_curve_dispatch();
wb_ecies_algos();
wb_forged_pubkey_order();
wb_forged_make_pub_sw();
wb_forged_validate_public_key();
wb_forged_is_point_neg();
wb_uncompress_parity();
wb_untrusted_import();
wb_forged_negative_strings();
wb_forged_order_minus_1();
wb_sign_hash_sw_indices();
wb_forged_rs_range();
wb_deterministic_k_retry();
wb_ecies_bad_kdf();
wb_fp_mul2add_kb_size();
printf("done (%s)\n", wb_fail ? "with skips" : "ok");
/* Setup failures are surfaced as skips, not test failures: the harness
* treats a nonzero exit as a failed variant and discards its coverage. */
return 0;
#endif
}