packages feed

libsecp256k1 0.2.1 → 0.3.0

raw patch · 3 files changed

+197/−39 lines, 3 filesdep +randomPVP ok

version bump matches the API change (PVP)

Dependencies added: random

API changes (from Hackage documentation)

+ Crypto.Secp256k1: data SchnorrSignature
+ Crypto.Secp256k1: exportSchnorrSignature :: SchnorrSignature -> ByteString
+ Crypto.Secp256k1: importSchnorrSignature :: ByteString -> Maybe SchnorrSignature
+ Crypto.Secp256k1: instance Control.DeepSeq.NFData Crypto.Secp256k1.SchnorrSignature
+ Crypto.Secp256k1: instance GHC.Classes.Eq Crypto.Secp256k1.SchnorrSignature
+ Crypto.Secp256k1: instance GHC.Read.Read Crypto.Secp256k1.SchnorrSignature
+ Crypto.Secp256k1: instance GHC.Show.Show Crypto.Secp256k1.SchnorrSignature
+ Crypto.Secp256k1: schnorrSignDeterministic :: KeyPair -> ByteString -> Maybe SchnorrSignature
+ Crypto.Secp256k1: schnorrSignNondeterministic :: KeyPair -> ByteString -> IO (Maybe SchnorrSignature)
- Crypto.Secp256k1: schnorrSign :: KeyPair -> ByteString -> Maybe Signature
+ Crypto.Secp256k1: schnorrSign :: Maybe StdGen -> KeyPair -> ByteString -> Maybe SchnorrSignature
- Crypto.Secp256k1: schnorrVerify :: PubKeyXO -> ByteString -> Signature -> Bool
+ Crypto.Secp256k1: schnorrVerify :: PubKeyXO -> ByteString -> SchnorrSignature -> Bool

Files

