csecp256k1

Haskell FFI bindings to bitcoin-core/secp256k1.
git clone git://git.ppad.tech/csecp256k1.git
Log | Files | Refs | README | LICENSE

commit e94cc04da98f2266a5885e302e3173e5fb6147dc
parent 283d2ff42c673f00e37d3c2f50b50303e823cd78
Author: Jared Tobin <jared@jtobin.io>
Date:   Fri,  1 Mar 2024 18:08:09 +0400

lib: add tweaks, compact sigs support

Diffstat:
Mlib/Crypto/Secp256k1.hs | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 103 insertions(+), 0 deletions(-)

diff --git a/lib/Crypto/Secp256k1.hs b/lib/Crypto/Secp256k1.hs @@ -28,10 +28,16 @@ module Crypto.Secp256k1 ( , 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 @@ -263,6 +269,78 @@ serialize_pub_in for (Context tex) (Pub pub) = 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 = 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 = 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 = 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 = 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. @@ -349,6 +427,31 @@ serialize_der (Context tex) (Sig sig) = nel = fromIntegral pek BS.packCStringLen (der, nel) +parse_compact + :: Context + -> BS.ByteString -- ^ 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_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