packages feed

secp256k1 (empty) → 0.1.0

raw patch · 8 files changed

+1336/−0 lines, 8 filesdep +HUnitdep +QuickCheckdep +basesetup-changed

Dependencies added: HUnit, QuickCheck, base, base16-bytestring, bytestring, cryptohash, entropy, mtl, secp256k1, test-framework, test-framework-hunit, test-framework-quickcheck2

Files

+ 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.cabal view
@@ -0,0 +1,51 @@+name:                secp256k1+version:             0.1.0+synopsis:            secp256k1 bindings for Haskell+description:         Please see README.md+homepage:            http://github.com/haskoin/secp256k1#readme+license:             PublicDomain+license-file:        UNLICENSE+author:              Jean-Pierre Rupp+maintainer:          root@haskoin.com+copyright:           2015 Jean-Pierre Rupp+category:            Crypto+build-type:          Simple+-- extra-source-files:+cabal-version:       >=1.10++library+  hs-source-dirs:      src+  exposed-modules:     Crypto.Secp256k1+                     , Crypto.Secp256k1.Internal+  build-depends:       base >= 4.8 && < 5+                     , bytestring+                     , mtl+                     , entropy+  default-language:    Haskell2010+  ghc-options:         -Wall+  extra-libraries:     secp256k1++test-suite secp256k1-test+  type:                exitcode-stdio-1.0+  hs-source-dirs:      test+  main-is:             Spec.hs+  other-modules:       Crypto.Secp256k1.Tests+                     , Crypto.Secp256k1.Internal.Tests+  build-depends:       base+                     , secp256k1+                     , HUnit+                     , QuickCheck+                     , test-framework+                     , test-framework-hunit+                     , test-framework-quickcheck2+                     , base16-bytestring+                     , cryptohash+                     , bytestring+                     , mtl+                     , entropy+  ghc-options:         -Wall -threaded -rtsopts -with-rtsopts=-N+  default-language:    Haskell2010++source-repository head+  type:     git+  location: https://github.com/xenog/secp256k1
+ src/Crypto/Secp256k1.hs view
@@ -0,0 +1,272 @@+{-|+Module      : Crypto.Secp256k1+Description : Public SECP256K1 cryptographic functions+License     : PublicDomain+Maintainer  : root@haskoin.com+Stability   : experimental+Portability : POSIX++This module exposes crytpographic functions from Bitcoin’s secp256k1 library.+Depends on <https://github.com/bitcoin/secp256k1 secp256k1>.+-}+module Crypto.Secp256k1+( -- * Messages+  Msg, msg, getMsg+  -- * Secret Keys+, SecKey, secKey, getSecKey, importSecKey, exportSecKey, pubKey+  -- * Public Keys+, PubKey, importPubKey, exportPubKey+  -- * Signatures+, Sig, importSig, exportSig+, signMsg, verifySig+  -- * Addition & Multiplication+, Tweak, tweak, getTweak+, tweakAddSecKey, tweakMulSecKey+, tweakAddPubKey, tweakMulPubKey+, combinePubKeys+) where++import           Control.Monad.Reader+import           Crypto.Secp256k1.Internal+import           Data.ByteString           (ByteString)+import qualified Data.ByteString           as BS+import           Foreign+import           System.IO.Unsafe++-- | Internal public key data type.+newtype PubKey = PubKey (ForeignPtr PubKey64)++-- | Internal message data type.+newtype Msg = Msg (ForeignPtr Msg32)++-- | Internal signature data type.+newtype Sig = Sig (ForeignPtr Sig64)++-- | Internal secret key data type.+newtype SecKey = SecKey (ForeignPtr SecKey32)++-- | Internal tweak data type for addition and multiplication.+newtype Tweak = Tweak (ForeignPtr Tweak32)++instance Show PubKey where+    show = show . exportPubKey True++instance Show Msg where+    show = show . getMsg++instance Show Sig where+    show = show . exportSig++instance Show SecKey where+    show = show . getSecKey++instance Show Tweak where+    show = show . 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 = getSig fg1 == getSig fg2++instance Eq SecKey where+    fk1 == fk2 = getSecKey fk1 == getSecKey fk2++instance Eq Tweak where+    ft1 == ft2 = getTweak ft1 == getTweak ft2++-- | Create internal message data from 32-byte 'ByteString'.+msg :: ByteString -> Maybe Msg+msg bs+    | BS.length bs == 32 = unsafePerformIO $ do+        fp <- mallocForeignPtr+        withForeignPtr fp $ flip poke (Msg32 bs)+        return $ Just $ Msg fp+    | otherwise = Nothing++-- | Create internal secret key data from 32-byte 'ByteString'.+secKey :: ByteString -> Maybe SecKey+secKey bs+    | BS.length bs == 32 = unsafePerformIO $ do+        fp <- mallocForeignPtr+        ret <- withForeignPtr fp $ \p -> do+            poke p (SecKey32 bs)+            ec_seckey_verify ctx p+        if isSuccess ret+            then return $ Just $ SecKey fp+            else return $ Nothing+    | otherwise = Nothing++-- | Create internal tweak data from 32-byte 'ByteString'.+tweak :: ByteString -> Maybe Tweak+tweak bs+    | BS.length bs == 32 = unsafePerformIO $ do+        fp <- mallocForeignPtr+        withForeignPtr fp $ flip poke (Tweak32 bs)+        return $ Just $ Tweak fp+    | otherwise = Nothing++-- | Get 32-byte secret key.+getSecKey :: SecKey -> ByteString+getSecKey (SecKey fk) = getSecKey32 $ unsafePerformIO $ withForeignPtr fk peek++getPubKey :: PubKey -> ByteString+getPubKey (PubKey fp) = getPubKey64 $ unsafePerformIO $ withForeignPtr fp peek++getSig :: Sig -> ByteString+getSig (Sig fg) = getSig64 $ unsafePerformIO $ withForeignPtr fg peek++-- | Get 32-byte message.+getMsg :: Msg -> ByteString+getMsg (Msg fm) = getMsg32 $ unsafePerformIO $ withForeignPtr fm $ peek++-- | Get 32-byte tweak.+getTweak :: Tweak -> ByteString+getTweak (Tweak ft) = getTweak32 $ unsafePerformIO $ withForeignPtr ft $ peek++-- | Read DER-encoded public key.+importPubKey :: ByteString -> Maybe PubKey+importPubKey bs = unsafePerformIO $ do+    useByteString bs $ \(b, l) -> do+        fp <- mallocForeignPtr+        ret <- withForeignPtr fp $ \p -> ec_pubkey_parse 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) = unsafePerformIO $+    withForeignPtr pub $ \p -> alloca $ \l -> allocaBytes 65 $ \o -> do+        poke l 65+        ret <- ec_pubkey_serialize 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++-- | Read DER-encoded signature.+importSig :: ByteString -> Maybe Sig+importSig bs = unsafePerformIO $+    useByteString bs $ \(b, l) -> do+        fg <- mallocForeignPtr+        ret <- withForeignPtr fg $ \g -> ecdsa_signature_parse_der ctx g b l+        if isSuccess ret then return $ Just $ Sig fg else return Nothing++-- | Encode signature as DER.+exportSig :: Sig -> ByteString+exportSig (Sig fg) = unsafePerformIO $+    withForeignPtr fg $ \g -> alloca $ \l -> allocaBytes 72 $ \o -> do+        poke l 72+        ret <- ecdsa_signature_serialize_der 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) = unsafePerformIO $+    withForeignPtr fp $ \p -> withForeignPtr fg $ \g ->+        withForeignPtr fm $ \m -> isSuccess <$> ecdsa_verify ctx g m p++-- | Sign message using secret key.+signMsg :: SecKey -> Msg -> Sig+signMsg (SecKey fk) (Msg fm) = unsafePerformIO $+    withForeignPtr fk $ \k -> withForeignPtr fm $ \m -> do+        fg <- mallocForeignPtr+        ret <- withForeignPtr fg $ \g -> ecdsa_sign ctx g m k nullFunPtr nullPtr+        unless (isSuccess ret) $ error "could not sign message"+        return $ Sig fg++-- | Obtain public key from secret key.+pubKey :: SecKey -> PubKey+pubKey (SecKey fk) = unsafePerformIO $+    withForeignPtr fk $ \k -> do+        fp <- mallocForeignPtr+        ret <- withForeignPtr fp $ \p -> ec_pubkey_create ctx p k+        unless (isSuccess ret) $ error "could not compute public key"+        return $ PubKey fp++-- | Read BER-encoded secret key.+importSecKey :: ByteString -> Maybe SecKey+importSecKey bs = unsafePerformIO $+    useByteString bs $ \(b, l) -> do+        fk <- mallocForeignPtr+        ret <- withForeignPtr fk $ \k -> ec_privkey_import ctx k b l+        if isSuccess ret then return $ Just $ SecKey fk else return Nothing++-- | Encode secret key as BER.  First argument 'True' for compressed output.+exportSecKey :: Bool -> SecKey -> ByteString+exportSecKey compress (SecKey fk) = unsafePerformIO $+    withForeignPtr fk $ \k -> alloca $ \l -> allocaBytes 279 $ \o -> do+        poke l 279+        ret <- ec_privkey_export ctx o l k c+        unless (isSuccess ret) $ error "could not export secret key"+        n <- peek l+        packByteString (o, n)+  where+    c = if compress then compressed else uncompressed++-- | Add tweak to secret key using ECDSA addition.+tweakAddSecKey :: SecKey -> Tweak -> Maybe SecKey+tweakAddSecKey (SecKey fk) (Tweak ft) = unsafePerformIO $+    withForeignPtr fk $ \k -> withForeignPtr ft $ \t -> do+        fk' <- mallocForeignPtr+        ret <- withForeignPtr fk' $ \k' ->  do+            key <- peek k+            poke k' key+            ec_privkey_tweak_add ctx k' t+        if isSuccess ret then return $ Just $ SecKey fk' else return Nothing++-- | Multiply secret key by tweak using ECDSA multiplication.+tweakMulSecKey :: SecKey -> Tweak -> Maybe SecKey+tweakMulSecKey (SecKey fk) (Tweak ft) = unsafePerformIO $+    withForeignPtr fk $ \k -> withForeignPtr ft $ \t -> do+        fk' <- mallocForeignPtr+        ret <- withForeignPtr fk' $ \k' ->  do+            key <- peek k+            poke k' key+            ec_privkey_tweak_mul ctx k' t+        if isSuccess ret then return $ Just $ SecKey fk' else return Nothing++-- | Perform ECDSA addition between the public key point and the point obtained+-- by multiplying the tweak scalar by the curve generator.+tweakAddPubKey :: PubKey -> Tweak -> Maybe PubKey+tweakAddPubKey (PubKey fp) (Tweak ft) = unsafePerformIO $+    withForeignPtr fp $ \p -> withForeignPtr ft $ \t -> do+        fp' <- mallocForeignPtr+        ret <- withForeignPtr fp' $ \p' ->  do+            pub <- peek p+            poke p' pub+            ec_pubkey_tweak_add ctx p' t+        if isSuccess ret then return $ Just $ PubKey fp' else return Nothing++-- | Perform ECDSA multiplication between the public key point and the point+-- obtained by multiplying the tweak scalar by the curve generator.+tweakMulPubKey :: PubKey -> Tweak -> Maybe PubKey+tweakMulPubKey (PubKey fp) (Tweak ft) = unsafePerformIO $+    withForeignPtr fp $ \p -> withForeignPtr ft $ \t -> do+        fp' <- mallocForeignPtr+        ret <- withForeignPtr fp' $ \p' ->  do+            pub <- peek p+            poke p' pub+            ec_pubkey_tweak_mul ctx p' t+        if isSuccess ret then return $ Just $ PubKey fp' else return Nothing++-- | Add multiple public keys together using ECDSA addition.+combinePubKeys :: [PubKey] -> Maybe PubKey+combinePubKeys pubs = unsafePerformIO $ pointers [] pubs $ \ps ->+    allocaArray (length ps) $ \a -> do+        pokeArray a ps+        fp <- mallocForeignPtr+        ret <- withForeignPtr fp $ \p ->+            ec_pubkey_combine 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
+ src/Crypto/Secp256k1/Internal.hs view
@@ -0,0 +1,344 @@+{-|+Module      : Crypto.Secp256k1.Internal+Description : Internal SECP256K1 cryptographic functions+License     : PublicDomain+Maintainer  : root@haskoin.com+Stability   : experimental+Portability : POSIX++The API in 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, packCStringLen)+import           Data.ByteString.Unsafe (unsafeUseAsCStringLen)++import           Foreign+import           Foreign.C++import           System.Entropy+import           System.IO.Unsafe++data Ctx = Ctx++newtype PubKey64 = PubKey64 { getPubKey64 :: ByteString }+    deriving (Read, Show, Eq, Ord)++newtype Msg32 = Msg32 { getMsg32 :: ByteString }+    deriving (Read, Show, Eq, Ord)++newtype Sig64 = Sig64 { getSig64 :: ByteString }+    deriving (Read, Show, Eq, Ord)++newtype Seed32 = Seed32 { getSeed32 :: ByteString }+    deriving (Read, Show, Eq, Ord)++newtype SecKey32 = SecKey32 { getSecKey32 :: ByteString }+    deriving (Read, Show, Eq, Ord)++newtype Tweak32 = Tweak32 { getTweak32 :: ByteString }+    deriving (Read, Show, Eq, Ord)++newtype Nonce32 = Nonce32 { getNonce32 :: ByteString }+    deriving (Read, Show, Eq, Ord)++newtype Algo16 = Algo16 { getAlgo16 :: ByteString }+    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 1++sign :: CtxFlags+sign = CtxFlags 2++signVerify :: CtxFlags+signVerify = CtxFlags 3++compressed :: SerFlags+compressed = SerFlags 1++uncompressed :: SerFlags+uncompressed = SerFlags 0++useByteString :: ByteString -> ((Ptr CUChar, CSize) -> IO a) -> IO a+useByteString bs f =+    unsafeUseAsCStringLen bs $ \(b, l) -> f (castPtr b, fromIntegral l)++packByteString :: (Ptr CUChar, CSize) -> IO ByteString+packByteString (b, l) = packCStringLen (castPtr b, fromIntegral l)++instance Storable PubKey64 where+    sizeOf _ = 64+    alignment _ = 1+    peek p = PubKey64 <$> packByteString (castPtr p, 64)+    poke p (PubKey64 k) = useByteString k $+        \(b, _) -> copyArray (castPtr p) b 64++instance Storable Sig64 where+    sizeOf _ = 64+    alignment _ = 1+    peek p = Sig64 <$> packByteString (castPtr p, 64)+    poke p (Sig64 k) = useByteString k $+        \(b, _) -> copyArray (castPtr p) b 64++instance Storable Msg32 where+    sizeOf _ = 32+    alignment _ = 1+    peek p = Msg32 <$> packByteString (castPtr p, 32)+    poke p (Msg32 k) = useByteString k $+        \(b, _) -> copyArray (castPtr p) b 32++instance Storable Seed32 where+    sizeOf _ = 32+    alignment _ = 1+    peek p = Seed32 <$> packByteString (castPtr p, 32)+    poke p (Seed32 k) = useByteString k $+        \(b, _) -> copyArray (castPtr p) b 32++instance Storable SecKey32 where+    sizeOf _ = 32+    alignment _ = 1+    peek p = SecKey32 <$> packByteString (castPtr p, 32)+    poke p (SecKey32 k) = useByteString k $+        \(b, _) -> copyArray (castPtr p) b 32++instance Storable Tweak32 where+    sizeOf _ = 32+    alignment _ = 1+    peek p = Tweak32 <$> packByteString (castPtr p, 32)+    poke p (Tweak32 k) = useByteString k $+        \(b, _) -> copyArray (castPtr p) b 32++instance Storable Nonce32 where+    sizeOf _ = 32+    alignment _ = 1+    peek p = Nonce32 <$> packByteString (castPtr p, 32)+    poke p (Nonce32 k) = useByteString k $+        \(b, _) -> copyArray (castPtr p) b 32++instance Storable Algo16 where+    sizeOf _ = 16+    alignment _ = 1+    peek p = Algo16 <$> packByteString (castPtr p, 16)+    poke p (Algo16 k) = useByteString k $+        \(b, _) -> copyArray (castPtr p) b 16++isSuccess :: Ret -> Bool+isSuccess (Ret 0) = False+isSuccess (Ret 1) = True+isSuccess _ = undefined++{-# NOINLINE ctx #-}+ctx :: Ptr Ctx+ctx = unsafePerformIO $ do+    x <- context_create signVerify+    e <- getEntropy 32+    ret <- alloca $ \s -> poke s (Seed32 e) >> context_randomize x s+    unless (isSuccess ret) $ error "failed to randomize context"+    return x++foreign import ccall+    "secp256k1.h secp256k1_context_create"+    context_create+    :: CtxFlags+    -> IO (Ptr Ctx)++foreign import ccall+    "secp256k1.h secp256k1_context_clone"+    context_clone+    :: Ptr Ctx+    -> IO (Ptr Ctx)++foreign import ccall+    "secp256k1.h &secp256k1_context_destroy"+    context_destroy+    :: FunPtr (Ptr Ctx -> IO ())++foreign import ccall+    "secp256k1.h secp256k1_context_set_illegal_callback"+    set_illegal_callback+    :: Ptr Ctx+    -> FunPtr (CString -> Ptr a -> IO ()) -- ^ message, data+    -> Ptr a                              -- ^ data+    -> IO ()++foreign import ccall+    "secp256k1.h secp256k1_context_set_error_callback"+    set_error_callback+    :: Ptr Ctx+    -> FunPtr (CString -> Ptr a -> IO ()) -- ^ message, data+    -> Ptr a                              -- ^ data+    -> IO ()++foreign import ccall+    "secp256k1.h secp256k1_ec_pubkey_parse"+    ec_pubkey_parse+    :: 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"+    ec_pubkey_serialize+    :: 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_der"+    ecdsa_signature_parse_der+    :: 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"+    ecdsa_signature_serialize_der+    :: 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_verify"+    ecdsa_verify+    :: Ptr Ctx+    -> Ptr Sig64+    -> Ptr Msg32+    -> Ptr PubKey64+    -> IO Ret++-- TODO:+-- foreign import ccall+--     "secp256k1.h &secp256k1_nonce_function_rfc6979"+--     nonce_function_rfc6979+--     :: FunPtr (NonceFunction Seed32)+--+-- TODO:+-- foreign import ccall+--     "secp256k1.h &secp256k1_nonce_function_default"+--     nonce_function_default+--     :: FunPtr (NonceFunction Seed32)++foreign import ccall+    "secp256k1.h secp256k1_ecdsa_sign"+    ecdsa_sign+    :: 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"+    ec_seckey_verify+    :: Ptr Ctx+    -> Ptr SecKey32+    -> IO Ret++foreign import ccall+    "secp256k1.h secp256k1_ec_pubkey_create"+    ec_pubkey_create+    :: Ptr Ctx+    -> Ptr PubKey64+    -> Ptr SecKey32+    -> IO Ret++foreign import ccall+    "secp256k1.h secp256k1_ec_privkey_export"+    ec_privkey_export+    :: Ptr Ctx+    -> Ptr CUChar -- ^ array to store BER-encoded key (allocate 279 bytes)+    -> Ptr CSize -- ^ size of previous array, will be updated+    -> Ptr SecKey32+    -> SerFlags+    -> IO Ret++foreign import ccall+    "secp256k1.h secp256k1_ec_privkey_import"+    ec_privkey_import+    :: Ptr Ctx+    -> Ptr SecKey32+    -> Ptr CUChar -- ^ BER-encoded private key+    -> CSize+    -> IO Ret++foreign import ccall+    "secp256k1.h secp256k1_ec_privkey_tweak_add"+    ec_privkey_tweak_add+    :: Ptr Ctx+    -> Ptr SecKey32+    -> Ptr Tweak32+    -> IO Ret++foreign import ccall+    "secp256k1.h secp256k1_ec_pubkey_tweak_add"+    ec_pubkey_tweak_add+    :: Ptr Ctx+    -> Ptr PubKey64+    -> Ptr Tweak32+    -> IO Ret++foreign import ccall+    "secp256k1.h secp256k1_ec_privkey_tweak_mul"+    ec_privkey_tweak_mul+    :: Ptr Ctx+    -> Ptr SecKey32+    -> Ptr Tweak32+    -> IO Ret++foreign import ccall+    "secp256k1.h secp256k1_ec_pubkey_tweak_mul"+    ec_pubkey_tweak_mul+    :: Ptr Ctx+    -> Ptr PubKey64+    -> Ptr Tweak32+    -> IO Ret++foreign import ccall+    "secp256k1.h secp256k1_context_randomize"+    context_randomize+    :: Ptr Ctx+    -> Ptr Seed32+    -> IO Ret++foreign import ccall+    "secp256k1.h secp256k1_ec_pubkey_combine"+    ec_pubkey_combine+    :: 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
+ test/Crypto/Secp256k1/Internal/Tests.hs view
@@ -0,0 +1,474 @@+{-# LANGUAGE OverloadedStrings #-}+module Crypto.Secp256k1.Internal.Tests (tests) where++import           Control.Monad+import           Control.Monad.Trans+import           Crypto.Secp256k1.Internal+import           Data.ByteString                (packCStringLen,+                                                 useAsCStringLen)+import qualified Data.ByteString.Base16         as B16+import           Foreign+import           System.Entropy+import           Test.Framework                 (Test, testGroup)+import           Test.Framework.Providers.HUnit (testCase)+import           Test.HUnit                     (Assertion, assertBool,+                                                 assertEqual)++tests :: [Test]+tests =+    [ testGroup "Housekeeping"+        [ testCase "Create context"           create_context_test+        , testCase "Randomize context"        randomize_context_test+        , testCase "Clone context"            clone_context_test+        -- TODO:+        -- , testCase "Set illegal callback" set_illegal_callback_test+        -- TODO:+        -- , testCase "Set error callback" set_error_callback_test+        ]+    , testGroup "Serialization"+        [ testCase "Parse public key"         ec_pubkey_parse_test+        , testCase "Serialize public key"     ec_pubkey_serialize_test+        , testCase "Storable public key"      pubkey_storable_test+        , testCase "Parse DER signature"      ecdsa_signature_parse_der_test+        , testCase "Serialize DER signature"  ecdsa_signature_serialize_der_test+        ]+    , testGroup "Signatures"+        [ testCase "ECDSA verify"             ecdsa_verify_test+        -- TODO:+        -- , testCase "RFC6979 nonce function"   nonce_function_rfc6979_test+        , testCase "ECDSA sign"               ecdsa_sign_test+        ]+    , testGroup "Secret keys"+        [ testCase "Verify secret key"        ec_seckey_verify_test+        , testCase "Create public key"        ec_pubkey_create_test+        , testCase "Serialize BER secret key" ec_privkey_export_test+        , testCase "Import BER secret key"    ec_privkey_import_test+        , testCase "Tweak add secret key"     ec_privkey_tweak_add_test+        , testCase "Tweak mult. secret key"   ec_privkey_tweak_mul_test+        ]+    , testGroup "Public keys"+        [ testCase "Tweak add public key"     ec_pubkey_tweak_add_test+        , testCase "Tweak mult. public key"   ec_pubkey_tweak_mul_test+        , testCase "Combine public keys"      ec_pubkey_combine_test+        ]+    ]++withEntropy :: (Ptr Seed32 -> IO a) -> IO a+withEntropy f = getEntropy 32 >>= \e -> alloca $ \s -> poke s (Seed32 e) >> f s++create_context_test :: Assertion+create_context_test = do+    context_ptr <- liftIO $ context_create signVerify+    assertBool "context not null" $ context_ptr /= nullPtr++randomize_context_test :: Assertion+randomize_context_test = do+    ret <- liftIO $ context_create sign >>= \x ->+        withEntropy (context_randomize x)+    assertBool "context randomized" $ isSuccess ret++clone_context_test :: Assertion+clone_context_test = do+    (x1, x2) <- liftIO $ do+        x1 <- context_create signVerify+        ret <- withEntropy $ context_randomize x1+        unless (isSuccess ret) $ error "failed to randomize context"+        x2 <- context_clone(x1)+        return (x1, x2)+    assertBool "original context not null" $ x1 /= nullPtr+    assertBool "cloned context not null" $ x2 /= nullPtr+    assertBool "context ptrs different" $ x1 /= x2++ec_pubkey_parse_test :: Assertion+ec_pubkey_parse_test = do+    ret <- liftIO $ useAsCStringLen der $ \(i, il) -> do+        x <- context_create verify+        alloca $ \pubkey ->+            ec_pubkey_parse x pubkey (castPtr i) (fromIntegral il)+    assertBool "parsed public key" (isSuccess ret)+  where+    der = fst $ B16.decode+        "03dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705"++ec_pubkey_serialize_test :: Assertion+ec_pubkey_serialize_test = do+    (ret, dec) <- liftIO $ useAsCStringLen der $ \(i, il) ->+        alloca $ \k -> alloca $ \ol -> allocaBytes 72 $ \o -> do+        poke ol 72+        x <- context_create verify+        ret1 <-+            ec_pubkey_parse x k (castPtr i) (fromIntegral il)+        unless (isSuccess ret1) $ error "failed to parse pubkey"+        ret2 <- ec_pubkey_serialize+            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"++pubkey_storable_test :: Assertion+pubkey_storable_test = do+    (pk1, pk2, dec) <- liftIO $ useAsCStringLen der $ \(i, il) -> do+        x <- context_create verify+        pk1 <- alloca $ \pk -> do+            ret <-+                ec_pubkey_parse 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 <-+                ec_pubkey_serialize 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"++ecdsa_signature_parse_der_test :: Assertion+ecdsa_signature_parse_der_test = do+    ret <- liftIO $ useAsCStringLen der $ \(d, dl) -> alloca $ \s -> do+        x <- context_create verify+        ecdsa_signature_parse_der x s (castPtr d) (fromIntegral dl)+    assertBool "parsed signature successfully" $ isSuccess ret+  where+    der = fst $ B16.decode $+        "3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\+        \fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\+        \45"++ecdsa_signature_serialize_der_test :: Assertion+ecdsa_signature_serialize_der_test = do+    (ret, enc) <- liftIO $ do+        x <- context_create verify+        sig <- useAsCStringLen der $ \(d, dl) -> alloca $ \s -> do+            ret <-+                ecdsa_signature_parse_der x s (castPtr d) (fromIntegral dl)+            unless (isSuccess ret) $ error "could not parse DER"+            peek s+        alloca $ \s -> alloca $ \ol -> allocaBytes 72 $ \o -> do+            poke ol 72+            poke s sig+            ret <-+                ecdsa_signature_serialize_der 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"++ecdsa_verify_test :: Assertion+ecdsa_verify_test = do+    ret <- liftIO $ do+        x <- context_create verify+        sig <- useAsCStringLen der $ \(d, dl) -> alloca $ \s -> do+            ret <-+                ecdsa_signature_parse_der x s (castPtr d) (fromIntegral dl)+            unless (isSuccess ret) $ error "could not parse DER"+            peek s+        pk <- useAsCStringLen pub $ \(p, pl) -> alloca $ \k -> do+            ret <-+                ec_pubkey_parse 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+            ecdsa_verify 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 $ fst $ B16.decode $+        "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"++-- TODO:+-- nonce_function_rfc6979_test :: Assertion+-- nonce_function_rfc6979_test = do+--     (ret, nonce) <- alloca $ \n -> alloca $ \m -> alloca $ \k -> do+--             poke m msg+--             poke k key+--             let ret = mkNonceFunction+--                     nonce_function_rfc6979 n m k nullPtr nullPtr 0+--             nonce <- peek n+--             return (ret, nonce)+--     assertBool "nonce calculated" $ isSuccess ret+--     assertEqual "nonce correct" expected nonce+--   where+--     msg = Msg32 $ fst $ B16.decode $+--         "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"+--     key = SecKey32 $ fst $ B16.decode $+--         "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"+--     expected = Nonce $ fst $ B16.decode $+--         "8f30c2bf5a3e1199b19a6703fa578376e5225d6ecfc7ecf817aa8b8b16203d64"++ecdsa_sign_test :: Assertion+ecdsa_sign_test = do+    (ret, sig) <- liftIO $ do+        x <- context_create sign+        retr <- withEntropy $ context_randomize x+        unless (isSuccess retr) $ error "failed to randomize context"+        alloca $ \s -> alloca $ \m -> alloca $ \k -> alloca $ \ol ->+            allocaBytes 72 $ \o -> do+                poke ol 72+                poke m msg+                poke k key+                ret1 <-+                    -- TODO:+                    -- ecdsa_sign x s m k nonce_function_default nullPtr+                    ecdsa_sign x s m k nullFunPtr nullPtr+                ret2 <- ecdsa_signature_serialize_der 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 $ fst $ B16.decode $+        "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"+    key = SecKey32 $ fst $ B16.decode $+        "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"+    der = fst $ B16.decode $+        "3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\+        \fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\+        \45"++ec_seckey_verify_test :: Assertion+ec_seckey_verify_test = do+    ret <- liftIO $ alloca $ \k -> do+        poke k key+        x <- context_create sign+        retr <- withEntropy $ context_randomize x+        unless (isSuccess retr) $ error "failed to randomize context"+        ec_seckey_verify x k+    assertBool "valid secret key" $ isSuccess ret+  where+    key = SecKey32 $ fst $ B16.decode $+        "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"++ec_pubkey_create_test :: Assertion+ec_pubkey_create_test = do+    (ret, pk) <- liftIO $ alloca $ \p -> alloca $ \k -> do+        poke k key+        x <- context_create sign+        retr <- withEntropy $ context_randomize x+        unless (isSuccess retr) $ error "failed to randomize context"+        ret <- ec_pubkey_create x p k+        allocaBytes 65 $ \o -> alloca $ \ol -> do+            poke ol 65+            rets <- ec_pubkey_serialize+                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 $ fst $ B16.decode $+        "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"+    pub = fst $ B16.decode $+        "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\+        \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"++ec_privkey_export_test :: Assertion+ec_privkey_export_test = do+    ret <- liftIO $ alloca $ \k ->+        allocaBytes 279 $ \o -> alloca $ \ol -> do+            poke ol 279+            poke k key+            x <- context_create sign+            retr <- withEntropy $ context_randomize x+            unless (isSuccess retr) $ error "failed to randomize context"+            ec_privkey_export x o ol k uncompressed+    assertBool "successful secret key BER serialization" $ isSuccess ret+  where+    key = SecKey32 $ fst $ B16.decode $+        "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"++ec_privkey_import_test :: Assertion+ec_privkey_import_test = do+    (ret, dec) <- liftIO $ do+        x <- context_create sign+        retr <- withEntropy $ context_randomize x+        unless (isSuccess retr) $ error "failed to randomize context"+        ber <- allocaBytes 279 $ \o -> alloca $ \ol -> alloca $ \k -> do+            poke ol 279+            poke k key+            rets <- ec_privkey_export x o ol k uncompressed+            unless (isSuccess rets) $ error "failed to serialize key"+            len <- fromIntegral <$> peek ol+            packCStringLen (castPtr o, len)+        useAsCStringLen ber $ \(b, bl) -> alloca $ \k -> do+            ret <- ec_privkey_import x k (castPtr b) (fromIntegral bl)+            dec <- peek k+            return (ret, dec)+    assertBool "successful secret key BER deserialization" $ isSuccess ret+    assertEqual "keys match" key dec+  where+    key = SecKey32 $ fst $ B16.decode $+        "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"++ec_privkey_tweak_add_test :: Assertion+ec_privkey_tweak_add_test = do+    (ret, tweaked) <- liftIO $ do+        x <- context_create sign+        retr <- withEntropy $ context_randomize x+        unless (isSuccess retr) $ error "failed to randomize context"+        alloca $ \w -> alloca $ \k -> do+            poke w tweak+            poke k key+            ret <- ec_privkey_tweak_add 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 $ fst $ B16.decode $+        "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"+    tweak = Tweak32 $ fst $ B16.decode $+        "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"+    expected = SecKey32 $ fst $ B16.decode $+        "ec1e3ce1cefa18a671d51125e2b249688d934b0e28f5d1665384d9b02f929059"++ec_privkey_tweak_mul_test :: Assertion+ec_privkey_tweak_mul_test = do+    (ret, tweaked) <- liftIO $ do+        x <- context_create sign+        retr <- withEntropy $ context_randomize x+        unless (isSuccess retr) $ error "failed to randomize context"+        alloca $ \w -> alloca $ \k -> do+            poke w tweak+            poke k key+            ret <- ec_privkey_tweak_mul 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 $ fst $ B16.decode $+        "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"+    tweak = Tweak32 $ fst $ B16.decode $+        "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"+    expected = SecKey32 $ fst $ B16.decode $+        "a96f5962493acb179f60a86a9785fc7a30e0c39b64c09d24fe064d9aef15e4c0"++ec_pubkey_tweak_add_test :: Assertion+ec_pubkey_tweak_add_test = do+    (ret, tweaked) <- liftIO $ do+        x <- context_create verify+        pk <- alloca $ \p -> useAsCStringLen pub $ \(d, dl) -> do+            ret <- ec_pubkey_parse x p (castPtr d) (fromIntegral dl)+            unless (isSuccess ret) $ error "could not parse public key"+            peek p+        alloca $ \w -> alloca $ \p -> allocaBytes 72 $ \d -> alloca $ \dl -> do+            poke w tweak+            poke p pk+            poke dl 72+            ret <- ec_pubkey_tweak_add x p w+            rets <- ec_pubkey_serialize x d dl p uncompressed+            unless (isSuccess rets) $ error "could not serialize public key"+            len <- peek dl+            tweaked <- packCStringLen (castPtr d, fromIntegral len)+            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 $ fst $ B16.decode $+        "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"+    expected = fst $ B16.decode $+        "04441c3982b97576646e0df0c96736063df6b42f2ee566d13b9f6424302d1379e518fd\+        \c87a14c5435bff7a5db4552042cb4120c6b86a4bbd3d0643f3c14ad01368"++ec_pubkey_tweak_mul_test :: Assertion+ec_pubkey_tweak_mul_test = do+    (ret, tweaked) <- liftIO $ do+        x <- context_create verify+        pk <- alloca $ \p -> useAsCStringLen pub $ \(d, dl) -> do+            ret <- ec_pubkey_parse x p (castPtr d) (fromIntegral dl)+            unless (isSuccess ret) $ error "could not parse public key"+            peek p+        alloca $ \w -> alloca $ \p -> allocaBytes 72 $ \d -> alloca $ \dl -> do+            poke w tweak+            poke p pk+            poke dl 72+            ret <- ec_pubkey_tweak_mul x p w+            rets <- ec_pubkey_serialize x d dl p uncompressed+            unless (isSuccess rets) $ error "could not serialize public key"+            len <- peek dl+            tweaked <- packCStringLen (castPtr d, fromIntegral len)+            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 $ fst $ B16.decode $+        "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"+    expected = fst $ B16.decode $+        "04f379dc99cdf5c83e433defa267fbb3377d61d6b779c06a0e4ce29ae3ff5353b12ae4\+        \9c9d07e7368f2ba5a446c203255ce912322991a2d6a9d5d5761c61ed1845"++ec_pubkey_combine_test :: Assertion+ec_pubkey_combine_test = do+    (ret, com) <- liftIO $ alloca $ \p1 -> alloca $ \p2 -> alloca $ \p3 ->+        allocaBytes 72 $ \d -> alloca $ \dl -> allocaArray 3 $ \a ->+            alloca $ \p -> do+                x <- context_create verify+                parse x pub1 p1+                parse x pub2 p2+                parse x pub3 p3+                pokeArray a [p1, p2, p3]+                poke dl 72+                ret <- ec_pubkey_combine x p a 3+                rets <- ec_pubkey_serialize+                    x d dl p uncompressed+                unless (isSuccess rets) $ error "could not serialize public key"+                len <- peek dl+                com <- packCStringLen (castPtr d, fromIntegral len)+                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 <- ec_pubkey_parse 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/Secp256k1/Tests.hs view
@@ -0,0 +1,161 @@+module Crypto.Secp256k1.Tests (tests) where++import           Crypto.Secp256k1+import qualified Data.ByteString                      as BS+import qualified Data.ByteString.Base16               as B16+import qualified Data.ByteString.Char8                as B8+import           Data.Maybe+import           Test.Framework                       (Test, testGroup)+import           Test.Framework.Providers.HUnit       (testCase)+import           Test.Framework.Providers.QuickCheck2 (testProperty)+import           Test.HUnit                           (Assertion, assertEqual)+import           Test.QuickCheck++tests :: [Test]+tests =+    [ testGroup "Signing"+        [ testProperty "Signing messages" signMsgTest+        , testProperty "Bad signatures" badSignatureTest+        ]+    , testGroup "Serialization"+        [ testProperty "Serialize public key" serializePubKeyTest+        , testProperty "Serialize signature" serializeSigTest+        , testProperty "Serialize secret key" serializeSecKeyTest+        ]+    , testGroup "Tweaks"+        [ testCase "Tweak add secret key" tweakAddSecKeyTest+        , testCase "Tweak multiply secret key" tweakMulSecKeyTest+        , testCase "Tweak add public key" tweakAddPubKeyTest+        , testCase "Tweak multiply public key" tweakMulPubKeyTest+        , testCase "Combine public keys" combinePubKeyTest+        ]+    ]++newtype GenMsg = GenMsg Msg deriving (Show, Eq)+newtype GenSecKey = GenSecKey SecKey deriving (Show, Eq)+newtype GenPubKey = GenPubKey PubKey deriving (Show, Eq)++instance Arbitrary GenMsg where+    arbitrary = gen_msg+      where+        valid_bs = bs_gen `suchThat` isJust+        bs_gen = (msg . BS.pack) <$> sequence (replicate 32 arbitrary)+        gen_msg = (GenMsg . fromJust) <$> valid_bs++instance Arbitrary GenSecKey where+    arbitrary = gen_key+      where+        valid_bs = bs_gen `suchThat` isJust+        bs_gen = (secKey . BS.pack) <$> sequence (replicate 32 arbitrary)+        gen_key = (GenSecKey . fromJust) <$> valid_bs++instance Arbitrary GenPubKey where+    arbitrary = do+        GenSecKey key <- arbitrary+        return $ GenPubKey $ pubKey key++signMsgTest :: (GenMsg, GenSecKey) -> Bool+signMsgTest (GenMsg fm, GenSecKey fk) = verifySig fp fg fm where+    fp = pubKey fk+    fg = signMsg fk fm++badSignatureTest :: (GenMsg, GenSecKey, GenPubKey) -> Bool+badSignatureTest (GenMsg fm, GenSecKey fk, GenPubKey fp) =+    not $ verifySig fp fg fm+  where+    fg = signMsg fk fm++serializePubKeyTest :: (GenPubKey, Bool) -> Bool+serializePubKeyTest (GenPubKey fp, b) =+    case importPubKey $ exportPubKey b fp of+        Just fp' -> fp == fp'+        Nothing -> False++serializeSigTest :: (GenMsg, GenSecKey) -> Bool+serializeSigTest (GenMsg fm, GenSecKey fk) =+    case importSig $ exportSig fg of+        Just fg' -> fg == fg'+        Nothing -> False+  where+    fg = signMsg fk fm++serializeSecKeyTest :: (GenSecKey, Bool) -> Bool+serializeSecKeyTest (GenSecKey fk, b) =+    case importSecKey $ exportSecKey b 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,8 @@+import Test.Framework (defaultMain)++-- Some simple signing tests+import qualified Crypto.Secp256k1.Tests as T+import qualified Crypto.Secp256k1.Internal.Tests as I++main :: IO ()+main = defaultMain $ T.tests ++ I.tests