mirror of
https://github.com/ilyakooo0/secp256k1-haskell.git
synced 2024-10-26 12:56:14 +03:00
BIP-340 (Schnorr) support (#26)
Compatible with jonasnick/secp256k1@372c4555ca
This commit is contained in:
parent
ae2c67e35e
commit
76f86ad0d6
@ -30,6 +30,8 @@ library:
|
||||
when:
|
||||
- condition: flag(ecdh)
|
||||
cpp-options: -DECDH
|
||||
- condition: flag(schnorr)
|
||||
cpp-options: -DSCHNORR
|
||||
tests:
|
||||
spec:
|
||||
main: Spec.hs
|
||||
@ -47,3 +49,7 @@ flags:
|
||||
description: "Enable (experimental) ECDH APIs"
|
||||
manual: true
|
||||
default: false
|
||||
schnorr:
|
||||
description: "Enable BIP-340 (Schnorr) APIs"
|
||||
manual: true
|
||||
default: false
|
||||
|
@ -63,6 +63,21 @@ module Crypto.Secp256k1
|
||||
-- * Diffie Hellman
|
||||
, ecdh
|
||||
#endif
|
||||
|
||||
#ifdef SCHNORR
|
||||
, XOnlyPubKey
|
||||
, SchnorrSig
|
||||
, signMsgSchnorr
|
||||
, exportSchnorrSig
|
||||
, importSchnorrSig
|
||||
, exportXOnlyPubKey
|
||||
, importXOnlyPubKey
|
||||
, verifyMsgSchnorr
|
||||
, deriveXOnlyPubKey
|
||||
, schnorrTweakAddPubKey
|
||||
, schnorrTweakAddSecKey
|
||||
, testTweakXOnlyPubKey
|
||||
#endif
|
||||
) where
|
||||
|
||||
import Control.Monad (replicateM, unless, (<=<))
|
||||
@ -79,6 +94,7 @@ import Foreign (ForeignPtr, alloca, allocaArray,
|
||||
allocaBytes, mallocForeignPtr,
|
||||
nullFunPtr, nullPtr, peek, poke,
|
||||
pokeArray, withForeignPtr)
|
||||
import Foreign.C (CInt)
|
||||
import System.IO.Unsafe (unsafePerformIO)
|
||||
import Test.QuickCheck (Arbitrary (..),
|
||||
arbitraryBoundedRandom, suchThat)
|
||||
@ -94,6 +110,10 @@ newtype Sig = Sig (ForeignPtr Sig64)
|
||||
newtype SecKey = SecKey (ForeignPtr SecKey32)
|
||||
newtype Tweak = Tweak (ForeignPtr Tweak32)
|
||||
newtype RecSig = RecSig (ForeignPtr RecSig65)
|
||||
#ifdef SCHNORR
|
||||
newtype XOnlyPubKey = XOnlyPubKey (ForeignPtr XOnlyPubKey64)
|
||||
newtype SchnorrSig = SchnorrSig (ForeignPtr SchnorrSig64)
|
||||
#endif
|
||||
|
||||
instance NFData PubKey where
|
||||
rnf (PubKey p) = p `seq` ()
|
||||
@ -232,6 +252,50 @@ instance Eq SecKey where
|
||||
instance Eq Tweak where
|
||||
ft1 == ft2 = getTweak ft1 == getTweak ft2
|
||||
|
||||
#ifdef SCHNORR
|
||||
instance NFData SchnorrSig where
|
||||
rnf (SchnorrSig p) = p `seq` ()
|
||||
|
||||
instance Show SchnorrSig where
|
||||
showsPrec _ = shows . B16.encode . exportSchnorrSig
|
||||
|
||||
instance Read SchnorrSig where
|
||||
readPrec = parens $ do
|
||||
String str <- lexP
|
||||
maybe pfail return $ importSchnorrSig =<< decodeHex str
|
||||
|
||||
instance Eq SchnorrSig where
|
||||
fg1 == fg2 = exportSchnorrSig fg1 == exportSchnorrSig fg2
|
||||
|
||||
instance IsString SchnorrSig where
|
||||
fromString = fromMaybe e . (importSchnorrSig <=< decodeHex) where
|
||||
e = error "Could not decode Schnorr signature from hex string"
|
||||
|
||||
instance Hashable SchnorrSig where
|
||||
i `hashWithSalt` s = i `hashWithSalt` exportSchnorrSig s
|
||||
|
||||
instance NFData XOnlyPubKey where
|
||||
rnf (XOnlyPubKey p) = p `seq` ()
|
||||
|
||||
instance Show XOnlyPubKey where
|
||||
showsPrec _ = shows . B16.encode . exportXOnlyPubKey
|
||||
|
||||
instance Read XOnlyPubKey where
|
||||
readPrec = do
|
||||
String str <- lexP
|
||||
maybe pfail return $ importXOnlyPubKey =<< decodeHex str
|
||||
|
||||
instance Eq XOnlyPubKey where
|
||||
fp1 == fp2 = getXOnlyPubKey fp1 == getXOnlyPubKey fp2
|
||||
|
||||
instance IsString XOnlyPubKey where
|
||||
fromString = fromMaybe e . (importXOnlyPubKey <=< decodeHex) where
|
||||
e = error "Could not decode public key from hex string"
|
||||
|
||||
instance Hashable XOnlyPubKey where
|
||||
i `hashWithSalt` k = i `hashWithSalt` exportXOnlyPubKey k
|
||||
#endif
|
||||
|
||||
-- | Import 32-byte 'ByteString' as 'Msg'.
|
||||
msg :: ByteString -> Maybe Msg
|
||||
msg bs
|
||||
@ -366,6 +430,7 @@ derivePubKey (SecKey fk) = withContext $ \ctx -> withForeignPtr fk $ \k -> do
|
||||
unless (isSuccess ret) $ error "could not compute public key"
|
||||
return $ PubKey fp
|
||||
|
||||
|
||||
-- | Add tweak to secret key.
|
||||
tweakAddSecKey :: SecKey -> Tweak -> Maybe SecKey
|
||||
tweakAddSecKey (SecKey fk) (Tweak ft) = withContext $ \ctx ->
|
||||
@ -507,6 +572,102 @@ ecdh (PubKey pk) (SecKey sk) = withContext $ \ctx ->
|
||||
size = 32
|
||||
#endif
|
||||
|
||||
#ifdef SCHNORR
|
||||
-- Get 64-byte x-only public key.
|
||||
getXOnlyPubKey :: XOnlyPubKey -> ByteString
|
||||
getXOnlyPubKey (XOnlyPubKey fp) =
|
||||
fromShort $ getXOnlyPubKey64 $ unsafePerformIO $ withForeignPtr fp peek
|
||||
|
||||
-- | Add tweak to public key. Tweak is multiplied first by G to obtain a point.
|
||||
schnorrTweakAddPubKey :: XOnlyPubKey -> Tweak -> Maybe (XOnlyPubKey, CInt)
|
||||
schnorrTweakAddPubKey (XOnlyPubKey fp) (Tweak ft) = withContext $ \ctx ->
|
||||
withForeignPtr fp $ \p -> withForeignPtr ft $ \t -> alloca $ \is_negated -> do
|
||||
fp' <- mallocForeignPtr
|
||||
ret <- withForeignPtr fp' $ \p' -> do
|
||||
pub <- peek p
|
||||
poke p' pub
|
||||
schnorrPubKeyTweakAdd ctx p' is_negated t
|
||||
peeked_is_negated <- peek is_negated
|
||||
if isSuccess ret then return $ Just $ (XOnlyPubKey fp', peeked_is_negated) else return Nothing
|
||||
|
||||
-- | Add tweak to secret key.
|
||||
schnorrTweakAddSecKey :: SecKey -> Tweak -> Maybe SecKey
|
||||
schnorrTweakAddSecKey (SecKey fk) (Tweak ft) = withContext $ \ctx ->
|
||||
withForeignPtr fk $ \k -> withForeignPtr ft $ \t -> do
|
||||
fk' <- mallocForeignPtr
|
||||
ret <- withForeignPtr fk' $ \k' -> do
|
||||
key <- peek k
|
||||
poke k' key
|
||||
schnorrSecKeyTweakAdd ctx k' t
|
||||
if isSuccess ret then return $ Just $ SecKey fk' else return Nothing
|
||||
|
||||
signMsgSchnorr :: SecKey -> Msg -> SchnorrSig
|
||||
signMsgSchnorr (SecKey fk) (Msg fm) = withContext $ \ctx ->
|
||||
withForeignPtr fk $ \k -> withForeignPtr fm $ \m -> do
|
||||
fg <- mallocForeignPtr
|
||||
ret <- withForeignPtr fg $ \g -> schnorrSign ctx g m k nullFunPtr nullPtr
|
||||
unless (isSuccess ret) $ error "could not schnorr-sign message"
|
||||
return $ SchnorrSig fg
|
||||
|
||||
exportSchnorrSig :: SchnorrSig -> ByteString
|
||||
exportSchnorrSig (SchnorrSig fg) = withContext $ \ctx ->
|
||||
withForeignPtr fg $ \g -> allocaBytes 64 $ \o -> do
|
||||
ret <- signatureSerializeSchnorr ctx o g
|
||||
unless (isSuccess ret) $ error "could not serialize schnorr signature"
|
||||
packByteString (o, 64)
|
||||
|
||||
importXOnlyPubKey :: ByteString -> Maybe XOnlyPubKey
|
||||
importXOnlyPubKey bs
|
||||
| BS.length bs == 32 = withContext $ \ctx -> do
|
||||
fp <- mallocForeignPtr
|
||||
ret <- withForeignPtr fp $ \pfp -> useByteString bs $ \(inp, _) ->
|
||||
schnorrXOnlyPubKeyParse ctx pfp inp
|
||||
if isSuccess ret
|
||||
then return $ Just $ XOnlyPubKey fp
|
||||
else return Nothing
|
||||
| otherwise = Nothing
|
||||
|
||||
importSchnorrSig :: ByteString -> Maybe SchnorrSig
|
||||
importSchnorrSig bs
|
||||
| BS.length bs == 64 = withContext $ \ctx -> do
|
||||
fp <- mallocForeignPtr
|
||||
ret <- withForeignPtr fp $ \pfp -> useByteString bs $ \(inp, _) ->
|
||||
schnorrSignatureParse ctx pfp inp
|
||||
if isSuccess ret
|
||||
then return $ Just $ SchnorrSig fp
|
||||
else return Nothing
|
||||
| otherwise = Nothing
|
||||
|
||||
verifyMsgSchnorr :: XOnlyPubKey -> SchnorrSig -> Msg -> Bool
|
||||
verifyMsgSchnorr (XOnlyPubKey fp) (SchnorrSig fg) (Msg fm) = withContext $ \ctx ->
|
||||
withForeignPtr fp $ \p -> withForeignPtr fg $ \g ->
|
||||
withForeignPtr fm $ \m -> isSuccess <$> schnorrSignatureVerify ctx g m p
|
||||
|
||||
exportXOnlyPubKey :: XOnlyPubKey -> ByteString
|
||||
exportXOnlyPubKey (XOnlyPubKey pub) = withContext $ \ctx ->
|
||||
withForeignPtr pub $ \p -> allocaBytes 32 $ \o -> do
|
||||
ret <- schnorrPubKeySerialize ctx o p
|
||||
unless (isSuccess ret) $ error "could not serialize x-only public key"
|
||||
packByteString (o, 32)
|
||||
|
||||
deriveXOnlyPubKey :: SecKey -> XOnlyPubKey
|
||||
deriveXOnlyPubKey (SecKey fk) = withContext $ \ctx -> withForeignPtr fk $ \k -> do
|
||||
fp <- mallocForeignPtr
|
||||
ret <- withForeignPtr fp $ \p -> schnorrXOnlyPubKeyCreate ctx p k
|
||||
unless (isSuccess ret) $ error "could not derive x-only public key"
|
||||
return $ XOnlyPubKey fp
|
||||
|
||||
testTweakXOnlyPubKey :: XOnlyPubKey -> CInt -> XOnlyPubKey -> Tweak -> Bool
|
||||
testTweakXOnlyPubKey (XOnlyPubKey fp) is_negated (XOnlyPubKey internal) (Tweak ft) =
|
||||
withContext $ \ctx ->
|
||||
withForeignPtr fp $ \p ->
|
||||
withForeignPtr internal $ \internalp ->
|
||||
withForeignPtr ft $ \t -> do
|
||||
ret <- xOnlyPubKeyTweakTest ctx p is_negated internalp t
|
||||
return $ isSuccess ret
|
||||
-- End of Schnorr block
|
||||
#endif
|
||||
|
||||
instance Arbitrary Msg where
|
||||
arbitrary = gen_msg
|
||||
where
|
||||
@ -524,3 +685,10 @@ instance Arbitrary PubKey where
|
||||
arbitrary = do
|
||||
key <- arbitrary
|
||||
return $ derivePubKey key
|
||||
|
||||
#ifdef SCHNORR
|
||||
instance Arbitrary XOnlyPubKey where
|
||||
arbitrary = do
|
||||
key <- arbitrary
|
||||
return $ deriveXOnlyPubKey key
|
||||
#endif
|
||||
|
@ -81,6 +81,14 @@ newtype SerFlags = SerFlags { getSerFlags :: CUInt }
|
||||
newtype Ret = Ret { getRet :: CInt }
|
||||
deriving (Read, Show, Eq, Ord, Generic, NFData)
|
||||
|
||||
#ifdef SCHNORR
|
||||
newtype XOnlyPubKey64 = XOnlyPubKey64 { getXOnlyPubKey64 :: ShortByteString }
|
||||
deriving (Read, Show, Eq, Ord, Generic, NFData)
|
||||
|
||||
newtype SchnorrSig64 = SchnorrSig64 { getSchnorrSig64 :: ShortByteString }
|
||||
deriving (Read, Show, Eq, Ord, Generic, NFData)
|
||||
#endif
|
||||
|
||||
-- | Nonce32-generating function
|
||||
type NonceFunction a
|
||||
= Ptr Nonce32
|
||||
@ -113,6 +121,22 @@ useByteString bs f =
|
||||
packByteString :: (Ptr CUChar, CSize) -> IO ByteString
|
||||
packByteString (b, l) = BS.packCStringLen (castPtr b, fromIntegral l)
|
||||
|
||||
#if SCHNORR
|
||||
instance Storable XOnlyPubKey64 where
|
||||
sizeOf _ = 64
|
||||
alignment _ = 1
|
||||
peek p = XOnlyPubKey64 . toShort <$> packByteString (castPtr p, 64)
|
||||
poke p (XOnlyPubKey64 k) = useByteString (fromShort k) $
|
||||
\(b, _) -> copyArray (castPtr p) b 64
|
||||
|
||||
instance Storable SchnorrSig64 where
|
||||
sizeOf _ = 64
|
||||
alignment _ = 1
|
||||
peek p = SchnorrSig64 . toShort <$> packByteString (castPtr p, 64)
|
||||
poke p (SchnorrSig64 k) = useByteString (fromShort k) $
|
||||
\(b, _) -> copyArray (castPtr p) b 64
|
||||
#endif
|
||||
|
||||
instance Storable PubKey64 where
|
||||
sizeOf _ = 64
|
||||
alignment _ = 1
|
||||
@ -484,6 +508,98 @@ foreign import ccall
|
||||
-> Ptr Msg32
|
||||
-> IO Ret
|
||||
|
||||
#ifdef SCHNORR
|
||||
foreign import ccall
|
||||
"secp256k1.h secp256k1_xonly_pubkey_tweak_add"
|
||||
schnorrPubKeyTweakAdd
|
||||
:: Ptr Ctx
|
||||
-> Ptr XOnlyPubKey64
|
||||
-> Ptr CInt
|
||||
-> Ptr Tweak32
|
||||
-> IO Ret
|
||||
|
||||
foreign import ccall
|
||||
"secp256k1.h secp256k1_xonly_seckey_tweak_add"
|
||||
schnorrSecKeyTweakAdd
|
||||
:: Ptr Ctx
|
||||
-> Ptr SecKey32
|
||||
-> Ptr Tweak32
|
||||
-> IO Ret
|
||||
|
||||
foreign import ccall
|
||||
"secp256k1.h secp256k1_schnorrsig_serialize"
|
||||
signatureSerializeSchnorr
|
||||
:: Ptr Ctx
|
||||
-> Ptr CUChar -- ^ array for encoded signature, must be large enough
|
||||
-> Ptr SchnorrSig64
|
||||
-> IO Ret
|
||||
|
||||
foreign import ccall
|
||||
"secp256k1.h secp256k1_schnorrsig_sign"
|
||||
schnorrSign
|
||||
:: Ptr Ctx
|
||||
-> Ptr SchnorrSig64
|
||||
-> Ptr Msg32
|
||||
-> Ptr SecKey32
|
||||
-- TODO
|
||||
-- This is actually an "extended nonce function" in the C code. So this signature is broken,
|
||||
-- but we pass a nullFunPtr (and this module is Internal), so it doesn't matter right now.
|
||||
-> FunPtr (NonceFunction a)
|
||||
-> Ptr a -- ^ nonce data
|
||||
-> IO Ret
|
||||
|
||||
foreign import ccall
|
||||
"secp256k1.h secp256k1_xonly_pubkey_tweak_test"
|
||||
xOnlyPubKeyTweakTest
|
||||
:: Ptr Ctx
|
||||
-> Ptr XOnlyPubKey64 -- output_pubkey
|
||||
-> CInt -- is_negated
|
||||
-> Ptr XOnlyPubKey64 -- internal_pubkey
|
||||
-> Ptr Tweak32
|
||||
-> IO Ret
|
||||
|
||||
foreign import ccall
|
||||
"secp256k1.h secp256k1_xonly_pubkey_serialize"
|
||||
schnorrPubKeySerialize
|
||||
:: Ptr Ctx
|
||||
-> Ptr CUChar -- 32 bytes output buffer
|
||||
-> Ptr XOnlyPubKey64
|
||||
-> IO Ret
|
||||
|
||||
foreign import ccall
|
||||
"secp256k1.h secp256k1_schnorrsig_verify"
|
||||
schnorrSignatureVerify
|
||||
:: Ptr Ctx
|
||||
-> Ptr SchnorrSig64
|
||||
-> Ptr Msg32
|
||||
-> Ptr XOnlyPubKey64
|
||||
-> IO Ret
|
||||
|
||||
foreign import ccall
|
||||
"secp256k1.h secp256k1_schnorrsig_parse"
|
||||
schnorrSignatureParse
|
||||
:: Ptr Ctx
|
||||
-> Ptr SchnorrSig64 -- out
|
||||
-> Ptr CUChar -- in
|
||||
-> IO Ret
|
||||
|
||||
foreign import ccall
|
||||
"secp256k1.h secp256k1_xonly_pubkey_parse"
|
||||
schnorrXOnlyPubKeyParse
|
||||
:: Ptr Ctx
|
||||
-> Ptr XOnlyPubKey64 -- out
|
||||
-> Ptr CUChar -- in
|
||||
-> IO Ret
|
||||
|
||||
foreign import ccall
|
||||
"secp256k1.h secp256k1_xonly_pubkey_create"
|
||||
schnorrXOnlyPubKeyCreate
|
||||
:: Ptr Ctx
|
||||
-> Ptr XOnlyPubKey64
|
||||
-> Ptr SecKey32
|
||||
-> IO Ret
|
||||
#endif
|
||||
|
||||
#ifdef ECDH
|
||||
foreign import ccall
|
||||
"secp256k1_ecdh.h secp256k1_ecdh"
|
||||
|
@ -2,6 +2,7 @@
|
||||
module Crypto.Secp256k1Spec (spec) where
|
||||
|
||||
import Crypto.Secp256k1
|
||||
import qualified Data.ByteString as BS
|
||||
import qualified Data.ByteString.Base16 as B16
|
||||
import qualified Data.ByteString.Char8 as B8
|
||||
import Data.Maybe (fromMaybe)
|
||||
@ -57,6 +58,28 @@ spec = do
|
||||
describe "ecdh" $ do
|
||||
it "computes dh secret" $ property $ computeDhSecret
|
||||
#endif
|
||||
#ifdef SCHNORR
|
||||
describe "schnorr (bip-340)" $ do
|
||||
it "validates test vector 0" $ property bip340Vector0
|
||||
it "rejects test vector 5" $ property $
|
||||
failingVectorToAssertion InvalidPubKey
|
||||
(
|
||||
hexToBytes "eefdea4cdb677750a420fee807eacf21eb9898ae79b9768766e4faa04a2d4a34",
|
||||
hexToBytes "243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c89",
|
||||
hexToBytes "667c2f778e0616e611bd0c14b8a600c5884551701a949ef0ebfd72d452d64e844160bcfc3f466ecb8facd19ade57d8699d74e7207d78c6aedc3799b52a8e0598"
|
||||
)
|
||||
it "rejects test vector 6" $ property $
|
||||
failingVectorToAssertion InvalidSig
|
||||
(
|
||||
hexToBytes "dff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659",
|
||||
hexToBytes "243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e6c89",
|
||||
hexToBytes "f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9935554d1aa5f0374e5cdaacb3925035c7c169b27c4426df0a6b19af3baeab138"
|
||||
)
|
||||
it "makes a valid taproot key spend signature" $ property taprootKeySpend
|
||||
#endif
|
||||
|
||||
hexToBytes :: String -> BS.ByteString
|
||||
hexToBytes = fst . B16.decode . B8.pack
|
||||
|
||||
isStringPubKey :: (PubKey, Bool) -> Bool
|
||||
isStringPubKey (k, c) = k == fromString (cs hex) where
|
||||
@ -178,12 +201,12 @@ tweakAddSecKeyTest =
|
||||
assertEqual "tweaked keys match" expected tweaked
|
||||
where
|
||||
tweaked = do
|
||||
key <- secKey $ fst $ B16.decode $ B8.pack
|
||||
key <- secKey $ hexToBytes
|
||||
"f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
|
||||
twk <- tweak $ fst $ B16.decode $ B8.pack
|
||||
twk <- tweak $ hexToBytes
|
||||
"f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
|
||||
tweakAddSecKey key twk
|
||||
expected = secKey $ fst $ B16.decode $ B8.pack
|
||||
expected = secKey $ hexToBytes
|
||||
"ec1e3ce1cefa18a671d51125e2b249688d934b0e28f5d1665384d9b02f929059"
|
||||
|
||||
tweakMulSecKeyTest :: Assertion
|
||||
@ -191,12 +214,12 @@ tweakMulSecKeyTest =
|
||||
assertEqual "tweaked keys match" expected tweaked
|
||||
where
|
||||
tweaked = do
|
||||
key <- secKey $ fst $ B16.decode $ B8.pack
|
||||
key <- secKey $ hexToBytes
|
||||
"f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a"
|
||||
twk <- tweak $ fst $ B16.decode $ B8.pack
|
||||
twk <- tweak $ hexToBytes
|
||||
"f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
|
||||
tweakMulSecKey key twk
|
||||
expected = secKey $ fst $ B16.decode $ B8.pack
|
||||
expected = secKey $ hexToBytes
|
||||
"a96f5962493acb179f60a86a9785fc7a30e0c39b64c09d24fe064d9aef15e4c0"
|
||||
|
||||
tweakAddPubKeyTest :: Assertion
|
||||
@ -204,12 +227,12 @@ tweakAddPubKeyTest =
|
||||
assertEqual "tweaked keys match" expected tweaked
|
||||
where
|
||||
tweaked = do
|
||||
pub <- importPubKey $ fst $ B16.decode $ B8.pack
|
||||
pub <- importPubKey $ hexToBytes
|
||||
"04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd4470512213d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
|
||||
twk <- tweak $ fst $ B16.decode $ B8.pack
|
||||
twk <- tweak $ hexToBytes
|
||||
"f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
|
||||
tweakAddPubKey pub twk
|
||||
expected = importPubKey $ fst $ B16.decode $ B8.pack
|
||||
expected = importPubKey $ hexToBytes
|
||||
"04441c3982b97576646e0df0c96736063df6b42f2ee566d13b9f6424302d1379e518fdc87a14c5435bff7a5db4552042cb4120c6b86a4bbd3d0643f3c14ad01368"
|
||||
|
||||
tweakMulPubKeyTest :: Assertion
|
||||
@ -217,12 +240,12 @@ tweakMulPubKeyTest =
|
||||
assertEqual "tweaked keys match" expected tweaked
|
||||
where
|
||||
tweaked = do
|
||||
pub <- importPubKey $ fst $ B16.decode $ B8.pack
|
||||
pub <- importPubKey $ hexToBytes
|
||||
"04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd4470512213d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
|
||||
twk <- tweak $ fst $ B16.decode $ B8.pack
|
||||
twk <- tweak $ hexToBytes
|
||||
"f5cbe7d88182a4b8e400f96b06128921864a18187d114c8ae8541b566c8ace00"
|
||||
tweakMulPubKey pub twk
|
||||
expected = importPubKey $ fst $ B16.decode $ B8.pack
|
||||
expected = importPubKey $ hexToBytes
|
||||
"04f379dc99cdf5c83e433defa267fbb3377d61d6b779c06a0e4ce29ae3ff5353b12ae49c9d07e7368f2ba5a446c203255ce912322991a2d6a9d5d5761c61ed1845"
|
||||
|
||||
combinePubKeyTest :: Assertion
|
||||
@ -230,14 +253,14 @@ combinePubKeyTest =
|
||||
assertEqual "combined keys match" expected combined
|
||||
where
|
||||
combined = do
|
||||
pub1 <- importPubKey $ fst $ B16.decode $ B8.pack
|
||||
pub1 <- importPubKey $ hexToBytes
|
||||
"04dded4203dac96a7e85f2c374a37ce3e9c9a155a72b64b4551b0bfe779dd4470512213d5ed790522c042dee8e85c4c0ec5f96800b72bc5940c8bc1c5e11e4fcbf"
|
||||
pub2 <- importPubKey $ fst $ B16.decode $ B8.pack
|
||||
pub2 <- importPubKey $ hexToBytes
|
||||
"0487d82042d93447008dfe2af762068a1e53ff394a5bf8f68a045fa642b99ea5d153f577dd2dba6c7ae4cfd7b6622409d7edd2d76dd13a8092cd3af97b77bd2c77"
|
||||
pub3 <- importPubKey $ fst $ B16.decode $ B8.pack
|
||||
pub3 <- importPubKey $ hexToBytes
|
||||
"049b101edcbe1ee37ff6b2318526a425b629e823d7d8d9154417880595a28000ee3febd908754b8ce4e491aa6fe488b41fb5d4bb3788e33c9ff95a7a9229166d59"
|
||||
combinePubKeys [pub1, pub2, pub3]
|
||||
expected = importPubKey $ fst $ B16.decode $ B8.pack
|
||||
expected = importPubKey $ hexToBytes
|
||||
"043d9a7ec70011efc23c33a7e62d2ea73cca87797e3b659d93bea6aa871aebde56c3bc6134ca82e324b0ab9c0e601a6d2933afe7fb5d9f3aae900f5c5dc6e362c8"
|
||||
|
||||
negateTweakTest :: Assertion
|
||||
@ -258,11 +281,77 @@ computeDhSecret =
|
||||
assertEqual "ecdh computes known secret" expected computed
|
||||
where
|
||||
computed = do
|
||||
pub <- importPubKey $ fst $ B16.decode $ B8.pack
|
||||
pub <- importPubKey $ hexToBytes
|
||||
"028d7500dd4c12685d1f568b4c2b5048e8534b873319f3a8daa612b469132ec7f7"
|
||||
sec <- secKey $ fst $ B16.decode $ B8.pack
|
||||
sec <- secKey $ hexToBytes
|
||||
"1212121212121212121212121212121212121212121212121212121212121212"
|
||||
pure $ ecdh pub sec
|
||||
expected = Just $ fst $ B16.decode $ B8.pack
|
||||
expected = Just $ hexToBytes
|
||||
"1e2fb3c8fe8fb9f262f649f64d26ecf0f2c0a805a767cf02dc2d77a6ef1fdcc3"
|
||||
#endif
|
||||
|
||||
#ifdef SCHNORR
|
||||
data VectorError = InvalidPubKey | InvalidSig | InvalidMsg | InvalidSigFormat
|
||||
deriving (Eq, Show)
|
||||
|
||||
failingVectorToAssertion :: VectorError -> (BS.ByteString, BS.ByteString, BS.ByteString) -> Assertion
|
||||
failingVectorToAssertion expectedFailure (pubBytes, msgBytes, sigBytes) =
|
||||
assertEqual ("expected error " <> show expectedFailure <> " occurs") (Left expectedFailure) computed
|
||||
where
|
||||
computed :: Either VectorError ()
|
||||
computed =
|
||||
let
|
||||
pubM = importXOnlyPubKey pubBytes
|
||||
sigM = importSchnorrSig sigBytes
|
||||
msgM = msg $ msgBytes
|
||||
in
|
||||
case (pubM, sigM, msgM) of
|
||||
(Nothing, _, _) -> Left InvalidPubKey
|
||||
(_, Nothing, _) -> Left InvalidSigFormat
|
||||
(_, _, Nothing) -> Left InvalidMsg
|
||||
(Just pub, Just sig, Just msg) ->
|
||||
if verifyMsgSchnorr pub sig msg
|
||||
then Right ()
|
||||
else Left InvalidSig
|
||||
|
||||
bip340Vector0 :: Assertion
|
||||
bip340Vector0 =
|
||||
passingVectorToAssertion 0
|
||||
(
|
||||
BS.replicate 31 0 <> B8.pack ['\x01']
|
||||
, BS.replicate 32 0
|
||||
, hexToBytes "db46b5cdc554edbd7765611b75d7bdbc639cf538fb6e9ef04a61884b765343aae148954f27eb69291a9045862f8ae4fa53436c117e9397c70275d5398066d44b"
|
||||
)
|
||||
|
||||
-- Integer is BIP-340 vector test number
|
||||
passingVectorToAssertion :: Integer -> (BS.ByteString, BS.ByteString, BS.ByteString) -> Assertion
|
||||
passingVectorToAssertion idx (secBytes, msgBytes, sigBytes) =
|
||||
assertEqual ("BIP-340 test vector " <> show idx <> " signature matches") expectedSig computedSig
|
||||
where
|
||||
expectedSig :: Maybe SchnorrSig
|
||||
expectedSig = importSchnorrSig $ sigBytes
|
||||
computedSig :: Maybe SchnorrSig
|
||||
computedSig = do
|
||||
sec <- secKey secBytes
|
||||
msg <- msg $ msgBytes
|
||||
pure $ signMsgSchnorr sec msg
|
||||
|
||||
-- This test is ported from the C code, it is called 'test_schnorrsig_taproot'.
|
||||
-- But this version was modified to use fixed keys and messages.
|
||||
taprootKeySpend :: Assertion
|
||||
taprootKeySpend =
|
||||
assertEqual "derivation succeded and resulting signature is valid" (Just True) computed
|
||||
where
|
||||
computed = do
|
||||
sec <- secKey $ BS.replicate 32 32
|
||||
msgToSign <- msg $ BS.replicate 32 00
|
||||
|
||||
let internalPub = deriveXOnlyPubKey sec
|
||||
twea <- tweak $ exportXOnlyPubKey internalPub
|
||||
(outputPub, isNegated) <- schnorrTweakAddPubKey internalPub twea
|
||||
|
||||
-- This is a 'key spend' in Taproot terminology:
|
||||
tweakSec <- schnorrTweakAddSecKey sec twea
|
||||
let sig = signMsgSchnorr tweakSec msgToSign
|
||||
pure $ verifyMsgSchnorr outputPub sig msgToSign
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user