mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-07-05 14:50:49 +02:00
eccsi: fix universal signature forgery via r=0/s=0
wc_VerifyEccsiHash did not validate that r and s lie in [1, q-1] after decoding them from the signature buffer. With s=0 the scalar multiplication [s](...) returns the point at infinity (J_x=0); with r=0 the final mp_cmp(0,0)==MP_EQ check then accepts the forged signature unconditionally against any message and any identity. Add [1, q-1] range checks for r (in wc_VerifyEccsiHash, after params are loaded) and for s (in eccsi_calc_j, after eccsi_decode_sig_s), mirroring the checks already present in wc_ecc_check_r_s_range. Add a defense-in-depth point-at-infinity guard on J before the final comparison. Reported-by: Nicholas Carlini (Anthropic) & Bronson Yen (Calif.io)
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user