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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
364 changes: 350 additions & 14 deletions doc/dox_comments/header_files/ecc.h

Large diffs are not rendered by default.

1,092 changes: 1,091 additions & 1 deletion tests/api/test_ecc.c

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions tests/api/test_ecc.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,18 @@ int test_wc_ecc_ctx_reset(void);
int test_wc_ecc_ctx_set_peer_salt(void);
int test_wc_ecc_ctx_set_info(void);
int test_wc_ecc_ctx_getters(void);
int test_wc_ecc_ctx_set_curve_id(void);
int test_wc_ecc_encryptDecrypt(void);
int test_wc_ecc_ecies_x25519(void);
int test_wc_ecc_ecies_x448(void);
int test_wc_ecc_ecies_gcm(void);
int test_wc_ecc_ecies_gcm_no_rng(void);
int test_wc_ecc_ecies_cbc_no_rng(void);
int test_wc_ecc_ecies_x25519_nonblock(void);
int test_wc_ecc_ecies_cryptocb(void);
int test_wc_ecc_ctx_algo_dev_ids(void);
int test_wc_ecc_ecies_cryptocb_x25519(void);
int test_wc_ecc_ecies_cryptocb_x448(void);
int test_wc_ecc_del_point(void);
int test_wc_ecc_pointFns(void);
int test_wc_ecc_shared_secret_ssh(void);
Expand Down Expand Up @@ -105,10 +113,18 @@ int test_wc_EccDecisionCoverage4(void);
TEST_DECL_GROUP("ecc", test_wc_ecc_ctx_set_peer_salt), \
TEST_DECL_GROUP("ecc", test_wc_ecc_ctx_set_info), \
TEST_DECL_GROUP("ecc", test_wc_ecc_ctx_getters), \
TEST_DECL_GROUP("ecc", test_wc_ecc_ctx_set_curve_id), \
TEST_DECL_GROUP("ecc", test_wc_ecc_encryptDecrypt), \
TEST_DECL_GROUP("ecc", test_wc_ecc_ecies_x25519), \
TEST_DECL_GROUP("ecc", test_wc_ecc_ecies_x448), \
TEST_DECL_GROUP("ecc", test_wc_ecc_ecies_gcm), \
TEST_DECL_GROUP("ecc", test_wc_ecc_ecies_gcm_no_rng), \
TEST_DECL_GROUP("ecc", test_wc_ecc_ecies_cbc_no_rng), \
TEST_DECL_GROUP("ecc", test_wc_ecc_ecies_x25519_nonblock), \
TEST_DECL_GROUP("ecc", test_wc_ecc_ecies_cryptocb), \
TEST_DECL_GROUP("ecc", test_wc_ecc_ctx_algo_dev_ids), \
TEST_DECL_GROUP("ecc", test_wc_ecc_ecies_cryptocb_x25519), \
TEST_DECL_GROUP("ecc", test_wc_ecc_ecies_cryptocb_x448), \
TEST_DECL_GROUP("ecc", test_wc_ecc_del_point), \
TEST_DECL_GROUP("ecc", test_wc_ecc_pointFns), \
TEST_DECL_GROUP("ecc", test_wc_ecc_shared_secret_ssh), \
Expand Down
44 changes: 27 additions & 17 deletions tests/unit-mcdc/test_ecc_whitebox.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,14 @@
* 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.
* Classes 1-3 are confirmed structurally unreachable through any public
* wrapper (every wrapper hard-codes the "safe" side of the static helper's
* own re-check). Classes 4-6 became API-reachable when
* wc_ecc_ctx_new()/wc_ecc_ctx_new_ex() started accepting flags==0 -
* tests/api/test_ecc.c (test_wc_ecc_ctx_set_peer_salt) now drives them
* through the public API - and are kept here so this binary still completes
* both halves of each pair with a directly-built ctx (see the note above on
* per-binary MC/DC independence). See RESIDUALS.md for everything else.
*/

