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
166 changes: 165 additions & 1 deletion tests/api/test_sakke.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@
* (LENGTH_ONLY_E); *len != 0 (BUFFER_E)
* wc_SetSakkeRsk (sakke.c:2350) key==NULL || rsk==NULL
* wc_ValidateSakkeRsk (sakke.c:2430)
* key==NULL||id==NULL||rsk==NULL||valid==NULL
* key==NULL||id==NULL||rsk==NULL||valid==NULL;
* idSz > SAKKE_ID_MAX_SIZE
* wc_GetSakkeAuthSize (sakke.c:2491) key==NULL || authSz==NULL
* wc_SetSakkeIdentity (sakke.c:6357)
* key==NULL || id==NULL || idSz > SAKKE_ID_MAX_SIZE
Expand Down Expand Up @@ -221,6 +222,8 @@ int test_wc_Sakke_DecisionCoverage(void)
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_ValidateSakkeRsk(&key, id, 1, rsk, NULL),
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_ValidateSakkeRsk(&key, data, SAKKE_ID_MAX_SIZE + 1, rsk,
&valid), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_ValidateSakkeRsk(&key, id, 1, rsk, &valid), 0);
ExpectIntEQ(valid, 1);

Expand Down Expand Up @@ -816,3 +819,164 @@ int test_wc_Sakke_FeatureCoverage(void)
#endif
return EXPECT_RESULT();
}

/* A KMS key object that also runs a client operation keeps its key pair. */
int test_wc_Sakke_KmsKeyAfterClientOp(void)
{
EXPECT_DECLS;
#ifdef WOLFCRYPT_HAVE_SAKKE
WC_RNG rng;
SakkeKey key;
ecc_point* rsk = NULL;
char mail[] = "test@wolfssl.com";
byte* id = (byte*)mail;
word16 idSz = (word16)XSTRLEN(mail);
int valid = 0;
byte keyData[384];
word32 keySz = sizeof(keyData);
byte keyData2[384];
word32 keySz2 = sizeof(keyData2);
byte privData[128];
word32 privSz = sizeof(privData);

XMEMSET(&rng, 0, sizeof(rng));
XMEMSET(&key, 0, sizeof(key));

ExpectIntEQ(wc_InitRng(&rng), 0);
ExpectNotNull(rsk = wc_ecc_new_point());
ExpectIntEQ(wc_InitSakkeKey_ex(&key, 128, ECC_SAKKE_1, NULL,
INVALID_DEVID), 0);
ExpectIntEQ(wc_MakeSakkeKey(&key, &rng), 0);
ExpectIntEQ(wc_ExportSakkeKey(&key, keyData, &keySz), 0);

ExpectIntEQ(wc_MakeSakkeRsk(&key, id, idSz, rsk), 0);
ExpectIntEQ(wc_ValidateSakkeRsk(&key, id, idSz, rsk, &valid), 0);
ExpectIntEQ(valid, 1);

ExpectIntEQ(wc_ExportSakkePrivateKey(&key, privData, &privSz), 0);
ExpectBufEQ(privData, keyData, sizeof(privData));
ExpectIntEQ(wc_ExportSakkeKey(&key, keyData2, &keySz2), 0);
ExpectBufEQ(keyData2, keyData, sizeof(keyData));

/* Validate again to set zMont before wc_ImportSakkeKey() and
* wc_MakeSakkeKey() store a normal-form key pair. */
ExpectIntEQ(wc_MakeSakkeRsk(&key, id, idSz, rsk), 0);
ExpectIntEQ(wc_ValidateSakkeRsk(&key, id, idSz, rsk, &valid), 0);
ExpectIntEQ(wc_ImportSakkeKey(&key, keyData, keySz), 0);
ExpectIntEQ(wc_MakeSakkeRsk(&key, id, idSz, rsk), 0);
valid = 0;
ExpectIntEQ(wc_ValidateSakkeRsk(&key, id, idSz, rsk, &valid), 0);
ExpectIntEQ(valid, 1);
ExpectIntEQ(wc_MakeSakkeKey(&key, &rng), 0);
ExpectIntEQ(wc_MakeSakkeRsk(&key, id, idSz, rsk), 0);
valid = 0;
ExpectIntEQ(wc_ValidateSakkeRsk(&key, id, idSz, rsk, &valid), 0);
ExpectIntEQ(valid, 1);

if (rsk != NULL) {
wc_ecc_forcezero_point(rsk);
wc_ecc_del_point(rsk);
}
wc_FreeSakkeKey(&key);
DoExpectIntEQ(wc_FreeRng(&rng), 0);
#endif
return EXPECT_RESULT();
}

