Skip to content

Commit 4c4e86a

Browse files
committed
add test suite
1 parent f407e52 commit 4c4e86a

File tree

8 files changed

+296
-158
lines changed

8 files changed

+296
-158
lines changed

hie.yaml

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
cradle:
2-
stack:
3-
- path: "./"
4-
component: "libsecp256k1:lib"
5-
- path: "./test"
2+
cabal:
3+
- path: "src"
4+
component: "lib:libsecp256k1"
5+
6+
- path: "test"
67
component: "libsecp256k1:test:spec"

src/Crypto/Secp256k1.hs

+23-68
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,14 @@ module Crypto.Secp256k1 (
5252
recSigToSig,
5353
derivePubKey,
5454
keyPairCreate,
55+
keyPairSecKey,
5556
keyPairPubKeyXY,
5657
keyPairPubKeyXO,
5758
xyToXO,
5859

5960
-- * Tweaks
60-
ecSecKeyTweakAdd,
61-
ecSecKeyTweakMul,
61+
secKeyTweakAdd,
62+
secKeyTweakMul,
6263
keyPairPubKeyXOTweakAdd,
6364
pubKeyCombine,
6465
pubKeyNegate,
@@ -70,8 +71,6 @@ module Crypto.Secp256k1 (
7071

7172
-- * Schnorr Operations
7273
schnorrSign,
73-
SchnorrExtra (..),
74-
schnorrSignCustom,
7574
schnorrVerify,
7675

7776
-- * Other
@@ -109,6 +108,8 @@ import Foreign (
109108
FunPtr,
110109
Ptr,
111110
Storable,
111+
Word32,
112+
Word64,
112113
Word8,
113114
alloca,
114115
allocaArray,
@@ -136,6 +137,7 @@ import Foreign (
136137
withForeignPtr,
137138
)
138139
import Foreign.C (CInt (..), CSize (..))
140+
import Foreign.Storable (Storable (..))
139141
import GHC.Generics (Generic)
140142
import GHC.IO.Handle.Text (memcpy)
141143
import System.IO.Unsafe (unsafePerformIO)
@@ -230,7 +232,7 @@ newtype Signature = Signature {signatureFPtr :: ForeignPtr Prim.Sig64}
230232

231233

232234
instance Show Signature where
233-
show sig = "0x" <> B8.unpack (BA.convertToBase BA.Base16 (exportSignatureCompact sig))
235+
show sig = "0x" <> (B8.unpack . encodeBase16) (exportSignatureCompact sig)
234236
instance Eq Signature where
235237
sig == sig' = unsafePerformIO . evalContT $ do
236238
sigp <- ContT $ withForeignPtr (signatureFPtr sig)
@@ -242,6 +244,10 @@ instance Eq Signature where
242244
newtype RecoverableSignature = RecoverableSignature {recoverableSignatureFPtr :: ForeignPtr Prim.RecSig65}
243245

244246

247+
instance Show RecoverableSignature where
248+
show recSig = "0x" <> (B8.unpack . encodeBase16) (exportRecoverableSignature recSig)
249+
250+
245251
instance Eq RecoverableSignature where
246252
rs == rs' = unsafePerformIO . evalContT $ do
247253
rsp <- ContT $ withForeignPtr (recoverableSignatureFPtr rs)
@@ -253,6 +259,10 @@ instance Eq RecoverableSignature where
253259
newtype Tweak = Tweak {tweakFPtr :: ForeignPtr Prim.Tweak32}
254260

255261

262+
instance Show Tweak where
263+
show (Tweak fptr) = show (SecKey $ castForeignPtr fptr)
264+
265+
256266
instance Eq Tweak where
257267
sk == sk' = unsafePerformIO . evalContT $ do
258268
skp <- ContT $ withForeignPtr (tweakFPtr sk)
@@ -410,6 +420,7 @@ exportRecoverableSignature RecoverableSignature{..} = unsafePerformIO . evalCont
410420
recIdPtr <- malloc
411421
_ret <- Prim.ecdsaRecoverableSignatureSerializeCompact ctx outBuf recIdPtr recSigPtr
412422
recId <- peek recIdPtr
423+
pokeByteOff outBuf 64 recId
413424
unsafePackByteString (outBuf, 65)
414425

415426

@@ -437,8 +448,7 @@ ecdsaSign (SecKey skFPtr) msgHash
437448
(msgHashPtr, _) <- ContT (unsafeUseByteString msgHash)
438449
lift $ do
439450
sigBuf <- mallocBytes 64
440-
entropy <- mallocBytes 32
441-
ret <- Prim.ecdsaSign ctx sigBuf msgHashPtr skPtr Prim.nonceFunctionRfc6979 entropy
451+
ret <- Prim.ecdsaSign ctx sigBuf msgHashPtr skPtr Prim.nonceFunctionDefault nullPtr
442452
if isSuccess ret
443453
then Just . Signature <$> newForeignPtr finalizerFree sigBuf
444454
else free sigBuf $> Nothing
@@ -513,8 +523,8 @@ ecdh SecKey{..} PubKeyXY{..} = unsafePerformIO . evalContT $ do
513523

514524

515525
-- -- | Add 'Tweak' to 'SecKey'.
516-
ecSecKeyTweakAdd :: SecKey -> Tweak -> Maybe SecKey
517-
ecSecKeyTweakAdd SecKey{..} Tweak{..} = unsafePerformIO . evalContT $ do
526+
secKeyTweakAdd :: SecKey -> Tweak -> Maybe SecKey
527+
secKeyTweakAdd SecKey{..} Tweak{..} = unsafePerformIO . evalContT $ do
518528
skPtr <- ContT (withForeignPtr secKeyFPtr)
519529
skOut <- lift (mallocBytes 32)
520530
lift (memcpy skOut skPtr 32)
@@ -527,8 +537,8 @@ ecSecKeyTweakAdd SecKey{..} Tweak{..} = unsafePerformIO . evalContT $ do
527537

528538

529539
-- | Multiply 'SecKey' by 'Tweak'.
530-
ecSecKeyTweakMul :: SecKey -> Tweak -> Maybe SecKey
531-
ecSecKeyTweakMul SecKey{..} Tweak{..} = unsafePerformIO . evalContT $ do
540+
secKeyTweakMul :: SecKey -> Tweak -> Maybe SecKey
541+
secKeyTweakMul SecKey{..} Tweak{..} = unsafePerformIO . evalContT $ do
532542
skPtr <- ContT (withForeignPtr secKeyFPtr)
533543
skOut <- lift (mallocBytes 32)
534544
lift (memcpy skOut skPtr 32)
@@ -627,62 +637,6 @@ schnorrSign KeyPair{..} bs
627637
else free sigBuf $> Nothing
628638

629639

630-
-- | Extra parameters object for alternative nonce generation
631-
data SchnorrExtra a = Storable a =>
632-
SchnorrExtra
633-
{ schnorrExtraNonceFunHardened :: ByteString -> SecKey -> PubKeyXO -> ByteString -> a -> Maybe (SizedByteArray 32 ByteString)
634-
, schnorrExtraData :: a
635-
}
636-
637-
638-
-- | Compute a schnorr signature with an alternative scheme for generating nonces, it is not recommended you use this
639-
-- unless you know what you are doing. Instead, favor the usage of 'schnorrSign'
640-
schnorrSignCustom :: forall a. KeyPair -> ByteString -> SchnorrExtra a -> Maybe Signature
641-
schnorrSignCustom KeyPair{..} msg SchnorrExtra{..} = unsafePerformIO . evalContT $ do
642-
(msgPtr, msgLen) <- ContT (unsafeUseByteString msg)
643-
keyPairPtr <- ContT (withForeignPtr keyPairFPtr)
644-
lift $ do
645-
sigBuf <- mallocBytes 64
646-
-- convert fn into funptr
647-
funptr <- mkNonceFunHardened primFn
648-
-- allocate memory for extra data ptr
649-
dataptr <- malloc
650-
-- copy data to new pointer
651-
poke dataptr schnorrExtraData
652-
-- allocate extraparams structure
653-
extraPtr <- mallocBytes (4 + sizeOf funptr + sizeOf dataptr)
654-
-- fill magic
655-
pokeByteOff extraPtr 0 (0xDA :: Word8)
656-
pokeByteOff extraPtr 1 (0x6F :: Word8)
657-
pokeByteOff extraPtr 2 (0xB3 :: Word8)
658-
pokeByteOff extraPtr 3 (0x8C :: Word8)
659-
-- fill funptr
660-
pokeByteOff extraPtr 4 funptr
661-
-- fill dataptr
662-
pokeByteOff extraPtr (4 + sizeOf funptr) dataptr
663-
ret <- Prim.schnorrsigSignCustom ctx sigBuf msgPtr msgLen keyPairPtr extraPtr
664-
freeHaskellFunPtr funptr
665-
free dataptr
666-
free extraPtr
667-
if isSuccess ret
668-
then Just . Signature <$> newForeignPtr finalizerFree sigBuf
669-
else free sigBuf $> Nothing
670-
where
671-
primFn :: Storable a => Prim.NonceFunHardened a
672-
primFn outBuf msgPtr msgLen sk xopk algo algolen dataPtr = do
673-
msg <- unsafePackByteString (msgPtr, msgLen)
674-
sk <- SecKey <$> newForeignPtr_ (castPtr sk)
675-
xopk <- PubKeyXO <$> newForeignPtr_ (castPtr xopk)
676-
algo <- unsafePackByteString (algo, algolen)
677-
extra <- peek dataPtr
678-
case schnorrExtraNonceFunHardened msg sk xopk algo extra of
679-
Nothing -> pure 0
680-
Just bs -> evalContT $ do
681-
(hashPtr, _) <- ContT (unsafeUseByteString (unSizedByteArray bs))
682-
lift (memcpy outBuf hashPtr 32)
683-
pure 1
684-
685-
686640
-- | Verify the authenticity of a schnorr signature. @True@ means the 'Signature' is correct.
687641
schnorrVerify :: PubKeyXO -> ByteString -> Signature -> Bool
688642
schnorrVerify PubKeyXO{..} bs Signature{..} = unsafePerformIO . evalContT $ do
@@ -711,7 +665,7 @@ taggedSha256 tag msg = unsafePerformIO . evalContT $ do
711665
-- | Combine a list of 'PubKeyXY's into a single 'PubKeyXY'. This will result in @Nothing@ if the group operation results
712666
-- in the Point at Infinity
713667
pubKeyCombine :: [PubKeyXY] -> Maybe PubKeyXY
714-
pubKeyCombine keys = unsafePerformIO $ do
668+
pubKeyCombine keys@(_ : _) = unsafePerformIO $ do
715669
let n = length keys
716670
keysBuf <- mallocBytes (64 * n)
717671
for_ (zip [0 ..] keys) $ \(i, PubKeyXY{..}) ->
@@ -721,6 +675,7 @@ pubKeyCombine keys = unsafePerformIO $ do
721675
if isSuccess ret
722676
then Just . PubKeyXY <$> newForeignPtr finalizerFree outBuf
723677
else free outBuf $> Nothing
678+
pubKeyCombine [] = Nothing
724679

725680

726681
-- | Negate a 'PubKeyXY'

src/Crypto/Secp256k1/Gen.hs

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ module Crypto.Secp256k1.Gen where
22

33
import Crypto.Secp256k1 (KeyPair, PubKeyXO, PubKeyXY, SecKey, Tweak, derivePubKey, importSecKey, importTweak, keyPairCreate, xyToXO)
44
import Hedgehog (MonadGen)
5-
import Hedgehog.Gen (bytes, discard)
5+
import Hedgehog.Gen (bytes, discard, prune)
66
import Hedgehog.Range (singleton)
77

88

99
secKeyGen :: MonadGen m => m SecKey
1010
secKeyGen = do
11-
bs <- bytes (singleton 32)
11+
bs <- prune $ bytes (singleton 32)
1212
maybe discard pure (importSecKey bs)
1313

1414

@@ -26,5 +26,5 @@ keyPairGen = keyPairCreate <$> secKeyGen
2626

2727
tweakGen :: MonadGen m => m Tweak
2828
tweakGen = do
29-
bs <- bytes (singleton 32)
29+
bs <- prune $ bytes (singleton 32)
3030
maybe discard pure (importTweak bs)

src/Crypto/Secp256k1/Internal.hs

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module Crypto.Secp256k1.Internal where
22

33
import Crypto.Secp256k1.Prim
4+
import qualified Data.ByteArray.Encoding as BA
45
import Data.ByteString (ByteString)
56
import qualified Data.ByteString as BS
67
import qualified Data.ByteString.Unsafe as BU
@@ -33,4 +34,12 @@ packByteString (b, l) =
3334
isSuccess :: Ret -> Bool
3435
isSuccess 0 = False
3536
isSuccess 1 = True
36-
isSuccess n = error $ "isSuccess expected 0 or 1 but got " ++ show n
37+
isSuccess n = error $ "isSuccess expected 0 or 1 but got " ++ show n
38+
39+
40+
encodeBase16 :: ByteString -> ByteString
41+
encodeBase16 = BA.convertToBase BA.Base16
42+
43+
44+
decodeBase16 :: ByteString -> Either String ByteString
45+
decodeBase16 = BA.convertFromBase BA.Base16

0 commit comments

Comments
 (0)