/* ecc.c refuses the AES-GCM ECIES DEM in the default IV mode unless one of
Expand Down Expand Up @@ -196,17 +199,19 @@ static void wb_import_private_key_ex(void)
*
* 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).
* (REQ_RESP_CLIENT/REQ_RESP_SERVER); wc_ecc_ctx_reset() skips the call
* entirely when protocol==0, 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).
* public constructor wc_ecc_ctx_new()/wc_ecc_ctx_new_ex() does accept
* flags==0 - such a context carries the algorithms and the key type but takes
* no part in the REQ/RESP salt exchange - and these three functions are the
* ones that reject it. Build the ctx 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)
{
Expand Down Expand Up @@ -1645,16 +1650,21 @@ static void wb_arg_guards(void)
}
}

/* ecc_public_key_size: key == NULL || key->dp == NULL */
/* ecies_pub_key_size: the ECC branch rejects a NULL key and a key with
* no domain parameters, both reported by wc_ecc_size() as size 0. */
#if defined(HAVE_ECC_ENCRYPT) && !defined(WOLFSSL_ECIES_OLD)
{
const ecc_set_type* savedDp = key.dp;
word32 pubSz = 0;

(void)ecc_public_key_size(NULL, &sz);
(void)ecies_pub_key_size(NULL, NULL, 0, &pubSz);
key.dp = NULL;
(void)ecc_public_key_size(&key, &sz);
(void)ecies_pub_key_size(NULL, &key, 0, &pubSz);
key.dp = savedDp;
(void)ecc_public_key_size(&key, &sz);
(void)ecies_pub_key_size(NULL, &key, 0, &pubSz);
(void)ecies_pub_key_size(NULL, &key, 1, &pubSz);
}
#endif

/* The accepting vectors below need the real curve constants: a zero
* modulus/order would make wc_ecc_gen_deterministic_k's RFC 6979 retry
Expand Down
7 changes: 7 additions & 0 deletions wolfcrypt/benchmark/benchmark.c
Original file line number Diff line number Diff line change
Expand Up @@ -14597,6 +14597,13 @@ void bench_eccEncrypt(int curveId)
goto exit;
}

#ifdef WOLF_CRYPTO_CB
/* Route the whole-op ECIES callback by the benchmark's devId, like
* every other benchmarked operation. */
(void)wc_ecc_ctx_set_dev_id(cliCtx, devId);
(void)wc_ecc_ctx_set_dev_id(srvCtx, devId);
#endif