/* A reused sender key object encapsulates the same bytes as a fresh one. */
int test_wc_Sakke_PointICache(void)
{
EXPECT_DECLS;
#ifdef WOLFCRYPT_HAVE_SAKKE
WC_RNG rng;
SakkeKey key;
SakkeKey key2;
char mailA[] = "alice@wolfssl.com";
char mailB[] = "bob@wolfssl.com";
byte* idA = (byte*)mailA;
byte* idB = (byte*)mailB;
word16 idASz = (word16)XSTRLEN(mailA);
word16 idBSz = (word16)XSTRLEN(mailB);
byte pubData[256];
word32 pubSz = sizeof(pubData);
byte pubData2[256];
word32 pubSz2 = sizeof(pubData2);
byte* table = NULL;
word32 tableLen = 0;
byte ssv[16];
byte ssv2[16];
byte auth[257];
byte auth2[257];
word16 authSz = sizeof(auth);
word16 authSz2 = sizeof(auth2);

XMEMSET(&rng, 0, sizeof(rng));
XMEMSET(&key, 0, sizeof(key));
XMEMSET(&key2, 0, sizeof(key2));
XMEMSET(ssv, 0x5A, sizeof(ssv));
XMEMSET(ssv2, 0x5A, sizeof(ssv2));

ExpectIntEQ(wc_InitRng(&rng), 0);
ExpectIntEQ(wc_InitSakkeKey_ex(&key, 128, ECC_SAKKE_1, NULL,
INVALID_DEVID), 0);
ExpectIntEQ(wc_MakeSakkeKey(&key, &rng), 0);
ExpectIntEQ(wc_ExportSakkePublicKey(&key, pubData, &pubSz, 1), 0);
ExpectIntEQ(wc_MakeSakkeKey(&key, &rng), 0);
ExpectIntEQ(wc_ExportSakkePublicKey(&key, pubData2, &pubSz2, 1), 0);
wc_FreeSakkeKey(&key);
XMEMSET(&key, 0, sizeof(key));

ExpectIntEQ(wc_InitSakkeKey_ex(&key, 128, ECC_SAKKE_1, NULL,
INVALID_DEVID), 0);
ExpectIntEQ(wc_ImportSakkePublicKey(&key, pubData, pubSz, 1), 0);
ExpectIntEQ(wc_InitSakkeKey_ex(&key2, 128, ECC_SAKKE_1, NULL,
INVALID_DEVID), 0);
ExpectIntEQ(wc_ImportSakkePublicKey(&key2, pubData, pubSz, 1), 0);

/* Point I table for id A, then point I for id B. */
ExpectIntEQ(wc_MakeSakkePointI(&key, idA, idASz), 0);
ExpectIntEQ(wc_GenerateSakkePointITable(&key, NULL, &tableLen),
WC_NO_ERR_TRACE(LENGTH_ONLY_E));
if (tableLen > 0) {
ExpectNotNull(table = (byte*)XMALLOC(tableLen, NULL,
DYNAMIC_TYPE_TMP_BUFFER));
ExpectIntEQ(wc_GenerateSakkePointITable(&key, table, &tableLen), 0);
}
ExpectIntEQ(wc_MakeSakkePointI(&key, idB, idBSz), 0);

ExpectIntEQ(wc_SetSakkeIdentity(&key, idB, idBSz), 0);
ExpectIntEQ(wc_MakeSakkeEncapsulatedSSV(&key, WC_HASH_TYPE_SHA256, ssv,
sizeof(ssv), auth, &authSz), 0);
ExpectIntEQ(wc_SetSakkeIdentity(&key2, idB, idBSz), 0);
ExpectIntEQ(wc_MakeSakkeEncapsulatedSSV(&key2, WC_HASH_TYPE_SHA256, ssv2,
sizeof(ssv2), auth2, &authSz2), 0);
ExpectBufEQ(auth, auth2, sizeof(auth));
ExpectBufEQ(ssv, ssv2, sizeof(ssv));

/* Same identity after a new KMS public key, against a new key object. */
ExpectIntEQ(wc_ImportSakkePublicKey(&key, pubData2, pubSz2, 1), 0);
wc_FreeSakkeKey(&key2);
XMEMSET(&key2, 0, sizeof(key2));
ExpectIntEQ(wc_InitSakkeKey_ex(&key2, 128, ECC_SAKKE_1, NULL,
INVALID_DEVID), 0);
ExpectIntEQ(wc_ImportSakkePublicKey(&key2, pubData2, pubSz2, 1), 0);
ExpectIntEQ(wc_SetSakkeIdentity(&key2, idB, idBSz), 0);
XMEMSET(ssv, 0x5A, sizeof(ssv));
XMEMSET(ssv2, 0x5A, sizeof(ssv2));
authSz = sizeof(auth);
authSz2 = sizeof(auth2);
ExpectIntEQ(wc_MakeSakkeEncapsulatedSSV(&key, WC_HASH_TYPE_SHA256, ssv,
sizeof(ssv), auth, &authSz), 0);
ExpectIntEQ(wc_MakeSakkeEncapsulatedSSV(&key2, WC_HASH_TYPE_SHA256, ssv2,
sizeof(ssv2), auth2, &authSz2), 0);
ExpectBufEQ(auth, auth2, sizeof(auth));
ExpectBufEQ(ssv, ssv2, sizeof(ssv));

ExpectIntEQ(wc_ClearSakkePointITable(&key), 0);
XFREE(table, NULL, DYNAMIC_TYPE_TMP_BUFFER);
wc_FreeSakkeKey(&key2);
wc_FreeSakkeKey(&key);
DoExpectIntEQ(wc_FreeRng(&rng), 0);
#endif
return EXPECT_RESULT();
}
6 changes: 5 additions & 1 deletion tests/api/test_sakke.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,13 @@

