secp256k1

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

commit 406c97d2685be0614a0cee210717cf63165c23c4
parent c76b5534a901308217f1e3aa2ef80f4aa5ee7c07
Author: Jared Tobin <jared@jtobin.io>
Date:   Sat,  5 Oct 2024 14:13:32 +0400

lib: nonce generation skeleton, s/modN/modQ

Diffstat:
Mlib/Crypto/Curve/Secp256k1.hs | 77++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
1 file changed, 56 insertions(+), 21 deletions(-)

diff --git a/lib/Crypto/Curve/Secp256k1.hs b/lib/Crypto/Curve/Secp256k1.hs @@ -10,6 +10,8 @@ module Crypto.Curve.Secp256k1 where 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.ByteString as BS import qualified Data.ByteString.Base16 as B16 import Data.Int (Int64) @@ -91,20 +93,20 @@ _CURVE_P :: Integer _CURVE_P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F -- secp256k1 group order -_CURVE_N :: Integer -_CURVE_N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 +_CURVE_Q :: Integer +_CURVE_Q = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 -- bitlength of group order -- --- = smallest integer such that _CURVE_N < 2 ^ _CURVE_N_BITS -_CURVE_N_BITS :: Int64 -_CURVE_N_BITS = 256 +-- = smallest integer such that _CURVE_Q < 2 ^ _CURVE_Q_BITS +_CURVE_Q_BITS :: Int64 +_CURVE_Q_BITS = 256 --- bytelength of _CURVE_N +-- bytelength of _CURVE_Q -- --- = _CURVE_N_BITS / 8 -_CURVE_N_BYTES :: Int64 -_CURVE_N_BYTES = 32 +-- = _CURVE_Q_BITS / 8 +_CURVE_Q_BYTES :: Int64 +_CURVE_Q_BYTES = 32 -- secp256k1 short weierstrass form, /a/ coefficient _CURVE_A :: Integer @@ -137,8 +139,8 @@ modP :: Integer -> Integer modP a = I.integerMod a _CURVE_P -- | Division modulo secp256k1 group order. -modN :: Integer -> Integer -modN a = I.integerMod a _CURVE_N +modQ :: Integer -> Integer +modQ a = I.integerMod a _CURVE_Q -- | Is field element? fe :: Integer -> Bool @@ -146,7 +148,7 @@ fe n = 0 < n && n < _CURVE_P -- | Is group element? ge :: Integer -> Bool -ge n = 0 < n && n < _CURVE_N +ge n = 0 < n && n < _CURVE_Q -- | Square root (Shanks-Tonelli) modulo secp256k1 field prime. -- @@ -395,7 +397,7 @@ parse (B16.decode -> ebs) = case ebs of Right bs -> case BS.uncons bs of Nothing -> Nothing Just (fi -> h, t) -> - let (roll -> x, etc) = BS.splitAt (fi _CURVE_N_BYTES) t + let (roll -> x, etc) = BS.splitAt (fi _CURVE_Q_BYTES) t len = BS.length bs in if len == 33 && (h == 0x02 || h == 0x03) -- compressed then if not (fe x) @@ -410,7 +412,7 @@ parse (B16.decode -> ebs) = case ebs of else Projective x y 1 else if len == 65 && h == 0x04 -- uncompressed - then let (roll -> y, _) = BS.splitAt (fi _CURVE_N_BYTES) etc + then let (roll -> y, _) = BS.splitAt (fi _CURVE_Q_BYTES) etc p = Projective x y 1 in if valid p then Just p @@ -435,38 +437,71 @@ unroll i = case i of bits2int :: BS.ByteString -> Integer bits2int bs = let (fi -> blen) = BS.length bs * 8 - (fi -> qlen) = _CURVE_N_BITS -- RFC6979 notation + (fi -> qlen) = _CURVE_Q_BITS -- RFC6979 notation del = blen - qlen in if del > 0 then roll bs `I.integerShiftR` del else roll bs +-- XX think this shouldn't be padded as it is + -- RFC6979 int2octets :: Integer -> BS.ByteString int2octets i = pad (unroll i) where pad !bs - | BS.length bs < fi _CURVE_N_BYTES = pad (BS.cons 0 bs) + | BS.length bs < fi _CURVE_Q_BYTES = pad (BS.cons 0 bs) | otherwise = bs -- RFC6979 bits2octets :: BS.ByteString -> BS.ByteString bits2octets bs = let z1 = bits2int bs - z2 = modN z1 + z2 = modQ z1 in int2octets z2 -- XX handle low-s sign :: BS.ByteString -> Integer -> Integer -> (Integer, Integer) -sign (modN . bits2int -> h) k x = +sign (modQ . bits2int -> h) k x = let kg = mul _CURVE_G k - Affine (modN -> r) _ = affine kg - s = case modinv k (fi _CURVE_N) of + Affine (modQ -> r) _ = affine kg + s = case modinv k (fi _CURVE_Q) of Nothing -> error "ppad-secp256k1 (sign): bad k value" - Just kinv -> modN (modN (h + modN (x * r)) * kinv) + Just kinv -> modQ (modQ (h + modQ (x * r)) * kinv) in if r == 0 then error "ppad-secp256k1 (sign): <negligible probability outcome>" else (r, s) +-- RFC6979 sec 2.4 +gen_k :: BS.ByteString -> Integer -> Integer +gen_k m x = runST $ do + let entropy = int2octets x + nonce = bits2octets (SHA256.hash m) + drbg <- DRBG.new SHA256.hmac entropy nonce mempty + loop drbg + where + qlen = fi _CURVE_Q_BITS -- RFC6979 notation + loop drbg = do + bytes <- DRBG.gen mempty qlen drbg + let !can = bits2int bytes + if can >= _CURVE_Q + then loop drbg + else pure can + +gen_k' :: BS.ByteString -> Integer -> Integer +gen_k' h x = runST $ do + let entropy = int2octets x + nonce = bits2octets h + drbg <- DRBG.new SHA256.hmac entropy nonce mempty + loop drbg + where + qlen = fi _CURVE_Q_BITS -- RFC6979 notation + loop drbg = do + bytes <- DRBG.gen mempty qlen drbg + let !can = bits2int bytes + if can >= _CURVE_Q + then loop drbg + else pure can + -- XX test test_h1 :: BS.ByteString