secp256k1-0.4.1: haskell/test/Crypto/Secp256k1/Internal/Tests.hs
{-# LANGUAGE OverloadedStrings #-}
module Crypto.Secp256k1.Internal.Tests (tests) where
import Control.Monad
import Control.Monad.Trans
import Crypto.Secp256k1.Internal
import Data.ByteString (ByteString, packCStringLen,
useAsCStringLen)
import qualified Data.ByteString.Base16 as B16
import 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" createContextTest
, testCase "Randomize context" randomizeContextTest
, testCase "Clone context" cloneContextTest
]
, testGroup "Serialization"
[ testCase "Parse public key" ecPubkeyParseTest
, testCase "Serialize public key" ecPubKeySerializeTest
, testCase "Storable public key" pubkeyStorableTest
, testCase "Storable signature" signatureStorableTest
, testCase "Parse DER signature" ecdsaSignatureParseDerTest
, testCase "Lax parse DER signature" laxDerParseTest
, testCase "Serialize DER signature" ecdsaSignatureSerializeDerTest
]
, testGroup "Signatures"
[ testCase "ECDSA verify" ecdsaVerifyTest
-- TODO:
-- , testCase "RFC6979 nonce function" nonce_function_rfc6979_test
, testCase "ECDSA sign" ecdsaSignTest
]
, testGroup "Secret keys"
[ testCase "Verify secret key" ecSecKeyVerifyTest
, testCase "Create public key" ecPubkeyCreateTest
, testCase "Tweak add secret key" ecSecKeyTweakAddTest
, testCase "Tweak mult. secret key" ecSecKeyTweakMulTest
]
, testGroup "Public keys"
[ testCase "Tweak add public key" ecPubKeyTweakAddTest
, testCase "Tweak mult. public key" ecPubKeyTweakMulTest
, testCase "Combine public keys" ecPubKeyCombineTest
]
]
withEntropy :: (Ptr Seed32 -> IO a) -> IO a
withEntropy f = getEntropy 32 >>= \e -> alloca $ \s -> poke s (Seed32 e) >> f s
createContextTest :: Assertion
createContextTest = do
context_ptr <- liftIO $ contextCreate signVerify
assertBool "context not null" $ context_ptr /= nullPtr
randomizeContextTest :: Assertion
randomizeContextTest = do
ret <- liftIO $ contextCreate sign >>= \x ->
withEntropy (contextRandomize x)
assertBool "context randomized" $ isSuccess ret
cloneContextTest :: Assertion
cloneContextTest = do
(x1, x2) <- liftIO $ do
x1 <- contextCreate signVerify
ret <- withEntropy $ contextRandomize x1
unless (isSuccess ret) $ error "failed to randomize context"
x2 <- contextClone x1
return (x1, x2)
assertBool "original context not null" $ x1 /= nullPtr
assertBool "cloned context not null" $ x2 /= nullPtr
assertBool "context ptrs different" $ x1 /= x2
ecPubkeyParseTest :: Assertion
ecPubkeyParseTest = do
ret <- liftIO $ useAsCStringLen der $ \(i, il) -> do
x <- contextCreate verify
alloca $ \pubkey ->
ecPubKeyParse x pubkey (castPtr i) (fromIntegral il)
assertBool "parsed public key" (isSuccess ret)
where
der = fst $ B16.decode
"03dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705"
ecPubKeySerializeTest :: Assertion
ecPubKeySerializeTest = do
(ret, dec) <- liftIO $ useAsCStringLen der $ \(i, il) ->
alloca $ \k -> alloca $ \ol -> allocaBytes 72 $ \o -> do
poke ol 72
x <- contextCreate verify
ret1 <-
ecPubKeyParse x k (castPtr i) (fromIntegral il)
unless (isSuccess ret1) $ error "failed to parse pubkey"
ret2 <- ecPubKeySerialize
x o ol k compressed
len <- fromIntegral <$> peek ol
decoded <- packCStringLen (castPtr o, len)
return (ret2, decoded)
assertBool "serialized public key successfully" $ isSuccess ret
assertEqual "public key matches" der dec
where
der = fst $ B16.decode
"03dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705"
pubkeyStorableTest :: Assertion
pubkeyStorableTest = do
(pk1, pk2, dec) <- liftIO $ useAsCStringLen der $ \(i, il) -> do
x <- contextCreate verify
pk1 <- alloca $ \pk -> do
ret <-
ecPubKeyParse x pk (castPtr i) (fromIntegral il)
unless (isSuccess ret) $ error "failed to parse pubkey"
peek pk
(pk2, dec) <- alloca $ \pk -> alloca $ \ol -> allocaBytes 72 $ \o -> do
poke ol 72
poke pk pk1
ret <-
ecPubKeySerialize x o ol pk compressed
unless (isSuccess ret) $ error "failed to serialize pubkey"
len <- fromIntegral <$> peek ol
dec <- packCStringLen (castPtr o, len)
pk2 <- peek pk
return (pk2, dec)
return (pk1, pk2, dec)
assertEqual "poke/peek public key" pk1 pk2
assertEqual "public key matches" der dec
where
der = fst $ B16.decode
"03dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705"
signatureStorableTest :: Assertion
signatureStorableTest = do
(sig, ret) <- liftIO $ do
x <- contextCreate verify
g <- alloca $ \pc -> alloca $ \pg -> do
poke pc cpt
ret <- ecdsaSignatureParseCompact x pg (castPtr pc)
unless (isSuccess ret) $ error "failed to parse signature"
peek pg
alloca $ \pc -> alloca $ \pg -> do
poke pg g
ret <- ecdsaSignatureSerializeCompact x pc pg
c <- peek pc
return (c, ret)
assertBool "successful serialization" (isSuccess ret)
assertEqual "signatures match" cpt sig
where
cpt = CompactSig
0xf502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37fb22
0x6f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e3045
ecdsaSignatureParseDerTest :: Assertion
ecdsaSignatureParseDerTest = do
ret <- liftIO $ useAsCStringLen der $ \(d, dl) -> alloca $ \s -> do
x <- contextCreate verify
ecdsaSignatureParseDer x s (castPtr d) (fromIntegral dl)
assertBool "parsed signature successfully" $ isSuccess ret
where
der = fst $ B16.decode
"3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\
\fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\
\45"
laxDerParseTest :: Assertion
laxDerParseTest = do
ret <- liftIO $ useAsCStringLen der $ \(d, dl) -> alloca $ \s -> do
x <- contextCreate verify
laxDerParse x s (castPtr d) (fromIntegral dl)
assertBool "parsed signature successfully" $ isSuccess ret
where
der = fst $ B16.decode
"30450220f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37fb\
\2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e3045"
parseDer :: Ptr Ctx -> ByteString -> IO Sig64
parseDer x bs = useAsCStringLen bs $ \(d, dl) -> alloca $ \s -> do
ret <- ecdsaSignatureParseDer x s (castPtr d) (fromIntegral dl)
unless (isSuccess ret) $ error "could not parse DER"
peek s
ecdsaSignatureSerializeDerTest :: Assertion
ecdsaSignatureSerializeDerTest = do
(ret, enc) <- liftIO $ do
x <- contextCreate verify
sig <- parseDer x der
alloca $ \s -> alloca $ \ol -> allocaBytes 72 $ \o -> do
poke ol 72
poke s sig
ret <-
ecdsaSignatureSerializeDer x o ol s
len <- fromIntegral <$> peek ol
enc <- packCStringLen (castPtr o, len)
return (ret, enc)
assertBool "serialization successful" $ isSuccess ret
assertEqual "signatures match" der enc
where
der = fst $ B16.decode
"3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\
\fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\
\45"
ecdsaVerifyTest :: Assertion
ecdsaVerifyTest = do
ret <- liftIO $ do
x <- contextCreate verify
sig <- parseDer x der
pk <- useAsCStringLen pub $ \(p, pl) -> alloca $ \k -> do
ret <-
ecPubKeyParse x k (castPtr p) (fromIntegral pl)
unless (isSuccess ret) $ error "could not parse public key"
peek k
alloca $ \m -> alloca $ \k -> alloca $ \s -> do
poke m msg
poke k pk
poke s sig
ecdsaVerify x s m k
assertBool "signature valid" $ isSuccess ret
where
der = fst $ B16.decode
"3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\
\fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\
\45"
pub = fst $ B16.decode
"04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
\3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
msg = Msg32 $ fst $ B16.decode
"f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
signCtx :: IO (Ptr Ctx)
signCtx = contextCreate sign >>= \c ->
withEntropy (contextRandomize c) >>= \r ->
unless (isSuccess r) (error "failed to randomize context") >> return c
ecdsaSignTest :: Assertion
ecdsaSignTest = do
(ret, sig) <- liftIO $ do
x <- signCtx
alloca $ \s -> alloca $ \m -> alloca $ \k -> alloca $ \ol ->
allocaBytes 72 $ \o -> do
poke ol 72
poke m msg
poke k key
ret1 <-
-- TODO:
-- ecdsaSign x s m k nonce_function_default nullPtr
ecdsaSign x s m k nullFunPtr nullPtr
ret2 <- ecdsaSignatureSerializeDer x o ol s
unless (isSuccess ret2) $error "could not serialize signature"
len <- peek ol
sig <- packCStringLen (castPtr o, fromIntegral len)
return (ret1, sig)
assertBool "successful signing" $ isSuccess ret
assertEqual "signature matches" sig der
where
msg = Msg32 $ fst $ B16.decode
"f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
key = SecKey32 $ fst $ B16.decode
"f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
der = fst $ B16.decode
"3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\
\fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\
\45"
ecSecKeyVerifyTest :: Assertion
ecSecKeyVerifyTest = do
ret <- liftIO $ alloca $ \k -> do
poke k key
x <- signCtx
ecSecKeyVerify x k
assertBool "valid secret key" $ isSuccess ret
where
key = SecKey32 $ fst $ B16.decode
"f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
ecPubkeyCreateTest :: Assertion
ecPubkeyCreateTest = do
(ret, pk) <- liftIO $ alloca $ \p -> alloca $ \k -> do
poke k key
x <- signCtx
ret <- ecPubKeyCreate x p k
allocaBytes 65 $ \o -> alloca $ \ol -> do
poke ol 65
rets <- ecPubKeySerialize
x o ol p uncompressed
unless (isSuccess rets) $ error "failed to serialive public key"
len <- fromIntegral <$> peek ol
pk <- packCStringLen (castPtr o, len)
return (ret, pk)
assertBool "successful pubkey creation" $ isSuccess ret
assertEqual "public key matches" pub pk
where
key = SecKey32 $ fst $ B16.decode
"f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
pub = fst $ B16.decode
"04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
\3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
ecSecKeyTweakAddTest :: Assertion
ecSecKeyTweakAddTest = do
(ret, tweaked) <- liftIO $ do
x <- signCtx
alloca $ \w -> alloca $ \k -> do
poke w tweak
poke k key
ret <- ecSecKeyTweakAdd x k w
tweaked <- peek k
return (ret, tweaked)
assertBool "successful secret key tweak" $ isSuccess ret
assertEqual "tweaked keys match" expected tweaked
where
key = SecKey32 $ fst $ B16.decode
"f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
tweak = Tweak32 $ fst $ B16.decode
"f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
expected = SecKey32 $ fst $ B16.decode
"ec1e3ce1cefa18a671d51125e2b249688d934b0e28f5d1665384d9b02f929059"
ecSecKeyTweakMulTest :: Assertion
ecSecKeyTweakMulTest = do
(ret, tweaked) <- liftIO $ do
x <- contextCreate sign
retr <- withEntropy $ contextRandomize x
unless (isSuccess retr) $ error "failed to randomize context"
alloca $ \w -> alloca $ \k -> do
poke w tweak
poke k key
ret <- ecSecKeyTweakMul x k w
tweaked <- peek k
return (ret, tweaked)
assertBool "successful secret key tweak" $ isSuccess ret
assertEqual "tweaked keys match" expected tweaked
where
key = SecKey32 $ fst $ B16.decode
"f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
tweak = Tweak32 $ fst $ B16.decode
"f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
expected = SecKey32 $ fst $ B16.decode
"a96f5962493acb179f60a86a9785fc7a30e0c39b64c09d24fe064d9aef15e4c0"
serializeKey :: Ptr Ctx -> Ptr PubKey64 -> IO ByteString
serializeKey x p = allocaBytes 72 $ \d -> alloca $ \dl -> do
ret <- ecPubKeySerialize x d dl p uncompressed
unless (isSuccess ret) $ error "could not serialize public key"
len <- peek dl
packCStringLen (castPtr d, fromIntegral len)
parseKey :: Ptr Ctx -> ByteString -> IO PubKey64
parseKey x bs = alloca $ \p -> useAsCStringLen bs $ \(d, dl) -> do
ret <- ecPubKeyParse x p (castPtr d) (fromIntegral dl)
unless (isSuccess ret) $ error "could not parse public key"
peek p
ecPubKeyTweakAddTest :: Assertion
ecPubKeyTweakAddTest = do
(ret, tweaked) <- liftIO $ do
x <- contextCreate verify
pk <- parseKey x pub
alloca $ \w -> alloca $ \p -> do
poke w tweak
poke p pk
ret <- ecPubKeyTweakAdd x p w
tweaked <- serializeKey x p
return (ret, tweaked)
assertBool "successful secret key tweak" $ isSuccess ret
assertEqual "tweaked keys match" expected tweaked
where
pub = fst $ B16.decode
"04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
\3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
tweak = Tweak32 $ fst $ B16.decode
"f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
expected = fst $ B16.decode
"04441c3982b97576646e0df0c96736063df6b42f2ee566d13b9f6424302d1379e518fd\
\c87a14c5435bff7a5db4552042cb4120c6b86a4bbd3d0643f3c14ad01368"
ecPubKeyTweakMulTest :: Assertion
ecPubKeyTweakMulTest = do
(ret, tweaked) <- liftIO $ do
x <- contextCreate verify
pk <- parseKey x pub
alloca $ \w -> alloca $ \p -> do
poke w tweak
poke p pk
ret <- ecPubKeyTweakMul x p w
tweaked <- serializeKey x p
return (ret, tweaked)
assertBool "successful secret key tweak" $ isSuccess ret
assertEqual "tweaked keys match" expected tweaked
where
pub = fst $ B16.decode
"04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
\3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
tweak = Tweak32 $ fst $ B16.decode
"f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
expected = fst $ B16.decode
"04f379dc99cdf5c83e433defa267fbb3377d61d6b779c06a0e4ce29ae3ff5353b12ae4\
\9c9d07e7368f2ba5a446c203255ce912322991a2d6a9d5d5761c61ed1845"
ecPubKeyCombineTest :: Assertion
ecPubKeyCombineTest = do
(ret, com) <- liftIO $ alloca $ \p1 -> alloca $ \p2 -> alloca $ \p3 ->
allocaArray 3 $ \a -> alloca $ \p -> do
x <- contextCreate verify
parse x pub1 p1
parse x pub2 p2
parse x pub3 p3
pokeArray a [p1, p2, p3]
ret <- ecPubKeyCombine x p a 3
com <- serializeKey x p
return (ret, com)
assertBool "successful key combination" $ isSuccess ret
assertEqual "combined keys match" expected com
where
parse x pub p = useAsCStringLen pub $ \(d, dl) -> do
ret <- ecPubKeyParse x p (castPtr d) (fromIntegral dl)
unless (isSuccess ret) $ error "could not parse public key"
pub1 = fst $ B16.decode
"04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
\3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
pub2 = fst $ B16.decode
"0487d82042d93447008dfe2af762068a1e53ff394a5bf8f68a045fa642b99ea5d153f5\
\77dd2dba6c7ae4cfd7b6622409d7edd2d76dd13a8092cd3af97b77bd2c77"
pub3 = fst $ B16.decode
"049b101edcbe1ee37ff6b2318526a425b629e823d7d8d9154417880595a28000ee3feb\
\d908754b8ce4e491aa6fe488b41fb5d4bb3788e33c9ff95a7a9229166d59"
expected = fst $ B16.decode
"043d9a7ec70011efc23c33a7e62d2ea73cca87797e3b659d93bea6aa871aebde56c3bc\
\6134ca82e324b0ab9c0e601a6d2933afe7fb5d9f3aae900f5c5dc6e362c8"