libsecp256k1.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           libsecp256k1-version:        0.2.1+version:        0.3.0 synopsis:       Bindings for secp256k1 description:    Sign and verify signatures using the secp256k1 library. category:       Crypto@@ -47,6 +47,7 @@     , hashable >=1.4.2 && <1.5     , hedgehog >=1.2 && <1.5     , memory >=0.14.15 && <1.0+    , random >=1.2.1.2 && <1.3     , transformers >=0.4.0.0 && <1.0   default-language: Haskell2010 @@ -79,5 +80,6 @@     , libsecp256k1     , memory >=0.14.15 && <1.0     , monad-par+    , random >=1.2.1.2 && <1.3     , transformers >=0.4.0.0 && <1.0   default-language: Haskell2010
src/Crypto/Secp256k1.hs view
@@ -25,6 +25,7 @@     KeyPair,     Signature,     RecoverableSignature,+    SchnorrSignature,     Tweak,      -- * Parsing and Serialization@@ -40,6 +41,8 @@     exportSignatureDer,     importRecoverableSignature,     exportRecoverableSignature,+    importSchnorrSignature,+    exportSchnorrSignature,     importTweak,      -- * ECDSA Operations@@ -73,6 +76,8 @@      -- * Schnorr Operations     schnorrSign,+    schnorrSignDeterministic,+    schnorrSignNondeterministic,     schnorrVerify,      -- * Other@@ -140,6 +145,7 @@ import GHC.Generics (Generic) import GHC.IO.Handle.Text (memcpy) import System.IO.Unsafe (unsafePerformIO)+import System.Random (StdGen, newStdGen, randoms, randomIO) import Text.Read (     Lexeme (String),     lexP,@@ -283,6 +289,28 @@     rnf Signature{..} = seq signatureFPtr ()  +-- | Structure containing Schnorr Signature+newtype SchnorrSignature = SchnorrSignature {schnorrSignatureFPtr :: ForeignPtr Prim.Sig64}+++instance Show SchnorrSignature where+    show sig = (B8.unpack . encodeBase16) (exportSchnorrSignature sig)+instance Read SchnorrSignature where+    readsPrec i cs = case decodeBase16 $ B8.pack token of+        Left e -> []+        Right a -> maybeToList $ (,rest) <$> importSchnorrSignature a+        where+            trimmed = dropWhile isSpace cs+            (token, rest) = span isAlphaNum trimmed+instance Eq SchnorrSignature where+    sig == sig' = unsafePerformIO . evalContT $ do+        sigp <- ContT $ withForeignPtr (schnorrSignatureFPtr sig)+        sigp' <- ContT $ withForeignPtr (schnorrSignatureFPtr sig')+        (EQ ==) <$> lift (memCompare (castPtr sigp) (castPtr sigp') 64)+instance NFData SchnorrSignature where+    rnf SchnorrSignature{..} = seq schnorrSignatureFPtr ()++ -- | Structure containing Signature AND recovery ID newtype RecoverableSignature = RecoverableSignature {recoverableSignatureFPtr :: ForeignPtr Prim.RecSig65} @@ -417,15 +445,16 @@  -- | Parses 'Signature' from Compact (64 bytes) representation. importSignatureCompact :: ByteString -> Maybe Signature-importSignatureCompact bs = unsafePerformIO $-    unsafeUseByteString bs $ \(inBuf, len) -> do+importSignatureCompact bs = unsafePerformIO+    $ unsafeUseByteString bs+    $ \(inBuf, len) -> do         outBuf <- mallocBytes 64         ret <-             if len == 64-                -- compact-                then Prim.ecdsaSignatureParseCompact ctx outBuf inBuf-                -- invalid-                else pure 0+                then -- compact+                    Prim.ecdsaSignatureParseCompact ctx outBuf inBuf+                else -- invalid+                    pure 0         if isSuccess ret             then Just . Signature <$> newForeignPtr finalizerFree outBuf             else free outBuf $> Nothing@@ -433,8 +462,9 @@  -- | Parses 'Signature' from DER representation. importSignatureDer :: ByteString -> Maybe Signature-importSignatureDer bs = unsafePerformIO $-    unsafeUseByteString bs $ \(inBuf, len) -> do+importSignatureDer bs = unsafePerformIO+    $ unsafeUseByteString bs+    $ \(inBuf, len) -> do         outBuf <- mallocBytes 64         ret <- Prim.ecdsaSignatureParseDer ctx outBuf inBuf len         if isSuccess ret@@ -474,8 +504,8 @@         recId <- lift (peekByteOff @Word8 ptr 64)         let recIdCInt = fromIntegral recId         ret <- lift (Prim.ecdsaRecoverableSignatureParseCompact ctx outBuf ptr recIdCInt)-        lift $-            if isSuccess ret+        lift+            $ if isSuccess ret                 then Just . RecoverableSignature <$> newForeignPtr finalizerFree outBuf                 else free outBuf $> Nothing @@ -493,6 +523,23 @@         unsafePackByteString (outBuf, 65)  +-- | Parses 'SchnorrSignature' from Schnorr (64 byte) representation+importSchnorrSignature :: ByteString -> Maybe SchnorrSignature+importSchnorrSignature bs+    | BS.length bs /= 64 = Nothing+    | otherwise = unsafePerformIO $ do+        outBuf <- mallocBytes 64+        unsafeUseByteString bs $ \(ptr, _) -> do+            memcpy outBuf ptr 64+        Just . SchnorrSignature <$> newForeignPtr finalizerFree outBuf+++-- | Serializes 'SchnorrSignature' to Schnorr (64 byte) representation+exportSchnorrSignature :: SchnorrSignature -> ByteString+exportSchnorrSignature (SchnorrSignature fptr) = unsafePerformIO $+    withForeignPtr fptr $ \ptr -> BS.packCStringLen (castPtr ptr, 64)++ -- | Parses 'Tweak' from 32 byte @ByteString@. If the @ByteString@ is an invalid 'SecKey' then this will yield @Nothing@ importTweak :: ByteString -> Maybe Tweak importTweak = fmap (Tweak . castForeignPtr . secKeyFPtr) . importSecKey@@ -500,8 +547,9 @@  -- | Verify message signature. 'True' means that the signature is correct. ecdsaVerify :: ByteString -> PubKeyXY -> Signature -> Bool-ecdsaVerify msgHash (PubKeyXY pkFPtr) (Signature sigFPtr) = unsafePerformIO $-    evalContT $ do+ecdsaVerify msgHash (PubKeyXY pkFPtr) (Signature sigFPtr) = unsafePerformIO+    $ evalContT+    $ do         pkPtr <- ContT (withForeignPtr pkFPtr)         sigPtr <- ContT (withForeignPtr sigFPtr)         (msgHashPtr, n) <- ContT (unsafeUseByteString msgHash)@@ -610,8 +658,8 @@     lift (memcpy skOut skPtr 32)     twkPtr <- ContT (withForeignPtr tweakFPtr)     ret <- lift (Prim.ecSeckeyTweakAdd ctx skOut twkPtr)-    lift $-        if isSuccess ret+    lift+        $ if isSuccess ret             then Just . SecKey <$> newForeignPtr finalizerFree skOut             else free skOut $> Nothing @@ -624,8 +672,8 @@     lift (memcpy skOut skPtr 32)     twkPtr <- ContT (withForeignPtr tweakFPtr)     ret <- lift (Prim.ecSeckeyTweakMul ctx skOut twkPtr)-    lift $-        if isSuccess ret+    lift+        $ if isSuccess ret             then Just . SecKey <$> newForeignPtr finalizerFree skOut             else free skOut $> Nothing @@ -700,30 +748,47 @@             else free keyPairOut $> Nothing  --- | Compute a schnorr signature using a 'KeyPair'. The @ByteString@ must be 32 bytes long to get a @Just@ out of this--- function-schnorrSign :: KeyPair -> ByteString -> Maybe Signature-schnorrSign KeyPair{..} bs+-- | Compute a schnorr signature using a 'KeyPair'. The @ByteString@ must be 32 bytes long to get +-- a @Just@ out of this function. Optionally takes a 'StdGen' for deterministic signing.+schnorrSign :: Maybe StdGen -> KeyPair -> ByteString -> Maybe SchnorrSignature+schnorrSign mGen KeyPair{..} bs     | BS.length bs /= 32 = Nothing     | otherwise = unsafePerformIO . evalContT $ do         (msgHashPtr, _) <- ContT (unsafeUseByteString bs)         keyPairPtr <- ContT (withForeignPtr keyPairFPtr)         lift $ do             sigBuf <- mallocBytes 64-            -- TODO: provide randomness here instead of supplying a null pointer-            ret <- Prim.schnorrsigSign ctx sigBuf msgHashPtr keyPairPtr nullPtr+            ret <- case mGen of+                Just gen -> do+                    let randomBytes = BS.pack $ Prelude.take 32 $ randoms gen+                    BS.useAsCStringLen randomBytes $ \(ptr, _) ->+                        Prim.schnorrsigSign ctx sigBuf msgHashPtr keyPairPtr (castPtr ptr)+                Nothing ->+                    Prim.schnorrsigSign ctx sigBuf msgHashPtr keyPairPtr nullPtr             if isSuccess ret-                then Just . Signature <$> newForeignPtr finalizerFree sigBuf-                else free sigBuf $> Nothing+                then Just . SchnorrSignature <$> newForeignPtr finalizerFree sigBuf+                else do+                    free sigBuf+                    return Nothing  +-- | Compute a deterministic schnorr signature using a 'KeyPair'.+schnorrSignDeterministic :: KeyPair -> ByteString -> Maybe SchnorrSignature+schnorrSignDeterministic = schnorrSign Nothing+++-- | Compute a non-deterministic schnorr signature using a 'KeyPair'.+schnorrSignNondeterministic :: KeyPair -> ByteString -> IO (Maybe SchnorrSignature)+schnorrSignNondeterministic kp bs = newStdGen >>= \gen -> pure $ schnorrSign (Just gen) kp bs++ -- | Verify the authenticity of a schnorr signature. @True@ means the 'Signature' is correct.-schnorrVerify :: PubKeyXO -> ByteString -> Signature -> Bool-schnorrVerify PubKeyXO{..} bs Signature{..} = unsafePerformIO . evalContT $ do+schnorrVerify :: PubKeyXO -> ByteString -> SchnorrSignature -> Bool+schnorrVerify PubKeyXO{..} bs SchnorrSignature{..} = unsafePerformIO . evalContT $ do     pubKeyPtr <- ContT (withForeignPtr pubKeyXOFPtr)-    signaturePtr <- ContT (withForeignPtr signatureFPtr)+    schnorrSignaturePtr <- ContT (withForeignPtr schnorrSignatureFPtr)     (msgPtr, msgLen) <- ContT (unsafeUseByteString bs)-    lift $ isSuccess <$> Prim.schnorrsigSignVerify ctx signaturePtr msgPtr msgLen pubKeyPtr+    lift $ isSuccess <$> Prim.schnorrsigSignVerify ctx schnorrSignaturePtr msgPtr msgLen pubKeyPtr   -- | Generate a tagged sha256 digest as specified in BIP340
test/Crypto/Secp256k1Prop.hs view
@@ -5,6 +5,7 @@  import Control.Applicative (Applicative (liftA2), empty) import Control.Monad (when)+import Control.Monad.IO.Class (liftIO) import Control.Monad.Trans.Class (lift) import Crypto.Secp256k1 import Crypto.Secp256k1.Gen@@ -16,6 +17,7 @@ import Hedgehog.Gen hiding (discard, maybe, prune) import Hedgehog.Range (linear, singleton) import Text.Read (readMaybe)+import System.Random (StdGen, mkStdGen)   prop_secKeyReadInvertsShow :: Property@@ -260,11 +262,19 @@     sk <- forAll secKeyGen     msg <- forAll $ bytes (singleton 32)     let kp = keyPairCreate sk-    case schnorrSign kp msg of-        Nothing -> failure-        Just sig -> assert $ schnorrVerify (fst $ keyPairPubKeyXO kp) msg sig+    sig <- maybe failure pure $ schnorrSignDeterministic kp msg+    assert $ schnorrVerify (fst $ keyPairPubKeyXO kp) msg sig  +prop_schnorrSignaturesProducedAreValidNonDeterministic :: Property+prop_schnorrSignaturesProducedAreValidNonDeterministic = property $ do+    sk <- forAll secKeyGen+    msg <- forAll $ bytes (singleton 32)+    let kp = keyPairCreate sk+    sig <- liftIO $ maybe (error "Failed to sign") pure =<< schnorrSignNondeterministic kp msg+    assert $ schnorrVerify (fst $ keyPairPubKeyXO kp) msg sig++ prop_pubKeyCombineTweakIdentity :: Property prop_pubKeyCombineTweakIdentity = property $ do     sk <- forAll secKeyGen@@ -297,11 +307,20 @@     let kp = keyPairCreate sk     pk <- forAll pubKeyXOGen     msg <- forAll $ bytes (singleton 32)-    case schnorrSign kp msg of-        Nothing -> failure-        Just sig -> assert . not $ schnorrVerify pk msg sig+    sig <- maybe failure pure $ schnorrSignDeterministic kp msg+    assert . not $ schnorrVerify pk msg sig  +prop_schnorrSignaturesUnforgeableNonDeterministic :: Property+prop_schnorrSignaturesUnforgeableNonDeterministic = property $ do+    sk <- forAll secKeyGen+    let kp = keyPairCreate sk+    pk <- forAll pubKeyXOGen+    msg <- forAll $ bytes (singleton 32)+    sig <- liftIO $ maybe (error "Failed to sign") pure =<< schnorrSignNondeterministic kp msg+    assert . not $ schnorrVerify pk msg sig++ newtype Wrapped a = Wrapped {secKey :: a} deriving (Show, Read, Eq)  @@ -333,13 +352,29 @@   prop_derivedCompositeReadShowInvertSignature :: Property-prop_derivedCompositeReadShowInvertSignature = derivedCompositeReadShowInvertTemplate $ choice [ecdsa, schnorr]+prop_derivedCompositeReadShowInvertSignature = derivedCompositeReadShowInvertTemplate ecdsaSignGen     where-        base = liftA2 (,) secKeyGen (bytes (singleton 32))-        ecdsa = base >>= maybe empty pure . uncurry ecdsaSign-        schnorr = base >>= maybe empty pure . uncurry (schnorrSign . keyPairCreate)+        ecdsaSignGen = do+            sk <- secKeyGen+            msg <- bytes (singleton 32)+            maybe empty pure $ ecdsaSign sk msg  +prop_derivedCompositeReadShowInvertSchnorrSignature :: Property+prop_derivedCompositeReadShowInvertSchnorrSignature = property $ do+    sk <- forAll secKeyGen+    let kp = keyPairCreate sk+    msg <- forAll $ bytes (singleton 32)+    sig <- maybe failure pure $ schnorrSignDeterministic kp msg+    let a = sig+    annotateShow a+    annotateShow (length $ show a)+    annotateShow (Wrapped a)+    case readMaybe (show (Wrapped a)) of+        Nothing -> failure+        Just x  -> x === Wrapped a++ prop_derivedCompositeReadShowInvertRecoverableSignature :: Property prop_derivedCompositeReadShowInvertRecoverableSignature = derivedCompositeReadShowInvertTemplate $ do     sk <- secKeyGen@@ -353,6 +388,62 @@     k0 <- maybe discard pure $ importSecKey bs     k1 <- maybe discard pure $ importSecKey bs     k0 === k1+++prop_schnorrSignatureParseInvertsSerialize :: Property+prop_schnorrSignatureParseInvertsSerialize = property $ do+    sk <- forAll secKeyGen+    msg <- forAll $ bytes (singleton 32)+    let kp = keyPairCreate sk+    sig <- maybe failure pure $ schnorrSignDeterministic kp msg+    let serialized = exportSchnorrSignature sig+    annotateShow serialized+    annotateShow (BS.length serialized)+    let parsed = importSchnorrSignature serialized+    parsed === Just sig+++prop_schnorrSignatureValidityPreservedOverSerialization :: Property+prop_schnorrSignatureValidityPreservedOverSerialization = property $ do+    sk <- forAll secKeyGen+    msg <- forAll $ bytes (singleton 32)+    let kp = keyPairCreate sk+    sig <- maybe failure pure $ schnorrSignDeterministic kp msg+    let serialized = exportSchnorrSignature sig+    let parsed = importSchnorrSignature serialized+    parsed === Just sig+    assert $ schnorrVerify (fst $ keyPairPubKeyXO kp) msg sig+++prop_schnorrSignatureDeterministic :: Property+prop_schnorrSignatureDeterministic = property $ do+    sk <- forAll secKeyGen+    msg <- forAll $ bytes (singleton 32)+    let kp = keyPairCreate sk+    sig1 <- maybe failure pure $ schnorrSignDeterministic kp msg+    sig2 <- maybe failure pure $ schnorrSignDeterministic kp msg+    sig1 === sig2+++prop_schnorrSignatureNonDeterministic :: Property+prop_schnorrSignatureNonDeterministic = property $ do+    sk <- forAll secKeyGen+    msg <- forAll $ bytes (singleton 32)+    let kp = keyPairCreate sk+    sig1 <- liftIO $ maybe (error "Failed to sign") pure =<< schnorrSignNondeterministic kp msg+    sig2 <- liftIO $ maybe (error "Failed to sign") pure =<< schnorrSignNondeterministic kp msg+    sig1 /== sig2+++prop_schnorrSignWithStdGen :: Property+prop_schnorrSignWithStdGen = property $ do+    sk <- forAll secKeyGen+    msg <- forAll $ bytes (singleton 32)+    let kp = keyPairCreate sk+    stdGen <- forAll $ mkStdGen <$> integral (linear 0 maxBound)+    sig1 <- maybe failure pure $ schnorrSign (Just stdGen) kp msg+    sig2 <- maybe failure pure $ schnorrSign (Just stdGen) kp msg+    sig1 === sig2   tests :: Group