commit aa6b4415aadbbec9304528849695c4000c897267
parent d0d57454fc76ecaa554356c9d583345bc49c197f
Author: Jared Tobin <jared@jtobin.io>
Date: Fri, 23 Feb 2024 17:10:53 +0400
Most basic stuff working.
Diffstat:
3 files changed, 274 insertions(+), 12 deletions(-)
diff --git a/secp256k1-sys/lib/Crypto/Secp256k1/Internal.hs b/secp256k1-sys/lib/Crypto/Secp256k1/Internal.hs
@@ -8,18 +8,37 @@ module Crypto.Secp256k1.Internal (
, secp256k1_context_create
, secp256k1_context_destroy
, secp256k1_context_randomize
+
+ -- ec
+ , secp256k1_ec_pubkey_parse
+ , secp256k1_ec_pubkey_serialize
+ , secp256k1_ec_pubkey_create
+
+ -- ecdsa
+ , MsgHash32
+ , PubKey64
+ , SecKey32
+ , Sig64
+ , secp256k1_ecdsa_sign
+ , secp256k1_ecdsa_verify
+ , secp256k1_ecdsa_signature_parse_der
+ , secp256k1_ecdsa_signature_serialize_der
+
+ -- ecdh
+ -- , secp256k1_ecdh
) where
import Foreign.Ptr (Ptr)
-import Foreign.C.Types (CInt(..), CUInt(..))
+import Foreign.C.Types (CUChar(..), CInt(..), CUInt(..), CSize(..))
+-- context
+
+-- secp256k1 context
data Context
-- 32-byte random seed
data Seed32
--- context
-
-- per secp256k1.h:
--
-- > The only valid non-deprecated flag in recent library versions is
@@ -52,3 +71,200 @@ foreign import capi
-> Ptr Seed32
-> IO CInt
+-- ec
+
+-- 64-byte public key
+data PubKey64
+
+-- 32-byte secret key
+data SecKey32
+
+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
+
+-- 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
+
+
+-- ecdh
+
+-- XX seems fine, but GHC bails on call
+--
+-- 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
+
+-- schnorr
+
+-- foreign import capi
+-- "secp256k1_schnorrsig.h haskellsecp256k1_v0_1_0_schnorrsig_sign32"
+-- secp256k1_schnorrsig_sign32
+-- :: Ptr Context
+-- -> Ptr CUChar
+-- -> Ptr CUChar
+-- -> Ptr KeyPair
+-- -> Ptr CUChar
+-- -> IO CInt
+
+-- foreign import capi
+-- "secp256k1_schnorrsig.h haskellsecp256k1_v0_1_0_schnorrsig_verify"
+-- secp256k1_schnorrsig_verify
+-- :: Ptr Context
+-- -> Ptr CUChar
+-- -> Ptr CUChar
+-- -> CSize
+-- -> Ptr XOnlyPublicKey
+-- -> IO CInt
+--
+-- -- foreign import capi
+-- -- "secp256k1_extrakeys.h haskellsecp256k1_v0_1_0_keypair_create"
+-- -- secp256k1_keypair_create
+-- -- :: Ptr Context
+-- -- -> Ptr KeyPair
+-- -- -> Ptr CUChar
+-- -- -> IO CInt
+-- --
+-- -- foreign import capi
+-- -- "secp256k1_extrakeys.h haskellsecp256k1_v0_1_0_xonly_pubkey_parse"
+-- -- secp256k1_xonly_pubkey_parse
+-- -- :: Ptr Context
+-- -- -> Ptr XOnlyPublicKey
+-- -- -> 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 XOnlyPublicKey
+-- -- -> IO CInt
+-- --
+-- -- foreign import capi
+-- -- "secp256k1_extrakeys.h haskellsecp256k1_v0_1_0_xonly_pubkey_from_pubkey"
+-- -- secp256k1_xonly_pubkey_from_pubkey
+-- -- :: Ptr Context
+-- -- -> Ptr XOnlyPublicKey
+-- -- -> Ptr CInt
+-- -- -> Ptr PublicKey
+-- -- -> IO CInt
+-- --
+-- -- foreign import capi
+-- -- "secp256k1_extrakeys.h haskellsecp256k1_v0_1_0_xonly_pubkey_cmp"
+-- -- secp256k1_xonly_pubkey_cmp
+-- -- :: Ptr Context
+-- -- -> Ptr XOnlyPublicKey
+-- -- -> Ptr XOnlyPublicKey
+-- -- -> IO CInt
+-- --
+-- -- foreign import capi
+-- -- "secp256k1_extrakeys.h haskellsecp256k1_v0_1_0_xonly_pubkey_tweak_add"
+-- -- secp256k1_xonly_pubkey_tweak_add
+-- -- :: Ptr Context
+-- -- -> Ptr PublicKey
+-- -- -> Ptr XOnlyPublicKey
+-- -- -> Ptr CUChar
+-- -- -> IO CInt
+-- --
+-- -- foreign import capi
+-- -- "secp256k1_extrakeys.h haskellsecp256k1_v0_1_0_keypair_xonly_pub"
+-- -- secp256k1_keypair_xonly_pub
+-- -- :: Ptr Context
+-- -- -> Ptr XOnlyPublicKey
+-- -- -> Ptr CInt
+-- -- -> Ptr KeyPair
+-- -- -> IO CInt
+-- --
+-- -- foreign import capi
+-- -- "secp256k1_extrakeys.h haskellsecp256k1_v0_1_0_keypair_xonly_tweak_add"
+-- -- secp256k1_keypair_xonly_tweak_add
+-- -- :: Ptr Context
+-- -- -> Ptr KeyPair
+-- -- -> Ptr CUChar
+-- -- -> IO CInt
+-- --
+-- -- foreign import capi
+-- -- "secp256k1_extrakeys.h haskellsecp256k1_v0_1_0_xonly_pubkey_tweak_add_check"
+-- -- secp256k1_xonly_pubkey_tweak_add_check
+-- -- :: Ptr Context
+-- -- -> Ptr CUChar
+-- -- -> CInt
+-- -- -> Ptr XOnlyPublicKey
+-- -- -> Ptr CUChar
+-- -- -> IO CInt
+-- --
+-- --
diff --git a/secp256k1-sys/secp256k1-sys.cabal b/secp256k1-sys/secp256k1-sys.cabal
@@ -35,6 +35,7 @@ library
, depend/secp256k1/src/secp256k1.c
cpp-options:
+ -DENABLE_MODULE_ECDH
-DENABLE_MODULE_SCHNORRSIG
-DENABLE_MODULE_EXTRAKEYS
-DENABLE_MODULE_ELLSWIFT
diff --git a/secp256k1-sys/test/Main.hs b/secp256k1-sys/test/Main.hs
@@ -34,6 +34,9 @@ _PUB_BYTES_UNCOMPRESSED = 65
_PUB_BYTES_INTERNAL :: Int
_PUB_BYTES_INTERNAL = 64
+_SEC_BYTES :: Int
+_SEC_BYTES = 32
+
_SIG_BYTES :: Int
_SIG_BYTES = 64
@@ -53,11 +56,13 @@ units = testGroup "unit tests" [
, ec_pubkey_parse
, ec_pubkey_serialize_compressed
, ec_pubkey_serialize_uncompressed
+ , ec_pubkey_create
, ecdsa_signature_parse_der
, ecdsa_signature_serialize_der
, ecdsa_sign
, ecdsa_verify_compressed
, ecdsa_verify_uncompressed
+ -- , ecdh_test
]
wcontext :: (Ptr Context -> IO a) -> IO a
@@ -108,6 +113,13 @@ ec_pubkey_serialize_uncompressed =
pub <- serialize_pubkey_uncompressed tex par
assertEqual "success" pub _PUB_UNCOMPRESSED
+ec_pubkey_create :: TestTree
+ec_pubkey_create =
+ testCase "secp256k1_ec_pubkey_create (success)" $
+ wcontext $ \tex -> do
+ _ <- create_pubkey tex _SEC
+ assertBool "success" True
+
-- ecdsa
ecdsa_signature_parse_der :: TestTree
@@ -147,6 +159,17 @@ ecdsa_verify_uncompressed =
suc <- verify_ecdsa tex _PUB_UNCOMPRESSED _HAS _DER
assertBool "success" suc
+-- ecdh
+
+-- XX getting dyld error when trying to run
+--
+-- ecdh_test :: TestTree
+-- ecdh_test = testCase "secp256k1_ecdh (success)" $
+-- wcontext $ \tex -> do
+-- -- throws on failure, so any return implies success
+-- _ <- ecdh tex _PUB_COMPRESSED _SEC
+-- assertBool "success" True
+
-- wrappers
parse_der :: Ptr Context -> BS.ByteString -> IO BS.ByteString
@@ -158,15 +181,6 @@ parse_der tex bs =
let par = F.castPtr out
BS.packCStringLen (par, _SIG_BYTES)
-parse_pubkey :: Ptr Context -> BS.ByteString -> IO BS.ByteString
-parse_pubkey 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
- BS.packCStringLen (par, _PUB_BYTES_INTERNAL)
-
serialize_der :: Ptr Context -> BS.ByteString -> IO BS.ByteString
serialize_der tex bs = A.alloca $ \len ->
A.allocaBytes _DER_BYTES $ \out ->
@@ -180,6 +194,24 @@ serialize_der tex bs = A.alloca $ \len ->
nel = fromIntegral pek
BS.packCStringLen (enc, nel)
+parse_pubkey :: Ptr Context -> BS.ByteString -> IO BS.ByteString
+parse_pubkey 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
+ BS.packCStringLen (par, _PUB_BYTES_INTERNAL)
+
+create_pubkey :: Ptr Context -> BS.ByteString -> IO BS.ByteString
+create_pubkey tex bs =
+ 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
+ BS.packCStringLen (pub, _PUB_BYTES_INTERNAL)
+
serialize_pubkey_compressed :: Ptr Context -> BS.ByteString -> IO BS.ByteString
serialize_pubkey_compressed tex bs =
BS.useAsCString bs $ \(F.castPtr -> pub) ->
@@ -236,6 +268,19 @@ verify_ecdsa tex key msg der = do
secp256k1_ecdsa_verify tex sip has kep
pure (suc == 1)
+-- XX resurrect when ecdh problems solved
+--
+-- ecdh :: Ptr Context -> BS.ByteString -> BS.ByteString -> IO BS.ByteString
+-- ecdh tex pub sec =
+-- A.allocaBytes _SEC_BYTES $ \out -> do
+-- par <- parse_pubkey tex pub
+-- BS.useAsCString par $ \(F.castPtr -> pab) ->
+-- BS.useAsCString sec $ \(F.castPtr -> sep) -> do
+-- suc <- secp256k1_ecdh tex out pab sep F.nullPtr F.nullPtr
+-- when (suc /= 1) $ throwIO Secp256k1Error
+-- let key = F.castPtr out
+-- BS.packCStringLen (key, _SEC_BYTES)
+
-- test inputs
-- a DER-encoded signature