@@ -0,0 +1,378 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+module Crypto.Secp256k1.InternalSpec (spec) where
3+
4+import Control.Monad
5+import Control.Monad.Trans
6+import Crypto.Secp256k1.Internal
7+import Data.ByteString (ByteString, copy, packCStringLen,
8+ useAsCStringLen)
9+import qualified Data.ByteString.Base16 as B16
10+import Data.Either (fromRight)
11+import Foreign
12+import System.Entropy
13+import Test.HUnit (Assertion, assertBool, assertEqual)
14+import Test.Hspec
15+
16+spec :: Spec
17+spec = do
18+ describe "housekeeping" $ do
19+ it "creates context" createContextTest
20+ it "randomizes context" randomizeContextTest
21+ it "clones context" cloneContextTest
22+ describe "serialization" $ do
23+ it "parses public key" ecPubkeyParseTest
24+ it "serializes public key" ecPubKeySerializeTest
25+ it "parses DER signature" ecdsaSignatureParseDerTest
26+ it "serializes DER signature" ecdsaSignatureSerializeDerTest
27+ describe "signatures" $ do
28+ it "verifies signature" ecdsaVerifyTest
29+ -- TODO:
30+ -- , testCase "RFC6979 nonce function" nonce_function_rfc6979_test
31+ it "signs message" ecdsaSignTest
32+ describe "secret keys" $ do
33+ it "verifies secret key" ecSecKeyVerifyTest
34+ it "creates public key" ecPubkeyCreateTest
35+ it "adds secret key" ecSecKeyTweakAddTest
36+ it "multiplies secret key" ecSecKeyTweakMulTest
37+ describe "public keys" $ do
38+ it "adds public key" ecPubKeyTweakAddTest
39+ it "multiplies public key" ecPubKeyTweakMulTest
40+ it "combines public keys" ecPubKeyCombineTest
41+
42+withEntropy :: (Ptr Seed32 -> IO a) -> IO a
43+withEntropy f =
44+ getEntropy 32 >>= \e ->
45+ useByteString e $ \(s, _) -> f s
46+
47+createContextTest :: Assertion
48+createContextTest = do
49+ context_ptr <- liftIO $ contextCreate signVerify
50+ assertBool "context not null" $ context_ptr /= nullPtr
51+
52+randomizeContextTest :: Assertion
53+randomizeContextTest = do
54+ ret <- liftIO $ contextCreate sign >>= \x ->
55+ withEntropy (contextRandomize x)
56+ assertBool "context randomized" $ isSuccess ret
57+
58+cloneContextTest :: Assertion
59+cloneContextTest = do
60+ (x1, x2) <- liftIO $ do
61+ x1 <- contextCreate signVerify
62+ ret <- withEntropy $ contextRandomize x1
63+ unless (isSuccess ret) $ error "failed to randomize context"
64+ x2 <- contextClone x1
65+ return (x1, x2)
66+ assertBool "original context not null" $ x1 /= nullPtr
67+ assertBool "cloned context not null" $ x2 /= nullPtr
68+ assertBool "context ptrs different" $ x1 /= x2
69+
70+ecPubkeyParseTest :: Assertion
71+ecPubkeyParseTest = do
72+ ret <- liftIO $ useAsCStringLen der $ \(i, il) -> do
73+ x <- contextCreate verify
74+ allocaBytes 64 $ \pubkey ->
75+ ecPubKeyParse x pubkey (castPtr i) (fromIntegral il)
76+ assertBool "parsed public key" (isSuccess ret)
77+ where
78+ der = fromRight undefined $ B16.decodeBase16
79+ "03dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705"
80+
81+ecPubKeySerializeTest :: Assertion
82+ecPubKeySerializeTest = do
83+ (ret, dec) <- liftIO $
84+ useByteString der $ \(i, il) ->
85+ allocaBytes 64 $ \k ->
86+ alloca $ \ol ->
87+ allocaBytes 72 $ \o -> do
88+ poke ol 72
89+ x <- contextCreate verify
90+ ret1 <- ecPubKeyParse x k i il
91+ unless (isSuccess ret1) $ error "failed to parse pubkey"
92+ ret2 <- ecPubKeySerialize x o ol k compressed
93+ len <- fromIntegral <$> peek ol
94+ decoded <- packCStringLen (castPtr o, len)
95+ return (ret2, decoded)
96+ assertBool "serialized public key successfully" $ isSuccess ret
97+ assertEqual "public key matches" der dec
98+ where
99+ der = fromRight undefined $ B16.decodeBase16
100+ "03dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd44705"
101+
102+ecdsaSignatureParseDerTest :: Assertion
103+ecdsaSignatureParseDerTest = do
104+ ret <- liftIO $ useAsCStringLen der $ \(d, dl) -> allocaBytes 64 $ \s -> do
105+ x <- contextCreate verify
106+ ecdsaSignatureParseDer x s (castPtr d) (fromIntegral dl)
107+ assertBool "parsed signature successfully" $ isSuccess ret
108+ where
109+ der = fromRight undefined $ B16.decodeBase16
110+ "3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\
111+ \fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\
112+ \45"
113+
114+parseDer :: Ctx -> ByteString -> IO ByteString
115+parseDer x bs =
116+ useAsCStringLen bs $ \(d, dl) ->
117+ allocaBytes 64 $ \s -> do
118+ ret <- ecdsaSignatureParseDer x s (castPtr d) (fromIntegral dl)
119+ unless (isSuccess ret) $ error "could not parse DER"
120+ packByteString (s, 64)
121+
122+ecdsaSignatureSerializeDerTest :: Assertion
123+ecdsaSignatureSerializeDerTest = do
124+ (ret, enc) <- liftIO $ do
125+ x <- contextCreate verify
126+ sig <- parseDer x der
127+ alloca $ \ol ->
128+ allocaBytes 72 $ \o ->
129+ useByteString sig $ \(s, _) -> do
130+ poke ol 72
131+ ret <- ecdsaSignatureSerializeDer x o ol s
132+ len <- fromIntegral <$> peek ol
133+ enc <- packCStringLen (castPtr o, len)
134+ return (ret, enc)
135+ assertBool "serialization successful" $ isSuccess ret
136+ assertEqual "signatures match" der enc
137+ where
138+ der = fromRight undefined $ B16.decodeBase16
139+ "3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\
140+ \fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\
141+ \45"
142+
143+ecdsaVerifyTest :: Assertion
144+ecdsaVerifyTest = do
145+ ret <- liftIO $ do
146+ x <- contextCreate verify
147+ sig <- parseDer x der
148+ pk <- useByteString pub $ \(p, pl) ->
149+ allocaBytes 64 $ \k -> do
150+ ret <- ecPubKeyParse x k p (fromIntegral pl)
151+ unless (isSuccess ret) $ error "could not parse public key"
152+ packByteString (k, 64)
153+ useByteString msg $ \(m, _) ->
154+ useByteString pk $ \(k, _) ->
155+ useByteString sig $ \(s, _) ->
156+ ecdsaVerify x s m k
157+ assertBool "signature valid" $ isSuccess ret
158+ where
159+ der = fromRight undefined $ B16.decodeBase16
160+ "3045022100f502bfa07af43e7ef265618b0d929a7619ee01d6150e37eb6eaaf2c8bd37\
161+ \fb2202206f0415ab0e9a977afd78b2c26ef39b3952096d319fd4b101c768ad6c132e30\
162+ \45"
163+ pub = fromRight undefined $ B16.decodeBase16
164+ "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
165+ \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
166+ msg = fromRight undefined $ B16.decodeBase16
167+ "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
168+
169+signCtx :: IO Ctx
170+signCtx = contextCreate sign >>= \c ->
171+ withEntropy (contextRandomize c) >>= \r ->
172+ unless (isSuccess r) (error "failed to randomize context") >> return c
173+
174+createPubKey :: Ctx -> Ptr SecKey32 -> Ptr PubKey64 -> IO ()
175+createPubKey x k p = do
176+ ret <- ecPubKeyCreate x p k
177+ unless (isSuccess ret) $ error "failed to create public key"
178+
179+ecdsaSignTest :: Assertion
180+ecdsaSignTest = do
181+ der <- liftIO $ do
182+ x <- signCtx
183+ allocaBytes 64 $ \s ->
184+ useByteString msg $ \(m, _) ->
185+ useByteString key $ \(k, _) ->
186+ alloca $ \ol ->
187+ allocaBytes 72 $ \o -> do
188+ poke ol 72
189+ ret1 <- ecdsaSign x s m k nullFunPtr nullPtr
190+ unless (isSuccess ret1) $ error "could not sign message"
191+ ret2 <- ecdsaSignatureSerializeDer x o ol s
192+ unless (isSuccess ret2) $ error "could not serialize signature"
193+ len <- peek ol
194+ packCStringLen (castPtr o, fromIntegral len)
195+ ret <- liftIO $ do
196+ pub <- allocaBytes 64 $ \p ->
197+ useByteString key $ \(s, _) -> do
198+ x <- signCtx
199+ createPubKey x s p
200+ packByteString (p, 64)
201+ useByteString msg $ \(m, _) ->
202+ useByteString pub $ \(p, _) -> do
203+ x <- contextCreate verify
204+ s' <- parseDer x der
205+ useByteString s' $ \(s, _) -> ecdsaVerify x s m p
206+ assertBool "signature matches" (isSuccess ret)
207+ where
208+ msg = fromRight undefined $ B16.decodeBase16
209+ "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
210+ key = fromRight undefined $ B16.decodeBase16
211+ "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
212+
213+
214+ecSecKeyVerifyTest :: Assertion
215+ecSecKeyVerifyTest = do
216+ ret <- liftIO $ useByteString key $ \(k, _) -> do
217+ x <- signCtx
218+ ecSecKeyVerify x k
219+ assertBool "valid secret key" $ isSuccess ret
220+ where
221+ key = fromRight undefined $ B16.decodeBase16
222+ "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
223+
224+ecPubkeyCreateTest :: Assertion
225+ecPubkeyCreateTest = do
226+ pk <- liftIO $
227+ useByteString key $ \(s, _) ->
228+ allocaBytes 64 $ \k -> do
229+ x <- signCtx
230+ createPubKey x s k
231+ allocaBytes 65 $ \o ->
232+ alloca $ \ol -> do
233+ poke ol 65
234+ rets <- ecPubKeySerialize x o ol k uncompressed
235+ unless (isSuccess rets) $ error "failed to serialize public key"
236+ len <- fromIntegral <$> peek ol
237+ packCStringLen (castPtr o, len)
238+ assertEqual "public key matches" pub pk
239+ where
240+ key = fromRight undefined $ B16.decodeBase16
241+ "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
242+ pub = fromRight undefined $ B16.decodeBase16
243+ "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
244+ \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
245+
246+ecSecKeyTweakAddTest :: Assertion
247+ecSecKeyTweakAddTest = do
248+ (ret, tweaked) <- liftIO $
249+ signCtx >>= \x ->
250+ useByteString tweak $ \(w, _) ->
251+ useByteString key $ \(k, _) -> do
252+ ret <- ecSecKeyTweakAdd x k w
253+ tweaked <- packByteString (k, 32)
254+ return (ret, tweaked)
255+ assertBool "successful secret key tweak" $ isSuccess ret
256+ assertEqual "tweaked keys match" expected tweaked
257+ where
258+ key = fromRight undefined $ B16.decodeBase16
259+ "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
260+ tweak = fromRight undefined $ B16.decodeBase16
261+ "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
262+ expected = fromRight undefined $ B16.decodeBase16
263+ "ec1e3ce1cefa18a671d51125e2b249688d934b0e28f5d1665384d9b02f929059"
264+
265+ecSecKeyTweakMulTest :: Assertion
266+ecSecKeyTweakMulTest = do
267+ (ret, tweaked) <- liftIO $ do
268+ x <- contextCreate sign
269+ retr <- withEntropy $ contextRandomize x
270+ unless (isSuccess retr) $ error "failed to randomize context"
271+ useByteString tweak $ \(w, _) -> useByteString key $ \(k, _) -> do
272+ ret <- ecSecKeyTweakMul x k w
273+ tweaked <- packByteString (k, 32)
274+ return (ret, tweaked)
275+ assertBool "successful secret key tweak" $ isSuccess ret
276+ assertEqual "tweaked keys match" expected tweaked
277+ where
278+ key = fromRight undefined $ B16.decodeBase16
279+ "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
280+ tweak = fromRight undefined $ B16.decodeBase16
281+ "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
282+ expected = fromRight undefined $ B16.decodeBase16
283+ "a96f5962493acb179f60a86a9785fc7a30e0c39b64c09d24fe064d9aef15e4c0"
284+
285+serializeKey :: Ctx -> Ptr PubKey64 -> IO ByteString
286+serializeKey x p = allocaBytes 72 $ \d -> alloca $ \dl -> do
287+ poke dl 72
288+ ret <- ecPubKeySerialize x d dl p uncompressed
289+ unless (isSuccess ret) $ error "could not serialize public key"
290+ len <- peek dl
291+ packCStringLen (castPtr d, fromIntegral len)
292+
293+parseKey :: Ctx -> ByteString -> IO ByteString
294+parseKey x bs =
295+ allocaBytes 64 $ \p ->
296+ useByteString bs $ \(d, dl) -> do
297+ ret <- ecPubKeyParse x p d dl
298+ unless (isSuccess ret) $ error "could not parse public key"
299+ packByteString (p, 64)
300+
301+ecPubKeyTweakAddTest :: Assertion
302+ecPubKeyTweakAddTest = do
303+ (ret, tweaked) <- liftIO $ do
304+ x <- contextCreate verify
305+ pk <- copy <$> parseKey x pub
306+ useByteString tweak $ \(w, _) ->
307+ useByteString pk $ \(p, _) -> do
308+ ret <- ecPubKeyTweakAdd x p w
309+ tweaked <- serializeKey x p
310+ return (ret, tweaked)
311+ assertBool "successful secret key tweak" $ isSuccess ret
312+ assertEqual "tweaked keys match" expected tweaked
313+ where
314+ pub = fromRight undefined $ B16.decodeBase16
315+ "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
316+ \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
317+ tweak = fromRight undefined $ B16.decodeBase16
318+ "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
319+ expected = fromRight undefined $ B16.decodeBase16
320+ "04441c3982b97576646e0df0c96736063df6b42f2ee566d13b9f6424302d1379e518fd\
321+ \c87a14c5435bff7a5db4552042cb4120c6b86a4bbd3d0643f3c14ad01368"
322+
323+ecPubKeyTweakMulTest :: Assertion
324+ecPubKeyTweakMulTest = do
325+ (ret, tweaked) <- liftIO $ do
326+ x <- contextCreate verify
327+ pk <- copy <$> parseKey x pub
328+ useByteString tweak $ \(w, _) ->
329+ useByteString pk $ \(p, _) -> do
330+ ret <- ecPubKeyTweakMul x p w
331+ tweaked <- serializeKey x p
332+ return (ret, tweaked)
333+ assertBool "successful secret key tweak" $ isSuccess ret
334+ assertEqual "tweaked keys match" expected tweaked
335+ where
336+ pub = fromRight undefined $ B16.decodeBase16
337+ "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
338+ \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
339+ tweak = fromRight undefined $ B16.decodeBase16
340+ "f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
341+ expected = fromRight undefined $ B16.decodeBase16
342+ "04f379dc99cdf5c83e433defa267fbb3377d61d6b779c06a0e4ce29ae3ff5353b12ae4\
343+ \9c9d07e7368f2ba5a446c203255ce912322991a2d6a9d5d5761c61ed1845"
344+
345+ecPubKeyCombineTest :: Assertion
346+ecPubKeyCombineTest = do
347+ (ret, com) <- liftIO $
348+ allocaBytes 64 $ \p1 ->
349+ allocaBytes 64 $ \p2 ->
350+ allocaBytes 64 $ \p3 ->
351+ allocaArray 3 $ \a ->
352+ allocaBytes 64 $ \p -> do
353+ x <- contextCreate verify
354+ parse x pub1 p1
355+ parse x pub2 p2
356+ parse x pub3 p3
357+ pokeArray a [p1, p2, p3]
358+ ret <- ecPubKeyCombine x p a 3
359+ com <- serializeKey x p
360+ return (ret, com)
361+ assertBool "successful key combination" $ isSuccess ret
362+ assertEqual "combined keys match" expected com
363+ where
364+ parse x pub p = useByteString pub $ \(d, dl) -> do
365+ ret <- ecPubKeyParse x p d dl
366+ unless (isSuccess ret) $ error "could not parse public key"
367+ pub1 = fromRight undefined $ B16.decodeBase16
368+ "04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd447051221\
369+ \3d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
370+ pub2 = fromRight undefined $ B16.decodeBase16
371+ "0487d82042d93447008dfe2af762068a1e53ff394a5bf8f68a045fa642b99ea5d153f5\
372+ \77dd2dba6c7ae4cfd7b6622409d7edd2d76dd13a8092cd3af97b77bd2c77"
373+ pub3 = fromRight undefined $ B16.decodeBase16
374+ "049b101edcbe1ee37ff6b2318526a425b629e823d7d8d9154417880595a28000ee3feb\
375+ \d908754b8ce4e491aa6fe488b41fb5d4bb3788e33c9ff95a7a9229166d59"
376+ expected = fromRight undefined $ B16.decodeBase16
377+ "043d9a7ec70011efc23c33a7e62d2ea73cca87797e3b659d93bea6aa871aebde56c3bc\
378+ \6134ca82e324b0ab9c0e601a6d2933afe7fb5d9f3aae900f5c5dc6e362c8"