int test_wc_Sakke_DecisionCoverage(void);
int test_wc_Sakke_FeatureCoverage(void);
int test_wc_Sakke_KmsKeyAfterClientOp(void);
int test_wc_Sakke_PointICache(void);

#define TEST_SAKKE_DECLS \
TEST_DECL_GROUP("sakke", test_wc_Sakke_DecisionCoverage), \
TEST_DECL_GROUP("sakke", test_wc_Sakke_FeatureCoverage)
TEST_DECL_GROUP("sakke", test_wc_Sakke_FeatureCoverage), \
TEST_DECL_GROUP("sakke", test_wc_Sakke_KmsKeyAfterClientOp), \
TEST_DECL_GROUP("sakke", test_wc_Sakke_PointICache)

#endif /* WOLFCRYPT_TEST_SAKKE_H */
29 changes: 13 additions & 16 deletions tests/unit-mcdc/test_sakke_fault_whitebox.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@
* 1505/1508/1511 sakke_tplmod three sequential reductions, same shape
* 2282 sakke_pairing loop "(err==0) && (i>=0)"
* 2302 sakke_pairing "(err==0) && (i>0) && mp_is_bit_set(..)"
* 2454 wc_ValidateSakkeRsk "(err==0) && (idSz<=SAKKE_ID_MAX_SIZE)"
* 2646 sakke_modexp_loop "(err==0) && (i>=0)"
* 6289 sakke_hash_to_range "(err==0) && (i<n)"
* 6657 sakke_compute_point_r "(err==0) && (idSz<=SAKKE_ID_MAX_SIZE)"
Expand All @@ -65,11 +64,10 @@
* armed call either returns an error before building anything, or fails a
* deeper allocation whose error the target's own cleanup absorbs (that cleanup
* is what is under test). The harness never dereferences a value an armed call
* returned. sakke_compute_point_i() (reached via ValidateSakkeRsk /
* compute_point_r) repurposes the ecc private-key mp_int as scratch, so a
* FRESH fully-prepared key is (re)built while disarmed before each sweep group
* that needs genuine key state, and the key that a sweep may leave partially
* mutated is never reused across groups. Runs clean under -fsanitize=address.
* returned. A FRESH fully-prepared key is (re)built while disarmed before each
* sweep group that needs genuine key state, and the key that a sweep may leave
* partially mutated is never reused across groups. Runs clean under
* -fsanitize=address.
*
* Invocation:
* ./test_sakke_fault_whitebox default: full fault-index sweeps
Expand Down Expand Up @@ -650,16 +648,15 @@ int main(int argc, char** argv)
}
#endif /* !WOLFSSL_HAVE_SP_ECC */

