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 +2/−0
- UNLICENSE +24/−0
- secp256k1.cabal +51/−0
- src/Crypto/Secp256k1.hs +272/−0
- src/Crypto/Secp256k1/Internal.hs +344/−0
- test/Crypto/Secp256k1/Internal/Tests.hs +474/−0
- test/Crypto/Secp256k1/Tests.hs +161/−0
- test/Spec.hs +8/−0
+ 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