commit 12197864a9b2b09ac24b4b9e594d6d9ec8a295f4
parent ac4037e56cea6626e60fe1a0c448526247be9037
Author: Jared Tobin <jared@jtobin.io>
Date: Sat, 1 Aug 2026 12:29:49 -0230
lib: retry on the degenerate ecdsa signing cases
gen_k checked only k < q, admitting k = 0; _mul then failed and
signing returned Nothing rather than retrying, where RFC6979 requires
k in [1, q - 1]. ge is exactly that predicate.
_sign_ecdsa likewise retried on r = 0 but not on s = 0, emitting a
signature that cannot verify. SEC1-v2 4.1.3 retries on either.
Both are 2 ^ -256 events.
Diffstat:
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/lib/Crypto/Curve/Secp256k1.hs b/lib/Crypto/Curve/Secp256k1.hs
@@ -1314,7 +1314,8 @@ _sign_ecdsa _mul ty hf _SECRET m
DRBG.wipe g
pure Nothing
Just (r, s)
- | W.eq_vartime r 0 -> sign_loop g -- negligible probability
+ -- sec1-v2 4.1.3 retries on either being zero
+ | W.eq_vartime r 0 || W.eq_vartime s 0 -> sign_loop g
| otherwise -> do
DRBG.wipe g
let !sig = Just $! ECDSA r s
@@ -1332,9 +1333,9 @@ gen_k g = loop g where
Left {} -> error "ppad-secp256k1: internal error (please report a bug!)"
Right bs -> do
let can = bits2int bs
- case W.cmp_vartime can _CURVE_Q of
- LT -> pure can
- _ -> loop drbg -- 2 ^ -128 probability
+ if ge can -- rfc6979 requires k in [1, q - 1]
+ then pure can
+ else loop drbg -- 2 ^ -128 probability
{-# INLINE gen_k #-}
-- | Verify a "low-s" ECDSA signature for the provided message and