libsecp256k1-0.2.0: test/Crypto/Secp256k1Prop.hs
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
module Crypto.Secp256k1Prop where
import Control.Applicative (Applicative (liftA2), empty)
import Control.Monad (when)
import Control.Monad.Trans.Class (lift)
import Crypto.Secp256k1
import Crypto.Secp256k1.Gen
import Data.ByteArray.Sized (sizedByteArray)
import Data.ByteString qualified as BS
import Data.Maybe (fromJust, isJust)
import Data.Void
import Hedgehog
import Hedgehog.Gen hiding (discard, maybe, prune)
import Hedgehog.Range (linear, singleton)
import Text.Read (readMaybe)
prop_secKeyReadInvertsShow :: Property
prop_secKeyReadInvertsShow = property $ do
sk <- forAll secKeyGen
let str = show sk
case readMaybe str of
Nothing -> failure
Just x -> x === sk
prop_secKeyParseInvertsSerialize :: Property
prop_secKeyParseInvertsSerialize = property $ do
sk <- forAll secKeyGen
case importSecKey $ exportSecKey sk of
Nothing -> failure
Just x -> x === sk
prop_secKeySerializeInvertsParse :: Property
prop_secKeySerializeInvertsParse = property $ do
bs <- forAll (bytes $ singleton 32)
case importSecKey bs of
Nothing -> discard
Just sk -> exportSecKey sk === bs
prop_pubKeyXYReadInvertsShow :: Property
prop_pubKeyXYReadInvertsShow = property $ do
pk <- forAll pubKeyXYGen
let str = show pk
case readMaybe str of
Nothing -> failure
Just x -> x === pk
prop_pubKeyXYParseInvertsSerialize :: Property
prop_pubKeyXYParseInvertsSerialize = property $ do
pk <- forAll pubKeyXYGen
compress <- forAll $ element [False, True]
case importPubKeyXY (exportPubKeyXY compress pk) of
Nothing -> failure
Just x -> x === pk
prop_pubKeyXYSerializeInvertsParse :: Property
prop_pubKeyXYSerializeInvertsParse = withDiscards 200 $
property $ do
bs <- forAll $ bytes (singleton 32)
negate <- forAll enumBounded
let withParity =
flip BS.cons bs $
if negate
then 0x03
else 0x02
case importPubKeyXY withParity of
Nothing -> discard
Just pk -> exportPubKeyXY True pk === withParity
prop_pubKeyXOReadInvertsShow :: Property
prop_pubKeyXOReadInvertsShow = property $ do
pk <- forAll pubKeyXOGen
let str = show pk
case readMaybe str of
Nothing -> failure
Just x -> x === pk
prop_pubKeyXOParseInvertsSerialize :: Property
prop_pubKeyXOParseInvertsSerialize = property $ do
pk <- forAll pubKeyXOGen
case importPubKeyXO (exportPubKeyXO pk) of
Nothing -> failure
Just x -> x === pk
prop_pubKeyXOSerializeInvertsParse :: Property
prop_pubKeyXOSerializeInvertsParse = withDiscards 200 . property $ do
bs <- forAll (bytes $ singleton 32)
case importPubKeyXO bs of
Nothing -> discard
Just pk -> exportPubKeyXO pk === bs
prop_signatureReadInvertsShow :: Property
prop_signatureReadInvertsShow = property $ do
sk <- forAll secKeyGen
bs <- forAll (bytes $ singleton 32)
sig <- maybe failure pure $ ecdsaSign sk bs
case readMaybe (show sig) of
Nothing -> failure
Just x -> sig === x
prop_signatureParseInvertsSerialize :: Property
prop_signatureParseInvertsSerialize = property $ do
sk <- forAll secKeyGen
bs <- forAll $ bytes (singleton 32)
sig <- maybe failure pure $ ecdsaSign sk bs
exportDer <- forAll $ element [False, True]
let export = if exportDer then exportSignatureDer else exportSignatureCompact
let serialized = export sig
annotateShow serialized
annotateShow (BS.length serialized)
let parse = if exportDer then importSignatureDer else importSignatureCompact
case parse serialized of
Nothing -> failure
Just x -> x === sig
prop_recoverableSignatureReadInvertsShow :: Property
prop_recoverableSignatureReadInvertsShow = property $ do
sk <- forAll secKeyGen
bs <- forAll $ bytes (singleton 32)
recSig <- maybe failure pure $ ecdsaSignRecoverable sk bs
let export = exportRecoverableSignature recSig
case importRecoverableSignature export of
Nothing -> failure
Just x -> x === recSig
prop_recoverableSignatureParseInvertsSerialize :: Property
prop_recoverableSignatureParseInvertsSerialize = property $ do
sk <- forAll secKeyGen
bs <- forAll $ bytes (singleton 32)
sig <- maybe failure pure (ecdsaSignRecoverable sk bs)
let serialized = exportRecoverableSignature sig
annotateShow serialized
annotateShow (BS.length serialized)
case importRecoverableSignature serialized of
Nothing -> failure
Just x -> x === sig
prop_validSecKeyImpliesValidTweak :: Property
prop_validSecKeyImpliesValidTweak = property $ do
sk <- forAll secKeyGen
assert $ isJust $ importTweak (exportSecKey sk)
prop_ecdsaSignaturesProducedAreValid :: Property
prop_ecdsaSignaturesProducedAreValid = property $ do
sk <- forAll secKeyGen
msg <- forAll $ bytes (singleton 32)
let sig = ecdsaSign sk msg
case sig of
Nothing -> failure
Just sig -> assert $ ecdsaVerify msg (derivePubKey sk) sig
prop_ecdsaRecoverableSignaturesProducedAreValid :: Property
prop_ecdsaRecoverableSignaturesProducedAreValid = property $ do
sk <- forAll secKeyGen
msg <- forAll $ bytes (singleton 32)
case ecdsaSignRecoverable sk msg of
Nothing -> failure
Just recSig -> assert $ ecdsaVerify msg (derivePubKey sk) (recSigToSig recSig)
prop_ecdsaSignatureValidityPreservedOverSerialization :: Property
prop_ecdsaSignatureValidityPreservedOverSerialization = property $ do
sk <- forAll secKeyGen
msg <- forAll $ bytes (singleton 32)
sig <- maybe failure pure $ ecdsaSign sk msg
useDer <- forAll enumBounded
let (serialize, parse) =
if useDer
then (exportSignatureDer, importSignatureDer)
else (exportSignatureCompact, importSignatureCompact)
let serialized = serialize sig
let parsed = fromJust (parse serialized)
assert $ ecdsaVerify msg (derivePubKey sk) parsed
prop_ecdsaRecoverableSignatureProducesValidPubKey :: Property
prop_ecdsaRecoverableSignatureProducesValidPubKey = property $ do
sk <- forAll secKeyGen
msg <- forAll $ bytes (singleton 32)
case ecdsaRecover (fromJust $ ecdsaSignRecoverable sk msg) msg of
Nothing -> failure
Just pk -> pk === derivePubKey sk
prop_ecdsaRecoverableSignatureIdentity :: Property
prop_ecdsaRecoverableSignatureIdentity = property $ do
sk <- forAll secKeyGen
msg <- forAll $ bytes (singleton 32)
case ecdsaSignRecoverable sk msg of
Nothing -> failure
Just sig -> Just (recSigToSig sig) === ecdsaSign sk msg
prop_keyPairIdentities :: Property
prop_keyPairIdentities = property $ do
sk <- forAll secKeyGen
let kp = keyPairCreate sk
keyPairSecKey kp === sk
keyPairPubKeyXY kp === derivePubKey sk
keyPairPubKeyXO kp === xyToXO (derivePubKey sk)
prop_tweakDistributesOverPubKeyDerivation :: Property
prop_tweakDistributesOverPubKeyDerivation = property $ do
sk <- forAll secKeyGen
tweak <- forAll tweakGen
(derivePubKey <$> secKeyTweakAdd sk tweak) === pubKeyTweakAdd (derivePubKey sk) tweak
(derivePubKey <$> secKeyTweakMul sk tweak) === pubKeyTweakMul (derivePubKey sk) tweak
prop_pubKeyCombineOrderIndependent :: Property
prop_pubKeyCombineOrderIndependent = property $ do
ls <- forAll $ list (linear 0 20) pubKeyXYGen
ls' <- forAll $ shuffle ls
pubKeyCombine ls === pubKeyCombine ls'
prop_negationDistributesOverPubKeyDerivation :: Property
prop_negationDistributesOverPubKeyDerivation = property $ do
sk <- forAll secKeyGen
derivePubKey (secKeyNegate sk) === pubKeyNegate (derivePubKey sk)
prop_secKeyDoubleNegationIdentity :: Property
prop_secKeyDoubleNegationIdentity = property $ do
sk <- forAll secKeyGen
(secKeyNegate . secKeyNegate) sk === sk
prop_pubKeyDoubleNegationIdentity :: Property
prop_pubKeyDoubleNegationIdentity = property $ do
pk <- forAll pubKeyXYGen
(pubKeyNegate . pubKeyNegate) pk === pk
prop_schnorrSignaturesProducedAreValid :: Property
prop_schnorrSignaturesProducedAreValid = property $ do
sk <- forAll secKeyGen
msg <- forAll $ bytes (singleton 32)
let kp = keyPairCreate sk
case schnorrSign kp msg of
Nothing -> failure
Just sig -> assert $ schnorrVerify (fst $ keyPairPubKeyXO kp) msg sig
prop_pubKeyCombineTweakIdentity :: Property
prop_pubKeyCombineTweakIdentity = property $ do
sk <- forAll secKeyGen
sk' <- forAll secKeyGen
pubKeyCombine [derivePubKey sk, derivePubKey sk'] === pubKeyTweakAdd (derivePubKey sk) (fromJust $ importTweak (exportSecKey sk'))
prop_ecdhIdentity :: Property
prop_ecdhIdentity = property $ do
sk <- forAll secKeyGen
sk' <- forAll secKeyGen
ecdh sk (derivePubKey sk') === ecdh sk' (derivePubKey sk)
prop_ecdsaSignaturesUnforgeable :: Property
prop_ecdsaSignaturesUnforgeable = property $ do
sk <- forAll secKeyGen
pk <- forAll pubKeyXYGen
when (pk == derivePubKey sk) discard
msg <- forAll $ bytes (singleton 32)
let sig = ecdsaSign sk msg
case sig of
Nothing -> failure
Just sig -> assert . not $ ecdsaVerify msg pk sig
prop_schnorrSignaturesUnforgeable :: Property
prop_schnorrSignaturesUnforgeable = property $ do
sk <- forAll secKeyGen
let kp = keyPairCreate sk
pk <- forAll pubKeyXOGen
msg <- forAll $ bytes (singleton 32)
case schnorrSign kp msg of
Nothing -> failure
Just sig -> assert . not $ schnorrVerify pk msg sig
newtype Wrapped a = Wrapped {secKey :: a} deriving (Show, Read, Eq)
derivedCompositeReadShowInvertTemplate :: (Eq a, Read a, Show a) => Gen a -> Property
derivedCompositeReadShowInvertTemplate gen = property $ do
a <- forAll gen
annotateShow a
annotateShow (length $ show a)
annotateShow (Wrapped a)
case readMaybe (show (Wrapped a)) of
Nothing -> failure
Just x -> x === Wrapped a
prop_derivedCompositeReadShowInvertSecKey :: Property
prop_derivedCompositeReadShowInvertSecKey = derivedCompositeReadShowInvertTemplate secKeyGen
prop_derivedCompositeReadShowInvertPubKeyXY :: Property
prop_derivedCompositeReadShowInvertPubKeyXY = derivedCompositeReadShowInvertTemplate pubKeyXYGen
prop_derivedCompositeReadShowInvertPubKeyXO :: Property
prop_derivedCompositeReadShowInvertPubKeyXO = derivedCompositeReadShowInvertTemplate pubKeyXOGen
prop_derivedCompositeReadShowInvertTweak :: Property
prop_derivedCompositeReadShowInvertTweak = derivedCompositeReadShowInvertTemplate tweakGen
prop_derivedCompositeReadShowInvertSignature :: Property
prop_derivedCompositeReadShowInvertSignature = derivedCompositeReadShowInvertTemplate $ choice [ecdsa, schnorr]
where
base = liftA2 (,) secKeyGen (bytes (singleton 32))
ecdsa = base >>= maybe empty pure . uncurry ecdsaSign
schnorr = base >>= maybe empty pure . uncurry (schnorrSign . keyPairCreate)
prop_derivedCompositeReadShowInvertRecoverableSignature :: Property
prop_derivedCompositeReadShowInvertRecoverableSignature = derivedCompositeReadShowInvertTemplate $ do
sk <- secKeyGen
msg <- bytes (singleton 32)
maybe empty pure $ ecdsaSignRecoverable sk msg
prop_eqImportImpliesEqSecKey :: Property
prop_eqImportImpliesEqSecKey = property $ do
bs <- forAll $ bytes $ singleton 32
k0 <- maybe discard pure $ importSecKey bs
k1 <- maybe discard pure $ importSecKey bs
k0 === k1
tests :: Group
tests = $$discover