for (c = 0; eciesCiphers[c].label != NULL; c++) {
byte algo = eciesCiphers[c].algo;

Expand Down
130 changes: 107 additions & 23 deletions wolfcrypt/src/cryptocb.c
Original file line number Diff line number Diff line change
Expand Up @@ -1129,67 +1129,151 @@ int wc_CryptoCb_EccCheckPubKey(ecc_key* key, int checkOrder, int checkPriv)
#endif /* HAVE_ECC_CHECK_KEY */

#ifdef HAVE_ECC_ENCRYPT
int wc_CryptoCb_EciesEncrypt(ecc_key* privKey, ecc_key* pubKey,
/* Resolve the ECIES dispatch devId - the one place the adopt logic lives.
* The ctx devId is only set while INVALID_DEVID, adopting an ecc_key's own
* devId (the key is never modified); otherwise the ctx devId wins. */
static int wc_CryptoCb_EciesDevId(void* privKey, ecEncCtx* ctx, int curveId,
int* devId)
{
int keyDevId = INVALID_DEVID;
int ctxDevId = INVALID_DEVID;

/* Only an ecc_key's own devId participates in the resolution; the
* Montgomery key types route exclusively by the context devId. */
if (curveId == ECC_CURVE_DEF) {
keyDevId = ((ecc_key*)privKey)->devId;
}

if (ctx == NULL) {
*devId = keyDevId;
return 0;
}

if (wc_ecc_ctx_get_dev_id(ctx, &ctxDevId) != 0)
return BAD_FUNC_ARG;

if (ctxDevId == INVALID_DEVID && keyDevId != INVALID_DEVID) {
(void)wc_ecc_ctx_set_dev_id(ctx, keyDevId);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ECIES crypto-callback dispatch writes the key devId into the caller's ecEncCtx, causing unbounded recursion · Resource leaks

wc_CryptoCb_EciesDevId persists privKey->devId into the caller's ecEncCtx. A callback that forwards to software by clearing only privKey->devId (the contract before this PR) re-enters dispatch because ctx->devId still names the device, recursing until the stack is exhausted. The stored devId also misroutes later operations that reuse the same ctx with an INVALID_DEVID key.

Fix: Use keyDevId as a local fallback for the dispatch decision only; do not mutate ctx->devId.

ctxDevId = keyDevId;
}

*devId = ctxDevId;
return 0;
}

int wc_CryptoCb_EciesEncrypt_ex(void* privKey, void* pubKey,
const byte* msg, word32 msgSz, byte* out, word32* outSz, ecEncCtx* ctx,
int compressed)
int compressed, int curveId)
{
int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
CryptoCb* dev;
int devId = INVALID_DEVID;

if (privKey == NULL)
return ret;

if (wc_CryptoCb_EciesDevId(privKey, ctx, curveId, &devId) != 0)
return BAD_FUNC_ARG;

/* locate registered callback */
dev = wc_CryptoCb_FindDevice(privKey->devId, WC_ALGO_TYPE_PK);
dev = wc_CryptoCb_FindDevice(devId, WC_ALGO_TYPE_PK);
if (dev && dev->cb) {
wc_CryptoInfo cryptoInfo;
XMEMSET(&cryptoInfo, 0, sizeof(cryptoInfo));
cryptoInfo.algo_type = WC_ALGO_TYPE_PK;
cryptoInfo.pk.type = WC_PK_TYPE_ECIES_ENCRYPT;
cryptoInfo.pk.eciesencrypt.privKey = privKey;
cryptoInfo.pk.eciesencrypt.pubKey = pubKey;
cryptoInfo.pk.eciesencrypt.msg = msg;
cryptoInfo.pk.eciesencrypt.msgSz = msgSz;
cryptoInfo.pk.eciesencrypt.out = out;
cryptoInfo.pk.eciesencrypt.outSz = outSz;
cryptoInfo.pk.eciesencrypt.ctx = ctx;
cryptoInfo.pk.eciesencrypt.compressed = compressed;
if (curveId == ECC_CURVE_DEF) {
cryptoInfo.pk.type = WC_PK_TYPE_ECIES_ENCRYPT;
cryptoInfo.pk.eciesencrypt.privKey = (ecc_key*)privKey;
cryptoInfo.pk.eciesencrypt.pubKey = (ecc_key*)pubKey;
cryptoInfo.pk.eciesencrypt.msg = msg;
cryptoInfo.pk.eciesencrypt.msgSz = msgSz;
cryptoInfo.pk.eciesencrypt.out = out;
cryptoInfo.pk.eciesencrypt.outSz = outSz;
cryptoInfo.pk.eciesencrypt.ctx = ctx;
cryptoInfo.pk.eciesencrypt.compressed = compressed;
}
else {
/* Montgomery key type; the void pointers are typed by curveId. */
cryptoInfo.pk.type = WC_PK_TYPE_ECIES_ENCRYPT_MONT;
cryptoInfo.pk.eciesencrypt_mont.privKey = privKey;
cryptoInfo.pk.eciesencrypt_mont.pubKey = pubKey;
cryptoInfo.pk.eciesencrypt_mont.msg = msg;
cryptoInfo.pk.eciesencrypt_mont.msgSz = msgSz;
cryptoInfo.pk.eciesencrypt_mont.out = out;
cryptoInfo.pk.eciesencrypt_mont.outSz = outSz;
cryptoInfo.pk.eciesencrypt_mont.ctx = ctx;
cryptoInfo.pk.eciesencrypt_mont.curveId = curveId;
}

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

return wc_CryptoCb_TranslateErrorCode(ret);
}

int wc_CryptoCb_EciesDecrypt(ecc_key* privKey, ecc_key* pubKey,
const byte* msg, word32 msgSz, byte* out, word32* outSz, ecEncCtx* ctx)
int wc_CryptoCb_EciesEncrypt(ecc_key* privKey, ecc_key* pubKey,
const byte* msg, word32 msgSz, byte* out, word32* outSz, ecEncCtx* ctx,
int compressed)
{
return wc_CryptoCb_EciesEncrypt_ex(privKey, pubKey, msg, msgSz, out,
outSz, ctx, compressed, ECC_CURVE_DEF);
}

int wc_CryptoCb_EciesDecrypt_ex(void* privKey, void* pubKey,
const byte* msg, word32 msgSz, byte* out, word32* outSz, ecEncCtx* ctx,
int curveId)
{
int ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
CryptoCb* dev;
int devId = INVALID_DEVID;

if (privKey == NULL)
return ret;

if (wc_CryptoCb_EciesDevId(privKey, ctx, curveId, &devId) != 0)
return BAD_FUNC_ARG;

/* locate registered callback */
dev = wc_CryptoCb_FindDevice(privKey->devId, WC_ALGO_TYPE_PK);
dev = wc_CryptoCb_FindDevice(devId, WC_ALGO_TYPE_PK);
if (dev && dev->cb) {
wc_CryptoInfo cryptoInfo;
XMEMSET(&cryptoInfo, 0, sizeof(cryptoInfo));
cryptoInfo.algo_type = WC_ALGO_TYPE_PK;
cryptoInfo.pk.type = WC_PK_TYPE_ECIES_DECRYPT;
cryptoInfo.pk.eciesdecrypt.privKey = privKey;
cryptoInfo.pk.eciesdecrypt.pubKey = pubKey;
cryptoInfo.pk.eciesdecrypt.msg = msg;
cryptoInfo.pk.eciesdecrypt.msgSz = msgSz;
cryptoInfo.pk.eciesdecrypt.out = out;
cryptoInfo.pk.eciesdecrypt.outSz = outSz;
cryptoInfo.pk.eciesdecrypt.ctx = ctx;
if (curveId == ECC_CURVE_DEF) {
cryptoInfo.pk.type = WC_PK_TYPE_ECIES_DECRYPT;
cryptoInfo.pk.eciesdecrypt.privKey = (ecc_key*)privKey;
cryptoInfo.pk.eciesdecrypt.pubKey = (ecc_key*)pubKey;
cryptoInfo.pk.eciesdecrypt.msg = msg;
cryptoInfo.pk.eciesdecrypt.msgSz = msgSz;
cryptoInfo.pk.eciesdecrypt.out = out;
cryptoInfo.pk.eciesdecrypt.outSz = outSz;
cryptoInfo.pk.eciesdecrypt.ctx = ctx;
}
else {
/* Montgomery key type; the void pointers are typed by curveId. */
cryptoInfo.pk.type = WC_PK_TYPE_ECIES_DECRYPT_MONT;
cryptoInfo.pk.eciesdecrypt_mont.privKey = privKey;
cryptoInfo.pk.eciesdecrypt_mont.pubKey = pubKey;
cryptoInfo.pk.eciesdecrypt_mont.msg = msg;
cryptoInfo.pk.eciesdecrypt_mont.msgSz = msgSz;
cryptoInfo.pk.eciesdecrypt_mont.out = out;
cryptoInfo.pk.eciesdecrypt_mont.outSz = outSz;
cryptoInfo.pk.eciesdecrypt_mont.ctx = ctx;
cryptoInfo.pk.eciesdecrypt_mont.curveId = curveId;
}

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

return wc_CryptoCb_TranslateErrorCode(ret);
}

int wc_CryptoCb_EciesDecrypt(ecc_key* privKey, ecc_key* pubKey,
const byte* msg, word32 msgSz, byte* out, word32* outSz, ecEncCtx* ctx)
{
return wc_CryptoCb_EciesDecrypt_ex(privKey, pubKey, msg, msgSz, out,
outSz, ctx, ECC_CURVE_DEF);
}
#endif /* HAVE_ECC_ENCRYPT */
#endif /* HAVE_ECC */

Expand Down
Loading
Loading