commit 4be6015e618ae285400ae68189ad392abf91c4b9
parent 036b7c46909251e8348f6d7d50115f04e3f38e98
Author: Jared Tobin <jared@jtobin.io>
Date: Sun, 20 Oct 2024 15:21:39 +0400
lib: rename modules to match ppad-secp256k1
Diffstat:
8 files changed, 1042 insertions(+), 1042 deletions(-)
diff --git a/bench/Main.hs b/bench/Main.hs
@@ -4,8 +4,8 @@ module Main where
import Control.DeepSeq
import Criterion.Main
-import qualified Crypto.Secp256k1 as S
-import qualified Crypto.Secp256k1.Internal as SI
+import qualified Crypto.Curve.Secp256k1 as S
+import qualified Crypto.Curve.Secp256k1.Internal as SI
import qualified Data.ByteString as BS
instance NFData S.Context
diff --git a/lib/Crypto/Curve/Secp256k1.hs b/lib/Crypto/Curve/Secp256k1.hs
@@ -0,0 +1,656 @@
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DerivingStrategies #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ViewPatterns #-}
+
+-- |
+-- Module: Crypto.Curve.Secp256k1
+-- Copyright: (c) 2024 Jared Tobin
+-- License: MIT
+-- Maintainer: Jared Tobin <jared@ppad.tech>
+--
+-- Bindings to bitcoin-core/secp256k1, a C library supporting digital
+-- signatures and other cryptographic primitives on the secp256k1
+-- elliptic curve.
+--
+-- This library exposes a minimal subset of functionality, primarily
+-- supporting ECDSA/Schnorr signatures and ECDH secret computation.
+--
+-- We're interacting with a C library here, and we don't pretend that we
+-- aren't. 'IO' is prevalent and unhidden, and asynchronous exceptions
+-- (in the form of 'Secp256k1Exception') are the error-handling method
+-- of choice.
+
+module Crypto.Curve.Secp256k1 (
+ Context(..)
+ , wcontext
+ , wrcontext
+
+ , Sig
+ , sign
+ , sign_schnorr
+ , verify
+ , verify_schnorr
+ , ecdh
+
+ , parse_der
+ , serialize_der
+ , parse_compact
+ , serialize_compact
+
+ , Pub
+ , derive_pub
+ , parse_pub
+ , tweak_pub_add
+ , tweak_pub_mul
+ , tweak_sec_add
+ , tweak_sec_mul
+ , serialize_pub
+ , serialize_pub_u
+ , XOnlyPub
+ , xonly
+ , parse_xonly
+ , serialize_xonly
+ , KeyPair
+ , keypair
+ , keypair_pub
+ , keypair_sec
+
+ , Secp256k1Exception(..)
+ ) where
+
+import Control.Exception (Exception, bracket, throwIO)
+import Control.Monad (when)
+import Crypto.Curve.Secp256k1.Internal hiding (Context, wcontext)
+import qualified Crypto.Curve.Secp256k1.Internal as I (Context)
+import GHC.Generics
+import qualified Data.ByteString as BS
+import qualified Foreign.Marshal.Alloc as A (alloca, allocaBytes)
+import Foreign.Ptr (Ptr)
+import qualified Foreign.Ptr as F (castPtr, nullPtr)
+import qualified Foreign.Storable as S (poke, peek)
+
+-- | A bitcoin-core/secp256k1 context.
+--
+-- bitcoin-core/secp256k1 computations typically require a context,
+-- the primary purpose of which is to store randomization data as
+-- increased protection against side-channel attacks (and the second
+-- of which is boring pointer storage to various library callbacks).
+--
+-- You should create and use values of this type via 'wrcontext' or
+-- 'wcontext'.
+--
+-- The data constructor is exported only to make the implementation
+-- easier to benchmark. You should /not/ pattern match on or
+-- manipulate context values.
+newtype Context = Context (Ptr I.Context)
+ deriving stock Generic
+
+instance Show Context where
+ show (Context tex) = "<bitcoin-core/secp256k1 context " <> show tex <> ">"
+
+-- | A bitcoin-core/secp256k1-internal public key.
+--
+-- Create a value of this type by parsing a compressed or uncompressed
+-- public key via 'parse_pub', deriving one from a secret key via
+-- 'create_pub', or extracting one from a keypair via 'keypair_pub'.
+newtype Pub = Pub BS.ByteString
+ deriving stock Generic
+
+instance Show Pub where
+ show _ = "<bitcoin-core/secp256k1 public key>"
+
+-- | A bitcoin-core/secp256k1-internal x-only public key.
+--
+-- An "x-only" public key corresponds to a public key with even
+-- y-coordinate.
+--
+-- Create a value of this type from a 'Pub' via 'xonly', or parse one
+-- directly via 'parse_xonly'.
+newtype XOnlyPub = XOnlyPub BS.ByteString
+ deriving stock Generic
+
+instance Show XOnlyPub where
+ show _ = "<bitcoin-core/secp256k1 x-only public key>"
+
+-- | A bitcoin-core/secp256k1-internal keypair.
+--
+-- Create a value of this type by passing a secret key to
+-- 'keypair'.
+newtype KeyPair = KeyPair BS.ByteString
+ deriving stock Generic
+
+instance Show KeyPair where
+ show _ = "<bitcoin-core/secp256k1 keypair>"
+
+-- | A bitcoin-core/secp256k1-internal ECDSA signature.
+--
+-- Create a value of this type via 'sign', or parse a DER-encoded
+-- signature via 'parse_der'.
+newtype Sig = Sig BS.ByteString
+ deriving stock Generic
+
+instance Show Sig where
+ show _ = "<bitcoin-core/secp256k1 signature>"
+
+-- exceptions
+
+-- | A catch-all exception type.
+--
+data Secp256k1Exception =
+ -- | Thrown when a bitcoin-core/secp256k1 function returns a value
+ -- indicating failure.
+ Secp256k1Error
+ -- | Thrown when a csecp256k1 function has been passed a bad (i.e.,
+ -- incorrectly-sized) input.
+ | CSecp256k1Error
+ deriving Show
+
+instance Exception Secp256k1Exception
+
+-- context
+
+-- | Execute the supplied continuation within a fresh
+-- bitcoin-core/secp256k1 context. The context will be destroyed
+-- afterwards.
+--
+-- This function executes the supplied continuation in a context
+-- that has /not/ been randomized, and so /doesn't/ offer additional
+-- side-channel attack protection. For that, use 'wrcontext'.
+--
+-- >>> wcontext $ \tex -> parse_pub tex bytestring
+-- "<bitcoin-core/secp256k1 public key>"
+wcontext
+ :: (Context -> IO ()) -- ^ continuation to run in the context
+ -> IO ()
+wcontext = bracket create destroy where
+ create = do
+ tex <- secp256k1_context_create _SECP256K1_CONTEXT_NONE
+ pure (Context tex)
+
+ destroy (Context tex) =
+ secp256k1_context_destroy tex
+
+-- | Same as 'wcontext', but randomize the bitcoin-core/secp256k1
+-- context with the provided 32 bytes of entropy before executing the
+-- supplied continuation.
+--
+-- Use this function to execute computations that may benefit from
+-- additional side-channel attack protection.
+--
+-- >>> wrcontext entropy $ \tex -> sign tex sec msg
+-- "<bitcoin-core/secp256k1 signature>"
+wrcontext
+ :: BS.ByteString -- ^ 32 bytes of fresh entropy
+ -> (Context -> IO ()) -- ^ continuation to run in the context
+ -> IO ()
+wrcontext enn con
+ | BS.length enn /= 32 = throwIO CSecp256k1Error
+ | otherwise = bracket create destroy con
+ where
+ create = do
+ tex <- secp256k1_context_create _SECP256K1_CONTEXT_NONE
+ BS.useAsCString enn $ \(F.castPtr -> sed) -> do
+ suc <- secp256k1_context_randomize tex sed
+ when (suc /= 1) $ throwIO Secp256k1Error
+ pure (Context tex)
+
+ destroy (Context tex) =
+ secp256k1_context_destroy tex
+
+-- ec
+
+-- | Derive a public key from a 32-byte secret key.
+--
+-- >>> wrcontext entropy $ \tex -> derive_pub tex sec
+-- "<bitcoin-core/secp256k1 public key>"
+derive_pub
+ :: Context
+ -> BS.ByteString -- ^ 32-byte secret key
+ -> IO Pub
+derive_pub (Context tex) bs
+ | BS.length bs /= 32 = throwIO CSecp256k1Error
+ | otherwise = BS.useAsCString bs $ \(F.castPtr -> sec) ->
+ A.allocaBytes _PUB_BYTES_INTERNAL $ \out -> do
+ suc <- secp256k1_ec_pubkey_create tex out sec
+ when (suc /= 1) $ throwIO Secp256k1Error
+ let pub = F.castPtr out
+ key <- BS.packCStringLen (pub, _PUB_BYTES_INTERNAL)
+ pure (Pub key)
+
+-- | Parse a compressed (33-byte) or uncompressed (65-byte) public key.
+--
+-- >>> wcontext $ \tex -> parse_pub tex bs
+-- "<bitcoin-core/secp256k1 public key>"
+parse_pub
+ :: Context
+ -> BS.ByteString -- ^ compressed or uncompressed public key
+ -> IO Pub
+parse_pub (Context tex) bs =
+ BS.useAsCStringLen bs $ \(F.castPtr -> pub, fromIntegral -> len) ->
+ A.allocaBytes _PUB_BYTES_INTERNAL $ \out -> do
+ suc <- secp256k1_ec_pubkey_parse tex out pub len
+ when (suc /= 1) $ throwIO Secp256k1Error
+ let par = F.castPtr out
+ key <- BS.packCStringLen (par, _PUB_BYTES_INTERNAL)
+ pure (Pub key)
+
+data PubFormat =
+ Compressed
+ | Uncompressed
+
+-- | Serialize a public key into a compressed (33-byte) bytestring
+-- representation.
+--
+-- >>> wcontext $ \tex -> serialize_pub tex pub
+serialize_pub
+ :: Context
+ -> Pub
+ -> IO BS.ByteString -- ^ serialized compressed public key
+serialize_pub = serialize_pub_in Compressed
+
+-- | Serialize a public key into an uncompressed (65-byte) bytestring
+-- represention.
+--
+-- >>> wcontext $ \tex -> serialize_pub_u tex pub
+serialize_pub_u
+ :: Context
+ -> Pub
+ -> IO BS.ByteString -- ^ serialized uncompressed public key
+serialize_pub_u = serialize_pub_in Uncompressed
+
+serialize_pub_in :: PubFormat -> Context -> Pub -> IO BS.ByteString
+serialize_pub_in for (Context tex) (Pub pub) =
+ BS.useAsCString pub $ \(F.castPtr -> key) ->
+ A.alloca $ \len ->
+ A.allocaBytes bys $ \out -> do
+ let siz = fromIntegral bys
+ S.poke len siz
+ suc <- secp256k1_ec_pubkey_serialize tex out len key fal
+ when (suc /= 1) $ throwIO Secp256k1Error
+ pec <- S.peek len
+ let enc = F.castPtr out
+ nel = fromIntegral pec
+ BS.packCStringLen (enc, nel)
+ where
+ bys = case for of
+ Compressed -> _PUB_BYTES_COMPRESSED
+ Uncompressed -> _PUB_BYTES_UNCOMPRESSED
+
+ fal = case for of
+ Compressed -> _COMPRESSED_FLAG
+ Uncompressed -> _UNCOMPRESSED_FLAG
+
+-- | Additively tweak a public key with the supplied 32-byte tweak.
+--
+-- >>> wrcontext $ \tex -> tweak_pub_add pub tweak
+tweak_pub_add
+ :: Context
+ -> Pub
+ -> BS.ByteString -- ^ 32-byte tweak value
+ -> IO Pub
+tweak_pub_add (Context tex) (Pub pub) wee
+ | BS.length wee /= 32 = throwIO CSecp256k1Error
+ | otherwise = do
+ let cop = BS.copy pub
+ BS.useAsCString cop $ \(F.castPtr -> out) ->
+ BS.useAsCString wee $ \(F.castPtr -> eek) -> do
+ suc <- secp256k1_ec_pubkey_tweak_add tex out eek
+ when (suc /= 1) $ throwIO Secp256k1Error
+ let enc = F.castPtr out
+ key <- BS.packCStringLen (enc, _PUB_BYTES_INTERNAL)
+ pure (Pub key)
+
+-- | Multiplicatively tweak a public key with the supplied 32-byte
+-- tweak.
+--
+-- >>> wrcontext $ \tex -> tweak_pub_mul pub tweak
+tweak_pub_mul
+ :: Context
+ -> Pub
+ -> BS.ByteString -- ^ 32-byte tweak value
+ -> IO Pub
+tweak_pub_mul (Context tex) (Pub pub) wee
+ | BS.length wee /= 32 = throwIO CSecp256k1Error
+ | otherwise = do
+ let cop = BS.copy pub
+ BS.useAsCString cop $ \(F.castPtr -> out) ->
+ BS.useAsCString wee $ \(F.castPtr -> eek) -> do
+ suc <- secp256k1_ec_pubkey_tweak_mul tex out eek
+ when (suc /= 1) $ throwIO Secp256k1Error
+ let enc = F.castPtr out
+ key <- BS.packCStringLen (enc, _PUB_BYTES_INTERNAL)
+ pure (Pub key)
+
+-- | Additively tweak a secret key with the supplied 32-byte tweak.
+--
+-- >>> wrcontext $ \tex -> tweak_sec_add sec tweak
+tweak_sec_add
+ :: Context
+ -> BS.ByteString -- ^ 32-byte secret key
+ -> BS.ByteString -- ^ 32-byte tweak value
+ -> IO BS.ByteString -- ^ 32-byte secret key
+tweak_sec_add (Context tex) key wee
+ | BS.length key /= 32 || BS.length wee /= 32 = throwIO CSecp256k1Error
+ | otherwise = do
+ let sec = BS.copy key
+ BS.useAsCString sec $ \(F.castPtr -> out) ->
+ BS.useAsCString wee $ \(F.castPtr -> eek) -> do
+ suc <- secp256k1_ec_seckey_tweak_add tex out eek
+ when (suc /= 1) $ throwIO Secp256k1Error
+ let enc = F.castPtr out
+ BS.packCStringLen (enc, _SEC_BYTES)
+
+-- | Multiplicatively tweak a secret key with the supplied 32-byte
+-- tweak.
+--
+-- >>> wrcontext $ \tex -> tweak_sec_mul sec tweak
+tweak_sec_mul
+ :: Context
+ -> BS.ByteString -- ^ 32-byte secret key
+ -> BS.ByteString -- ^ 32-byte tweak value
+ -> IO BS.ByteString -- ^ 32-byte secret key
+tweak_sec_mul (Context tex) key wee
+ | BS.length key /= 32 || BS.length wee /= 32 = throwIO CSecp256k1Error
+ | otherwise = do
+ let sec = BS.copy key
+ BS.useAsCString sec $ \(F.castPtr -> out) ->
+ BS.useAsCString wee $ \(F.castPtr -> eek) -> do
+ suc <- secp256k1_ec_seckey_tweak_mul tex out eek
+ when (suc /= 1) $ throwIO Secp256k1Error
+ let enc = F.castPtr out
+ BS.packCStringLen (enc, _SEC_BYTES)
+
+-- ecdsa
+
+-- | Sign a 32-byte message hash with the provided secret key.
+--
+-- >>> wrcontext entropy $ \tex -> sign tex sec msg
+-- "<bitcoin-core/secp256k1 signature>"
+sign
+ :: Context
+ -> BS.ByteString -- ^ 32-byte secret key
+ -> BS.ByteString -- ^ 32-byte message hash
+ -> IO Sig
+sign (Context tex) key msg
+ | BS.length key /= 32 || BS.length msg /= 32 = throwIO CSecp256k1Error
+ | otherwise = A.allocaBytes _SIG_BYTES $ \out ->
+ BS.useAsCString msg $ \(F.castPtr -> has) ->
+ BS.useAsCString key $ \(F.castPtr -> sec) -> do
+ suc <- secp256k1_ecdsa_sign tex out has sec F.nullPtr F.nullPtr
+ when (suc /= 1) $ throwIO Secp256k1Error
+ let sig = F.castPtr out
+ enc <- BS.packCStringLen (sig, _SIG_BYTES)
+ pure (Sig enc)
+
+-- | Verify an ECDSA signature for the provided message hash with the
+-- supplied public key.
+--
+-- Returns 'True' for a verifying signature, 'False' otherwise.
+--
+-- >>> wcontext $ \tex -> verify tex pub msg good_sig
+-- True
+-- >>> wcontext $ \tex -> verify tex pub msg bad_sig
+-- False
+verify
+ :: Context
+ -> Pub
+ -> BS.ByteString -- ^ 32-byte message hash
+ -> Sig
+ -> IO Bool
+verify (Context tex) (Pub pub) msg (Sig sig)
+ | BS.length msg /= 32 = throwIO CSecp256k1Error
+ | otherwise = BS.useAsCString pub $ \(F.castPtr -> key) ->
+ BS.useAsCString sig $ \(F.castPtr -> sip) ->
+ BS.useAsCString msg $ \(F.castPtr -> has) -> do
+ suc <- secp256k1_ecdsa_verify tex sip has key
+ pure (suc == 1)
+
+-- | Parse a DER-encoded bytestring into a signature.
+--
+-- >>> wcontext $ \tex -> parse_der tex bytestring
+-- "<bitcoin-core/secp256k1 signature>"
+-- >>> wcontext $ \tex -> parse_der tex bad_bytestring
+-- *** Exception: Secp256k1Error
+parse_der
+ :: Context
+ -> BS.ByteString -- ^ DER-encoded signature
+ -> IO Sig
+parse_der (Context tex) bs =
+ BS.useAsCStringLen bs $ \(F.castPtr -> der, fromIntegral -> len) ->
+ A.allocaBytes _SIG_BYTES $ \out -> do
+ suc <- secp256k1_ecdsa_signature_parse_der tex out der len
+ when (suc /= 1) $ throwIO Secp256k1Error
+ let par = F.castPtr out
+ sig <- BS.packCStringLen (par, _SIG_BYTES)
+ pure (Sig sig)
+
+-- | Serialize a signature into a DER-encoded bytestring.
+--
+-- >>> wcontext $ \tex -> serialize_der tex sig
+serialize_der
+ :: Context
+ -> Sig
+ -> IO BS.ByteString -- ^ DER-encoded signature
+serialize_der (Context tex) (Sig sig) =
+ A.alloca $ \len ->
+ A.allocaBytes _DER_BYTES $ \out ->
+ BS.useAsCString sig $ \(F.castPtr -> sip) -> do
+ let siz = fromIntegral _DER_BYTES
+ S.poke len siz
+ suc <- secp256k1_ecdsa_signature_serialize_der tex out len sip
+ when (suc /= 1) $ throwIO Secp256k1Error
+ pek <- S.peek len
+ let der = F.castPtr out
+ nel = fromIntegral pek
+ BS.packCStringLen (der, nel)
+
+-- | Parse a bytestring encoding a compact (64-byte) signature.
+--
+-- >>> wcontext $ \tex -> parse_compact tex bytestring
+parse_compact
+ :: Context
+ -> BS.ByteString -- ^ bytestring encoding a 64-byte compact signature
+ -> IO Sig
+parse_compact (Context tex) bs =
+ BS.useAsCString bs $ \(F.castPtr -> com) ->
+ A.allocaBytes _SIG_BYTES $ \out -> do
+ suc <- secp256k1_ecdsa_signature_parse_compact tex out com
+ when (suc /= 1) $ throwIO Secp256k1Error
+ let par = F.castPtr out
+ enc <- BS.packCStringLen (par, _SIG_BYTES)
+ pure (Sig enc)
+
+-- | Serialize a signature into a compact (64-byte) bytestring.
+--
+-- >>> wcontext $ \tex -> serialize_compact tex sig
+serialize_compact
+ :: Context
+ -> Sig
+ -> IO BS.ByteString
+serialize_compact (Context tex) (Sig sig) =
+ BS.useAsCString sig $ \(F.castPtr -> sip) ->
+ A.allocaBytes _SIG_BYTES $ \out -> do
+ -- always returns 1
+ _ <- secp256k1_ecdsa_signature_serialize_compact tex out sip
+ let enc = F.castPtr out
+ BS.packCStringLen (enc, _SIG_BYTES)
+
+-- extrakeys
+
+-- | Convert a public key into an x-only public key (i.e. one with even
+-- y coordinate).
+--
+-- >>> wcontext $ \tex -> xonly tex pub
+-- "<bitcoin-core/secp256k1 x-only public key>"
+xonly :: Context -> Pub -> IO XOnlyPub
+xonly (Context tex) (Pub pub) =
+ A.allocaBytes _PUB_BYTES_INTERNAL $ \out ->
+ BS.useAsCString pub $ \(F.castPtr -> pup) -> do
+ -- returns 1 always
+ _ <- secp256k1_xonly_pubkey_from_pubkey tex out F.nullPtr pup
+ let key = F.castPtr out
+ pux <- BS.packCStringLen (key, _PUB_BYTES_INTERNAL)
+ pure (XOnlyPub pux)
+
+-- | Parse a compressed (33-byte) or uncompressed (65-byte) public key into
+-- an x-only public key.
+--
+-- >>> wcontext $ \tex -> parse_xonly tex bytestring
+-- "<bitcoin-core/secp256k1 x-only public key>"
+parse_xonly
+ :: Context
+ -> BS.ByteString -- ^ compressed or uncompressed public key
+ -> IO XOnlyPub
+parse_xonly (Context tex) bs =
+ A.allocaBytes _PUB_BYTES_INTERNAL $ \out ->
+ BS.useAsCString bs $ \(F.castPtr -> pub) -> do
+ suc <- secp256k1_xonly_pubkey_parse tex out pub
+ when (suc /= 1) $ throwIO Secp256k1Error
+ let key = F.castPtr out
+ pux <- BS.packCStringLen (key, _PUB_BYTES_INTERNAL)
+ pure (XOnlyPub pux)
+
+-- | Serialize an x-only public key into a 32-byte bytestring
+-- representation.
+--
+-- >>> wcontext $ \tex -> serialize_xonly tex xonly
+serialize_xonly
+ :: Context
+ -> XOnlyPub
+ -> IO BS.ByteString -- ^ serialized x-only public key
+serialize_xonly (Context tex) (XOnlyPub pux) =
+ A.allocaBytes _PUB_BYTES_XONLY $ \out -> do
+ BS.useAsCString pux $ \(F.castPtr -> pub) -> do
+ -- returns 1 always
+ _ <- secp256k1_xonly_pubkey_serialize tex out pub
+ let enc = F.castPtr out
+ BS.packCStringLen (enc, _PUB_BYTES_XONLY)
+
+-- | Derive a keypair from the provided 32-byte secret key.
+--
+-- >>> wrcontext entropy $ \tex -> keypair tex sec
+-- "<bitcoin-core/secp256k1 keypair>"
+keypair
+ :: Context
+ -> BS.ByteString -- ^ 32-byte secret key
+ -> IO KeyPair
+keypair (Context tex) sec
+ | BS.length sec /= 32 = throwIO CSecp256k1Error
+ | otherwise = A.allocaBytes _KEYPAIR_BYTES $ \out ->
+ BS.useAsCString sec $ \(F.castPtr -> key) -> do
+ suc <- secp256k1_keypair_create tex out key
+ when (suc /= 1) $ throwIO Secp256k1Error
+ let enc = F.castPtr out
+ per <- BS.packCStringLen (enc, _KEYPAIR_BYTES)
+ pure (KeyPair per)
+
+-- | Extract a public key from a keypair.
+--
+-- >>> wrcontext entropy $ \tex -> keypair_pub tex keypair
+-- "<bitcoin-core/secp256k1 public key>"
+keypair_pub :: Context -> KeyPair -> IO Pub
+keypair_pub (Context tex) (KeyPair per) =
+ A.allocaBytes _PUB_BYTES_INTERNAL $ \out ->
+ BS.useAsCString per $ \(F.castPtr -> par) -> do
+ -- returns 1 always
+ _ <- secp256k1_keypair_pub tex out par
+ let enc = F.castPtr out
+ pub <- BS.packCStringLen (enc, _PUB_BYTES_INTERNAL)
+ pure (Pub pub)
+
+-- | Extract a secret key from a keypair.
+--
+-- >>> wrcontext entropy $ \tex -> keypair_sec tex keypair
+keypair_sec
+ :: Context
+ -> KeyPair
+ -> IO BS.ByteString -- ^ 32-byte secret key
+keypair_sec (Context tex) (KeyPair per) =
+ A.allocaBytes _SEC_BYTES $ \out ->
+ BS.useAsCString per $ \(F.castPtr -> par) -> do
+ _ <- secp256k1_keypair_sec tex out par
+ let enc = F.castPtr out
+ BS.packCStringLen (enc, _SEC_BYTES)
+
+-- ecdh
+
+-- | Compute an ECDH secret key from the provided public key and
+-- (32-byte) secret key.
+--
+-- >>> wrcontext entropy $ \tex -> ecdh tex pub sec
+ecdh
+ :: Context
+ -> Pub
+ -> BS.ByteString -- ^ 32-byte secret key
+ -> IO BS.ByteString -- ^ 32-byte secret key
+ecdh (Context tex) (Pub pub) sec
+ | BS.length sec /= 32 = throwIO CSecp256k1Error
+ | otherwise =
+ A.allocaBytes _SEC_BYTES $ \out ->
+ BS.useAsCString pub $ \(F.castPtr -> pup) ->
+ BS.useAsCString sec $ \(F.castPtr -> sep) -> do
+ suc <- secp256k1_ecdh tex out pup sep F.nullPtr F.nullPtr
+ when (suc /= 1) $ throwIO Secp256k1Error
+ let key = F.castPtr out
+ BS.packCStringLen (key, _SEC_BYTES)
+
+-- schnorr
+
+-- | Sign a 32-byte message hash with the provided secret key, using the
+-- provided 32 bytes of fresh auxiliary entropy.
+--
+-- BIP340 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.
+--
+-- The resulting 64-byte Schnorr signature is portable, and so is not
+-- wrapped in a newtype.
+--
+-- >>> import qualified System.Entropy as E -- example entropy source
+-- >>> enn <- E.getEntropy 32
+-- >>> aux <- E.getEntropy 32
+-- >>> wrcontext enn $ \tex -> sign_schnorr tex msg sec aux
+sign_schnorr
+ :: Context
+ -> BS.ByteString -- ^ 32-byte message hash
+ -> BS.ByteString -- ^ 32-byte secret key
+ -> BS.ByteString -- ^ 32 bytes of fresh entropy
+ -> IO BS.ByteString -- ^ 64-byte signature
+sign_schnorr c@(Context tex) msg sec aux
+ | BS.length msg /= 32 || BS.length sec /= 32 || BS.length aux /= 32 =
+ throwIO CSecp256k1Error
+ | otherwise = A.allocaBytes _SIG_BYTES $ \out ->
+ BS.useAsCString msg $ \(F.castPtr -> has) ->
+ BS.useAsCString aux $ \(F.castPtr -> enn) -> do
+ KeyPair per <- keypair c sec
+ BS.useAsCString per $ \(F.castPtr -> pur) -> do
+ suc <- secp256k1_schnorrsig_sign32 tex out has pur enn
+ when (suc /= 1) $ throwIO Secp256k1Error
+ let enc = F.castPtr out
+ BS.packCStringLen (enc, _SIG_BYTES)
+
+-- | Verify a 64-byte Schnorr signature for the provided 32-byte message
+-- hash with the supplied public key.
+--
+-- >>> wrcontext entropy $ \tex -> verify_schnorr tex pub msg sig
+verify_schnorr
+ :: Context
+ -> Pub
+ -> BS.ByteString -- ^ 32-byte message hash
+ -> BS.ByteString -- ^ 64-byte signature
+ -> IO Bool
+verify_schnorr c@(Context tex) pub msg sig
+ | BS.length msg /= 32 || BS.length sig /= 64 = throwIO CSecp256k1Error
+ | otherwise =
+ BS.useAsCString sig $ \(F.castPtr -> sip) ->
+ BS.useAsCStringLen msg $ \(F.castPtr -> has, fromIntegral -> len) -> do
+ XOnlyPub pux <- xonly c pub
+ BS.useAsCString pux $ \(F.castPtr -> pax) -> do
+ suc <- secp256k1_schnorrsig_verify tex sip has len pax
+ pure (suc == 1)
+
diff --git a/lib/Crypto/Secp256k1.hs b/lib/Crypto/Secp256k1.hs
@@ -1,656 +0,0 @@
-{-# LANGUAGE DeriveGeneric #-}
-{-# LANGUAGE DerivingStrategies #-}
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE ViewPatterns #-}
-
--- |
--- Module: Crypto.Secp256k1
--- Copyright: (c) 2024 Jared Tobin
--- License: MIT
--- Maintainer: Jared Tobin <jared@ppad.tech>
---
--- Bindings to bitcoin-core/secp256k1, a C library supporting digital
--- signatures and other cryptographic primitives on the secp256k1
--- elliptic curve.
---
--- This library exposes a minimal subset of functionality, primarily
--- supporting ECDSA/Schnorr signatures and ECDH secret computation.
---
--- We're interacting with a C library here, and we don't pretend that we
--- aren't. 'IO' is prevalent and unhidden, and asynchronous exceptions
--- (in the form of 'Secp256k1Exception') are the error-handling method
--- of choice.
-
-module Crypto.Secp256k1 (
- Context(..)
- , wcontext
- , wrcontext
-
- , Sig
- , sign
- , sign_schnorr
- , verify
- , verify_schnorr
- , ecdh
-
- , parse_der
- , serialize_der
- , parse_compact
- , serialize_compact
-
- , Pub
- , derive_pub
- , parse_pub
- , tweak_pub_add
- , tweak_pub_mul
- , tweak_sec_add
- , tweak_sec_mul
- , serialize_pub
- , serialize_pub_u
- , XOnlyPub
- , xonly
- , parse_xonly
- , serialize_xonly
- , KeyPair
- , keypair
- , keypair_pub
- , keypair_sec
-
- , Secp256k1Exception(..)
- ) where
-
-import Control.Exception (Exception, bracket, throwIO)
-import Control.Monad (when)
-import Crypto.Secp256k1.Internal hiding (Context, wcontext)
-import qualified Crypto.Secp256k1.Internal as I (Context)
-import GHC.Generics
-import qualified Data.ByteString as BS
-import qualified Foreign.Marshal.Alloc as A (alloca, allocaBytes)
-import Foreign.Ptr (Ptr)
-import qualified Foreign.Ptr as F (castPtr, nullPtr)
-import qualified Foreign.Storable as S (poke, peek)
-
--- | A bitcoin-core/secp256k1 context.
---
--- bitcoin-core/secp256k1 computations typically require a context,
--- the primary purpose of which is to store randomization data as
--- increased protection against side-channel attacks (and the second
--- of which is boring pointer storage to various library callbacks).
---
--- You should create and use values of this type via 'wrcontext' or
--- 'wcontext'.
---
--- The data constructor is exported only to make the implementation
--- easier to benchmark. You should /not/ pattern match on or
--- manipulate context values.
-newtype Context = Context (Ptr I.Context)
- deriving stock Generic
-
-instance Show Context where
- show (Context tex) = "<bitcoin-core/secp256k1 context " <> show tex <> ">"
-
--- | A bitcoin-core/secp256k1-internal public key.
---
--- Create a value of this type by parsing a compressed or uncompressed
--- public key via 'parse_pub', deriving one from a secret key via
--- 'create_pub', or extracting one from a keypair via 'keypair_pub'.
-newtype Pub = Pub BS.ByteString
- deriving stock Generic
-
-instance Show Pub where
- show _ = "<bitcoin-core/secp256k1 public key>"
-
--- | A bitcoin-core/secp256k1-internal x-only public key.
---
--- An "x-only" public key corresponds to a public key with even
--- y-coordinate.
---
--- Create a value of this type from a 'Pub' via 'xonly', or parse one
--- directly via 'parse_xonly'.
-newtype XOnlyPub = XOnlyPub BS.ByteString
- deriving stock Generic
-
-instance Show XOnlyPub where
- show _ = "<bitcoin-core/secp256k1 x-only public key>"
-
--- | A bitcoin-core/secp256k1-internal keypair.
---
--- Create a value of this type by passing a secret key to
--- 'keypair'.
-newtype KeyPair = KeyPair BS.ByteString
- deriving stock Generic
-
-instance Show KeyPair where
- show _ = "<bitcoin-core/secp256k1 keypair>"
-
--- | A bitcoin-core/secp256k1-internal ECDSA signature.
---
--- Create a value of this type via 'sign', or parse a DER-encoded
--- signature via 'parse_der'.
-newtype Sig = Sig BS.ByteString
- deriving stock Generic
-
-instance Show Sig where
- show _ = "<bitcoin-core/secp256k1 signature>"
-
--- exceptions
-
--- | A catch-all exception type.
---
-data Secp256k1Exception =
- -- | Thrown when a bitcoin-core/secp256k1 function returns a value
- -- indicating failure.
- Secp256k1Error
- -- | Thrown when a csecp256k1 function has been passed a bad (i.e.,
- -- incorrectly-sized) input.
- | CSecp256k1Error
- deriving Show
-
-instance Exception Secp256k1Exception
-
--- context
-
--- | Execute the supplied continuation within a fresh
--- bitcoin-core/secp256k1 context. The context will be destroyed
--- afterwards.
---
--- This function executes the supplied continuation in a context
--- that has /not/ been randomized, and so /doesn't/ offer additional
--- side-channel attack protection. For that, use 'wrcontext'.
---
--- >>> wcontext $ \tex -> parse_pub tex bytestring
--- "<bitcoin-core/secp256k1 public key>"
-wcontext
- :: (Context -> IO ()) -- ^ continuation to run in the context
- -> IO ()
-wcontext = bracket create destroy where
- create = do
- tex <- secp256k1_context_create _SECP256K1_CONTEXT_NONE
- pure (Context tex)
-
- destroy (Context tex) =
- secp256k1_context_destroy tex
-
--- | Same as 'wcontext', but randomize the bitcoin-core/secp256k1
--- context with the provided 32 bytes of entropy before executing the
--- supplied continuation.
---
--- Use this function to execute computations that may benefit from
--- additional side-channel attack protection.
---
--- >>> wrcontext entropy $ \tex -> sign tex sec msg
--- "<bitcoin-core/secp256k1 signature>"
-wrcontext
- :: BS.ByteString -- ^ 32 bytes of fresh entropy
- -> (Context -> IO ()) -- ^ continuation to run in the context
- -> IO ()
-wrcontext enn con
- | BS.length enn /= 32 = throwIO CSecp256k1Error
- | otherwise = bracket create destroy con
- where
- create = do
- tex <- secp256k1_context_create _SECP256K1_CONTEXT_NONE
- BS.useAsCString enn $ \(F.castPtr -> sed) -> do
- suc <- secp256k1_context_randomize tex sed
- when (suc /= 1) $ throwIO Secp256k1Error
- pure (Context tex)
-
- destroy (Context tex) =
- secp256k1_context_destroy tex
-
--- ec
-
--- | Derive a public key from a 32-byte secret key.
---
--- >>> wrcontext entropy $ \tex -> derive_pub tex sec
--- "<bitcoin-core/secp256k1 public key>"
-derive_pub
- :: Context
- -> BS.ByteString -- ^ 32-byte secret key
- -> IO Pub
-derive_pub (Context tex) bs
- | BS.length bs /= 32 = throwIO CSecp256k1Error
- | otherwise = BS.useAsCString bs $ \(F.castPtr -> sec) ->
- A.allocaBytes _PUB_BYTES_INTERNAL $ \out -> do
- suc <- secp256k1_ec_pubkey_create tex out sec
- when (suc /= 1) $ throwIO Secp256k1Error
- let pub = F.castPtr out
- key <- BS.packCStringLen (pub, _PUB_BYTES_INTERNAL)
- pure (Pub key)
-
--- | Parse a compressed (33-byte) or uncompressed (65-byte) public key.
---
--- >>> wcontext $ \tex -> parse_pub tex bs
--- "<bitcoin-core/secp256k1 public key>"
-parse_pub
- :: Context
- -> BS.ByteString -- ^ compressed or uncompressed public key
- -> IO Pub
-parse_pub (Context tex) bs =
- BS.useAsCStringLen bs $ \(F.castPtr -> pub, fromIntegral -> len) ->
- A.allocaBytes _PUB_BYTES_INTERNAL $ \out -> do
- suc <- secp256k1_ec_pubkey_parse tex out pub len
- when (suc /= 1) $ throwIO Secp256k1Error
- let par = F.castPtr out
- key <- BS.packCStringLen (par, _PUB_BYTES_INTERNAL)
- pure (Pub key)
-
-data PubFormat =
- Compressed
- | Uncompressed
-
--- | Serialize a public key into a compressed (33-byte) bytestring
--- representation.
---
--- >>> wcontext $ \tex -> serialize_pub tex pub
-serialize_pub
- :: Context
- -> Pub
- -> IO BS.ByteString -- ^ serialized compressed public key
-serialize_pub = serialize_pub_in Compressed
-
--- | Serialize a public key into an uncompressed (65-byte) bytestring
--- represention.
---
--- >>> wcontext $ \tex -> serialize_pub_u tex pub
-serialize_pub_u
- :: Context
- -> Pub
- -> IO BS.ByteString -- ^ serialized uncompressed public key
-serialize_pub_u = serialize_pub_in Uncompressed
-
-serialize_pub_in :: PubFormat -> Context -> Pub -> IO BS.ByteString
-serialize_pub_in for (Context tex) (Pub pub) =
- BS.useAsCString pub $ \(F.castPtr -> key) ->
- A.alloca $ \len ->
- A.allocaBytes bys $ \out -> do
- let siz = fromIntegral bys
- S.poke len siz
- suc <- secp256k1_ec_pubkey_serialize tex out len key fal
- when (suc /= 1) $ throwIO Secp256k1Error
- pec <- S.peek len
- let enc = F.castPtr out
- nel = fromIntegral pec
- BS.packCStringLen (enc, nel)
- where
- bys = case for of
- Compressed -> _PUB_BYTES_COMPRESSED
- Uncompressed -> _PUB_BYTES_UNCOMPRESSED
-
- fal = case for of
- Compressed -> _COMPRESSED_FLAG
- Uncompressed -> _UNCOMPRESSED_FLAG
-
--- | Additively tweak a public key with the supplied 32-byte tweak.
---
--- >>> wrcontext $ \tex -> tweak_pub_add pub tweak
-tweak_pub_add
- :: Context
- -> Pub
- -> BS.ByteString -- ^ 32-byte tweak value
- -> IO Pub
-tweak_pub_add (Context tex) (Pub pub) wee
- | BS.length wee /= 32 = throwIO CSecp256k1Error
- | otherwise = do
- let cop = BS.copy pub
- BS.useAsCString cop $ \(F.castPtr -> out) ->
- BS.useAsCString wee $ \(F.castPtr -> eek) -> do
- suc <- secp256k1_ec_pubkey_tweak_add tex out eek
- when (suc /= 1) $ throwIO Secp256k1Error
- let enc = F.castPtr out
- key <- BS.packCStringLen (enc, _PUB_BYTES_INTERNAL)
- pure (Pub key)
-
--- | Multiplicatively tweak a public key with the supplied 32-byte
--- tweak.
---
--- >>> wrcontext $ \tex -> tweak_pub_mul pub tweak
-tweak_pub_mul
- :: Context
- -> Pub
- -> BS.ByteString -- ^ 32-byte tweak value
- -> IO Pub
-tweak_pub_mul (Context tex) (Pub pub) wee
- | BS.length wee /= 32 = throwIO CSecp256k1Error
- | otherwise = do
- let cop = BS.copy pub
- BS.useAsCString cop $ \(F.castPtr -> out) ->
- BS.useAsCString wee $ \(F.castPtr -> eek) -> do
- suc <- secp256k1_ec_pubkey_tweak_mul tex out eek
- when (suc /= 1) $ throwIO Secp256k1Error
- let enc = F.castPtr out
- key <- BS.packCStringLen (enc, _PUB_BYTES_INTERNAL)
- pure (Pub key)
-
--- | Additively tweak a secret key with the supplied 32-byte tweak.
---
--- >>> wrcontext $ \tex -> tweak_sec_add sec tweak
-tweak_sec_add
- :: Context
- -> BS.ByteString -- ^ 32-byte secret key
- -> BS.ByteString -- ^ 32-byte tweak value
- -> IO BS.ByteString -- ^ 32-byte secret key
-tweak_sec_add (Context tex) key wee
- | BS.length key /= 32 || BS.length wee /= 32 = throwIO CSecp256k1Error
- | otherwise = do
- let sec = BS.copy key
- BS.useAsCString sec $ \(F.castPtr -> out) ->
- BS.useAsCString wee $ \(F.castPtr -> eek) -> do
- suc <- secp256k1_ec_seckey_tweak_add tex out eek
- when (suc /= 1) $ throwIO Secp256k1Error
- let enc = F.castPtr out
- BS.packCStringLen (enc, _SEC_BYTES)
-
--- | Multiplicatively tweak a secret key with the supplied 32-byte
--- tweak.
---
--- >>> wrcontext $ \tex -> tweak_sec_mul sec tweak
-tweak_sec_mul
- :: Context
- -> BS.ByteString -- ^ 32-byte secret key
- -> BS.ByteString -- ^ 32-byte tweak value
- -> IO BS.ByteString -- ^ 32-byte secret key
-tweak_sec_mul (Context tex) key wee
- | BS.length key /= 32 || BS.length wee /= 32 = throwIO CSecp256k1Error
- | otherwise = do
- let sec = BS.copy key
- BS.useAsCString sec $ \(F.castPtr -> out) ->
- BS.useAsCString wee $ \(F.castPtr -> eek) -> do
- suc <- secp256k1_ec_seckey_tweak_mul tex out eek
- when (suc /= 1) $ throwIO Secp256k1Error
- let enc = F.castPtr out
- BS.packCStringLen (enc, _SEC_BYTES)
-
--- ecdsa
-
--- | Sign a 32-byte message hash with the provided secret key.
---
--- >>> wrcontext entropy $ \tex -> sign tex sec msg
--- "<bitcoin-core/secp256k1 signature>"
-sign
- :: Context
- -> BS.ByteString -- ^ 32-byte secret key
- -> BS.ByteString -- ^ 32-byte message hash
- -> IO Sig
-sign (Context tex) key msg
- | BS.length key /= 32 || BS.length msg /= 32 = throwIO CSecp256k1Error
- | otherwise = A.allocaBytes _SIG_BYTES $ \out ->
- BS.useAsCString msg $ \(F.castPtr -> has) ->
- BS.useAsCString key $ \(F.castPtr -> sec) -> do
- suc <- secp256k1_ecdsa_sign tex out has sec F.nullPtr F.nullPtr
- when (suc /= 1) $ throwIO Secp256k1Error
- let sig = F.castPtr out
- enc <- BS.packCStringLen (sig, _SIG_BYTES)
- pure (Sig enc)
-
--- | Verify an ECDSA signature for the provided message hash with the
--- supplied public key.
---
--- Returns 'True' for a verifying signature, 'False' otherwise.
---
--- >>> wcontext $ \tex -> verify tex pub msg good_sig
--- True
--- >>> wcontext $ \tex -> verify tex pub msg bad_sig
--- False
-verify
- :: Context
- -> Pub
- -> BS.ByteString -- ^ 32-byte message hash
- -> Sig
- -> IO Bool
-verify (Context tex) (Pub pub) msg (Sig sig)
- | BS.length msg /= 32 = throwIO CSecp256k1Error
- | otherwise = BS.useAsCString pub $ \(F.castPtr -> key) ->
- BS.useAsCString sig $ \(F.castPtr -> sip) ->
- BS.useAsCString msg $ \(F.castPtr -> has) -> do
- suc <- secp256k1_ecdsa_verify tex sip has key
- pure (suc == 1)
-
--- | Parse a DER-encoded bytestring into a signature.
---
--- >>> wcontext $ \tex -> parse_der tex bytestring
--- "<bitcoin-core/secp256k1 signature>"
--- >>> wcontext $ \tex -> parse_der tex bad_bytestring
--- *** Exception: Secp256k1Error
-parse_der
- :: Context
- -> BS.ByteString -- ^ DER-encoded signature
- -> IO Sig
-parse_der (Context tex) bs =
- BS.useAsCStringLen bs $ \(F.castPtr -> der, fromIntegral -> len) ->
- A.allocaBytes _SIG_BYTES $ \out -> do
- suc <- secp256k1_ecdsa_signature_parse_der tex out der len
- when (suc /= 1) $ throwIO Secp256k1Error
- let par = F.castPtr out
- sig <- BS.packCStringLen (par, _SIG_BYTES)
- pure (Sig sig)
-
--- | Serialize a signature into a DER-encoded bytestring.
---
--- >>> wcontext $ \tex -> serialize_der tex sig
-serialize_der
- :: Context
- -> Sig
- -> IO BS.ByteString -- ^ DER-encoded signature
-serialize_der (Context tex) (Sig sig) =
- A.alloca $ \len ->
- A.allocaBytes _DER_BYTES $ \out ->
- BS.useAsCString sig $ \(F.castPtr -> sip) -> do
- let siz = fromIntegral _DER_BYTES
- S.poke len siz
- suc <- secp256k1_ecdsa_signature_serialize_der tex out len sip
- when (suc /= 1) $ throwIO Secp256k1Error
- pek <- S.peek len
- let der = F.castPtr out
- nel = fromIntegral pek
- BS.packCStringLen (der, nel)
-
--- | Parse a bytestring encoding a compact (64-byte) signature.
---
--- >>> wcontext $ \tex -> parse_compact tex bytestring
-parse_compact
- :: Context
- -> BS.ByteString -- ^ bytestring encoding a 64-byte compact signature
- -> IO Sig
-parse_compact (Context tex) bs =
- BS.useAsCString bs $ \(F.castPtr -> com) ->
- A.allocaBytes _SIG_BYTES $ \out -> do
- suc <- secp256k1_ecdsa_signature_parse_compact tex out com
- when (suc /= 1) $ throwIO Secp256k1Error
- let par = F.castPtr out
- enc <- BS.packCStringLen (par, _SIG_BYTES)
- pure (Sig enc)
-
--- | Serialize a signature into a compact (64-byte) bytestring.
---
--- >>> wcontext $ \tex -> serialize_compact tex sig
-serialize_compact
- :: Context
- -> Sig
- -> IO BS.ByteString
-serialize_compact (Context tex) (Sig sig) =
- BS.useAsCString sig $ \(F.castPtr -> sip) ->
- A.allocaBytes _SIG_BYTES $ \out -> do
- -- always returns 1
- _ <- secp256k1_ecdsa_signature_serialize_compact tex out sip
- let enc = F.castPtr out
- BS.packCStringLen (enc, _SIG_BYTES)
-
--- extrakeys
-
--- | Convert a public key into an x-only public key (i.e. one with even
--- y coordinate).
---
--- >>> wcontext $ \tex -> xonly tex pub
--- "<bitcoin-core/secp256k1 x-only public key>"
-xonly :: Context -> Pub -> IO XOnlyPub
-xonly (Context tex) (Pub pub) =
- A.allocaBytes _PUB_BYTES_INTERNAL $ \out ->
- BS.useAsCString pub $ \(F.castPtr -> pup) -> do
- -- returns 1 always
- _ <- secp256k1_xonly_pubkey_from_pubkey tex out F.nullPtr pup
- let key = F.castPtr out
- pux <- BS.packCStringLen (key, _PUB_BYTES_INTERNAL)
- pure (XOnlyPub pux)
-
--- | Parse a compressed (33-byte) or uncompressed (65-byte) public key into
--- an x-only public key.
---
--- >>> wcontext $ \tex -> parse_xonly tex bytestring
--- "<bitcoin-core/secp256k1 x-only public key>"
-parse_xonly
- :: Context
- -> BS.ByteString -- ^ compressed or uncompressed public key
- -> IO XOnlyPub
-parse_xonly (Context tex) bs =
- A.allocaBytes _PUB_BYTES_INTERNAL $ \out ->
- BS.useAsCString bs $ \(F.castPtr -> pub) -> do
- suc <- secp256k1_xonly_pubkey_parse tex out pub
- when (suc /= 1) $ throwIO Secp256k1Error
- let key = F.castPtr out
- pux <- BS.packCStringLen (key, _PUB_BYTES_INTERNAL)
- pure (XOnlyPub pux)
-
--- | Serialize an x-only public key into a 32-byte bytestring
--- representation.
---
--- >>> wcontext $ \tex -> serialize_xonly tex xonly
-serialize_xonly
- :: Context
- -> XOnlyPub
- -> IO BS.ByteString -- ^ serialized x-only public key
-serialize_xonly (Context tex) (XOnlyPub pux) =
- A.allocaBytes _PUB_BYTES_XONLY $ \out -> do
- BS.useAsCString pux $ \(F.castPtr -> pub) -> do
- -- returns 1 always
- _ <- secp256k1_xonly_pubkey_serialize tex out pub
- let enc = F.castPtr out
- BS.packCStringLen (enc, _PUB_BYTES_XONLY)
-
--- | Derive a keypair from the provided 32-byte secret key.
---
--- >>> wrcontext entropy $ \tex -> keypair tex sec
--- "<bitcoin-core/secp256k1 keypair>"
-keypair
- :: Context
- -> BS.ByteString -- ^ 32-byte secret key
- -> IO KeyPair
-keypair (Context tex) sec
- | BS.length sec /= 32 = throwIO CSecp256k1Error
- | otherwise = A.allocaBytes _KEYPAIR_BYTES $ \out ->
- BS.useAsCString sec $ \(F.castPtr -> key) -> do
- suc <- secp256k1_keypair_create tex out key
- when (suc /= 1) $ throwIO Secp256k1Error
- let enc = F.castPtr out
- per <- BS.packCStringLen (enc, _KEYPAIR_BYTES)
- pure (KeyPair per)
-
--- | Extract a public key from a keypair.
---
--- >>> wrcontext entropy $ \tex -> keypair_pub tex keypair
--- "<bitcoin-core/secp256k1 public key>"
-keypair_pub :: Context -> KeyPair -> IO Pub
-keypair_pub (Context tex) (KeyPair per) =
- A.allocaBytes _PUB_BYTES_INTERNAL $ \out ->
- BS.useAsCString per $ \(F.castPtr -> par) -> do
- -- returns 1 always
- _ <- secp256k1_keypair_pub tex out par
- let enc = F.castPtr out
- pub <- BS.packCStringLen (enc, _PUB_BYTES_INTERNAL)
- pure (Pub pub)
-
--- | Extract a secret key from a keypair.
---
--- >>> wrcontext entropy $ \tex -> keypair_sec tex keypair
-keypair_sec
- :: Context
- -> KeyPair
- -> IO BS.ByteString -- ^ 32-byte secret key
-keypair_sec (Context tex) (KeyPair per) =
- A.allocaBytes _SEC_BYTES $ \out ->
- BS.useAsCString per $ \(F.castPtr -> par) -> do
- _ <- secp256k1_keypair_sec tex out par
- let enc = F.castPtr out
- BS.packCStringLen (enc, _SEC_BYTES)
-
--- ecdh
-
--- | Compute an ECDH secret key from the provided public key and
--- (32-byte) secret key.
---
--- >>> wrcontext entropy $ \tex -> ecdh tex pub sec
-ecdh
- :: Context
- -> Pub
- -> BS.ByteString -- ^ 32-byte secret key
- -> IO BS.ByteString -- ^ 32-byte secret key
-ecdh (Context tex) (Pub pub) sec
- | BS.length sec /= 32 = throwIO CSecp256k1Error
- | otherwise =
- A.allocaBytes _SEC_BYTES $ \out ->
- BS.useAsCString pub $ \(F.castPtr -> pup) ->
- BS.useAsCString sec $ \(F.castPtr -> sep) -> do
- suc <- secp256k1_ecdh tex out pup sep F.nullPtr F.nullPtr
- when (suc /= 1) $ throwIO Secp256k1Error
- let key = F.castPtr out
- BS.packCStringLen (key, _SEC_BYTES)
-
--- schnorr
-
--- | Sign a 32-byte message hash with the provided secret key, using the
--- provided 32 bytes of fresh auxiliary entropy.
---
--- BIP340 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.
---
--- The resulting 64-byte Schnorr signature is portable, and so is not
--- wrapped in a newtype.
---
--- >>> import qualified System.Entropy as E -- example entropy source
--- >>> enn <- E.getEntropy 32
--- >>> aux <- E.getEntropy 32
--- >>> wrcontext enn $ \tex -> sign_schnorr tex msg sec aux
-sign_schnorr
- :: Context
- -> BS.ByteString -- ^ 32-byte message hash
- -> BS.ByteString -- ^ 32-byte secret key
- -> BS.ByteString -- ^ 32 bytes of fresh entropy
- -> IO BS.ByteString -- ^ 64-byte signature
-sign_schnorr c@(Context tex) msg sec aux
- | BS.length msg /= 32 || BS.length sec /= 32 || BS.length aux /= 32 =
- throwIO CSecp256k1Error
- | otherwise = A.allocaBytes _SIG_BYTES $ \out ->
- BS.useAsCString msg $ \(F.castPtr -> has) ->
- BS.useAsCString aux $ \(F.castPtr -> enn) -> do
- KeyPair per <- keypair c sec
- BS.useAsCString per $ \(F.castPtr -> pur) -> do
- suc <- secp256k1_schnorrsig_sign32 tex out has pur enn
- when (suc /= 1) $ throwIO Secp256k1Error
- let enc = F.castPtr out
- BS.packCStringLen (enc, _SIG_BYTES)
-
--- | Verify a 64-byte Schnorr signature for the provided 32-byte message
--- hash with the supplied public key.
---
--- >>> wrcontext entropy $ \tex -> verify_schnorr tex pub msg sig
-verify_schnorr
- :: Context
- -> Pub
- -> BS.ByteString -- ^ 32-byte message hash
- -> BS.ByteString -- ^ 64-byte signature
- -> IO Bool
-verify_schnorr c@(Context tex) pub msg sig
- | BS.length msg /= 32 || BS.length sig /= 64 = throwIO CSecp256k1Error
- | otherwise =
- BS.useAsCString sig $ \(F.castPtr -> sip) ->
- BS.useAsCStringLen msg $ \(F.castPtr -> has, fromIntegral -> len) -> do
- XOnlyPub pux <- xonly c pub
- BS.useAsCString pux $ \(F.castPtr -> pax) -> do
- suc <- secp256k1_schnorrsig_verify tex sip has len pax
- pure (suc == 1)
-
diff --git a/ppad-csecp256k1.cabal b/ppad-csecp256k1.cabal
@@ -28,7 +28,7 @@ library
ghc-options:
-Wall
exposed-modules:
- Crypto.Secp256k1
+ Crypto.Curve.Secp256k1
build-depends:
base >= 4.9 && < 5
, bytestring >= 0.9 && < 0.13
@@ -72,7 +72,7 @@ library secp256k1-sys
hs-source-dirs: secp256k1-sys/lib
exposed-modules:
- Crypto.Secp256k1.Internal
+ Crypto.Curve.Secp256k1.Internal
build-depends:
base
diff --git a/secp256k1-sys/lib/Crypto/Curve/Secp256k1/Internal.hs b/secp256k1-sys/lib/Crypto/Curve/Secp256k1/Internal.hs
@@ -0,0 +1,380 @@
+{-# LANGUAGE CApiFFI #-}
+
+module Crypto.Curve.Secp256k1.Internal (
+ -- constants
+ _DER_BYTES
+ , _PUB_BYTES_INTERNAL
+ , _PUB_BYTES_COMPRESSED
+ , _PUB_BYTES_UNCOMPRESSED
+ , _PUB_BYTES_XONLY
+ , _SEC_BYTES
+ , _SIG_BYTES
+ , _KEYPAIR_BYTES
+ , _COMPRESSED_FLAG
+ , _UNCOMPRESSED_FLAG
+
+ -- context
+ , _SECP256K1_CONTEXT_NONE
+ , Context
+ , Seed32
+ , secp256k1_context_create
+ , secp256k1_context_destroy
+ , secp256k1_context_randomize
+ , wcontext
+
+ -- ec
+ , PubKey64
+ , SecKey32
+ , Tweak32
+ , secp256k1_ec_pubkey_parse
+ , secp256k1_ec_pubkey_serialize
+ , secp256k1_ec_pubkey_create
+ , secp256k1_ec_pubkey_tweak_add
+ , secp256k1_ec_pubkey_tweak_mul
+ , secp256k1_ec_seckey_tweak_add
+ , secp256k1_ec_seckey_tweak_mul
+
+ -- ecdsa
+ , MsgHash32
+ , Sig64
+ , secp256k1_ecdsa_sign
+ , secp256k1_ecdsa_verify
+ , secp256k1_ecdsa_signature_parse_der
+ , secp256k1_ecdsa_signature_serialize_der
+ , secp256k1_ecdsa_signature_parse_compact
+ , secp256k1_ecdsa_signature_serialize_compact
+
+ -- ecdh
+ , secp256k1_ecdh
+
+ -- extrakeys
+ , KeyPair96
+ , XOnlyPublicKey64
+ , secp256k1_keypair_create
+ , secp256k1_keypair_sec
+ , secp256k1_keypair_pub
+ , secp256k1_xonly_pubkey_parse
+ , secp256k1_xonly_pubkey_serialize
+ , secp256k1_xonly_pubkey_from_pubkey
+
+ -- schnorr
+ , secp256k1_schnorrsig_sign32
+ , secp256k1_schnorrsig_verify
+ ) where
+
+import Control.Exception (bracket)
+import Foreign.Ptr (Ptr)
+import Foreign.C.Types (CUChar(..), CInt(..), CUInt(..), CSize(..))
+
+-- size constants
+
+-- bytesize of a DER-encoded signature
+_DER_BYTES :: Int
+_DER_BYTES = 72
+
+-- bytesize of an x-only pubkey
+_PUB_BYTES_XONLY :: Int
+_PUB_BYTES_XONLY = 32
+
+-- bytesize of a compressed pubkey
+_PUB_BYTES_COMPRESSED :: Int
+_PUB_BYTES_COMPRESSED = 33
+
+-- bytesize of an uncompressed pubkey
+_PUB_BYTES_UNCOMPRESSED :: Int
+_PUB_BYTES_UNCOMPRESSED = 65
+
+-- bytesize of a secp256k1-internal pubkey
+_PUB_BYTES_INTERNAL :: Int
+_PUB_BYTES_INTERNAL = 64
+
+-- bytesize of a secret key
+_SEC_BYTES :: Int
+_SEC_BYTES = 32
+
+-- bytesize of a secp256k1-internal signature
+_SIG_BYTES :: Int
+_SIG_BYTES = 64
+
+-- bytesize of a secp256k1-internal keypair
+_KEYPAIR_BYTES :: Int
+_KEYPAIR_BYTES = 96
+
+-- flag to indicate a compressed pubkey when parsing
+_COMPRESSED_FLAG :: CUInt
+_COMPRESSED_FLAG = 0x0102
+
+-- flag to indicate an uncompressed pubkey when parsing
+_UNCOMPRESSED_FLAG :: CUInt
+_UNCOMPRESSED_FLAG = 0x0002
+
+-- context
+
+-- secp256k1 context
+data Context
+
+-- 32-byte random seed
+data Seed32
+
+-- per secp256k1.h:
+--
+-- > The only valid non-deprecated flag in recent library versions is
+-- > SECP256K1_CONTEXT_NONE, which will create a context sufficient for
+-- > all functionality
+--
+-- where SECP256K1_CONTEXT_NONE = 1, via:
+--
+-- #define SECP256K1_FLAGS_TYPE_CONTEXT (1 << 0)
+-- #define SECP256K1_CONTEXT_NONE (SECP256K1_FLAGS_TYPE_CONTEXT)
+_SECP256K1_CONTEXT_NONE :: Integral a => a
+_SECP256K1_CONTEXT_NONE = 1
+
+foreign import capi
+ "secp256k1.h haskellsecp256k1_v0_1_0_context_create"
+ secp256k1_context_create
+ :: CUInt
+ -> IO (Ptr Context)
+
+foreign import capi
+ "secp256k1.h haskellsecp256k1_v0_1_0_context_destroy"
+ secp256k1_context_destroy
+ :: Ptr Context
+ -> IO ()
+
+foreign import capi
+ "secp256k1.h haskellsecp256k1_v0_1_0_context_randomize"
+ secp256k1_context_randomize
+ :: Ptr Context
+ -> Ptr Seed32
+ -> IO CInt
+
+-- returning the context itself and attempting to use it outside of a
+-- 'wcontext' block will produce segfaults
+wcontext :: (Ptr Context -> IO a) -> IO a
+wcontext =
+ bracket
+ (secp256k1_context_create _SECP256K1_CONTEXT_NONE)
+ secp256k1_context_destroy
+
+-- ec
+
+-- 64-byte public key
+data PubKey64
+
+-- 32-byte secret key
+data SecKey32
+
+-- 32-byte tweak
+data Tweak32
+
+foreign import capi
+ "secp256k1.h haskellsecp256k1_v0_1_0_ec_pubkey_parse"
+ secp256k1_ec_pubkey_parse
+ :: Ptr Context
+ -> Ptr PubKey64
+ -> Ptr CUChar
+ -> CSize
+ -> IO CInt
+
+foreign import capi
+ "secp256k1.h haskellsecp256k1_v0_1_0_ec_pubkey_serialize"
+ secp256k1_ec_pubkey_serialize
+ :: Ptr Context
+ -> Ptr CUChar
+ -> Ptr CSize
+ -> Ptr PubKey64
+ -> CUInt
+ -> IO CInt
+
+foreign import capi
+ "secp256k1.h haskellsecp256k1_v0_1_0_ec_pubkey_create"
+ secp256k1_ec_pubkey_create
+ :: Ptr Context
+ -> Ptr PubKey64
+ -> Ptr SecKey32
+ -> IO CInt
+
+foreign import capi
+ "secp256k1.h haskellsecp256k1_v0_1_0_ec_seckey_tweak_add"
+ secp256k1_ec_seckey_tweak_add
+ :: Ptr Context
+ -> Ptr SecKey32
+ -> Ptr Tweak32
+ -> IO CInt
+
+foreign import capi
+ "secp256k1.h haskellsecp256k1_v0_1_0_ec_pubkey_tweak_add"
+ secp256k1_ec_pubkey_tweak_add
+ :: Ptr Context
+ -> Ptr PubKey64
+ -> Ptr Tweak32
+ -> IO CInt
+
+foreign import capi
+ "secp256k1.h haskellsecp256k1_v0_1_0_ec_seckey_tweak_mul"
+ secp256k1_ec_seckey_tweak_mul
+ :: Ptr Context
+ -> Ptr SecKey32
+ -> Ptr Tweak32
+ -> IO CInt
+
+foreign import capi
+ "secp256k1.h haskellsecp256k1_v0_1_0_ec_pubkey_tweak_mul"
+ secp256k1_ec_pubkey_tweak_mul
+ :: Ptr Context
+ -> Ptr PubKey64
+ -> Ptr Tweak32
+ -> IO CInt
+
+-- ecdsa
+
+-- 32-byte message hash
+data MsgHash32
+
+-- 64-byte signature
+data Sig64
+
+foreign import capi
+ "secp256k1.h haskellsecp256k1_v0_1_0_ecdsa_sign"
+ secp256k1_ecdsa_sign
+ :: Ptr Context
+ -> Ptr Sig64
+ -> Ptr MsgHash32
+ -> Ptr SecKey32
+ -> Ptr a
+ -> Ptr b
+ -> IO CInt
+
+foreign import capi
+ "secp256k1.h haskellsecp256k1_v0_1_0_ecdsa_verify"
+ secp256k1_ecdsa_verify
+ :: Ptr Context
+ -> Ptr Sig64
+ -> Ptr MsgHash32
+ -> Ptr PubKey64
+ -> IO CInt
+
+foreign import capi
+ "secp256k1.h haskellsecp256k1_v0_1_0_ecdsa_signature_parse_der"
+ secp256k1_ecdsa_signature_parse_der
+ :: Ptr Context
+ -> Ptr Sig64
+ -> Ptr CUChar
+ -> CSize
+ -> IO CInt
+
+foreign import capi
+ "secp256k1.h haskellsecp256k1_v0_1_0_ecdsa_signature_serialize_der"
+ secp256k1_ecdsa_signature_serialize_der
+ :: Ptr Context
+ -> Ptr CUChar
+ -> Ptr CSize
+ -> Ptr Sig64
+ -> IO CInt
+
+foreign import capi
+ "secp256k1.h haskellsecp256k1_v0_1_0_ecdsa_signature_parse_compact"
+ secp256k1_ecdsa_signature_parse_compact
+ :: Ptr Context
+ -> Ptr Sig64
+ -> Ptr CUChar
+ -> IO CInt
+
+foreign import capi
+ "secp256k1.h haskellsecp256k1_v0_1_0_ecdsa_signature_serialize_compact"
+ secp256k1_ecdsa_signature_serialize_compact
+ :: Ptr Context
+ -> Ptr CUChar
+ -> Ptr Sig64
+ -> IO CInt
+
+-- ecdh
+
+foreign import capi
+ "secp256k1_ecdh.h haskellsecp256k1_v0_1_0_ecdh"
+ secp256k1_ecdh
+ :: Ptr Context
+ -> Ptr CUChar
+ -> Ptr PubKey64
+ -> Ptr SecKey32
+ -> Ptr a
+ -> Ptr b
+ -> IO CInt
+
+-- extrakeys
+
+data KeyPair96
+
+data XOnlyPublicKey64
+
+foreign import capi
+ "secp256k1_extrakeys.h haskellsecp256k1_v0_1_0_keypair_create"
+ secp256k1_keypair_create
+ :: Ptr Context
+ -> Ptr KeyPair96
+ -> Ptr SecKey32
+ -> IO CInt
+
+foreign import capi
+ "secp256k1_extrakeys.h haskellsecp256k1_v0_1_0_keypair_pub"
+ secp256k1_keypair_pub
+ :: Ptr Context
+ -> Ptr PubKey64
+ -> Ptr KeyPair96
+ -> IO CInt
+
+foreign import capi
+ "secp256k1_extrakeys.h haskellsecp256k1_v0_1_0_keypair_sec"
+ secp256k1_keypair_sec
+ :: Ptr Context
+ -> Ptr SecKey32
+ -> Ptr KeyPair96
+ -> IO CInt
+
+foreign import capi
+ "secp256k1_extrakeys.h haskellsecp256k1_v0_1_0_xonly_pubkey_parse"
+ secp256k1_xonly_pubkey_parse
+ :: Ptr Context
+ -> Ptr XOnlyPublicKey64
+ -> Ptr CUChar
+ -> IO CInt
+
+foreign import capi
+ "secp256k1_extrakeys.h haskellsecp256k1_v0_1_0_xonly_pubkey_serialize"
+ secp256k1_xonly_pubkey_serialize
+ :: Ptr Context
+ -> Ptr CUChar
+ -> Ptr XOnlyPublicKey64
+ -> IO CInt
+
+foreign import capi
+ "secp256k1_extrakeys.h haskellsecp256k1_v0_1_0_xonly_pubkey_from_pubkey"
+ secp256k1_xonly_pubkey_from_pubkey
+ :: Ptr Context
+ -> Ptr XOnlyPublicKey64
+ -> Ptr CInt
+ -> Ptr PubKey64
+ -> IO CInt
+
+-- schnorr
+
+foreign import capi
+ "secp256k1_schnorrsig.h haskellsecp256k1_v0_1_0_schnorrsig_sign32"
+ secp256k1_schnorrsig_sign32
+ :: Ptr Context
+ -> Ptr Sig64
+ -> Ptr MsgHash32
+ -> Ptr KeyPair96
+ -> Ptr CUChar
+ -> IO CInt
+
+foreign import capi
+ "secp256k1_schnorrsig.h haskellsecp256k1_v0_1_0_schnorrsig_verify"
+ secp256k1_schnorrsig_verify
+ :: Ptr Context
+ -> Ptr Sig64
+ -> Ptr CUChar
+ -> CSize
+ -> Ptr XOnlyPublicKey64
+ -> IO CInt
+
diff --git a/secp256k1-sys/lib/Crypto/Secp256k1/Internal.hs b/secp256k1-sys/lib/Crypto/Secp256k1/Internal.hs
@@ -1,380 +0,0 @@
-{-# LANGUAGE CApiFFI #-}
-
-module Crypto.Secp256k1.Internal (
- -- constants
- _DER_BYTES
- , _PUB_BYTES_INTERNAL
- , _PUB_BYTES_COMPRESSED
- , _PUB_BYTES_UNCOMPRESSED
- , _PUB_BYTES_XONLY
- , _SEC_BYTES
- , _SIG_BYTES
- , _KEYPAIR_BYTES
- , _COMPRESSED_FLAG
- , _UNCOMPRESSED_FLAG
-
- -- context
- , _SECP256K1_CONTEXT_NONE
- , Context
- , Seed32
- , secp256k1_context_create
- , secp256k1_context_destroy
- , secp256k1_context_randomize
- , wcontext
-
- -- ec
- , PubKey64
- , SecKey32
- , Tweak32
- , secp256k1_ec_pubkey_parse
- , secp256k1_ec_pubkey_serialize
- , secp256k1_ec_pubkey_create
- , secp256k1_ec_pubkey_tweak_add
- , secp256k1_ec_pubkey_tweak_mul
- , secp256k1_ec_seckey_tweak_add
- , secp256k1_ec_seckey_tweak_mul
-
- -- ecdsa
- , MsgHash32
- , Sig64
- , secp256k1_ecdsa_sign
- , secp256k1_ecdsa_verify
- , secp256k1_ecdsa_signature_parse_der
- , secp256k1_ecdsa_signature_serialize_der
- , secp256k1_ecdsa_signature_parse_compact
- , secp256k1_ecdsa_signature_serialize_compact
-
- -- ecdh
- , secp256k1_ecdh
-
- -- extrakeys
- , KeyPair96
- , XOnlyPublicKey64
- , secp256k1_keypair_create
- , secp256k1_keypair_sec
- , secp256k1_keypair_pub
- , secp256k1_xonly_pubkey_parse
- , secp256k1_xonly_pubkey_serialize
- , secp256k1_xonly_pubkey_from_pubkey
-
- -- schnorr
- , secp256k1_schnorrsig_sign32
- , secp256k1_schnorrsig_verify
- ) where
-
-import Control.Exception (bracket)
-import Foreign.Ptr (Ptr)
-import Foreign.C.Types (CUChar(..), CInt(..), CUInt(..), CSize(..))
-
--- size constants
-
--- bytesize of a DER-encoded signature
-_DER_BYTES :: Int
-_DER_BYTES = 72
-
--- bytesize of an x-only pubkey
-_PUB_BYTES_XONLY :: Int
-_PUB_BYTES_XONLY = 32
-
--- bytesize of a compressed pubkey
-_PUB_BYTES_COMPRESSED :: Int
-_PUB_BYTES_COMPRESSED = 33
-
--- bytesize of an uncompressed pubkey
-_PUB_BYTES_UNCOMPRESSED :: Int
-_PUB_BYTES_UNCOMPRESSED = 65
-
--- bytesize of a secp256k1-internal pubkey
-_PUB_BYTES_INTERNAL :: Int
-_PUB_BYTES_INTERNAL = 64
-
--- bytesize of a secret key
-_SEC_BYTES :: Int
-_SEC_BYTES = 32
-
--- bytesize of a secp256k1-internal signature
-_SIG_BYTES :: Int
-_SIG_BYTES = 64
-
--- bytesize of a secp256k1-internal keypair
-_KEYPAIR_BYTES :: Int
-_KEYPAIR_BYTES = 96
-
--- flag to indicate a compressed pubkey when parsing
-_COMPRESSED_FLAG :: CUInt
-_COMPRESSED_FLAG = 0x0102
-
--- flag to indicate an uncompressed pubkey when parsing
-_UNCOMPRESSED_FLAG :: CUInt
-_UNCOMPRESSED_FLAG = 0x0002
-
--- context
-
--- secp256k1 context
-data Context
-
--- 32-byte random seed
-data Seed32
-
--- per secp256k1.h:
---
--- > The only valid non-deprecated flag in recent library versions is
--- > SECP256K1_CONTEXT_NONE, which will create a context sufficient for
--- > all functionality
---
--- where SECP256K1_CONTEXT_NONE = 1, via:
---
--- #define SECP256K1_FLAGS_TYPE_CONTEXT (1 << 0)
--- #define SECP256K1_CONTEXT_NONE (SECP256K1_FLAGS_TYPE_CONTEXT)
-_SECP256K1_CONTEXT_NONE :: Integral a => a
-_SECP256K1_CONTEXT_NONE = 1
-
-foreign import capi
- "secp256k1.h haskellsecp256k1_v0_1_0_context_create"
- secp256k1_context_create
- :: CUInt
- -> IO (Ptr Context)
-
-foreign import capi
- "secp256k1.h haskellsecp256k1_v0_1_0_context_destroy"
- secp256k1_context_destroy
- :: Ptr Context
- -> IO ()
-
-foreign import capi
- "secp256k1.h haskellsecp256k1_v0_1_0_context_randomize"
- secp256k1_context_randomize
- :: Ptr Context
- -> Ptr Seed32
- -> IO CInt
-
--- returning the context itself and attempting to use it outside of a
--- 'wcontext' block will produce segfaults
-wcontext :: (Ptr Context -> IO a) -> IO a
-wcontext =
- bracket
- (secp256k1_context_create _SECP256K1_CONTEXT_NONE)
- secp256k1_context_destroy
-
--- ec
-
--- 64-byte public key
-data PubKey64
-
--- 32-byte secret key
-data SecKey32
-
--- 32-byte tweak
-data Tweak32
-
-foreign import capi
- "secp256k1.h haskellsecp256k1_v0_1_0_ec_pubkey_parse"
- secp256k1_ec_pubkey_parse
- :: Ptr Context
- -> Ptr PubKey64
- -> Ptr CUChar
- -> CSize
- -> IO CInt
-
-foreign import capi
- "secp256k1.h haskellsecp256k1_v0_1_0_ec_pubkey_serialize"
- secp256k1_ec_pubkey_serialize
- :: Ptr Context
- -> Ptr CUChar
- -> Ptr CSize
- -> Ptr PubKey64
- -> CUInt
- -> IO CInt
-
-foreign import capi
- "secp256k1.h haskellsecp256k1_v0_1_0_ec_pubkey_create"
- secp256k1_ec_pubkey_create
- :: Ptr Context
- -> Ptr PubKey64
- -> Ptr SecKey32
- -> IO CInt
-
-foreign import capi
- "secp256k1.h haskellsecp256k1_v0_1_0_ec_seckey_tweak_add"
- secp256k1_ec_seckey_tweak_add
- :: Ptr Context
- -> Ptr SecKey32
- -> Ptr Tweak32
- -> IO CInt
-
-foreign import capi
- "secp256k1.h haskellsecp256k1_v0_1_0_ec_pubkey_tweak_add"
- secp256k1_ec_pubkey_tweak_add
- :: Ptr Context
- -> Ptr PubKey64
- -> Ptr Tweak32
- -> IO CInt
-
-foreign import capi
- "secp256k1.h haskellsecp256k1_v0_1_0_ec_seckey_tweak_mul"
- secp256k1_ec_seckey_tweak_mul
- :: Ptr Context
- -> Ptr SecKey32
- -> Ptr Tweak32
- -> IO CInt
-
-foreign import capi
- "secp256k1.h haskellsecp256k1_v0_1_0_ec_pubkey_tweak_mul"
- secp256k1_ec_pubkey_tweak_mul
- :: Ptr Context
- -> Ptr PubKey64
- -> Ptr Tweak32
- -> IO CInt
-
--- ecdsa
-
--- 32-byte message hash
-data MsgHash32
-
--- 64-byte signature
-data Sig64
-
-foreign import capi
- "secp256k1.h haskellsecp256k1_v0_1_0_ecdsa_sign"
- secp256k1_ecdsa_sign
- :: Ptr Context
- -> Ptr Sig64
- -> Ptr MsgHash32
- -> Ptr SecKey32
- -> Ptr a
- -> Ptr b
- -> IO CInt
-
-foreign import capi
- "secp256k1.h haskellsecp256k1_v0_1_0_ecdsa_verify"
- secp256k1_ecdsa_verify
- :: Ptr Context
- -> Ptr Sig64
- -> Ptr MsgHash32
- -> Ptr PubKey64
- -> IO CInt
-
-foreign import capi
- "secp256k1.h haskellsecp256k1_v0_1_0_ecdsa_signature_parse_der"
- secp256k1_ecdsa_signature_parse_der
- :: Ptr Context
- -> Ptr Sig64
- -> Ptr CUChar
- -> CSize
- -> IO CInt
-
-foreign import capi
- "secp256k1.h haskellsecp256k1_v0_1_0_ecdsa_signature_serialize_der"
- secp256k1_ecdsa_signature_serialize_der
- :: Ptr Context
- -> Ptr CUChar
- -> Ptr CSize
- -> Ptr Sig64
- -> IO CInt
-
-foreign import capi
- "secp256k1.h haskellsecp256k1_v0_1_0_ecdsa_signature_parse_compact"
- secp256k1_ecdsa_signature_parse_compact
- :: Ptr Context
- -> Ptr Sig64
- -> Ptr CUChar
- -> IO CInt
-
-foreign import capi
- "secp256k1.h haskellsecp256k1_v0_1_0_ecdsa_signature_serialize_compact"
- secp256k1_ecdsa_signature_serialize_compact
- :: Ptr Context
- -> Ptr CUChar
- -> Ptr Sig64
- -> IO CInt
-
--- ecdh
-
-foreign import capi
- "secp256k1_ecdh.h haskellsecp256k1_v0_1_0_ecdh"
- secp256k1_ecdh
- :: Ptr Context
- -> Ptr CUChar
- -> Ptr PubKey64
- -> Ptr SecKey32
- -> Ptr a
- -> Ptr b
- -> IO CInt
-
--- extrakeys
-
-data KeyPair96
-
-data XOnlyPublicKey64
-
-foreign import capi
- "secp256k1_extrakeys.h haskellsecp256k1_v0_1_0_keypair_create"
- secp256k1_keypair_create
- :: Ptr Context
- -> Ptr KeyPair96
- -> Ptr SecKey32
- -> IO CInt
-
-foreign import capi
- "secp256k1_extrakeys.h haskellsecp256k1_v0_1_0_keypair_pub"
- secp256k1_keypair_pub
- :: Ptr Context
- -> Ptr PubKey64
- -> Ptr KeyPair96
- -> IO CInt
-
-foreign import capi
- "secp256k1_extrakeys.h haskellsecp256k1_v0_1_0_keypair_sec"
- secp256k1_keypair_sec
- :: Ptr Context
- -> Ptr SecKey32
- -> Ptr KeyPair96
- -> IO CInt
-
-foreign import capi
- "secp256k1_extrakeys.h haskellsecp256k1_v0_1_0_xonly_pubkey_parse"
- secp256k1_xonly_pubkey_parse
- :: Ptr Context
- -> Ptr XOnlyPublicKey64
- -> Ptr CUChar
- -> IO CInt
-
-foreign import capi
- "secp256k1_extrakeys.h haskellsecp256k1_v0_1_0_xonly_pubkey_serialize"
- secp256k1_xonly_pubkey_serialize
- :: Ptr Context
- -> Ptr CUChar
- -> Ptr XOnlyPublicKey64
- -> IO CInt
-
-foreign import capi
- "secp256k1_extrakeys.h haskellsecp256k1_v0_1_0_xonly_pubkey_from_pubkey"
- secp256k1_xonly_pubkey_from_pubkey
- :: Ptr Context
- -> Ptr XOnlyPublicKey64
- -> Ptr CInt
- -> Ptr PubKey64
- -> IO CInt
-
--- schnorr
-
-foreign import capi
- "secp256k1_schnorrsig.h haskellsecp256k1_v0_1_0_schnorrsig_sign32"
- secp256k1_schnorrsig_sign32
- :: Ptr Context
- -> Ptr Sig64
- -> Ptr MsgHash32
- -> Ptr KeyPair96
- -> Ptr CUChar
- -> IO CInt
-
-foreign import capi
- "secp256k1_schnorrsig.h haskellsecp256k1_v0_1_0_schnorrsig_verify"
- secp256k1_schnorrsig_verify
- :: Ptr Context
- -> Ptr Sig64
- -> Ptr CUChar
- -> CSize
- -> Ptr XOnlyPublicKey64
- -> IO CInt
-
diff --git a/secp256k1-sys/test/Main.hs b/secp256k1-sys/test/Main.hs
@@ -5,7 +5,7 @@ module Main where
import Control.Monad (when)
import Control.Exception (Exception, throwIO)
-import Crypto.Secp256k1.Internal
+import Crypto.Curve.Secp256k1.Internal
import qualified Data.ByteString as BS
import Foreign.Ptr (Ptr)
import qualified Foreign.Ptr as F (nullPtr, castPtr)
diff --git a/test/Main.hs b/test/Main.hs
@@ -3,7 +3,7 @@
module Main where
import qualified Data.ByteString as BS
-import Crypto.Secp256k1
+import Crypto.Curve.Secp256k1
import Test.Tasty
import Test.Tasty.HUnit