/* --- wc_ValidateSakkeRsk (2454, and 2282/2302 in sakke_pairing):
* sakke_compute_point_i() runs first (heavy ECC precompute) -- a fault
* there sets err != 0 before the 2454 idSz guard, driving its err==0
* false half. Faults deeper in the run land inside sakke_pairing's
* accumulate-line loop, setting err != 0 so the 2282 loop check and
* 2302 inner guard take their err==0 false halves on the next
* iteration. Validate does not need the master secret (only the public
* key + params), and resets key->i.table = NULL on recompute, so the
* shared prepared key is reused; a fault leaves key->i partial but
* every subsequent validate call recomputes it. --- */
/* --- wc_ValidateSakkeRsk (2282/2302 in sakke_pairing):
* sakke_compute_point_i() runs first (heavy ECC precompute). Faults
* deeper in the run land inside sakke_pairing's accumulate-line loop,
* setting err != 0 so the 2282 loop check and 2302 inner guard take
* their err==0 false halves on the next iteration. Validate does not
* need the master secret (only the public key + params), and resets
* key->i.table = NULL on recompute, so the shared prepared key is
* reused; a fault leaves key->i partial but every subsequent validate
* call recomputes it. --- */
for (n = 1; n <= SAKKE_K_VALIDATE; n++) {
int a = 0;
wc_ecc_fp_free(); /* cold sakke_compute_point_i mul (FP build) */
Expand Down
40 changes: 7 additions & 33 deletions tests/unit-mcdc/test_sakke_whitebox.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,6 @@
* sakke.c:1505/1508/1511 sakke_tplmod() -- same shape, three sequential
* reduction checks; a/m chosen so each of the three independently sees
* both "still >= m" and "already < m" across the four calls made.
* sakke.c:2454 wc_ValidateSakkeRsk() "(err == 0) && (idSz <=
* SAKKE_ID_MAX_SIZE)" -- unlike wc_SetSakkeIdentity()/
* wc_MakeSakkePointI(), this public wrapper never bounds idSz before
* computing point I, so idSz > SAKKE_ID_MAX_SIZE is reachable directly
* through the public API; driven here to isolate the guard that keeps
* the fixed 128-byte key->i.id copy from overflowing.
* sakke.c:2466 wc_ValidateSakkeRsk() "*valid = ((err == 0) &&
* (mp_cmp(a, &key->params.g) == MP_EQ))" -- true (matching RSK/identity
* pair) and false (RSK computed for a different identity) both driven.
Expand All @@ -62,9 +56,9 @@
* operand toggled independently by controlling key->i.* state directly
* (never-cached, size-mismatch, content-mismatch, cache-hit).
* sakke.c:6657 sakke_compute_point_r() "(err == 0) && (idSz <=
* SAKKE_ID_MAX_SIZE)" -- same shape/reason as 2454, driven as an
* idSz > SAKKE_ID_MAX_SIZE call that also forces the 6653 recompute
* path.
* SAKKE_ID_MAX_SIZE)" -- its callers pass key->id, which
* wc_SetSakkeIdentity() bounds; driven as an idSz > SAKKE_ID_MAX_SIZE
* call that also forces the 6653 recompute path.
*
* Loop decisions at sakke.c:2282, 2302, 2646 and 6289 (bit/byte iteration
* bounded by mp_count_bits(&params->q) or a digest size) are exercised for
Expand All @@ -91,13 +85,7 @@
* mp_int/ecc_point locals, or on a single SakkeKey ("key") that is fully
* initialized (params, base point and a real master secret via
* wc_MakeSakkeKey()) before any file-static function that dereferences its
* internals is called. sakke_compute_point_i() -- reached through both
* wc_ValidateSakkeRsk() and sakke_compute_point_r() -- repurposes the ecc
* private-key mp_int as scratch space for whatever identity integer it is
* given, so calls that rely on the real master secret (key generation, RSK
* generation) are all made first; every call after the first
* wc_ValidateSakkeRsk() below only needs id/idSz/rsk state, never the
* master secret again.
* internals is called.
*/

