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
189 changes: 189 additions & 0 deletions tests/api/test_eccsi.c
Original file line number Diff line number Diff line change
Expand Up @@ -860,3 +860,192 @@ int test_wc_Eccsi_FeatureCoverage(void)
#endif
return EXPECT_RESULT();
}

/* wc_FreeEccsiKey() must wipe the SSK before releasing it. Only the heap math
* backend keeps mp_int digits in a separate allocation, so only there can a
* free hook see what is handed back to the allocator. WOLFSSL_CHECK_MEM_ZERO
* and WOLFSSL_MEMORY_LOG put a header in front of each allocation, so the free
* hook would never be handed key.ssk.dp itself. Platforms whose XFREE skips
* the hooks entirely are detected at run time by eccsi_zero_probe(). */
#if defined(WOLFCRYPT_HAVE_ECCSI) && defined(WOLFCRYPT_ECCSI_CLIENT) && \
defined(USE_INTEGER_HEAP_MATH) && defined(USE_WOLFSSL_MEMORY) && \
!defined(WOLFSSL_NO_MALLOC) && !defined(WOLFSSL_STATIC_MEMORY) && \
!defined(WOLFSSL_CHECK_MEM_ZERO) && !defined(WOLFSSL_MEMORY_LOG)
#define TEST_ECCSI_FREE_ZEROIZE

/* These callback types take (func, line) too under WOLFSSL_DEBUG_MEMORY. */
#ifdef WOLFSSL_DEBUG_MEMORY
#define ECCSI_ZERO_CB_TAIL , const char* func, unsigned int line
#define ECCSI_ZERO_CB_ARGS , func, line
#else
#define ECCSI_ZERO_CB_TAIL
#define ECCSI_ZERO_CB_ARGS
#endif

/* The hooks delegate to the allocators that were installed before the test
* (e.g. WOLFSSL_TRACK_MEMORY's), so blocks that outlive the test, such as
* cached ECC curve values, can still be freed by them later. */
static wolfSSL_Malloc_cb eccsi_zero_prev_mc;
static wolfSSL_Free_cb eccsi_zero_prev_fc;
static wolfSSL_Realloc_cb eccsi_zero_prev_rc;

/* State for eccsi_zero_probe(). */
static int eccsi_zero_probing;
static const void* eccsi_zero_probe_alloc;
static int eccsi_zero_probe_freed;

static const void* eccsi_zero_watch;
static size_t eccsi_zero_watchSz;
static int eccsi_zero_seen;
static int eccsi_zero_clean;

static void* eccsi_zero_malloc_cb(size_t size ECCSI_ZERO_CB_TAIL)
{
void* p;

if (eccsi_zero_prev_mc != NULL) {
p = eccsi_zero_prev_mc(size ECCSI_ZERO_CB_ARGS);
}
else {
p = malloc(size);
}
if (eccsi_zero_probing && (eccsi_zero_probe_alloc == NULL)) {
eccsi_zero_probe_alloc = p;
}
return p;
}

static void eccsi_zero_free_cb(void* ptr ECCSI_ZERO_CB_TAIL)
{
if (eccsi_zero_probing && (ptr != NULL) &&
(ptr == eccsi_zero_probe_alloc)) {
eccsi_zero_probe_freed = 1;
}
if ((ptr != NULL) && (ptr == eccsi_zero_watch)) {
const byte* p = (const byte*)ptr;
size_t i;
byte acc = 0;

for (i = 0; i < eccsi_zero_watchSz; i++) {
acc |= p[i];
}
eccsi_zero_seen = 1;
eccsi_zero_clean = (acc == 0);
}
if (eccsi_zero_prev_fc != NULL) {
eccsi_zero_prev_fc(ptr ECCSI_ZERO_CB_ARGS);
}
else {
free(ptr);
}
}

static void* eccsi_zero_realloc_cb(void* ptr, size_t size ECCSI_ZERO_CB_TAIL)
{
if (eccsi_zero_prev_rc != NULL) {
return eccsi_zero_prev_rc(ptr, size ECCSI_ZERO_CB_ARGS);
}
return realloc(ptr, size);
}

