commit 8f4a27271ba16f7f97ef5f7fdadfd9a6b032991b
parent 0d6f9a8e9511d9a8c56e6d14d6b62b696f47a8cf
Author: Jared Tobin <jared@jtobin.io>
Date: Tue, 15 Oct 2024 14:48:06 +0400
lib: pub synonym
Diffstat:
2 files changed, 31 insertions(+), 8 deletions(-)
diff --git a/lib/Crypto/Curve/Secp256k1.hs b/lib/Crypto/Curve/Secp256k1.hs
@@ -31,7 +31,8 @@ module Crypto.Curve.Secp256k1 (
, verify_ecdsa
, verify_ecdsa_unrestricted
- -- * Point parsing
+ -- * Parsing
+ , parse_integer
, parse_point
-- Elliptic curve group operations
@@ -43,6 +44,7 @@ module Crypto.Curve.Secp256k1 (
-- Coordinate systems and transformations
, Affine(..)
, Projective(..)
+ , Pub
, affine
, projective
, valid
@@ -161,6 +163,9 @@ instance Eq Projective where
y2z1 = modP (by * az)
in x1z2 == x2z1 && y1z2 == y2z1
+-- | A Schnorr and ECDSA-flavoured alias for a secp256k1 point.
+type Pub = Projective
+
-- Convert to affine coordinates.
affine :: Projective -> Affine
affine p@(Projective x y z)
@@ -482,6 +487,10 @@ mul p n
-- parsing --------------------------------------------------------------------
+-- | Parse a hex-encoded integer.
+parse_integer :: BS.ByteString -> Integer
+parse_integer = roll . B16.decodeLenient
+
-- | Parse hex-encoded compressed point (33 bytes), uncompressed point
-- (65 bytes), or BIP0340-style point (32 bytes).
parse_point :: BS.ByteString -> Maybe Projective
@@ -518,6 +527,16 @@ parse_point (B16.decode -> ebs) = case ebs of
-- schnorr --------------------------------------------------------------------
-- see https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki
+-- | Create a 64-byte Schnorr signature for the provided message, using
+-- the provided secret key.
+--
+-- BIP0340 recommends that 32 bytes of fresh auxiliary entropy be
+-- generated and added at signing time as additional protection
+-- against side-channel attacks (namely, to thwart so-called "fault
+-- injection" attacks). This entropy is /supplemental/ to security,
+-- and the cryptographic security of the signature scheme itself does
+-- not rely on it, so it is not strictly required; 32 zero bytes can
+-- be used in its stead (and can be supplied via 'mempty').
sign_schnorr
:: Integer -- ^ secret key
-> BS.ByteString -- ^ message
@@ -526,7 +545,8 @@ sign_schnorr
sign_schnorr d' m a
| not (ge d') = error "ppad-secp256k1 (sign_schnorr): invalid secret key"
| otherwise =
- let p@(Affine x_p y_p) = affine (mul _CURVE_G d')
+ let p_proj = mul _CURVE_G d'
+ Affine x_p y_p = affine p_proj
d | y_p `rem` 2 == 0 = d' -- d' group element assures p nonzero
| otherwise = _CURVE_Q - d'
@@ -554,16 +574,18 @@ sign_schnorr d' m a
sig = bytes_r <> bytes_ked
- in if verify_schnorr m p sig
+ in if verify_schnorr m p_proj sig
then sig
else error "ppad-secp256k1 (sign_schnorr): invalid signature"
+-- | Verify a 64-byte Schnorr signature for the provided message with
+-- the supplied public key.
verify_schnorr
:: BS.ByteString -- ^ message
- -> Affine -- ^ public key
+ -> Pub -- ^ public key
-> BS.ByteString -- ^ 64-byte schnorr signature
-> Bool
-verify_schnorr m (Affine x_p _) sig = case lift x_p of
+verify_schnorr m (affine -> Affine x_p _) sig = case lift x_p of
Nothing -> False
Just capP@(Affine x_P _) ->
let (roll -> r, roll -> s) = BS.splitAt 32 sig
@@ -604,6 +626,7 @@ bits2octets bs =
z2 = modQ z1
in int2octets z2
+-- | An ECDSA signature.
data ECDSA = ECDSA {
ecdsa_r :: !Integer
, ecdsa_s :: !Integer
@@ -713,7 +736,7 @@ low (ECDSA r s) = ECDSA r ms where
-- public key.
verify_ecdsa
:: BS.ByteString -- ^ message
- -> Projective -- ^ public key
+ -> Pub -- ^ public key
-> ECDSA -- ^ signature
-> Bool
verify_ecdsa m p sig@(ECDSA _ s)
@@ -724,7 +747,7 @@ verify_ecdsa m p sig@(ECDSA _ s)
-- public key.
verify_ecdsa_unrestricted
:: BS.ByteString -- ^ message
- -> Projective -- ^ public key
+ -> Pub -- ^ public key
-> ECDSA -- ^ signature
-> Bool
verify_ecdsa_unrestricted (SHA256.hash -> h) p (ECDSA r s)
diff --git a/test/BIP340.hs b/test/BIP340.hs
@@ -41,7 +41,7 @@ execute :: Case -> TestTree
execute Case {..} = testCase ("bip0340 " <> show c_index) $
case parse_point c_pk of
Nothing -> assertBool mempty (not c_res)
- Just (affine -> pk) -> do
+ Just pk -> do
if c_sk == mempty
then do -- no signature; test verification
let ver = verify_schnorr c_msg pk c_sig