#include <wolfcrypt/src/sakke.c>
Expand Down Expand Up @@ -148,8 +136,8 @@ int main(void)
/* --- sakke.c:411 sakke_mulmod_base_add(): "(err == 0) && map" ---
* Only exists without WOLFSSL_HAVE_SP_ECC. Every in-tree caller
* hardcodes map=1 (sakke_compute_point_i()); map=0 is otherwise
* unreachable. Uses the real public key computed by wc_MakeSakkeKey()
* above, before it is repurposed below. */
* unreachable. Uses the public key computed by wc_MakeSakkeKey() above,
* before wc_ValidateSakkeRsk() below converts it to Montgomery form. */
#ifndef WOLFSSL_HAVE_SP_ECC
{
mp_int scalarN;
Expand Down Expand Up @@ -222,7 +210,7 @@ int main(void)
"(SP-math substitutes single-shot sp_ecc_* calls); skipped");
#endif

/* --- sakke.c:2454/2466 wc_ValidateSakkeRsk() --- */
/* --- sakke.c:2466 wc_ValidateSakkeRsk() --- */
/* Matching RSK/identity pair: valid == 1 (line 2466 true). Also
* exercises the loops at 2282/2302 (sakke_pairing()) with real data. */
valid = -1;
Expand All @@ -239,20 +227,6 @@ int main(void)
if (valid != 0) wb_fail = 1;
WB_NOTE("wc_ValidateSakkeRsk() valid==1/valid==0 (line 2466) exercised");

/* idSz > SAKKE_ID_MAX_SIZE: wc_ValidateSakkeRsk() never bounds idSz
* before computing point I (unlike wc_SetSakkeIdentity()/
* wc_MakeSakkePointI()), so this is reachable directly through the
* public API. Isolates line 2454's "idSz <= SAKKE_ID_MAX_SIZE" operand
* to false while err stays 0, confirming the fixed 128-byte key->i.id
* copy is skipped rather than overflowed. From this call on, "key"'s
* ecc private-key mp_int has been repurposed as scratch by
* sakke_compute_point_i() and no longer holds the master secret. */
valid = -1;
if (wc_ValidateSakkeRsk(&key, idBig, (word16)sizeof(idBig), rsk, &valid)
!= 0) wb_fail = 1;
WB_NOTE("wc_ValidateSakkeRsk() idSz > SAKKE_ID_MAX_SIZE (line 2454) "
"exercised");

/* --- sakke.c:6653/6657 sakke_compute_point_r() ---
* Called directly (it is file-static) so key->i.* cache state can be
* controlled precisely; none of these calls need the master secret. */
Expand Down
Loading
Loading