From f60f8cd9659ca29c76bfb659ec176e4209918459 Mon Sep 17 00:00:00 2001 From: JeremiahM37 Date: Thu, 7 May 2026 22:19:53 -0400 Subject: [PATCH 1/4] Clamp sakke_xor_in_v write to buffer length --- wolfcrypt/src/sakke.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/wolfcrypt/src/sakke.c b/wolfcrypt/src/sakke.c index f07f4cf4b23..52f8bc20844 100644 --- a/wolfcrypt/src/sakke.c +++ b/wolfcrypt/src/sakke.c @@ -6164,18 +6164,29 @@ static void sakke_xor_in_v(const byte* v, word32 hashSz, byte* out, word32 idx, { int o; word32 i; + word32 len; if (idx == 0) { i = hashSz - (n % hashSz); if (i == hashSz) { i = 0; } + len = hashSz - i; } else { i = 0; + /* Clamp to bytes still remaining in the caller's buffer. Without + * this clamp, the final iteration of sakke_hash_to_range (when + * n > hashSz and (n % hashSz) != 0) writes hashSz bytes at + * out+idx and overshoots the n-byte buffer by hashSz - (n%hashSz) + * bytes. */ + len = (n > idx) ? (n - idx) : 0; + if (len > hashSz) { + len = hashSz; + } } o = (int)i; - xorbuf(out + idx + i - o, v + i, hashSz - i); + xorbuf(out + idx + i - o, v + i, len); } /* From 623ab4957291ada18b634d341a73c7f86f42f86b Mon Sep 17 00:00:00 2001 From: JeremiahM37 Date: Fri, 8 May 2026 12:45:21 -0600 Subject: [PATCH 2/4] Fix sakke_xor_in_v write offset and read base --- wolfcrypt/src/sakke.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/wolfcrypt/src/sakke.c b/wolfcrypt/src/sakke.c index 52f8bc20844..b5a83847438 100644 --- a/wolfcrypt/src/sakke.c +++ b/wolfcrypt/src/sakke.c @@ -6162,31 +6162,28 @@ static int sakke_calc_h_v(SakkeKey* key, enum wc_HashType hashType, static void sakke_xor_in_v(const byte* v, word32 hashSz, byte* out, word32 idx, word32 n) { - int o; - word32 i; + word32 skip; + word32 off; word32 len; + /* RFC 6508, Section 5.1: output is the low n octets of + * v_1||v_2||...||v_l (the concatenation of l hash outputs taken + * modulo 2^(n*8)). When n is not a multiple of hashSz, drop the + * leading 'skip' high bytes of the first hash output. */ + skip = n % hashSz; + skip = (skip == 0) ? 0 : (hashSz - skip); + if (idx == 0) { - i = hashSz - (n % hashSz); - if (i == hashSz) { - i = 0; - } - len = hashSz - i; + xorbuf(out, v + skip, hashSz - skip); } else { - i = 0; - /* Clamp to bytes still remaining in the caller's buffer. Without - * this clamp, the final iteration of sakke_hash_to_range (when - * n > hashSz and (n % hashSz) != 0) writes hashSz bytes at - * out+idx and overshoots the n-byte buffer by hashSz - (n%hashSz) - * bytes. */ - len = (n > idx) ? (n - idx) : 0; + off = idx - skip; + len = n - off; if (len > hashSz) { len = hashSz; } + xorbuf(out + off, v, len); } - o = (int)i; - xorbuf(out + idx + i - o, v + i, len); } /* From cd34cefbad1446d5ce51857ec65f705ec11e8fef Mon Sep 17 00:00:00 2001 From: Jeremiah Mackey Date: Tue, 12 May 2026 17:01:38 +0000 Subject: [PATCH 3/4] Reject ssvSz=0 in SAKKE public APIs --- wolfcrypt/src/sakke.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/wolfcrypt/src/sakke.c b/wolfcrypt/src/sakke.c index b5a83847438..184bcc9c4d9 100644 --- a/wolfcrypt/src/sakke.c +++ b/wolfcrypt/src/sakke.c @@ -6690,8 +6690,9 @@ static int sakke_compute_point_r(SakkeKey* key, const byte* id, word16 idSz, * @param [out] auth Authentication data. * @param [out] authSz Size of authentication data in bytes. * @return 0 on success. - * @return BAD_FUNC_ARG when key, ssv or encSz is NULL, ssvSz is to big or - * encSz is too small. + * @return BAD_FUNC_ARG when key, ssv or authSz is NULL, ssvSz is 0 or + * larger than the curve modulus byte length, or *authSz is too + * small when encapsulating. * @return BAD_STATE_E when identity not set. * @return LENGTH_ONLY_E when auth is NULL. authSz contains required size of * auth in bytes. @@ -6707,7 +6708,7 @@ int wc_MakeSakkeEncapsulatedSSV(SakkeKey* key, enum wc_HashType hashType, word16 outSz = 0; byte a[WC_MAX_DIGEST_SIZE]; - if ((key == NULL) || (ssv == NULL) || (authSz == NULL)) { + if ((key == NULL) || (ssv == NULL) || (authSz == NULL) || (ssvSz == 0)) { err = BAD_FUNC_ARG; } if ((err == 0) && (key->idSz == 0)) { @@ -6726,7 +6727,14 @@ int wc_MakeSakkeEncapsulatedSSV(SakkeKey* key, enum wc_HashType hashType, /* Uncompressed point */ outSz = (word16)(1 + 2 * n); - if ((auth != NULL) && (*authSz < outSz)) { + /* RFC 6508, Section 6.2.1, Step 1 places SSV in 0..2^n-1, so + * ssvSz must be <= n. Enforced on both the encapsulation and + * size-query paths so callers cannot probe authSz with an + * invalid ssvSz. */ + if (ssvSz > n) { + err = BAD_FUNC_ARG; + } + else if ((auth != NULL) && (*authSz < outSz)) { err = BAD_FUNC_ARG; } } @@ -6821,7 +6829,7 @@ int wc_GenerateSakkeSSV(SakkeKey* key, WC_RNG* rng, byte* ssv, word16* ssvSz) if (err == 0) { n = (word16)WC_BITS_TO_BYTES(mp_count_bits(&key->params.prime)); - if ((ssv != NULL) && (*ssvSz > n)) { + if ((ssv != NULL) && ((*ssvSz == 0) || (*ssvSz > n))) { err = BAD_FUNC_ARG; } } @@ -6865,7 +6873,8 @@ int wc_GenerateSakkeSSV(SakkeKey* key, WC_RNG* rng, byte* ssv, word16* ssvSz) * @param [in] auth Authentication data. * @param [in] authSz Size of authentication data in bytes. * @return 0 on success. - * @return BAD_FUNC_ARG when key, ssv or auth is NULL. + * @return BAD_FUNC_ARG when key, ssv or auth is NULL, ssvSz is 0 or + * larger than the curve modulus byte length. * @return BAD_STATE_E when RSK or identity not set. * @return SAKKE_VERIFY_FAIL_E when calculated R doesn't match the encapsulated * data's R. @@ -6884,7 +6893,7 @@ int wc_DeriveSakkeSSV(SakkeKey* key, enum wc_HashType hashType, byte* ssv, byte* test = NULL; byte a[WC_MAX_DIGEST_SIZE] = {0}; - if ((key == NULL) || (ssv == NULL) || (auth == NULL)) { + if ((key == NULL) || (ssv == NULL) || (auth == NULL) || (ssvSz == 0)) { err = BAD_FUNC_ARG; } if ((err == 0) && (!key->rsk.set || (key->idSz == 0))) { @@ -6903,6 +6912,11 @@ int wc_DeriveSakkeSSV(SakkeKey* key, enum wc_HashType hashType, byte* ssv, if (authSz != 2 * n + 1) { err = BAD_FUNC_ARG; } + /* RFC 6508, Section 6.2.1: SSV is in 0..2^n-1, so ssvSz must + * be <= n. */ + else if (ssvSz > n) { + err = BAD_FUNC_ARG; + } } if (err == 0) { err = sakke_load_base_point(key); From e346cf93cc97119ff04268c82475fca74311886f Mon Sep 17 00:00:00 2001 From: Jeremiah Mackey Date: Tue, 12 May 2026 17:01:38 +0000 Subject: [PATCH 4/4] Add SSV size coverage to sakke_op_test --- wolfcrypt/test/test.c | 67 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 6d3b7d4793e..0e21c379cae 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -58430,6 +58430,73 @@ static wc_test_ret_t sakke_op_test(SakkeKey* priv, SakkeKey* pub, WC_RNG* rng, authSz); if (ret != 0) return WC_TEST_RET_ENC_EC(ret); + + { + /* All three SSV entry points reject ssvSz=0 and ssvSz>n. */ + word16 zeroSz = 0; + word16 hugeSz = 0xFFFF; + + ret = wc_GenerateSakkeSSV(pub, rng, ssv, &zeroSz); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + return WC_TEST_RET_ENC_NC; + zeroSz = 0; + ret = wc_MakeSakkeEncapsulatedSSV(pub, WC_HASH_TYPE_SHA256, ssv, zeroSz, + auth, &authSz); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + return WC_TEST_RET_ENC_NC; + ret = wc_DeriveSakkeSSV(priv, WC_HASH_TYPE_SHA256, ssv, zeroSz, auth, + authSz); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + return WC_TEST_RET_ENC_NC; + ret = wc_GenerateSakkeSSV(pub, rng, ssv, &hugeSz); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + return WC_TEST_RET_ENC_NC; + hugeSz = 0xFFFF; + ret = wc_MakeSakkeEncapsulatedSSV(pub, WC_HASH_TYPE_SHA256, ssv, hugeSz, + auth, &authSz); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + return WC_TEST_RET_ENC_NC; + ret = wc_DeriveSakkeSSV(priv, WC_HASH_TYPE_SHA256, ssv, hugeSz, auth, + authSz); + if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) + return WC_TEST_RET_ENC_NC; + } + + { + /* Round-trip encap/derive across ssvSz shapes that exercise the + * multi-iteration paths in sakke_xor_in_v. SHA-256 hashSz=32: + * 48: 2 iterations, partial first block. + * 72: 3 iterations, exercises the len-clamp on the middle iter. + * 64: 2 iterations, exact-multiple (skip==0). */ + static const word16 extSzs[3] = { 48, 72, 64 }; + byte ssvExt[72]; + byte ssvRef[72]; + word16 extAuthSz; + int i; + + for (i = 0; i < 3; i++) { + word16 extSz = extSzs[i]; + + ret = wc_RNG_GenerateBlock(rng, ssvRef, extSz); + if (ret != 0) + return WC_TEST_RET_ENC_EC(ret); + XMEMCPY(ssvExt, ssvRef, extSz); + + extAuthSz = 257; + ret = wc_MakeSakkeEncapsulatedSSV(pub, WC_HASH_TYPE_SHA256, ssvExt, + extSz, auth, &extAuthSz); + if (ret != 0) + return WC_TEST_RET_ENC_EC(ret); + + ret = wc_DeriveSakkeSSV(priv, WC_HASH_TYPE_SHA256, ssvExt, extSz, + auth, extAuthSz); + if (ret != 0) + return WC_TEST_RET_ENC_EC(ret); + if (XMEMCMP(ssvExt, ssvRef, extSz) != 0) + return WC_TEST_RET_ENC_NC; + } + } + return 0; }