secp256k1

Pure Haskell cryptographic primitives on the secp256k1 elliptic curve.
git clone git://git.ppad.tech/secp256k1.git
Log | Files | Refs | LICENSE

commit 067ece1f756b91b9e8886ed6b8c5b6f3dbd8059d
parent d4265e0cb446474bbd03120b8b78042039ae1d0e
Author: Jared Tobin <jared@jtobin.io>
Date:   Tue,  8 Oct 2024 11:18:48 +0400

lib: ecdsa low-s

Diffstat:
Mlib/Crypto/Curve/Secp256k1.hs | 54++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 48 insertions(+), 6 deletions(-)

diff --git a/lib/Crypto/Curve/Secp256k1.hs b/lib/Crypto/Curve/Secp256k1.hs @@ -12,6 +12,7 @@ import Control.Monad (when) import Control.Monad.ST import qualified Crypto.DRBG.HMAC as DRBG import qualified Crypto.Hash.SHA256 as SHA256 +import qualified Data.Bits as B import qualified Data.ByteString as BS import qualified Data.ByteString.Base16 as B16 import Data.Int (Int64) @@ -435,6 +436,9 @@ unroll i = case i of step 0 = Nothing step m = Just (fi m, m `I.integerShiftR` 8) +-- ecdsa ---------------------------------------------------------------------- +-- see https://www.rfc-editor.org/rfc/rfc6979 + -- RFC6979 2.3.2 bits2int :: BS.ByteString -> Integer bits2int bs = @@ -459,9 +463,6 @@ bits2octets bs = z2 = modQ z1 in int2octets z2 --- ecdsa ---------------------------------------------------------------------- --- see https://www.rfc-editor.org/rfc/rfc6979 - data ECDSA = ECDSA { ecdsa_r :: !Integer , ecdsa_s :: !Integer @@ -470,8 +471,38 @@ data ECDSA = ECDSA { -- XX handle low-s -sign :: BS.ByteString -> Integer -> ECDSA -sign (SHA256.hash -> h) x = runST $ do +data SigType = + LowS + | Unrestricted + deriving Show + + +-- | Produce an ECDSA signature for the provided message, using the +-- provided private key. +-- +-- 'sign' produces a "low-s" signature, as is commonly required +-- in applications. If you need a generic ECDSA signature, use +-- 'sign_unrestricted'. +sign + :: Integer + -> BS.ByteString + -> ECDSA +sign = _sign LowS + +-- | Produce an ECDSA signature for the provided message, using the +-- provided private key. +-- +-- 'sign_unrestricted' produces an unrestricted ECDSA signature, which is +-- less common in applications. If you need a conventional "low-S" signature, +-- use 'sign'. +sign_unrestricted + :: Integer + -> BS.ByteString + -> ECDSA +sign_unrestricted = _sign Unrestricted + +_sign :: SigType -> Integer -> BS.ByteString -> ECDSA +_sign ty x (SHA256.hash -> h) = runST $ do -- RFC6979 sec 3.3a let entropy = int2octets x nonce = bits2octets h @@ -490,7 +521,10 @@ sign (SHA256.hash -> h) x = runST $ do Just kinv -> modQ (modQ (h_modQ + modQ (x * r)) * kinv) if r == 0 -- negligible probability then sign_loop g - else pure (ECDSA r s) + else let sig = ECDSA r s + in case ty of + Unrestricted -> pure sig + LowS -> pure (low sig) -- RFC6979 sec 3.3b gen_k :: DRBG.DRBG s -> ST s Integer @@ -503,6 +537,14 @@ gen_k g = loop g where else pure can {-# INLINE gen_k #-} +-- Convert an ECDSA signature to low-S form. +low :: ECDSA -> ECDSA +low (ECDSA r s) = ECDSA r ms where + ms + | s > B.unsafeShiftR _CURVE_Q 1 = modQ (negate s) + | otherwise = s +{-# INLINE low #-} + -- XX test test_h1 :: BS.ByteString