diff --git a/wolfcrypt/src/eccsi.c b/wolfcrypt/src/eccsi.c index b4cf859e50..d919dd8a34 100644 --- a/wolfcrypt/src/eccsi.c +++ b/wolfcrypt/src/eccsi.c @@ -2159,6 +2159,18 @@ static int eccsi_calc_j(EccsiKey* key, const mp_int* hem, const byte* sig, if (err == 0) { err = eccsi_decode_sig_s(key, sig, sigSz, s); } + /* Validate s is in [1, q-1]: reject zero or out-of-range second signature + * component. With s=0, [s](...) yields the point at infinity whose + * affine x-coordinate is 0, making the final mp_cmp(0,0) accept any + * forged signature. */ + if (err == 0) { + if (mp_iszero(s)) { + err = MP_ZERO_E; + } + else if (mp_cmp(s, &key->params.order) != MP_LT) { + err = ECC_OUT_OF_RANGE_E; + } + } /* [s]( [HE]G + [r]Y ) */ if (err == 0) { err = eccsi_mulmod_point(key, s, j, j, 1); @@ -2238,6 +2250,19 @@ int wc_VerifyEccsiHash(EccsiKey* key, enum wc_HashType hashType, err = mp_montgomery_setup(¶ms->prime, &mp); } + /* Validate r is in [1, q-1]: reject zero or out-of-range first signature + * component before any scalar multiplication takes place. + * Without this check, r=0 causes J_x=0 and the final mp_cmp(0,0)==MP_EQ + * comparison accepts the forged signature unconditionally. */ + if (err == 0) { + if (mp_iszero(r)) { + err = MP_ZERO_E; + } + else if (mp_cmp(r, ¶ms->order) != MP_LT) { + err = ECC_OUT_OF_RANGE_E; + } + } + /* Step 1: Validate PVT is on curve */ if (err == 0) { err = wc_ecc_is_point(pvt, ¶ms->a, ¶ms->b, ¶ms->prime); @@ -2273,6 +2298,16 @@ int wc_VerifyEccsiHash(EccsiKey* key, enum wc_HashType hashType, key->params.haveBase = 0; } + /* Defense-in-depth: reject J = point at infinity before the final + * comparison. Catches any future path that might reach this point + * with a neutral-element result (e.g. s = 0 mod q for a non-zero + * encoded s). */ + if (err == 0) { + if (wc_ecc_point_is_at_infinity(j)) { + err = ECC_INF_E; + } + } + /* Step 6: Jx fitting, compare with r */ if (err == 0) { jx = &key->tmp;