/* Checks that XMALLOC()/XFREE() reach the hooks with the pointer unchanged.
* Returns 1 when they do and 0 when both bypass the hooks, as on platforms
* that map them to an OS or pool allocator (see types.h); the wipe cannot be
* observed there. Returns -1 when the hooks are reached but see a different
* pointer, or only one of the pair reaches them: that is a failure. */
static int eccsi_zero_probe(void)
{
void* probe;
int ret;

eccsi_zero_probe_alloc = NULL;
eccsi_zero_probe_freed = 0;
eccsi_zero_probing = 1;
probe = XMALLOC(8, NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (probe == NULL) {
ret = -1;
}
else {
ret = (eccsi_zero_probe_alloc == probe) ? 1 :
(eccsi_zero_probe_alloc == NULL) ? 0 : -1;
XFREE(probe, NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (eccsi_zero_probe_freed != ret) {
ret = -1;
}
}
eccsi_zero_probing = 0;
return ret;
}
#endif

int test_wc_Eccsi_FreeZeroizesSsk(void)
{
EXPECT_DECLS;
#if defined(TEST_ECCSI_FREE_ZEROIZE) && defined(USE_WOLFSSL_MEMORY)
EccsiKey key;
WC_RNG rng;
mp_int ssk;
ecc_point* pvt = NULL;
char mail[] = "test@wolfssl.com";
int hooked = 0;

XMEMSET(&key, 0, sizeof(key));
XMEMSET(&rng, 0, sizeof(rng));
XMEMSET(&ssk, 0, sizeof(ssk));
eccsi_zero_watch = NULL;
eccsi_zero_watchSz = 0;
eccsi_zero_seen = 0;
eccsi_zero_clean = 0;
eccsi_zero_prev_mc = NULL;
eccsi_zero_prev_fc = NULL;
eccsi_zero_prev_rc = NULL;

ExpectIntEQ(wolfSSL_GetAllocators(&eccsi_zero_prev_mc,
&eccsi_zero_prev_fc, &eccsi_zero_prev_rc), 0);
ExpectIntEQ(wolfSSL_SetAllocators(eccsi_zero_malloc_cb,
eccsi_zero_free_cb, eccsi_zero_realloc_cb), 0);
Comment thread
stenslae marked this conversation as resolved.
Resolved
if (EXPECT_SUCCESS()) {
hooked = eccsi_zero_probe();
ExpectIntGE(hooked, 0);
}

ExpectIntEQ(wc_InitRng(&rng), 0);
ExpectNotNull(pvt = wc_ecc_new_point());
ExpectIntEQ(mp_init(&ssk), MP_OKAY);
ExpectIntEQ(wc_InitEccsiKey(&key, HEAP_HINT, INVALID_DEVID), 0);
ExpectIntEQ(wc_MakeEccsiKey(&key, &rng), 0);
ExpectIntEQ(wc_MakeEccsiPair(&key, &rng, WC_HASH_TYPE_SHA256,
(byte*)mail, (word32)XSTRLEN(mail), &ssk, pvt), 0);
ExpectIntEQ(wc_SetEccsiPair(&key, &ssk, pvt), 0);

/* The key's copy of the SSK lives in its own digit allocation. */
ExpectFalse(mp_iszero(&key.ssk));
ExpectNotNull(key.ssk.dp);
if (EXPECT_SUCCESS()) {
eccsi_zero_watch = key.ssk.dp;
eccsi_zero_watchSz = (size_t)key.ssk.used * sizeof(mp_digit);
}

wc_FreeEccsiKey(&key);
eccsi_zero_watch = NULL;

if (hooked == 1) {
ExpectIntEQ(eccsi_zero_seen, 1);
ExpectIntEQ(eccsi_zero_clean, 1);
}

if (pvt != NULL) {
wc_ecc_del_point(pvt);
}
mp_forcezero(&ssk);
wc_FreeRng(&rng);
(void)wolfSSL_SetAllocators(eccsi_zero_prev_mc, eccsi_zero_prev_fc,
eccsi_zero_prev_rc);
/* Report the test as skipped, not passed, where XMALLOC() and XFREE()
* bypass the hooks. */
if ((hooked == 0) && EXPECT_SUCCESS()) {
return TEST_SKIPPED;
}
#endif
return EXPECT_RESULT();
}
4 changes: 3 additions & 1 deletion tests/api/test_eccsi.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@

int test_wc_Eccsi_DecisionCoverage(void);
int test_wc_Eccsi_FeatureCoverage(void);
int test_wc_Eccsi_FreeZeroizesSsk(void);

#define TEST_ECCSI_DECLS \
TEST_DECL_GROUP("eccsi", test_wc_Eccsi_DecisionCoverage), \
TEST_DECL_GROUP("eccsi", test_wc_Eccsi_FeatureCoverage)
TEST_DECL_GROUP("eccsi", test_wc_Eccsi_FeatureCoverage), \
TEST_DECL_GROUP("eccsi", test_wc_Eccsi_FreeZeroizesSsk)

#endif /* WOLFCRYPT_TEST_ECCSI_H */
10 changes: 9 additions & 1 deletion wolfcrypt/src/eccsi.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ int wc_InitEccsiKey_ex(EccsiKey* key, int keySz, int curveId, void* heap,
#endif
);
}
#if defined(WOLFCRYPT_ECCSI_CLIENT) && defined(WOLFSSL_CHECK_MEM_ZERO)
if (err == 0) {
mp_memzero_add("ECCSI ssk", &key->ssk);
}
#endif
if (err == 0) {
err = wc_ecc_set_curve(&key->ecc, keySz, curveId);
}
Expand Down Expand Up @@ -164,7 +169,10 @@ void wc_FreeEccsiKey(EccsiKey* key)

wc_ecc_del_point_h(params->base, key->heap);
#ifdef WOLFCRYPT_ECCSI_CLIENT
mp_free(&key->ssk);
mp_forcezero(&key->ssk);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Add a WOLFSSL_CHECK_MEM_ZERO check.
When private data is put into key->ssk or key->tmp, call mp_memzero_add().

#ifdef WOLFSSL_CHECK_MEM_ZERO
mp_memzero_check(&key->ssk);
#endif
mp_free(&key->tmp);
mp_free(&params->prime);
mp_free(&params->b);
Expand Down
Loading