packages feed

musig2-0.2.0: test/NonceGenProperty.hs

{-# LANGUAGE OverloadedStrings #-}

module NonceGenProperty (propertyNonceGen) where

import Crypto.Curve.Secp256k1 (derive_pub, mul, _CURVE_G)
import Crypto.Curve.Secp256k1.MuSig2 (PubNonce (..), SecKey (..), SecNonceGenParams (..), publicNonce, secNonceGenWithRand)
import Crypto.Curve.Secp256k1.MuSig2.Internal (curveOrder, hashTag, integerToBytes32, secNonceScalars, xorByteStrings)
import Data.ByteString ()
import Data.Maybe (fromMaybe)
import Test.Tasty
import Test.Tasty.QuickCheck as QC
import Util (Rand32 (..), Scalar (..), unsafeRight)

propertyNonceGen :: TestTree
propertyNonceGen =
  testGroup
    "secNonceGenWithRand Properties"
    [ testProperty "Valid Nonce Range" prop_validRange
    , testProperty "Correct Public Nonce Computation" prop_correctPubNonce
    , testProperty "Secret Key XOR Consistency" prop_skConsistency
    ]

-- | Property: Generated k1 and k2 are in the valid range [1, Q-1]
prop_validRange :: Rand32 -> SecNonceGenParams -> Property
prop_validRange (Rand32 rand) params =
  let (k1, k2) = secNonceScalars (unsafeRight $ secNonceGenWithRand rand params)
   in (1 <= k1 && k1 < curveOrder) .&&. (1 <= k2 && k2 < curveOrder)

-- | Property: The public nonce points correspond to G multiplied by k1 and k2
prop_correctPubNonce :: Rand32 -> SecNonceGenParams -> Property
prop_correctPubNonce (Rand32 rand) params =
  let sn = unsafeRight $ secNonceGenWithRand rand params
      (k1, k2) = secNonceScalars sn
      PubNonce nonceR1 nonceR2 = unsafeRight $ publicNonce sn
   in nonceR1 === fromMaybe (error "Failed to multiply scalar by generator") (mul _CURVE_G (fromInteger k1))
        .&&. nonceR2
          === fromMaybe (error "Failed to multiply scalar by generator") (mul _CURVE_G (fromInteger k2))

-- | Property: Generating with sk provided is equivalent to XORing rand with the aux hash and generating without sk
prop_skConsistency :: Rand32 -> SecNonceGenParams -> Scalar -> Property
prop_skConsistency (Rand32 rand) params (Scalar sk) =
  let pkForSk = case derive_pub (fromInteger sk) of
        Just pub -> pub
        Nothing -> error "Failed to derive pubkey for property"
      paramsNoSk = params{_pk = pkForSk, _sk = Nothing}
      paramsWithSk = params{_pk = pkForSk, _sk = Just (SecKey sk)}
      auxHash = hashTag "MuSig/aux" rand
      skBytes = integerToBytes32 sk
      rand' = xorByteStrings skBytes auxHash
      (k1l, k2l) = secNonceScalars (unsafeRight $ secNonceGenWithRand rand paramsWithSk)
      (k1r, k2r) = secNonceScalars (unsafeRight $ secNonceGenWithRand rand' paramsNoSk)
   in k1l === k1r .&&. k2l === k2r