packages feed

secp256k1-haskell (empty) → 0.1.0

raw patch · 10 files changed

+1719/−0 lines, 10 filesdep +HUnitdep +QuickCheckdep +basesetup-changed

Dependencies added: HUnit, QuickCheck, base, base16-bytestring, bytestring, cereal, cryptohash, entropy, hspec, mtl, secp256k1-haskell, string-conversions

Files

+ CHANGELOG.md view
@@ -0,0 +1,31 @@+# Changelog+All notable changes to this project will be documented in this file.++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.2+### Changed+- Simplify `Show` and `Read` instances.++## 1.1.1+### Removed+- Old obvious installation instructions from `README.md` file.++### Changed+- Fix arbitrary instances such that unwanted colissions are less likely.++## 1.1.0+### Removed+- Lax parsing for DER.+- DER parsing of secret keys.+- Embedded code for library.++## 1.0.0 [YANKED]+### Added+- Changelog.++### Changed+- `Show`/`Read` instances now isomorphic to Haskell code.+- Semantic versioning.+- `Hspec` tests.
+ README.md view
@@ -0,0 +1,3 @@+# Haskell bindings for secp256k1++This project contains Haskell bindings for the [secp256k1](https://github.com/bitcoin-core/secp256k1) library from the Bitcoin Core project.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ UNLICENSE view
@@ -0,0 +1,24 @@+This is free and unencumbered software released into the public domain.++Anyone is free to copy, modify, publish, use, compile, sell, or+distribute this software, either in source code form or as a compiled+binary, for any purpose, commercial or non-commercial, and by any+means.++In jurisdictions that recognize copyright laws, the author or authors+of this software dedicate any and all copyright interest in the+software to the public domain. We make this dedication for the benefit+of the public at large and to the detriment of our heirs and+successors. We intend this dedication to be an overt act of+relinquishment in perpetuity of all present and future rights to this+software under copyright law.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR+OTHER DEALINGS IN THE SOFTWARE.++For more information, please refer to <http://unlicense.org/>
+ secp256k1-haskell.cabal view
@@ -0,0 +1,76 @@+-- This file has been generated from package.yaml by hpack version 0.28.2.+--+-- see: https://github.com/sol/hpack+--+-- hash: 5fa20b19c25e16beba80f187b074b53c7d0bef071771bc3875c6de4afba11a33++name:           secp256k1-haskell+version:        0.1.0+synopsis:       Bindings for secp256k1 library from Bitcoin Core+description:    Sign and verify signatures using the very fast C secp256k1 library from Pieter Wuille. Has Haskell types and abstractions for keys and signatures.+category:       Crypto+homepage:       http://github.com/haskoin/secp256k1-haskell#readme+bug-reports:    https://github.com/haskoin/secp256k1-haskell.git/issues+author:         Jean-Pierre Rupp+maintainer:     xenog@protonmail.com+copyright:      (c) 2017 Jean-Pierre Rupp+license:        PublicDomain+license-file:   UNLICENSE+build-type:     Simple+cabal-version:  >= 1.10+extra-source-files:+    CHANGELOG.md+    README.md++source-repository head+  type: git+  location: https://github.com/haskoin/secp256k1-haskell.git++library+  exposed-modules:+      Crypto.Secp256k1+      Crypto.Secp256k1.Internal+  other-modules:+      Paths_secp256k1_haskell+  hs-source-dirs:+      src+  extra-libraries:+      secp256k1+  build-depends:+      HUnit+    , QuickCheck+    , base >=4.8 && <5+    , base16-bytestring+    , bytestring+    , cereal+    , cryptohash+    , entropy+    , hspec+    , mtl+    , string-conversions+  default-language: Haskell2010++test-suite spec+  type: exitcode-stdio-1.0+  main-is: Spec.hs+  other-modules:+      Crypto.Secp256k1.InternalSpec+      Crypto.Secp256k1Spec+      Paths_secp256k1_haskell+  hs-source-dirs:+      test+  build-depends:+      HUnit+    , QuickCheck+    , base >=4.8 && <5+    , base16-bytestring+    , bytestring+    , cereal+    , cryptohash+    , entropy+    , hspec ==2.*+    , mtl+    , secp256k1-haskell+    , string-conversions+  default-language: Haskell2010+  build-tool-depends: hspec-discover:hspec-discover == 2.*
+ src/Crypto/Secp256k1.hs view
@@ -0,0 +1,449 @@+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-|+Module      : Crypto.Secp256k1+License     : MIT+Maintainer  : Jean-Pierre Rupp <root@haskoin.com>+Stability   : experimental+Portability : POSIX++Crytpographic functions from Bitcoin’s secp256k1 library.+-}+module Crypto.Secp256k1+    ( -- * Messages+    Msg+    , msg+    , getMsg++    -- * Secret Keys+    , SecKey+    , secKey+    , getSecKey+    , derivePubKey++    -- * Public Keys+    , PubKey+    , importPubKey+    , exportPubKey++    -- * Signatures+    , Sig+    , signMsg+    , verifySig+    , normalizeSig+    -- ** DER+    , importSig+    , exportSig+    -- ** Compact+    , CompactSig(..)+    , exportCompactSig+    , importCompactSig+    -- * Recoverable+    , RecSig+    , CompactRecSig(..)+    , importCompactRecSig+    , exportCompactRecSig+    , convertRecSig+    , signRecMsg+    , recover++    -- * Addition & Multiplication+    , Tweak+    , tweak+    , getTweak+    , tweakAddSecKey+    , tweakMulSecKey+    , tweakAddPubKey+    , tweakMulPubKey+    , combinePubKeys+    ) where++import           Control.Monad+import           Crypto.Secp256k1.Internal+import           Data.Serialize+import           Data.ByteString           (ByteString)+import qualified Data.ByteString           as BS+import qualified Data.ByteString.Base16    as B16+import           Data.ByteString.Short     (fromShort, toShort)+import           Data.Maybe+import           Data.String+import           Data.String.Conversions+import           Foreign+import           System.IO.Unsafe+import           Test.QuickCheck+import           Text.Read++newtype PubKey = PubKey (ForeignPtr PubKey64)+newtype Msg = Msg (ForeignPtr Msg32)+newtype Sig = Sig (ForeignPtr Sig64)+newtype SecKey = SecKey (ForeignPtr SecKey32)+newtype Tweak = Tweak (ForeignPtr Tweak32)+newtype RecSig = RecSig (ForeignPtr RecSig65)++decodeHex :: ConvertibleStrings a ByteString => a -> Maybe ByteString+decodeHex str = if BS.null r then Just bs else Nothing where+    (bs, r) = B16.decode $ cs str++instance Read PubKey where+    readPrec = do+        String str <- lexP+        maybe pfail return $ importPubKey =<< decodeHex str++instance IsString PubKey where+    fromString = fromMaybe e . (importPubKey <=< decodeHex) where+        e = error "Could not decode public key from hex string"++instance Show PubKey where+    showsPrec _ = shows . B16.encode . exportPubKey True++instance Read Msg where+    readPrec = parens $ do+        String str <- lexP+        maybe pfail return $ msg =<< decodeHex str++instance IsString Msg where+    fromString = fromMaybe e . (msg <=< decodeHex)  where+        e = error "Could not decode message from hex string"++instance Show Msg where+    showsPrec _ = shows . B16.encode . getMsg++instance Read Sig where+    readPrec = parens $ do+        String str <- lexP+        maybe pfail return $ importSig =<< decodeHex str++instance IsString Sig where+    fromString = fromMaybe e . (importSig <=< decodeHex) where+        e = error "Could not decode signature from hex string"++instance Show Sig where+    showsPrec _ = shows . B16.encode . exportSig++recSigFromString :: String -> Maybe RecSig+recSigFromString str = do+    bs <- decodeHex str+    rs <- either (const Nothing) Just $ decode bs+    importCompactRecSig rs++instance Read RecSig where+    readPrec = parens $ do+        String str <- lexP+        maybe pfail return $ recSigFromString str++instance IsString RecSig where+    fromString = fromMaybe e . recSigFromString+      where+        e = error "Could not decode signature from hex string"++instance Show RecSig where+    showsPrec _ = shows . B16.encode . encode . exportCompactRecSig++instance Read SecKey where+    readPrec = parens $ do+        String str <- lexP+        maybe pfail return $ secKey =<< decodeHex str++instance IsString SecKey where+    fromString = fromMaybe e . (secKey <=< decodeHex) where+        e = error "Colud not decode secret key from hex string"++instance Show SecKey where+    showsPrec _ = shows . B16.encode . getSecKey++instance Read Tweak where+    readPrec = parens $ do+        String str <- lexP+        maybe pfail return $ tweak =<< decodeHex str++instance IsString Tweak where+    fromString = fromMaybe e . (tweak <=< decodeHex) where+        e = error "Could not decode tweak from hex string"++instance Show Tweak where+    showsPrec _ = shows . B16.encode . getTweak++instance Eq PubKey where+    fp1 == fp2 = getPubKey fp1 == getPubKey fp2++instance Eq Msg where+    fm1 == fm2 = getMsg fm1 == getMsg fm2++instance Eq Sig where+    fg1 == fg2 = exportCompactSig fg1 == exportCompactSig fg2++instance Eq RecSig where+    fg1 == fg2 = exportCompactRecSig fg1 == exportCompactRecSig fg2++instance Eq SecKey where+    fk1 == fk2 = getSecKey fk1 == getSecKey fk2++instance Eq Tweak where+    ft1 == ft2 = getTweak ft1 == getTweak ft2++-- | Import 32-byte 'ByteString' as 'Msg'.+msg :: ByteString -> Maybe Msg+msg bs+    | BS.length bs == 32 = unsafePerformIO $ do+        fp <- mallocForeignPtr+        withForeignPtr fp $ flip poke (Msg32 (toShort bs))+        return $ Just $ Msg fp+    | otherwise = Nothing++-- | Import 32-byte 'ByteString' as 'SecKey'.+secKey :: ByteString -> Maybe SecKey+secKey bs+    | BS.length bs == 32 = withContext $ \ctx -> do+        fp <- mallocForeignPtr+        ret <- withForeignPtr fp $ \p -> do+            poke p (SecKey32 (toShort bs))+            ecSecKeyVerify ctx p+        if isSuccess ret+            then return $ Just $ SecKey fp+            else return Nothing+    | otherwise = Nothing++-- | Convert signature to a normalized lower-S form. Boolean value 'True'+-- indicates that the signature changed, 'False' indicates that it was already+-- normal.+normalizeSig :: Sig -> (Sig, Bool)+normalizeSig (Sig fg) = withContext $ \ctx -> do+    fg' <- mallocForeignPtr+    ret <- withForeignPtr fg $ \pg -> withForeignPtr fg' $ \pg' ->+        ecdsaSignatureNormalize ctx pg' pg+    return (Sig fg', isSuccess ret)++-- | 32-Byte 'ByteString' as 'Tweak'.+tweak :: ByteString -> Maybe Tweak+tweak bs+    | BS.length bs == 32 = unsafePerformIO $ do+        fp <- mallocForeignPtr+        withForeignPtr fp $ flip poke (Tweak32 (toShort bs))+        return $ Just $ Tweak fp+    | otherwise = Nothing++-- | Get 32-byte secret key.+getSecKey :: SecKey -> ByteString+getSecKey (SecKey fk) =+    fromShort $ getSecKey32 $ unsafePerformIO $ withForeignPtr fk peek++-- Get 64-byte public key.+getPubKey :: PubKey -> ByteString+getPubKey (PubKey fp) =+    fromShort $ getPubKey64 $ unsafePerformIO $ withForeignPtr fp peek++-- | Get 32-byte message.+getMsg :: Msg -> ByteString+getMsg (Msg fm) =+    fromShort $ getMsg32 $ unsafePerformIO $ withForeignPtr fm peek++-- | Get 32-byte tweak.+getTweak :: Tweak -> ByteString+getTweak (Tweak ft) =+    fromShort $ getTweak32 $ unsafePerformIO $ withForeignPtr ft peek++-- | Import DER-encoded public key.+importPubKey :: ByteString -> Maybe PubKey+importPubKey bs =  withContext $ \ctx -> useByteString bs $ \(b, l) -> do+    fp <- mallocForeignPtr+    ret <- withForeignPtr fp $ \p -> ecPubKeyParse ctx p b l+    if isSuccess ret then return $ Just $ PubKey fp else return Nothing++-- | Encode public key as DER. First argument 'True' for compressed output.+exportPubKey :: Bool -> PubKey -> ByteString+exportPubKey compress (PubKey pub) = withContext $ \ctx ->+    withForeignPtr pub $ \p -> alloca $ \l -> allocaBytes z $ \o -> do+        poke l (fromIntegral z)+        ret <- ecPubKeySerialize ctx o l p c+        unless (isSuccess ret) $ error "could not serialize public key"+        n <- peek l+        packByteString (o, n)+  where+    c = if compress then compressed else uncompressed+    z = if compress then 33 else 65++exportCompactSig :: Sig -> CompactSig+exportCompactSig (Sig fg) = withContext $ \ctx ->+    withForeignPtr fg $ \pg -> alloca $ \pc -> do+        ret <- ecdsaSignatureSerializeCompact ctx pc pg+        unless (isSuccess ret) $ error "Could not obtain compact signature"+        peek pc++importCompactSig :: CompactSig -> Maybe Sig+importCompactSig c = withContext $ \ctx -> alloca $ \pc -> do+    poke pc c+    fg <- mallocForeignPtr+    ret <- withForeignPtr fg $ \pg -> ecdsaSignatureParseCompact ctx pg pc+    if isSuccess ret then return $ Just $ Sig fg else return Nothing++-- | Import DER-encoded signature.+importSig :: ByteString -> Maybe Sig+importSig bs = withContext $ \ctx ->+    useByteString bs $ \(b, l) -> do+        fg <- mallocForeignPtr+        ret <- withForeignPtr fg $ \g -> ecdsaSignatureParseDer ctx g b l+        if isSuccess ret then return $ Just $ Sig fg else return Nothing++-- | Encode signature as strict DER.+exportSig :: Sig -> ByteString+exportSig (Sig fg) = withContext $ \ctx ->+    withForeignPtr fg $ \g -> alloca $ \l -> allocaBytes 72 $ \o -> do+        poke l 72+        ret <- ecdsaSignatureSerializeDer ctx o l g+        unless (isSuccess ret) $ error "could not serialize signature"+        n <- peek l+        packByteString (o, n)++-- | Verify message signature. 'True' means that the signature is correct.+verifySig :: PubKey -> Sig -> Msg -> Bool+verifySig (PubKey fp) (Sig fg) (Msg fm) = withContext $ \ctx ->+    withForeignPtr fp $ \p -> withForeignPtr fg $ \g ->+        withForeignPtr fm $ \m -> isSuccess <$> ecdsaVerify ctx g m p++signMsg :: SecKey -> Msg -> Sig+signMsg (SecKey fk) (Msg fm) = withContext $ \ctx ->+    withForeignPtr fk $ \k -> withForeignPtr fm $ \m -> do+        fg <- mallocForeignPtr+        ret <- withForeignPtr fg $ \g -> ecdsaSign ctx g m k nullFunPtr nullPtr+        unless (isSuccess ret) $ error "could not sign message"+        return $ Sig fg++derivePubKey :: SecKey -> PubKey+derivePubKey (SecKey fk) = withContext $ \ctx -> withForeignPtr fk $ \k -> do+    fp <- mallocForeignPtr+    ret <- withForeignPtr fp $ \p -> ecPubKeyCreate ctx p k+    unless (isSuccess ret) $ error "could not compute public key"+    return $ PubKey fp++-- | Add tweak to secret key.+tweakAddSecKey :: SecKey -> Tweak -> Maybe SecKey+tweakAddSecKey (SecKey fk) (Tweak ft) = withContext $ \ctx ->+    withForeignPtr fk $ \k -> withForeignPtr ft $ \t -> do+        fk' <- mallocForeignPtr+        ret <- withForeignPtr fk' $ \k' ->  do+            key <- peek k+            poke k' key+            ecSecKeyTweakAdd ctx k' t+        if isSuccess ret then return $ Just $ SecKey fk' else return Nothing++-- | Multiply secret key by tweak.+tweakMulSecKey :: SecKey -> Tweak -> Maybe SecKey+tweakMulSecKey (SecKey fk) (Tweak ft) = withContext $ \ctx ->+    withForeignPtr fk $ \k -> withForeignPtr ft $ \t -> do+        fk' <- mallocForeignPtr+        ret <- withForeignPtr fk' $ \k' ->  do+            key <- peek k+            poke k' key+            ecSecKeyTweakMul ctx k' t+        if isSuccess ret then return $ Just $ SecKey fk' else return Nothing++-- | Add tweak to public key. Tweak is multiplied first by G to obtain a point.+tweakAddPubKey :: PubKey -> Tweak -> Maybe PubKey+tweakAddPubKey (PubKey fp) (Tweak ft) = withContext $ \ctx ->+    withForeignPtr fp $ \p -> withForeignPtr ft $ \t -> do+        fp' <- mallocForeignPtr+        ret <- withForeignPtr fp' $ \p' ->  do+            pub <- peek p+            poke p' pub+            ecPubKeyTweakAdd ctx p' t+        if isSuccess ret then return $ Just $ PubKey fp' else return Nothing++-- | Multiply public key by tweak. Tweak is multiplied first by G to obtain a+-- point.+tweakMulPubKey :: PubKey -> Tweak -> Maybe PubKey+tweakMulPubKey (PubKey fp) (Tweak ft) = withContext $ \ctx ->+    withForeignPtr fp $ \p -> withForeignPtr ft $ \t -> do+        fp' <- mallocForeignPtr+        ret <- withForeignPtr fp' $ \p' ->  do+            pub <- peek p+            poke p' pub+            ecPubKeyTweakMul ctx p' t+        if isSuccess ret then return $ Just $ PubKey fp' else return Nothing++-- | Add multiple public keys together.+combinePubKeys :: [PubKey] -> Maybe PubKey+combinePubKeys pubs = withContext $ \ctx -> pointers [] pubs $ \ps ->+    allocaArray (length ps) $ \a -> do+        pokeArray a ps+        fp <- mallocForeignPtr+        ret <- withForeignPtr fp $ \p ->+            ecPubKeyCombine ctx p a (fromIntegral $ length ps)+        if isSuccess ret+            then return $ Just $ PubKey fp+            else return Nothing+  where+    pointers ps [] f = f ps+    pointers ps (PubKey fp : pubs') f =+        withForeignPtr fp $ \p -> pointers (p:ps) pubs' f++-- | Parse a compact ECDSA signature (64 bytes + recovery id).+importCompactRecSig :: CompactRecSig -> Maybe RecSig+importCompactRecSig cr =+  if getCompactRecSigV cr `notElem` [0,1,2,3]+  then Nothing+  else withContext $ \ctx -> alloca $ \pc -> do+    let+      c = CompactSig (getCompactRecSigR cr) (getCompactRecSigS cr)+      recid = fromIntegral $ getCompactRecSigV cr+    poke pc c+    fg <- mallocForeignPtr+    ret <- withForeignPtr fg $ \pg ->+        ecdsaRecoverableSignatureParseCompact ctx pg pc recid+    if isSuccess ret then return $ Just $ RecSig fg else return Nothing++-- | Serialize an ECDSA signature in compact format (64 bytes + recovery id).+exportCompactRecSig :: RecSig -> CompactRecSig+exportCompactRecSig (RecSig fg) = withContext $ \ctx ->+    withForeignPtr fg $ \pg -> alloca $ \pc -> alloca $ \pr -> do+        ret <- ecdsaRecoverableSignatureSerializeCompact ctx pc pr pg+        unless (isSuccess ret) $ error "Could not obtain compact signature"+        CompactSig r s <- peek pc+        v <- fromIntegral <$> peek pr+        return $ CompactRecSig r s v++-- | Convert a recoverable signature into a normal signature.+convertRecSig :: RecSig -> Sig+convertRecSig (RecSig frg) = withContext $ \ctx ->+    withForeignPtr frg $ \prg -> do+        fg <- mallocForeignPtr+        ret <- withForeignPtr fg $ \pg ->+            ecdsaRecoverableSignatureConvert ctx pg prg+        unless (isSuccess ret) $+            error "Could not convert a recoverable signature"+        return $ Sig fg++-- | Create a recoverable ECDSA signature.+signRecMsg :: SecKey -> Msg -> RecSig+signRecMsg (SecKey fk) (Msg fm) = withContext $ \ctx ->+    withForeignPtr fk $ \k -> withForeignPtr fm $ \m -> do+        fg <- mallocForeignPtr+        ret <- withForeignPtr fg $ \g ->+            ecdsaSignRecoverable ctx g m k nullFunPtr nullPtr+        unless (isSuccess ret) $ error "could not sign message"+        return $ RecSig fg++-- | Recover an ECDSA public key from a signature.+recover :: RecSig -> Msg -> Maybe PubKey+recover (RecSig frg) (Msg fm) = withContext $ \ctx ->+    withForeignPtr frg $ \prg -> withForeignPtr fm $ \pm -> do+        fp <- mallocForeignPtr+        ret <- withForeignPtr fp $ \pp -> ecdsaRecover ctx pp prg pm+        if isSuccess ret then return $ Just $ PubKey fp else return Nothing++instance Arbitrary Msg where+    arbitrary = gen_msg+      where+        valid_bs = bs_gen `suchThat` isJust+        bs_gen = (msg . BS.pack) <$> replicateM 32 arbitraryBoundedRandom+        gen_msg = fromJust <$> valid_bs++instance Arbitrary SecKey where+    arbitrary = gen_key where+        valid_bs = bs_gen `suchThat` isJust+        bs_gen = (secKey . BS.pack) <$> replicateM 32 arbitraryBoundedRandom+        gen_key = fromJust <$> valid_bs++instance Arbitrary PubKey where+    arbitrary = do+        key <- arbitrary+        return $ derivePubKey key
+ src/Crypto/Secp256k1/Internal.hs view
@@ -0,0 +1,473 @@+{-# LANGUAGE RecordWildCards #-}+{-|+Module      : Crypto.Secp256k1.Internal+License     : MIT+Maintainer  : Jean-Pierre Rupp <root@haskoin.com>+Stability   : experimental+Portability : POSIX++The API for this module may change at any time. This is an internal module only+exposed for hacking and experimentation.+-}+module Crypto.Secp256k1.Internal where++import           Control.Monad+import           Data.ByteString       (ByteString)+import qualified Data.ByteString       as BS+import           Data.ByteString.Short (ShortByteString, fromShort, toShort)+import           Data.Serialize        (Serialize (..))+import qualified Data.Serialize.Get    as Get+import qualified Data.Serialize.Put    as Put+import           Foreign+import           Foreign.C+import           System.Entropy+import           System.IO.Unsafe++data Ctx = Ctx++newtype PubKey64 = PubKey64 { getPubKey64 :: ShortByteString }+    deriving (Read, Show, Eq, Ord)++newtype Msg32 = Msg32 { getMsg32 :: ShortByteString }+    deriving (Read, Show, Eq, Ord)++newtype Sig64 = Sig64 { getSig64 :: ShortByteString }+    deriving (Read, Show, Eq, Ord)++data CompactSig =+    CompactSig+        { getCompactSigR :: !ShortByteString+        , getCompactSigS :: !ShortByteString+        }+    deriving (Show, Eq, Ord)++newtype RecSig65 = RecSig65 { getRecSig65 :: ShortByteString }+    deriving (Read, Show, Eq, Ord)++data CompactRecSig =+    CompactRecSig+        { getCompactRecSigR :: !ShortByteString+        , getCompactRecSigS :: !ShortByteString+        , getCompactRecSigV :: !Word8+        }+    deriving (Show, Eq, Ord)++newtype Seed32 = Seed32 { getSeed32 :: ShortByteString }+    deriving (Read, Show, Eq, Ord)++newtype SecKey32 = SecKey32 { getSecKey32 :: ShortByteString }+    deriving (Read, Show, Eq, Ord)++newtype Tweak32 = Tweak32 { getTweak32 :: ShortByteString }+    deriving (Read, Show, Eq, Ord)++newtype Nonce32 = Nonce32 { getNonce32 :: ShortByteString }+    deriving (Read, Show, Eq, Ord)++newtype Algo16 = Algo16 { getAlgo16 :: ShortByteString }+    deriving (Read, Show, Eq, Ord)++newtype CtxFlags = CtxFlags { getCtxFlags :: CUInt }+    deriving (Read, Show, Eq, Ord)++newtype SerFlags = SerFlags { getSerFlags :: CUInt }+    deriving (Read, Show, Eq, Ord)++newtype Ret = Ret { getRet :: CInt }+    deriving (Read, Show, Eq, Ord)++-- | Nonce32-generating function+type NonceFunction a+    =  Ptr Nonce32+    -> Ptr Msg32+    -> Ptr SecKey32+    -> Ptr Algo16+    -> Ptr a       -- ^ extra data+    -> CUInt       -- ^ attempt+    -> Ret++verify :: CtxFlags+verify = CtxFlags 0x0101++sign :: CtxFlags+sign = CtxFlags 0x0201++signVerify :: CtxFlags+signVerify = CtxFlags 0x0301++compressed :: SerFlags+compressed = SerFlags 0x0102++uncompressed :: SerFlags+uncompressed = SerFlags 0x0002++useByteString :: ByteString -> ((Ptr CUChar, CSize) -> IO a) -> IO a+useByteString bs f =+    BS.useAsCStringLen bs $ \(b, l) -> f (castPtr b, fromIntegral l)++packByteString :: (Ptr CUChar, CSize) -> IO ByteString+packByteString (b, l) = BS.packCStringLen (castPtr b, fromIntegral l)++instance Storable PubKey64 where+    sizeOf _ = 64+    alignment _ = 1+    peek p = PubKey64 . toShort <$> packByteString (castPtr p, 64)+    poke p (PubKey64 k) = useByteString (fromShort k) $+        \(b, _) -> copyArray (castPtr p) b 64++instance Storable Sig64 where+    sizeOf _ = 64+    alignment _ = 1+    peek p = Sig64 . toShort <$> packByteString (castPtr p, 64)+    poke p (Sig64 k) = useByteString (fromShort k) $+        \(b, _) -> copyArray (castPtr p) b 64++instance Storable CompactSig where+    sizeOf _ = 64+    alignment _ = 1+    peek p = do+        bs <- BS.packCStringLen (castPtr p, 64)+        let (s, r) = BS.splitAt 32 bs+        guard $ BS.length s == 32+        guard $ BS.length r == 32+        return CompactSig { getCompactSigR = toShort r+                          , getCompactSigS = toShort s+                          }+    poke p CompactSig{..} =+        useByteString bs $ \(b, _) -> copyArray (castPtr p) b 64+      where+        bs = fromShort getCompactSigS `BS.append` fromShort getCompactSigR++instance Serialize CompactSig where+    get = do+        s <- Get.getByteString 32+        r <- Get.getByteString 32+        return CompactSig { getCompactSigR = toShort r+                          , getCompactSigS = toShort s+                          }+    put (CompactSig r s) = do+        Put.putShortByteString s+        Put.putShortByteString r++instance Storable RecSig65 where+    sizeOf _ = 65+    alignment _ = 1+    peek p = RecSig65 . toShort <$> packByteString (castPtr p, 65)+    poke p (RecSig65 k) = useByteString (fromShort k) $+        \(b, _) -> copyArray (castPtr p) b 65++instance Storable CompactRecSig where+    sizeOf _ = 65+    alignment _ = 1+    peek p = do+        bs <- BS.packCStringLen (castPtr p, 65)+        let (s, r) = BS.splitAt 32 $ BS.take 64 bs+            v = BS.last bs+        return CompactRecSig { getCompactRecSigR = toShort r+                             , getCompactRecSigS = toShort s+                             , getCompactRecSigV = v+                             }+    poke p CompactRecSig{..} =+        useByteString bs $ \(b, _) -> copyArray (castPtr p) b 65+      where+        bs = fromShort getCompactRecSigS `BS.append`+             fromShort getCompactRecSigR `BS.snoc`+             getCompactRecSigV++instance Serialize CompactRecSig where+    get = do+        s <- Get.getByteString 32+        r <- Get.getByteString 32+        v <- Get.getWord8+        return CompactRecSig { getCompactRecSigR = toShort r+                             , getCompactRecSigS = toShort s+                             , getCompactRecSigV = v+                             }+    put (CompactRecSig r s v) = do+        Put.putShortByteString s+        Put.putShortByteString r+        Put.putWord8 v++instance Storable Msg32 where+    sizeOf _ = 32+    alignment _ = 1+    peek p = Msg32 . toShort <$> packByteString (castPtr p, 32)+    poke p (Msg32 k) = useByteString (fromShort k) $+        \(b, _) -> copyArray (castPtr p) b 32++instance Storable Seed32 where+    sizeOf _ = 32+    alignment _ = 1+    peek p = Seed32 . toShort <$> packByteString (castPtr p, 32)+    poke p (Seed32 k) = useByteString (fromShort k) $+        \(b, _) -> copyArray (castPtr p) b 32++instance Storable SecKey32 where+    sizeOf _ = 32+    alignment _ = 1+    peek p = SecKey32 . toShort <$> packByteString (castPtr p, 32)+    poke p (SecKey32 k) = useByteString (fromShort k) $+        \(b, _) -> copyArray (castPtr p) b 32++instance Storable Tweak32 where+    sizeOf _ = 32+    alignment _ = 1+    peek p = Tweak32 . toShort <$> packByteString (castPtr p, 32)+    poke p (Tweak32 k) = useByteString (fromShort k) $+        \(b, _) -> copyArray (castPtr p) b 32++instance Storable Nonce32 where+    sizeOf _ = 32+    alignment _ = 1+    peek p = Nonce32 . toShort <$> packByteString (castPtr p, 32)+    poke p (Nonce32 k) = useByteString (fromShort k) $+        \(b, _) -> copyArray (castPtr p) b 32++instance Storable Algo16 where+    sizeOf _ = 16+    alignment _ = 1+    peek p = Algo16 . toShort <$> packByteString (castPtr p, 16)+    poke p (Algo16 k) = useByteString (fromShort k) $+        \(b, _) -> copyArray (castPtr p) b 16++isSuccess :: Ret -> Bool+isSuccess (Ret 0) = False+isSuccess (Ret 1) = True+isSuccess _       = undefined++{-# NOINLINE fctx #-}+fctx :: ForeignPtr Ctx+fctx = unsafePerformIO $ do+    x <- contextCreate signVerify+    e <- getEntropy 32+    ret <- alloca $ \s -> poke s (Seed32 (toShort e)) >> contextRandomize x s+    unless (isSuccess ret) $ error "failed to randomize context"+    newForeignPtr contextDestroy x++{-# INLINE withContext #-}+withContext :: (Ptr Ctx -> IO a) -> a+withContext f = unsafeDupablePerformIO (withForeignPtr fctx f)++foreign import ccall+    "secp256k1.h secp256k1_context_create"+    contextCreate+    :: CtxFlags+    -> IO (Ptr Ctx)++foreign import ccall+    "secp256k1.h secp256k1_context_clone"+    contextClone+    :: Ptr Ctx+    -> IO (Ptr Ctx)++foreign import ccall+    "secp256k1.h &secp256k1_context_destroy"+    contextDestroy+    :: FunPtr (Ptr Ctx -> IO ())++foreign import ccall+    "secp256k1.h secp256k1_context_set_illegal_callback"+    setIllegalCallback+    :: Ptr Ctx+    -> FunPtr (CString -> Ptr a -> IO ()) -- ^ message, data+    -> Ptr a                              -- ^ data+    -> IO ()++foreign import ccall+    "secp256k1.h secp256k1_context_set_error_callback"+    setErrorCallback+    :: Ptr Ctx+    -> FunPtr (CString -> Ptr a -> IO ()) -- ^ message, data+    -> Ptr a                              -- ^ data+    -> IO ()++foreign import ccall+    "secp256k1.h secp256k1_ec_pubkey_parse"+    ecPubKeyParse+    :: Ptr Ctx+    -> Ptr PubKey64+    -> Ptr CUChar -- ^ encoded public key array+    -> CSize      -- ^ size of encoded public key array+    -> IO Ret++foreign import ccall+    "secp256k1.h secp256k1_ec_pubkey_serialize"+    ecPubKeySerialize+    :: Ptr Ctx+    -> Ptr CUChar -- ^ array for encoded public key, must be large enough+    -> Ptr CSize  -- ^ size of encoded public key, will be updated+    -> Ptr PubKey64+    -> SerFlags+    -> IO Ret++foreign import ccall+    "secp256k1.h secp256k1_ecdsa_signature_parse_compact"+    ecdsaSignatureParseCompact+    :: Ptr Ctx+    -> Ptr Sig64+    -> Ptr CompactSig+    -> IO Ret+++foreign import ccall+    "secp256k1.h secp256k1_ecdsa_signature_parse_der"+    ecdsaSignatureParseDer+    :: Ptr Ctx+    -> Ptr Sig64+    -> Ptr CUChar -- ^ encoded DER signature+    -> CSize      -- ^ size of encoded signature+    -> IO Ret++foreign import ccall+    "secp256k1.h secp256k1_ecdsa_signature_serialize_der"+    ecdsaSignatureSerializeDer+    :: Ptr Ctx+    -> Ptr CUChar -- ^ array for encoded signature, must be large enough+    -> Ptr CSize  -- ^ size of encoded signature, will be updated+    -> Ptr Sig64+    -> IO Ret++foreign import ccall+    "secp256k1.h secp256k1_ecdsa_signature_serialize_compact"+    ecdsaSignatureSerializeCompact+    :: Ptr Ctx+    -> Ptr CompactSig+    -> Ptr Sig64+    -> IO Ret++foreign import ccall+    "secp256k1.h secp256k1_ecdsa_verify"+    ecdsaVerify+    :: Ptr Ctx+    -> Ptr Sig64+    -> Ptr Msg32+    -> Ptr PubKey64+    -> IO Ret++foreign import ccall+    "secp256k1.h secp256k1_ecdsa_signature_normalize"+    ecdsaSignatureNormalize+    :: Ptr Ctx+    -> Ptr Sig64 -- ^ output+    -> Ptr Sig64 -- ^ input+    -> IO Ret++foreign import ccall+    "secp256k1.h secp256k1_ecdsa_sign"+    ecdsaSign+    :: Ptr Ctx+    -> Ptr Sig64+    -> Ptr Msg32+    -> Ptr SecKey32+    -> FunPtr (NonceFunction a)+    -> Ptr a -- ^ nonce data+    -> IO Ret++foreign import ccall+    "secp256k1.h secp256k1_ec_seckey_verify"+    ecSecKeyVerify+    :: Ptr Ctx+    -> Ptr SecKey32+    -> IO Ret++foreign import ccall+    "secp256k1.h secp256k1_ec_pubkey_create"+    ecPubKeyCreate+    :: Ptr Ctx+    -> Ptr PubKey64+    -> Ptr SecKey32+    -> IO Ret++foreign import ccall+    "secp256k1.h secp256k1_ec_privkey_tweak_add"+    ecSecKeyTweakAdd+    :: Ptr Ctx+    -> Ptr SecKey32+    -> Ptr Tweak32+    -> IO Ret++foreign import ccall+    "secp256k1.h secp256k1_ec_pubkey_tweak_add"+    ecPubKeyTweakAdd+    :: Ptr Ctx+    -> Ptr PubKey64+    -> Ptr Tweak32+    -> IO Ret++foreign import ccall+    "secp256k1.h secp256k1_ec_privkey_tweak_mul"+    ecSecKeyTweakMul+    :: Ptr Ctx+    -> Ptr SecKey32+    -> Ptr Tweak32+    -> IO Ret++foreign import ccall+    "secp256k1.h secp256k1_ec_pubkey_tweak_mul"+    ecPubKeyTweakMul+    :: Ptr Ctx+    -> Ptr PubKey64+    -> Ptr Tweak32+    -> IO Ret++foreign import ccall+    "secp256k1.h secp256k1_context_randomize"+    contextRandomize+    :: Ptr Ctx+    -> Ptr Seed32+    -> IO Ret++foreign import ccall+    "secp256k1.h secp256k1_ec_pubkey_combine"+    ecPubKeyCombine+    :: Ptr Ctx+    -> Ptr PubKey64 -- ^ pointer to public key storage+    -> Ptr (Ptr PubKey64) -- ^ pointer to array of public keys+    -> CInt -- ^ number of public keys+    -> IO Ret++foreign import ccall+    "secp256k1_recovery.h secp256k1_ecdsa_recoverable_signature_parse_compact"+    ecdsaRecoverableSignatureParseCompact+    :: Ptr Ctx+    -> Ptr RecSig65+    -> Ptr CompactSig+    -> CInt+    -> IO Ret++foreign import ccall+    "secp256k1_recovery.h secp256k1_ecdsa_recoverable_signature_convert"+    ecdsaRecoverableSignatureConvert+    :: Ptr Ctx+    -> Ptr Sig64+    -> Ptr RecSig65+    -> IO Ret++foreign import ccall+    "secp256k1_recovery.h secp256k1_ecdsa_recoverable_signature_serialize_compact"+    ecdsaRecoverableSignatureSerializeCompact+    :: Ptr Ctx+    -> Ptr CompactSig+    -> Ptr CInt+    -> Ptr RecSig65+    -> IO Ret++foreign import ccall+    "secp256k1_recovery.h secp256k1_ecdsa_sign_recoverable"+    ecdsaSignRecoverable+    :: Ptr Ctx+    -> Ptr RecSig65+    -> Ptr Msg32+    -> Ptr SecKey32+    -> FunPtr (NonceFunction a)+    -> Ptr a -- ^ nonce data+    -> IO Ret++foreign import ccall+    "secp256k1_recovery.h secp256k1_ecdsa_recover"+    ecdsaRecover+    :: Ptr Ctx+    -> Ptr PubKey64+    -> Ptr RecSig65+    -> Ptr Msg32+    -> IO Ret
+ test/Crypto/Secp256k1/InternalSpec.hs view
@@ -0,0 +1,417 @@+{-# LANGUAGE OverloadedStrings #-}+module Crypto.Secp256k1.InternalSpec (spec) where++import           Control.Monad+import           Control.Monad.Trans+import           Crypto.Secp256k1.Internal+import           Data.ByteString           (ByteString, packCStringLen,+                                            useAsCStringLen)+import qualified Data.ByteString.Base16    as B16+import           Data.ByteString.Short     (toShort)+import           Foreign+import           System.Entropy+import           Test.Hspec+import           Test.HUnit                (Assertion, assertBool, assertEqual)++spec :: Spec+spec = do+    describe "housekeeping" $ do+        it "creates context" createContextTest+        it "randomizez context" randomizeContextTest+        it "clones context" cloneContextTest+    describe "serialization" $ do+        it "parses public key" ecPubkeyParseTest+        it "serializes public key" ecPubKeySerializeTest+        it "handles storable public key" pubkeyStorableTest+        it "handles storable signature" signatureStorableTest+        it "parses DER signature" ecdsaSignatureParseDerTest+        it "serializes DER signature" ecdsaSignatureSerializeDerTest+    describe "signatures" $ do+        it "verifies signature" ecdsaVerifyTest+        -- TODO:+        -- , testCase "RFC6979 nonce function"   nonce_function_rfc6979_test+        it "signs message" ecdsaSignTest+    describe "secret keys" $ do+        it "verifies secret key" ecSecKeyVerifyTest+        it "creates public key" ecPubkeyCreateTest+        it "adds secret key" ecSecKeyTweakAddTest+        it "multiplies secret key" ecSecKeyTweakMulTest+    describe "public keys" $ do+        it "adds public key" ecPubKeyTweakAddTest+        it "multiplies public key" ecPubKeyTweakMulTest+        it "combines public keys" ecPubKeyCombineTest++withEntropy :: (Ptr Seed32 -> IO a) -> IO a+withEntropy f =+    getEntropy 32 >>= \e ->+    alloca $ \s ->+    poke s (Seed32 (toShort e)) >> f s++createContextTest :: Assertion+createContextTest = do+    context_ptr <- liftIO $ contextCreate signVerify+    assertBool "context not null" $ context_ptr /= nullPtr++randomizeContextTest :: Assertion+randomizeContextTest = do+    ret <- liftIO $ contextCreate sign >>= \x ->+        withEntropy (contextRandomize x)+    assertBool "context randomized" $ isSuccess ret++cloneContextTest :: Assertion+cloneContextTest = do+    (x1, x2) <- liftIO $ do+        x1 <- contextCreate signVerify+        ret <- withEntropy $ contextRandomize x1+        unless (isSuccess ret) $ error "failed to randomize context"+        x2 <- contextClone x1+        return (x1, x2)+    assertBool "original context not null" $ x1 /= nullPtr+    assertBool "cloned context not null" $ x2 /= nullPtr+    assertBool "context ptrs different" $ x1 /= x2++ecPubkeyParseTest :: Assertion+ecPubkeyParseTest = do+    ret <- liftIO $ useAsCStringLen der $ \(i, il) -> do+        x <- contextCreate verify+        alloca $ \pubkey ->+            ecPubKeyParse x pubkey (castPtr i) (fromIntegral il)+    assertBool "parsed public key" (isSuccess ret)+  where+    der = fst $ B16.decode+        "03dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705"++ecPubKeySerializeTest :: Assertion+ecPubKeySerializeTest = do+    (ret, dec) <- liftIO $ useAsCStringLen der $ \(i, il) ->+        alloca $ \k -> alloca $ \ol -> allocaBytes 72 $ \o -> do+        poke ol 72+        x <- contextCreate verify+        ret1 <-+            ecPubKeyParse x k (castPtr i) (fromIntegral il)+        unless (isSuccess ret1) $ error "failed to parse pubkey"+        ret2 <- ecPubKeySerialize+            x o ol k compressed+        len <- fromIntegral <$> peek ol+        decoded <- packCStringLen (castPtr o, len)+        return (ret2, decoded)+    assertBool  "serialized public key successfully" $ isSuccess ret+    assertEqual "public key matches" der dec+  where+    der = fst $ B16.decode+        "03dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705"++pubkeyStorableTest :: Assertion+pubkeyStorableTest = do+    (pk1, pk2, dec) <- liftIO $ useAsCStringLen der $ \(i, il) -> do+        x <- contextCreate verify+        pk1 <- alloca $ \pk -> do+            ret <-+                ecPubKeyParse x pk (castPtr i) (fromIntegral il)+            unless (isSuccess ret) $ error "failed to parse pubkey"+            peek pk+        (pk2, dec) <- alloca $ \pk -> alloca $ \ol -> allocaBytes 72 $ \o -> do+            poke ol 72+            poke pk pk1+            ret <-+                ecPubKeySerialize x o ol pk compressed+            unless (isSuccess ret) $ error "failed to serialize pubkey"+            len <- fromIntegral <$> peek ol+            dec <- packCStringLen (castPtr o, len)+            pk2 <- peek pk+            return (pk2, dec)+        return (pk1, pk2, dec)+    assertEqual "poke/peek public key" pk1 pk2+    assertEqual "public key matches" der dec+  where+    der = fst $ B16.decode+        "03dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705"++signatureStorableTest :: Assertion+signatureStorableTest = do+    (sig, ret) <- liftIO $ do+        x <- contextCreate verify+        g <- alloca $ \pc -> alloca $ \pg -> do+            poke pc cpt+            ret <- ecdsaSignatureParseCompact x pg (castPtr pc)+            unless (isSuccess ret) $ error "failed to parse signature"+            peek pg+        alloca $ \pc -> alloca $ \pg -> do+            poke pg g+            ret <- ecdsaSignatureSerializeCompact x pc pg+            c <- peek pc+            return (c, ret)+    assertBool "successful serialization" (isSuccess ret)+    assertEqual "signatures match" cpt sig+  where+    cpt = CompactSig+        (toShort $ fst $ B16.decode "f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37fb22")+        (toShort $ fst $ B16.decode "6f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e3045")++ecdsaSignatureParseDerTest :: Assertion+ecdsaSignatureParseDerTest = do+    ret <- liftIO $ useAsCStringLen der $ \(d, dl) -> alloca $ \s -> do+        x <- contextCreate verify+        ecdsaSignatureParseDer x s (castPtr d) (fromIntegral dl)+    assertBool "parsed signature successfully" $ isSuccess ret+  where+    der = fst $ B16.decode+        "3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\+        \fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\+        \45"++parseDer :: Ptr Ctx -> ByteString -> IO Sig64+parseDer x bs = useAsCStringLen bs $ \(d, dl) -> alloca $ \s -> do+    ret <- ecdsaSignatureParseDer x s (castPtr d) (fromIntegral dl)+    unless (isSuccess ret) $ error "could not parse DER"+    peek s++ecdsaSignatureSerializeDerTest :: Assertion+ecdsaSignatureSerializeDerTest = do+    (ret, enc) <- liftIO $ do+        x <- contextCreate verify+        sig <- parseDer x der+        alloca $ \s -> alloca $ \ol -> allocaBytes 72 $ \o -> do+            poke ol 72+            poke s sig+            ret <-+                ecdsaSignatureSerializeDer x o ol s+            len <- fromIntegral <$> peek ol+            enc <- packCStringLen (castPtr o, len)+            return (ret, enc)+    assertBool  "serialization successful" $ isSuccess ret+    assertEqual "signatures match" der enc+  where+    der = fst $ B16.decode+        "3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\+        \fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\+        \45"++ecdsaVerifyTest :: Assertion+ecdsaVerifyTest = do+    ret <- liftIO $ do+        x <- contextCreate verify+        sig <- parseDer x der+        pk <- useAsCStringLen pub $ \(p, pl) -> alloca $ \k -> do+            ret <-+                ecPubKeyParse x k (castPtr p) (fromIntegral pl)+            unless (isSuccess ret) $ error "could not parse public key"+            peek k+        alloca $ \m -> alloca $ \k -> alloca $ \s -> do+            poke m msg+            poke k pk+            poke s sig+            ecdsaVerify x s m k+    assertBool "signature valid" $ isSuccess ret+  where+    der = fst $ B16.decode+        "3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\+        \fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\+        \45"+    pub = fst $ B16.decode+        "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\+        \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"+    msg = Msg32 $ toShort $ fst $ B16.decode+        "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"++signCtx :: IO (Ptr Ctx)+signCtx = contextCreate sign >>= \c ->+    withEntropy (contextRandomize c) >>= \r ->+        unless (isSuccess r) (error "failed to randomize context") >> return c++ecdsaSignTest :: Assertion+ecdsaSignTest = do+    (ret, sig) <- liftIO $ do+        x <- signCtx+        alloca $ \s -> alloca $ \m -> alloca $ \k -> alloca $ \ol ->+            allocaBytes 72 $ \o -> do+                poke ol 72+                poke m msg+                poke k key+                ret1 <-+                    -- TODO:+                    -- ecdsaSign x s m k nonce_function_default nullPtr+                    ecdsaSign x s m k nullFunPtr nullPtr+                ret2 <- ecdsaSignatureSerializeDer x o ol s+                unless (isSuccess ret2) $ error "could not serialize signature"+                len <- peek ol+                sig <- packCStringLen (castPtr o, fromIntegral len)+                return (ret1, sig)+    assertBool "successful signing" $ isSuccess ret+    assertEqual "signature matches" sig der+  where+    msg = Msg32 $ toShort $ fst $ B16.decode+        "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"+    key = SecKey32 $ toShort $ fst $ B16.decode+        "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"+    der = fst $ B16.decode+        "3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\+        \fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\+        \45"++ecSecKeyVerifyTest :: Assertion+ecSecKeyVerifyTest = do+    ret <- liftIO $ alloca $ \k -> do+        poke k key+        x <- signCtx+        ecSecKeyVerify x k+    assertBool "valid secret key" $ isSuccess ret+  where+    key = SecKey32 $ toShort $ fst $ B16.decode+        "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"++ecPubkeyCreateTest :: Assertion+ecPubkeyCreateTest = do+    (ret, pk) <- liftIO $ alloca $ \p -> alloca $ \k -> do+        poke k key+        x <- signCtx+        ret <- ecPubKeyCreate x p k+        allocaBytes 65 $ \o -> alloca $ \ol -> do+            poke ol 65+            rets <- ecPubKeySerialize+                x o ol p uncompressed+            unless (isSuccess rets) $ error "failed to serialive public key"+            len <- fromIntegral <$> peek ol+            pk <- packCStringLen (castPtr o, len)+            return (ret, pk)+    assertBool "successful pubkey creation" $ isSuccess ret+    assertEqual "public key matches" pub pk+  where+    key = SecKey32 $ toShort $ fst $ B16.decode+        "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"+    pub = fst $ B16.decode+        "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\+        \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"++ecSecKeyTweakAddTest :: Assertion+ecSecKeyTweakAddTest = do+    (ret, tweaked) <- liftIO $ do+        x <- signCtx+        alloca $ \w -> alloca $ \k -> do+            poke w tweak+            poke k key+            ret <- ecSecKeyTweakAdd x k w+            tweaked <- peek k+            return (ret, tweaked)+    assertBool "successful secret key tweak" $ isSuccess ret+    assertEqual "tweaked keys match" expected tweaked+  where+    key = SecKey32 $ toShort $ fst $ B16.decode+        "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"+    tweak = Tweak32 $ toShort $ fst $ B16.decode+        "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"+    expected = SecKey32 $ toShort $ fst $ B16.decode+        "ec1e3ce1cefa18a671d51125e2b249688d934b0e28f5d1665384d9b02f929059"++ecSecKeyTweakMulTest :: Assertion+ecSecKeyTweakMulTest = do+    (ret, tweaked) <- liftIO $ do+        x <- contextCreate sign+        retr <- withEntropy $ contextRandomize x+        unless (isSuccess retr) $ error "failed to randomize context"+        alloca $ \w -> alloca $ \k -> do+            poke w tweak+            poke k key+            ret <- ecSecKeyTweakMul x k w+            tweaked <- peek k+            return (ret, tweaked)+    assertBool "successful secret key tweak" $ isSuccess ret+    assertEqual "tweaked keys match" expected tweaked+  where+    key = SecKey32 $ toShort $ fst $ B16.decode+        "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"+    tweak = Tweak32 $ toShort $ fst $ B16.decode+        "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"+    expected = SecKey32 $ toShort $ fst $ B16.decode+        "a96f5962493acb179f60a86a9785fc7a30e0c39b64c09d24fe064d9aef15e4c0"++serializeKey :: Ptr Ctx -> Ptr PubKey64 -> IO ByteString+serializeKey x p = allocaBytes 72 $ \d -> alloca $ \dl -> do+    poke dl 72+    ret <- ecPubKeySerialize x d dl p uncompressed+    unless (isSuccess ret) $ error "could not serialize public key"+    len <- peek dl+    packCStringLen (castPtr d, fromIntegral len)++parseKey :: Ptr Ctx -> ByteString -> IO PubKey64+parseKey x bs = alloca $ \p -> useAsCStringLen bs $ \(d, dl) -> do+    ret <- ecPubKeyParse x p (castPtr d) (fromIntegral dl)+    unless (isSuccess ret) $ error "could not parse public key"+    peek p++ecPubKeyTweakAddTest :: Assertion+ecPubKeyTweakAddTest = do+    (ret, tweaked) <- liftIO $ do+        x <- contextCreate verify+        pk <- parseKey x pub+        alloca $ \w -> alloca $ \p -> do+            poke w tweak+            poke p pk+            ret <- ecPubKeyTweakAdd x p w+            tweaked <- serializeKey x p+            return (ret, tweaked)+    assertBool "successful secret key tweak" $ isSuccess ret+    assertEqual "tweaked keys match" expected tweaked+  where+    pub = fst $ B16.decode+        "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\+        \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"+    tweak = Tweak32 $ toShort $ fst $ B16.decode+        "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"+    expected = fst $ B16.decode+        "04441c3982b97576646e0df0c96736063df6b42f2ee566d13b9f6424302d1379e518fd\+        \c87a14c5435bff7a5db4552042cb4120c6b86a4bbd3d0643f3c14ad01368"++ecPubKeyTweakMulTest :: Assertion+ecPubKeyTweakMulTest = do+    (ret, tweaked) <- liftIO $ do+        x <- contextCreate verify+        pk <- parseKey x pub+        alloca $ \w -> alloca $ \p -> do+            poke w tweak+            poke p pk+            ret <- ecPubKeyTweakMul x p w+            tweaked <- serializeKey x p+            return (ret, tweaked)+    assertBool "successful secret key tweak" $ isSuccess ret+    assertEqual "tweaked keys match" expected tweaked+  where+    pub = fst $ B16.decode+        "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\+        \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"+    tweak = Tweak32 $ toShort $ fst $ B16.decode+        "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"+    expected = fst $ B16.decode+        "04f379dc99cdf5c83e433defa267fbb3377d61d6b779c06a0e4ce29ae3ff5353b12ae4\+        \9c9d07e7368f2ba5a446c203255ce912322991a2d6a9d5d5761c61ed1845"++ecPubKeyCombineTest :: Assertion+ecPubKeyCombineTest = do+    (ret, com) <- liftIO $ alloca $ \p1 -> alloca $ \p2 -> alloca $ \p3 ->+        allocaArray 3 $ \a -> alloca $ \p -> do+            x <- contextCreate verify+            parse x pub1 p1+            parse x pub2 p2+            parse x pub3 p3+            pokeArray a [p1, p2, p3]+            ret <- ecPubKeyCombine x p a 3+            com <- serializeKey x p+            return (ret, com)+    assertBool "successful key combination" $ isSuccess ret+    assertEqual "combined keys match" expected com+  where+    parse x pub p = useAsCStringLen pub $ \(d, dl) -> do+        ret <- ecPubKeyParse x p (castPtr d) (fromIntegral dl)+        unless (isSuccess ret) $ error "could not parse public key"+    pub1 = fst $ B16.decode+        "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\+        \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"+    pub2 = fst $ B16.decode+        "0487d82042d93447008dfe2af762068a1e53ff394a5bf8f68a045fa642b99ea5d153f5\+        \77dd2dba6c7ae4cfd7b6622409d7edd2d76dd13a8092cd3af97b77bd2c77"+    pub3 = fst $ B16.decode+        "049b101edcbe1ee37ff6b2318526a425b629e823d7d8d9154417880595a28000ee3feb\+        \d908754b8ce4e491aa6fe488b41fb5d4bb3788e33c9ff95a7a9229166d59"+    expected = fst $ B16.decode+        "043d9a7ec70011efc23c33a7e62d2ea73cca87797e3b659d93bea6aa871aebde56c3bc\+        \6134ca82e324b0ab9c0e601a6d2933afe7fb5d9f3aae900f5c5dc6e362c8"
+ test/Crypto/Secp256k1Spec.hs view
@@ -0,0 +1,243 @@+module Crypto.Secp256k1Spec (spec) where++import           Crypto.Secp256k1+import qualified Data.ByteString.Base16  as B16+import qualified Data.ByteString.Char8   as B8+import           Data.Maybe              (fromMaybe)+import           Data.Serialize+import           Data.String             (fromString)+import           Data.String.Conversions (cs)+import           Test.Hspec+import           Test.HUnit              (Assertion, assertEqual)+import           Test.QuickCheck         (Property, property, (==>))++spec :: Spec+spec = do+    describe "signatures" $ do+        it "signs message" $ property $ signMsgTest+        it "recovers key from signed message" $ property $ signRecMsgTest+        it "detects bad signature" $ property $ badSignatureTest+        it "detects bad recoverable signature" $ property $ badRecSignatureTest+        it "normalizes signatures" $ property $ normalizeSigTest+        it "recovers public keys" $ property $ recoverTest+        it "Bad recover public keys" $ property $ badRecoverTest+    describe "serialization" $ do+        it "serializes public key" $ property $ serializePubKeyTest+        it "serializes DER signature" $ property $ serializeSigTest+        it "serializes compact signature" $ property $ serializeCompactSigTest+        it "serializes compact recoverable signature" $+            property $ serializeCompactRecSigTest+        it "serialize secret key" $ property $ serializeSecKeyTest+        it "shows and reads public key" $+            property $ (showRead :: PubKey -> Bool)+        it "shows and reads secret key" $+            property $ (showRead :: SecKey -> Bool)+        it "shows and reads tweak" $+            property $ (showReadTweak :: SecKey -> Bool)+        it "shows and reads signature" $+            property $ (showReadSig :: (SecKey, Msg) -> Bool)+        it "shows and reads recoverable signature" $+            property $ (showReadRecSig :: (SecKey, Msg) -> Bool)+        it "shows and reads message" $ property $ (showRead :: Msg -> Bool)+        it "reads public key from string" $ property $ isStringPubKey+        it "reads secret key from string" $ property $ isStringSecKey+        it "reads signature from string" $ property $ isStringSig+        it "reads recoverable signature from string" $ property $ isStringRecSig+        it "reads message from string" $ property $ isStringMsg+        it "reads tweak from string" $ property $ isStringTweak+    describe "tweaks" $ do+        it "add secret key" $ property $ tweakAddSecKeyTest+        it "multiply secret key" $ property $ tweakMulSecKeyTest+        it "add public key" $ property $ tweakAddPubKeyTest+        it "multiply public key" $ property $ tweakMulPubKeyTest+        it "combine public keys" $ property $ combinePubKeyTest++isStringPubKey :: (PubKey, Bool) -> Bool+isStringPubKey (k, c) = k == fromString (cs hex) where+    hex = B16.encode $ exportPubKey c k++isStringSig :: (SecKey, Msg) -> Bool+isStringSig (k, m) = g == fromString (cs hex) where+    g = signMsg k m+    hex = B16.encode $ exportSig g++isStringRecSig :: (SecKey, Msg) -> Bool+isStringRecSig (k, m) = g == fromString (cs hex) where+    g = signRecMsg k m+    hex = B16.encode . encode $ exportCompactRecSig g++isStringMsg :: Msg -> Bool+isStringMsg m = m == fromString (cs m') where+    m' = B16.encode $ getMsg m++isStringSecKey :: SecKey -> Bool+isStringSecKey k = k == fromString (cs hex) where+    hex = B16.encode $ getSecKey k++isStringTweak :: SecKey -> Bool+isStringTweak k = t == fromString (cs hex) where+    t = fromMaybe e . tweak $ getSecKey k+    hex = B16.encode $ getTweak t+    e = error "Could not extract tweak from secret key"++showReadTweak :: SecKey -> Bool+showReadTweak k = showRead t where+    t = tweak $ getSecKey k++showReadSig :: (SecKey, Msg) -> Bool+showReadSig (k, m) = showRead sig where+    sig = signMsg k m++showReadRecSig :: (SecKey, Msg) -> Bool+showReadRecSig (k, m) = showRead recSig where+    recSig = signRecMsg k m++showRead :: (Show a, Read a, Eq a) => a -> Bool+showRead x = read (show x) == x++signMsgTest :: (Msg, SecKey) -> Bool+signMsgTest (fm, fk) = verifySig fp fg fm where+    fp = derivePubKey fk+    fg = signMsg fk fm++signRecMsgTest :: (Msg, SecKey) -> Bool+signRecMsgTest (fm, fk) = verifySig fp fg fm where+    fp = derivePubKey fk+    fg = convertRecSig $ signRecMsg fk fm++recoverTest :: (Msg, SecKey) -> Bool+recoverTest (fm, fk) = recover fg fm == Just fp where+    fp = derivePubKey fk+    fg = signRecMsg fk fm++badRecoverTest :: (Msg, SecKey, Msg) -> Property+badRecoverTest (fm, fk, fm') =+  fm' /= fm ==> fp' /= Nothing ==> fp' /= Just fp+  where+    fg  = signRecMsg fk fm+    fp  = derivePubKey fk+    fp' = recover fg fm'++badSignatureTest :: (Msg, SecKey, PubKey) -> Bool+badSignatureTest (fm, fk, fp) = not $ verifySig fp fg fm where+    fg = signMsg fk fm++badRecSignatureTest :: (Msg, SecKey, PubKey) -> Bool+badRecSignatureTest (fm, fk, fp) = not $ verifySig fp fg fm where+    fg = convertRecSig $ signRecMsg fk fm++normalizeSigTest :: (Msg, SecKey) -> Bool+normalizeSigTest (fm, fk) = not norm && sig == fg where+    fg = signMsg fk fm+    (sig, norm) = normalizeSig fg++serializePubKeyTest :: (PubKey, Bool) -> Bool+serializePubKeyTest (fp, b) =+    case importPubKey $ exportPubKey b fp of+        Just fp' -> fp == fp'+        Nothing  -> False++serializeSigTest :: (Msg, SecKey) -> Bool+serializeSigTest (fm, fk) =+    case importSig $ exportSig fg of+        Just fg' -> fg == fg'+        Nothing  -> False+  where+    fg = signMsg fk fm++serializeCompactSigTest :: (Msg, SecKey) -> Bool+serializeCompactSigTest (fm, fk) =+    case importCompactSig $ exportCompactSig fg of+        Just fg' -> fg == fg'+        Nothing  -> False+  where+    fg = signMsg fk fm++serializeCompactRecSigTest :: (Msg, SecKey) -> Bool+serializeCompactRecSigTest (fm, fk) =+    case importCompactRecSig $ exportCompactRecSig fg of+        Just fg' -> fg == fg'+        Nothing  -> False+  where+    fg = signRecMsg fk fm++serializeSecKeyTest :: SecKey -> Bool+serializeSecKeyTest fk =+    case secKey $ getSecKey fk of+        Just fk' -> fk == fk'+        Nothing  -> False++tweakAddSecKeyTest :: Assertion+tweakAddSecKeyTest =+    assertEqual "tweaked keys match" expected tweaked+  where+    tweaked = do+        key <- secKey $ fst $ B16.decode $ B8.pack+            "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"+        twk <- tweak $ fst $ B16.decode $ B8.pack+            "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"+        tweakAddSecKey key twk+    expected = secKey $ fst $ B16.decode $ B8.pack+        "ec1e3ce1cefa18a671d51125e2b249688d934b0e28f5d1665384d9b02f929059"++tweakMulSecKeyTest :: Assertion+tweakMulSecKeyTest =+    assertEqual "tweaked keys match" expected tweaked+  where+    tweaked = do+        key <- secKey $ fst $ B16.decode $ B8.pack+            "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"+        twk <- tweak $ fst $ B16.decode $ B8.pack+            "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"+        tweakMulSecKey key twk+    expected = secKey $ fst $ B16.decode $ B8.pack+        "a96f5962493acb179f60a86a9785fc7a30e0c39b64c09d24fe064d9aef15e4c0"++tweakAddPubKeyTest :: Assertion+tweakAddPubKeyTest =+    assertEqual "tweaked keys match" expected tweaked+  where+    tweaked = do+        pub <- importPubKey $ fst $ B16.decode $ B8.pack+            "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705\+            \12213d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"+        twk <- tweak $ fst $ B16.decode $ B8.pack+            "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"+        tweakAddPubKey pub twk+    expected = importPubKey $ fst $ B16.decode $ B8.pack+        "04441c3982b97576646e0df0c96736063df6b42f2ee566d13b9f6424302d1379e518fd\+        \c87a14c5435bff7a5db4552042cb4120c6b86a4bbd3d0643f3c14ad01368"++tweakMulPubKeyTest :: Assertion+tweakMulPubKeyTest =+    assertEqual "tweaked keys match" expected tweaked+  where+    tweaked = do+        pub <- importPubKey $ fst $ B16.decode $ B8.pack+            "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705\+            \12213d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"+        twk <- tweak $ fst $ B16.decode $ B8.pack+            "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"+        tweakMulPubKey pub twk+    expected = importPubKey $ fst $ B16.decode $ B8.pack+        "04f379dc99cdf5c83e433defa267fbb3377d61d6b779c06a0e4ce29ae3ff5353b12ae4\+        \9c9d07e7368f2ba5a446c203255ce912322991a2d6a9d5d5761c61ed1845"++combinePubKeyTest :: Assertion+combinePubKeyTest =+    assertEqual "combined keys match" expected combined+  where+    combined = do+        pub1 <- importPubKey $ fst $ B16.decode $ B8.pack+            "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705\+            \12213d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"+        pub2 <- importPubKey $ fst $ B16.decode $ B8.pack+            "0487d82042d93447008dfe2af762068a1e53ff394a5bf8f68a045fa642b99ea5d1\+            \53f577dd2dba6c7ae4cfd7b6622409d7edd2d76dd13a8092cd3af97b77bd2c77"+        pub3 <- importPubKey $ fst $ B16.decode $ B8.pack+            "049b101edcbe1ee37ff6b2318526a425b629e823d7d8d9154417880595a28000ee\+            \3febd908754b8ce4e491aa6fe488b41fb5d4bb3788e33c9ff95a7a9229166d59"+        combinePubKeys [pub1, pub2, pub3]+    expected = importPubKey $ fst $ B16.decode $ B8.pack+        "043d9a7ec70011efc23c33a7e62d2ea73cca87797e3b659d93bea6aa871aebde56c3bc\+        \6134ca82e324b0ab9c0e601a6d2933afe7fb5d9f3aae900f5c5dc6e362c8"
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}