musig2-0.2.0: test/SignVerifyTweakProperty.hs
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedStrings #-}
module SignVerifyTweakProperty (propertySignVerifyTweak) where
import Crypto.Curve.Secp256k1 (Pub)
import Crypto.Curve.Secp256k1.MuSig2 (PubNonce, SessionContext, Tweak (..), aggNonces, mkSessionContext, partialSigVerify, publicNonce, sign)
import Crypto.Curve.Secp256k1.MuSig2.Internal (curveOrder)
import Data.ByteString (ByteString)
import Test.Tasty
import Test.Tasty.QuickCheck
import Util (SignerMaterial (..), unsafeRight)
propertySignVerifyTweak :: TestTree
propertySignVerifyTweak =
testGroup
"sign and partialSigVerify with Tweaks Properties"
[ testProperty "Valid Signature Range with Tweaks" prop_validSignatureRangeWithTweaks
, testProperty "Sign-Verify Roundtrip with Tweaks" prop_signVerifyRoundtripWithTweaks
, testProperty "Signature Determinism with Tweaks" prop_signatureDeterminismWithTweaks
, testProperty "Empty Tweaks Equals No Tweaks" prop_emptyTweaksEqualsNoTweaks
, testProperty "Plain Tweaks Are Commutative" prop_plainTweaksCommutative
]
mkTweakedContext :: SignerMaterial -> [Tweak] -> ByteString -> ([PubNonce], [Pub], SessionContext)
mkTweakedContext signer tweaks msg =
let pubNonce = unsafeRight $ publicNonce signer.signerSecNonce
pubNonces = [pubNonce]
pubkeys = [signer.signerPubKey]
aggNonce = unsafeRight $ aggNonces pubNonces
ctx = unsafeRight $ mkSessionContext aggNonce pubkeys tweaks msg
in (pubNonces, pubkeys, ctx)
-- | Property: Generated signatures with tweaks are in the valid range \([0, Q-1]\).
prop_validSignatureRangeWithTweaks :: SignerMaterial -> Property
prop_validSignatureRangeWithTweaks signer =
forAll (resize 5 $ listOf arbitrary :: Gen [Tweak]) $ \tweaks ->
forAll (arbitrary :: Gen ByteString) $ \msg ->
let (_, _, ctx) = mkTweakedContext signer tweaks msg
sig = unsafeRight $ sign signer.signerSecNonce signer.signerSecKey ctx
in (sig >= 0) .&&. (sig < curveOrder)
-- | Property: A signature created with tweaks verifies with 'partialSigVerify'.
prop_signVerifyRoundtripWithTweaks :: SignerMaterial -> Property
prop_signVerifyRoundtripWithTweaks signer =
forAll (resize 5 $ listOf arbitrary :: Gen [Tweak]) $ \tweaks ->
forAll (arbitrary :: Gen ByteString) $ \msg ->
let (pubNonces, pubkeys, ctx) = mkTweakedContext signer tweaks msg
sig = unsafeRight $ sign signer.signerSecNonce signer.signerSecKey ctx
result = partialSigVerify sig pubNonces pubkeys tweaks msg 0
in result === Right True
-- | Property: Signing the same message with the same tweaks produces the same signature.
prop_signatureDeterminismWithTweaks :: SignerMaterial -> Property
prop_signatureDeterminismWithTweaks signer =
forAll (resize 5 $ listOf arbitrary :: Gen [Tweak]) $ \tweaks ->
forAll (arbitrary :: Gen ByteString) $ \msg ->
let (_, _, ctx) = mkTweakedContext signer tweaks msg
sig1 = unsafeRight $ sign signer.signerSecNonce signer.signerSecKey ctx
sig2 = unsafeRight $ sign signer.signerSecNonce signer.signerSecKey ctx
in sig1 === sig2
-- | Property: Empty tweaks should produce the same result as no tweaks.
prop_emptyTweaksEqualsNoTweaks :: SignerMaterial -> ByteString -> Property
prop_emptyTweaksEqualsNoTweaks signer msg =
let (_, _, ctxWithEmptyTweaks) = mkTweakedContext signer [] msg
sigWithEmptyTweaks = unsafeRight $ sign signer.signerSecNonce signer.signerSecKey ctxWithEmptyTweaks
(_, _, ctxWithNoTweaks) = mkTweakedContext signer [] msg
sigWithNoTweaks = unsafeRight $ sign signer.signerSecNonce signer.signerSecKey ctxWithNoTweaks
in sigWithEmptyTweaks === sigWithNoTweaks
-- | Property: Plain tweaks are commutative.
prop_plainTweaksCommutative :: SignerMaterial -> Integer -> Integer -> ByteString -> Property
prop_plainTweaksCommutative signer t1 t2 msg =
t1 > 0
&& t1 < curveOrder
&& t2 > 0
&& t2 < curveOrder
&& t1 /= t2
==> let
tweak1 = PlainTweak t1
tweak2 = PlainTweak t2
(_, _, ctx1) = mkTweakedContext signer [tweak1, tweak2] msg
sig1 = unsafeRight $ sign signer.signerSecNonce signer.signerSecKey ctx1
(_, _, ctx2) = mkTweakedContext signer [tweak2, tweak1] msg
sig2 = unsafeRight $ sign signer.signerSecNonce signer.signerSecKey ctx2
in
sig1 === sig2