commit 52967380909d1980604ee10f0683f068f3a865e0
parent 12197864a9b2b09ac24b4b9e594d6d9ec8a295f4
Author: Jared Tobin <jared@jtobin.io>
Date: Sat, 1 Aug 2026 12:31:09 -0230
lib: match bip0340's schnorr signature range checks
Verification used fe r && ge s, which additionally rejects r = 0 and
s = 0. BIP0340 fails only on r >= p and s >= n.
Being stricter than the spec can only cause a false reject, and a
valid signature with either component zero would take a fixed-point
search to construct, so this changes no reachable behaviour. It is
still a deviation.
s = 0 needs handling at the call site: mul_wnaf rejects a zero scalar
where mul_vartime returns the identity, so verify_schnorr and
verify_schnorr' would otherwise disagree.
Diffstat:
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/lib/Crypto/Curve/Secp256k1.hs b/lib/Crypto/Curve/Secp256k1.hs
@@ -1111,11 +1111,16 @@ _verify_schnorr _mul m p sig
| otherwise = M.isJust $ do
let capP = even_y_vartime p
(unsafe_roll32 -> r, unsafe_roll32 -> s) = BS.splitAt 32 sig
- guard (fe r && ge s)
+ -- bip0340 fails only on r >= p and s >= n, so zero is permitted
+ guard (W.lt_vartime r _CURVE_P && W.lt_vartime s _CURVE_Q)
let Affine (C.retr -> x_P) _ = affine capP
e = modQ . unsafe_roll32 $
hash_challenge (unroll32 r <> unroll32 x_P <> m)
- pt0 <- _mul s
+ -- mul_wnaf rejects a zero scalar, mul_vartime does not; agree on
+ -- the identity so both multiplication functions behave alike
+ pt0 <- if W.eq_vartime s 0
+ then pure _CURVE_ZERO
+ else _mul s
pt1 <- mul_vartime capP e
let dif = add pt0 (neg pt1)
guard (dif /= _CURVE_ZERO)