packages feed

secp256k1-haskell 1.0.1 → 1.1.0

raw patch · 5 files changed

+202/−163 lines, 5 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Crypto.Secp256k1.Internal.Context: contextDestroyFunPtr :: FunPtr (Ptr LCtx -> IO ())
- Crypto.Secp256k1: Ctx :: Ptr LCtx -> Ctx
+ Crypto.Secp256k1: Ctx :: ForeignPtr LCtx -> Ctx
- Crypto.Secp256k1: [$sel:get:Ctx] :: Ctx -> Ptr LCtx
+ Crypto.Secp256k1: [$sel:get:Ctx] :: Ctx -> ForeignPtr LCtx
- Crypto.Secp256k1.Internal.Context: Ctx :: Ptr LCtx -> Ctx
+ Crypto.Secp256k1.Internal.Context: Ctx :: ForeignPtr LCtx -> Ctx
- Crypto.Secp256k1.Internal.Context: [$sel:get:Ctx] :: Ctx -> Ptr LCtx
+ Crypto.Secp256k1.Internal.Context: [$sel:get:Ctx] :: Ctx -> ForeignPtr LCtx

Files

CHANGELOG.md view
@@ -4,6 +4,12 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [1.1.0] - 2023-11-01++### Changed++- Use ForeignPtr to allow the garbage collector manage the context object (Ctx).+ ## [1.0.1] - 2023-09-20  ### Changed
secp256k1-haskell.cabal view
@@ -1,11 +1,11 @@ cabal-version: 2.0 --- This file has been generated from package.yaml by hpack version 0.35.2.+-- This file has been generated from package.yaml by hpack version 0.36.0. -- -- see: https://github.com/sol/hpack  name:           secp256k1-haskell-version:        1.0.1+version:        1.1.0 synopsis:       Bindings for secp256k1 description:    Sign and verify signatures using the secp256k1 library. category:       Crypto
src/Crypto/Secp256k1/Internal/Base.hs view
@@ -73,6 +73,7 @@     peek,     poke,     pokeArray,+    withForeignPtr,   ) import GHC.Generics (Generic) import System.IO.Unsafe (unsafePerformIO)@@ -204,17 +205,18 @@ -- | Convert signature to a normalized lower-S form. 'Nothing' indicates that it -- was already normal. normalizeSig :: Ctx -> Sig -> Maybe Sig-normalizeSig (Ctx ctx) (Sig sig) = unsafePerformIO $-  unsafeUseByteString sig $ \(sig_in, _) -> do-    sig_out <- mallocBytes 64-    ret <- ecdsaSignatureNormalize ctx sig_out sig_in-    if isSuccess ret-      then do-        bs <- unsafePackByteString (sig_out, 64)-        return (Just (Sig bs))-      else do-        free sig_out-        return Nothing+normalizeSig (Ctx fctx) (Sig sig) =+  unsafePerformIO $ withForeignPtr fctx $ \ctx ->+    unsafeUseByteString sig $ \(sig_in, _) -> do+      sig_out <- mallocBytes 64+      ret <- ecdsaSignatureNormalize ctx sig_out sig_in+      if isSuccess ret+        then do+          bs <- unsafePackByteString (sig_out, 64)+          return (Just (Sig bs))+        else do+          free sig_out+          return Nothing  -- | 32-Byte 'ByteString' as 'Tweak'. tweak :: ByteString -> Maybe Tweak@@ -224,181 +226,195 @@  -- | Import DER-encoded public key. importPubKey :: Ctx -> ByteString -> Maybe PubKey-importPubKey (Ctx ctx) bs+importPubKey (Ctx fctx) bs   | BS.null bs = Nothing-  | otherwise = unsafePerformIO $-      unsafeUseByteString bs $ \(input, len) -> do-        pub_key <- mallocBytes 64-        ret <- ecPubKeyParse ctx pub_key input len-        if isSuccess ret-          then do-            out <- unsafePackByteString (pub_key, 64)-            return (Just (PubKey out))-          else do-            free pub_key-            return Nothing+  | otherwise =+      unsafePerformIO $ withForeignPtr fctx $ \ctx ->+        unsafeUseByteString bs $ \(input, len) -> do+          pub_key <- mallocBytes 64+          ret <- ecPubKeyParse ctx pub_key input len+          if isSuccess ret+            then do+              out <- unsafePackByteString (pub_key, 64)+              return (Just (PubKey out))+            else do+              free pub_key+              return Nothing  -- | Encode public key as DER. First argument 'True' for compressed output. exportPubKey :: Ctx -> Bool -> PubKey -> ByteString-exportPubKey (Ctx ctx) compress (PubKey in_bs) = unsafePerformIO $-  unsafeUseByteString in_bs $ \(in_ptr, _) ->-    alloca $ \len_ptr ->-      allocaBytes len $ \out_ptr -> do-        poke len_ptr $ fromIntegral len-        ret <- ecPubKeySerialize ctx out_ptr len_ptr in_ptr flags-        unless (isSuccess ret) $ error "could not serialize public key"-        final_len <- peek len_ptr-        packByteString (out_ptr, final_len)+exportPubKey (Ctx fctx) compress (PubKey in_bs) =+  unsafePerformIO $ withForeignPtr fctx $ \ctx ->+    unsafeUseByteString in_bs $ \(in_ptr, _) ->+      alloca $ \len_ptr ->+        allocaBytes len $ \out_ptr -> do+          poke len_ptr $ fromIntegral len+          ret <- ecPubKeySerialize ctx out_ptr len_ptr in_ptr flags+          unless (isSuccess ret) $ error "could not serialize public key"+          final_len <- peek len_ptr+          packByteString (out_ptr, final_len)   where     len = if compress then 33 else 65     flags = if compress then compressed else uncompressed  exportCompactSig :: Ctx -> Sig -> CompactSig-exportCompactSig (Ctx ctx) (Sig sig_bs) = unsafePerformIO $-  unsafeUseByteString sig_bs $ \(sig_ptr, _) -> do-    out_ptr <- mallocBytes 64-    ret <- ecdsaSignatureSerializeCompact ctx out_ptr sig_ptr-    unless (isSuccess ret) $ do-      free out_ptr-      error "Could not obtain compact signature"-    out_bs <- unsafePackByteString (out_ptr, 64)-    return $ CompactSig out_bs+exportCompactSig (Ctx fctx) (Sig sig_bs) =+  unsafePerformIO $ withForeignPtr fctx $ \ctx ->+    unsafeUseByteString sig_bs $ \(sig_ptr, _) -> do+      out_ptr <- mallocBytes 64+      ret <- ecdsaSignatureSerializeCompact ctx out_ptr sig_ptr+      unless (isSuccess ret) $ do+        free out_ptr+        error "Could not obtain compact signature"+      out_bs <- unsafePackByteString (out_ptr, 64)+      return $ CompactSig out_bs  importCompactSig :: Ctx -> CompactSig -> Maybe Sig-importCompactSig (Ctx ctx) (CompactSig compact_sig) = unsafePerformIO $-  unsafeUseByteString compact_sig $ \(compact_ptr, _) -> do-    out_sig <- mallocBytes 64-    ret <- ecdsaSignatureParseCompact ctx out_sig compact_ptr-    if isSuccess ret-      then do-        out_bs <- unsafePackByteString (out_sig, 64)-        return (Just (Sig out_bs))-      else do-        free out_sig-        return Nothing+importCompactSig (Ctx fctx) (CompactSig compact_sig) =+  unsafePerformIO $ withForeignPtr fctx $ \ctx ->+    unsafeUseByteString compact_sig $ \(compact_ptr, _) -> do+      out_sig <- mallocBytes 64+      ret <- ecdsaSignatureParseCompact ctx out_sig compact_ptr+      if isSuccess ret+        then do+          out_bs <- unsafePackByteString (out_sig, 64)+          return (Just (Sig out_bs))+        else do+          free out_sig+          return Nothing  -- | Import DER-encoded signature. importSig :: Ctx -> ByteString -> Maybe Sig-importSig (Ctx ctx) bs+importSig (Ctx fctx) bs   | BS.null bs = Nothing-  | otherwise = unsafePerformIO $-      unsafeUseByteString bs $ \(in_ptr, in_len) -> do-        out_sig <- mallocBytes 64-        ret <- ecdsaSignatureParseDer ctx out_sig in_ptr in_len-        if isSuccess ret-          then do-            out_bs <- unsafePackByteString (out_sig, 64)-            return (Just (Sig out_bs))-          else do-            free out_sig-            return Nothing+  | otherwise =+      unsafePerformIO $ withForeignPtr fctx $ \ctx ->+        unsafeUseByteString bs $ \(in_ptr, in_len) -> do+          out_sig <- mallocBytes 64+          ret <- ecdsaSignatureParseDer ctx out_sig in_ptr in_len+          if isSuccess ret+            then do+              out_bs <- unsafePackByteString (out_sig, 64)+              return (Just (Sig out_bs))+            else do+              free out_sig+              return Nothing  -- | Encode signature as strict DER. exportSig :: Ctx -> Sig -> ByteString-exportSig (Ctx ctx) (Sig in_sig) = unsafePerformIO $-  unsafeUseByteString in_sig $ \(in_ptr, _) ->-    alloca $ \out_len ->-      allocaBytes 72 $ \out_ptr -> do-        poke out_len 72-        ret <- ecdsaSignatureSerializeDer ctx out_ptr out_len in_ptr-        unless (isSuccess ret) $ error "could not serialize signature"-        final_len <- peek out_len-        packByteString (out_ptr, final_len)+exportSig (Ctx fctx) (Sig in_sig) =+  unsafePerformIO $ withForeignPtr fctx $ \ctx ->+    unsafeUseByteString in_sig $ \(in_ptr, _) ->+      alloca $ \out_len ->+        allocaBytes 72 $ \out_ptr -> do+          poke out_len 72+          ret <- ecdsaSignatureSerializeDer ctx out_ptr out_len in_ptr+          unless (isSuccess ret) $ error "could not serialize signature"+          final_len <- peek out_len+          packByteString (out_ptr, final_len)  -- | Verify message signature. 'True' means that the signature is correct. verifySig :: Ctx -> PubKey -> Sig -> Msg -> Bool-verifySig (Ctx ctx) (PubKey pub_key) (Sig sig) (Msg m) = unsafePerformIO $-  unsafeUseByteString pub_key $ \(pub_key_ptr, _) ->-    unsafeUseByteString sig $ \(sig_ptr, _) ->-      unsafeUseByteString m $ \(msg_ptr, _) ->-        isSuccess <$> ecdsaVerify ctx sig_ptr msg_ptr pub_key_ptr+verifySig (Ctx fctx) (PubKey pub_key) (Sig sig) (Msg m) =+  unsafePerformIO $ withForeignPtr fctx $ \ctx ->+    unsafeUseByteString pub_key $ \(pub_key_ptr, _) ->+      unsafeUseByteString sig $ \(sig_ptr, _) ->+        unsafeUseByteString m $ \(msg_ptr, _) ->+          isSuccess <$> ecdsaVerify ctx sig_ptr msg_ptr pub_key_ptr  signMsg :: Ctx -> SecKey -> Msg -> Sig-signMsg (Ctx ctx) (SecKey sec_key) (Msg m) = unsafePerformIO $-  unsafeUseByteString sec_key $ \(sec_key_ptr, _) ->-    unsafeUseByteString m $ \(msg_ptr, _) -> do-      sig_ptr <- mallocBytes 64-      ret <- ecdsaSign ctx sig_ptr msg_ptr sec_key_ptr nullFunPtr nullPtr-      unless (isSuccess ret) $ do-        free sig_ptr-        error "could not sign message"-      Sig <$> unsafePackByteString (sig_ptr, 64)+signMsg (Ctx fctx) (SecKey sec_key) (Msg m) =+  unsafePerformIO $ withForeignPtr fctx $ \ctx ->+    unsafeUseByteString sec_key $ \(sec_key_ptr, _) ->+      unsafeUseByteString m $ \(msg_ptr, _) -> do+        sig_ptr <- mallocBytes 64+        ret <- ecdsaSign ctx sig_ptr msg_ptr sec_key_ptr nullFunPtr nullPtr+        unless (isSuccess ret) $ do+          free sig_ptr+          error "could not sign message"+        Sig <$> unsafePackByteString (sig_ptr, 64)  derivePubKey :: Ctx -> SecKey -> PubKey-derivePubKey (Ctx ctx) (SecKey sec_key) = unsafePerformIO $-  unsafeUseByteString sec_key $ \(sec_key_ptr, _) -> do-    pub_key_ptr <- mallocBytes 64-    ret <- ecPubKeyCreate ctx pub_key_ptr sec_key_ptr-    unless (isSuccess ret) $ do-      free pub_key_ptr-      error "could not compute public key"-    PubKey <$> unsafePackByteString (pub_key_ptr, 64)+derivePubKey (Ctx fctx) (SecKey sec_key) =+  unsafePerformIO $ withForeignPtr fctx $ \ctx ->+    unsafeUseByteString sec_key $ \(sec_key_ptr, _) -> do+      pub_key_ptr <- mallocBytes 64+      ret <- ecPubKeyCreate ctx pub_key_ptr sec_key_ptr+      unless (isSuccess ret) $ do+        free pub_key_ptr+        error "could not compute public key"+      PubKey <$> unsafePackByteString (pub_key_ptr, 64)  -- | Add tweak to secret key. tweakAddSecKey :: Ctx -> SecKey -> Tweak -> Maybe SecKey-tweakAddSecKey (Ctx ctx) (SecKey sec_key) (Tweak t) = unsafePerformIO $-  unsafeUseByteString new_bs $ \(sec_key_ptr, _) ->-    unsafeUseByteString t $ \(tweak_ptr, _) -> do-      ret <- ecSecKeyTweakAdd ctx sec_key_ptr tweak_ptr-      if isSuccess ret-        then return (Just (SecKey new_bs))-        else return Nothing+tweakAddSecKey (Ctx fctx) (SecKey sec_key) (Tweak t) =+  unsafePerformIO $ withForeignPtr fctx $ \ctx ->+    unsafeUseByteString new_bs $ \(sec_key_ptr, _) ->+      unsafeUseByteString t $ \(tweak_ptr, _) -> do+        ret <- ecSecKeyTweakAdd ctx sec_key_ptr tweak_ptr+        if isSuccess ret+          then return (Just (SecKey new_bs))+          else return Nothing   where     new_bs = BS.copy sec_key  -- | Multiply secret key by tweak. tweakMulSecKey :: Ctx -> SecKey -> Tweak -> Maybe SecKey-tweakMulSecKey (Ctx ctx) (SecKey sec_key) (Tweak t) = unsafePerformIO $-  unsafeUseByteString new_bs $ \(sec_key_ptr, _) ->-    unsafeUseByteString t $ \(tweak_ptr, _) -> do-      ret <- ecSecKeyTweakMul ctx sec_key_ptr tweak_ptr-      if isSuccess ret-        then return (Just (SecKey new_bs))-        else return Nothing+tweakMulSecKey (Ctx fctx) (SecKey sec_key) (Tweak t) =+  unsafePerformIO $ withForeignPtr fctx $ \ctx ->+    unsafeUseByteString new_bs $ \(sec_key_ptr, _) ->+      unsafeUseByteString t $ \(tweak_ptr, _) -> do+        ret <- ecSecKeyTweakMul ctx sec_key_ptr tweak_ptr+        if isSuccess ret+          then return (Just (SecKey new_bs))+          else return Nothing   where     new_bs = BS.copy sec_key  -- | Add tweak to public key. Tweak is multiplied first by G to obtain a point. tweakAddPubKey :: Ctx -> PubKey -> Tweak -> Maybe PubKey-tweakAddPubKey (Ctx ctx) (PubKey pub_key) (Tweak t) = unsafePerformIO $-  unsafeUseByteString new_bs $ \(pub_key_ptr, _) ->-    unsafeUseByteString t $ \(tweak_ptr, _) -> do-      ret <- ecPubKeyTweakAdd ctx pub_key_ptr tweak_ptr-      if isSuccess ret-        then return (Just (PubKey new_bs))-        else return Nothing+tweakAddPubKey (Ctx fctx) (PubKey pub_key) (Tweak t) =+  unsafePerformIO $ withForeignPtr fctx $ \ctx ->+    unsafeUseByteString new_bs $ \(pub_key_ptr, _) ->+      unsafeUseByteString t $ \(tweak_ptr, _) -> do+        ret <- ecPubKeyTweakAdd ctx pub_key_ptr tweak_ptr+        if isSuccess ret+          then return (Just (PubKey new_bs))+          else return Nothing   where     new_bs = BS.copy pub_key  -- | Multiply public key by tweak. Tweak is multiplied first by G to obtain a -- point. tweakMulPubKey :: Ctx -> PubKey -> Tweak -> Maybe PubKey-tweakMulPubKey (Ctx ctx) (PubKey pub_key) (Tweak t) = unsafePerformIO $-  unsafeUseByteString new_bs $ \(pub_key_ptr, _) ->-    unsafeUseByteString t $ \(tweak_ptr, _) -> do-      ret <- ecPubKeyTweakMul ctx pub_key_ptr tweak_ptr-      if isSuccess ret-        then return (Just (PubKey new_bs))-        else return Nothing+tweakMulPubKey (Ctx fctx) (PubKey pub_key) (Tweak t) =+  unsafePerformIO $ withForeignPtr fctx $ \ctx ->+    unsafeUseByteString new_bs $ \(pub_key_ptr, _) ->+      unsafeUseByteString t $ \(tweak_ptr, _) -> do+        ret <- ecPubKeyTweakMul ctx pub_key_ptr tweak_ptr+        if isSuccess ret+          then return (Just (PubKey new_bs))+          else return Nothing   where     new_bs = BS.copy pub_key  -- | Add multiple public keys together. combinePubKeys :: Ctx -> [PubKey] -> Maybe PubKey combinePubKeys _ [] = Nothing-combinePubKeys (Ctx ctx) pubs = unsafePerformIO $-  pointers [] pubs $ \ps ->-    allocaArray (length ps) $ \a -> do-      out <- mallocBytes 64-      pokeArray a ps-      ret <- ecPubKeyCombine ctx out a (fromIntegral $ length ps)-      if isSuccess ret-        then do-          bs <- unsafePackByteString (out, 64)-          return (Just (PubKey bs))-        else do-          free out-          return Nothing+combinePubKeys (Ctx fctx) pubs =+  unsafePerformIO $ withForeignPtr fctx $ \ctx ->+    pointers [] pubs $ \ps ->+      allocaArray (length ps) $ \a -> do+        out <- mallocBytes 64+        pokeArray a ps+        ret <- ecPubKeyCombine ctx out a (fromIntegral $ length ps)+        if isSuccess ret+          then do+            bs <- unsafePackByteString (out, 64)+            return (Just (PubKey bs))+          else do+            free out+            return Nothing   where     pointers ps [] f = f ps     pointers ps (PubKey pub_key : pub_keys) f =@@ -406,12 +422,13 @@         pointers (p : ps) pub_keys f  tweakNegate :: Ctx -> Tweak -> Maybe Tweak-tweakNegate (Ctx ctx) (Tweak t) = unsafePerformIO $-  unsafeUseByteString new $ \(out, _) -> do-    ret <- ecTweakNegate ctx out-    if isSuccess ret-      then return (Just (Tweak new))-      else return Nothing+tweakNegate (Ctx fctx) (Tweak t) =+  unsafePerformIO $ withForeignPtr fctx $ \ctx ->+    unsafeUseByteString new $ \(out, _) -> do+      ret <- ecTweakNegate ctx out+      if isSuccess ret+        then return (Just (Tweak new))+        else return Nothing   where     new = BS.copy t 
src/Crypto/Secp256k1/Internal/Context.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE OverloadedRecordDot #-} {-# LANGUAGE NoFieldSelectors #-}  -- |@@ -12,38 +11,54 @@ -- exposed for hacking and experimentation. module Crypto.Secp256k1.Internal.Context where -import Control.Exception (bracket)+import Control.Exception (bracket, mask_) import Control.Monad (unless)-import Crypto.Secp256k1.Internal.ForeignTypes (CtxFlags, LCtx, Ret, Seed32, isSuccess)+import Crypto.Secp256k1.Internal.ForeignTypes+  ( CtxFlags,+    LCtx,+    Ret,+    Seed32,+    isSuccess,+  ) import Crypto.Secp256k1.Internal.Util (withRandomSeed) import Foreign (FunPtr, Ptr) import Foreign.C (CInt (..), CString, CUInt (..))+import Foreign.ForeignPtr+  ( FinalizerPtr,+    ForeignPtr,+    finalizeForeignPtr,+    newForeignPtr,+    withForeignPtr,+  )+import GHC.Conc (writeTVar) import System.IO.Unsafe (unsafePerformIO) -newtype Ctx = Ctx {get :: Ptr LCtx}+newtype Ctx = Ctx {get :: ForeignPtr LCtx}  randomizeContext :: Ctx -> IO ()-randomizeContext (Ctx ctx) = do+randomizeContext (Ctx fctx) = withForeignPtr fctx $ \ctx -> do   ret <- withRandomSeed $ contextRandomize ctx   unless (isSuccess ret) $ error "Could not randomize context"  createContext :: IO Ctx-createContext = Ctx <$> contextCreate signVerify+createContext = do+  ctx <- mask_ $ do+    pctx <- contextCreate signVerify+    Ctx <$> newForeignPtr contextDestroyFunPtr pctx+  randomizeContext ctx+  return ctx  cloneContext :: Ctx -> IO Ctx-cloneContext = fmap Ctx . contextClone . (.get)+cloneContext (Ctx fctx) =+  withForeignPtr fctx $ \ctx -> mask_ $ do+    ctx' <- contextClone ctx+    Ctx <$> newForeignPtr contextDestroyFunPtr ctx'  destroyContext :: Ctx -> IO ()-destroyContext = contextDestroy . (.get)+destroyContext (Ctx fctx)= finalizeForeignPtr fctx  withContext :: (Ctx -> IO a) -> IO a-withContext = bracket create destroy-  where-    create = do-      ctx <- createContext-      randomizeContext ctx-      return ctx-    destroy = destroyContext+withContext = (createContext >>=)  verify :: CtxFlags verify = 0x0101@@ -65,9 +80,10 @@     IO (Ptr LCtx)  foreign import ccall safe "secp256k1.h secp256k1_context_destroy"-  contextDestroy ::-    Ptr LCtx ->-    IO ()+  contextDestroy :: Ptr LCtx -> IO ()++foreign import ccall safe "secp256k1.h &secp256k1_context_destroy"+  contextDestroyFunPtr :: FunPtr (Ptr LCtx -> IO ())  foreign import ccall safe "secp256k1.h secp256k1_context_set_illegal_callback"   setIllegalCallback ::
test/Crypto/Secp256k1/InternalSpec.hs view
@@ -19,7 +19,7 @@ import Data.ByteString.Base16 (decodeBase16) import Foreign import System.Entropy-import Test.HUnit (Assertion, assertBool, assertEqual)+import Test.HUnit (Assertion, assertBool, assertEqual, assertFailure) import Test.Hspec  spec :: Spec