@@ -25,6 +25,7 @@ module Crypto.Secp256k1 (
25
25
KeyPair ,
26
26
Signature ,
27
27
RecoverableSignature ,
28
+ SchnorrSignature ,
28
29
Tweak ,
29
30
30
31
-- * Parsing and Serialization
@@ -40,6 +41,8 @@ module Crypto.Secp256k1 (
40
41
exportSignatureDer ,
41
42
importRecoverableSignature ,
42
43
exportRecoverableSignature ,
44
+ importSchnorrSignature ,
45
+ exportSchnorrSignature ,
43
46
importTweak ,
44
47
45
48
-- * ECDSA Operations
@@ -89,7 +92,7 @@ import Crypto.Secp256k1.Internal
89
92
import Crypto.Secp256k1.Prim (flagsEcUncompressed )
90
93
import Crypto.Secp256k1.Prim qualified as Prim
91
94
import Data.ByteArray.Encoding qualified as BA
92
- import Data.ByteArray.Sized
95
+ import Data.ByteArray.Sized hiding ( take )
93
96
import Data.ByteString (ByteString )
94
97
import Data.ByteString qualified as BS
95
98
import Data.ByteString.Char8 qualified as B8
@@ -140,6 +143,7 @@ import Foreign.Storable (Storable (..))
140
143
import GHC.Generics (Generic )
141
144
import GHC.IO.Handle.Text (memcpy )
142
145
import System.IO.Unsafe (unsafePerformIO )
146
+ import System.Random (newStdGen , randoms )
143
147
import Text.Read (
144
148
Lexeme (String ),
145
149
lexP ,
@@ -283,6 +287,28 @@ instance NFData Signature where
283
287
rnf Signature {.. } = seq signatureFPtr ()
284
288
285
289
290
+ -- | Structure containing Schnorr Signature
291
+ newtype SchnorrSignature = SchnorrSignature { schnorrSignatureFPtr :: ForeignPtr Prim. Sig64}
292
+
293
+
294
+ instance Show SchnorrSignature where
295
+ show sig = (B8. unpack . encodeBase16) (exportSchnorrSignature sig)
296
+ instance Read SchnorrSignature where
297
+ readsPrec i cs = case decodeBase16 $ B8. pack token of
298
+ Left e -> []
299
+ Right a -> maybeToList $ (,rest) <$> importSchnorrSignature a
300
+ where
301
+ trimmed = dropWhile isSpace cs
302
+ (token, rest) = span isAlphaNum trimmed
303
+ instance Eq SchnorrSignature where
304
+ sig == sig' = unsafePerformIO . evalContT $ do
305
+ sigp <- ContT $ withForeignPtr (schnorrSignatureFPtr sig)
306
+ sigp' <- ContT $ withForeignPtr (schnorrSignatureFPtr sig')
307
+ (EQ == ) <$> lift (memCompare (castPtr sigp) (castPtr sigp') 64 )
308
+ instance NFData SchnorrSignature where
309
+ rnf SchnorrSignature {.. } = seq schnorrSignatureFPtr ()
310
+
311
+
286
312
-- | Structure containing Signature AND recovery ID
287
313
newtype RecoverableSignature = RecoverableSignature { recoverableSignatureFPtr :: ForeignPtr Prim. RecSig65}
288
314
@@ -493,6 +519,23 @@ exportRecoverableSignature RecoverableSignature{..} = unsafePerformIO . evalCont
493
519
unsafePackByteString (outBuf, 65 )
494
520
495
521
522
+ -- | Parses 'SchnorrSignature' from Schnorr (64 byte) representation
523
+ importSchnorrSignature :: ByteString -> Maybe SchnorrSignature
524
+ importSchnorrSignature bs
525
+ | BS. length bs /= 64 = Nothing
526
+ | otherwise = unsafePerformIO $ do
527
+ outBuf <- mallocBytes 64
528
+ unsafeUseByteString bs $ \ (ptr, _) -> do
529
+ memcpy outBuf ptr 64
530
+ Just . SchnorrSignature <$> newForeignPtr finalizerFree outBuf
531
+
532
+
533
+ -- | Serializes 'SchnorrSignature' to Schnorr (64 byte) representation
534
+ exportSchnorrSignature :: SchnorrSignature -> ByteString
535
+ exportSchnorrSignature (SchnorrSignature fptr) = unsafePerformIO $
536
+ withForeignPtr fptr $ \ ptr -> BS. packCStringLen (castPtr ptr, 64 )
537
+
538
+
496
539
-- | Parses 'Tweak' from 32 byte @ByteString@. If the @ByteString@ is an invalid 'SecKey' then this will yield @Nothing@
497
540
importTweak :: ByteString -> Maybe Tweak
498
541
importTweak = fmap (Tweak . castForeignPtr . secKeyFPtr) . importSecKey
@@ -702,28 +745,30 @@ keyPairPubKeyXOTweakAdd KeyPair{..} Tweak{..} = unsafePerformIO . evalContT $ do
702
745
703
746
-- | Compute a schnorr signature using a 'KeyPair'. The @ByteString@ must be 32 bytes long to get a @Just@ out of this
704
747
-- function
705
- schnorrSign :: KeyPair -> ByteString -> Maybe Signature
748
+ schnorrSign :: KeyPair -> ByteString -> Maybe SchnorrSignature
706
749
schnorrSign KeyPair {.. } bs
707
750
| BS. length bs /= 32 = Nothing
708
751
| otherwise = unsafePerformIO . evalContT $ do
709
752
(msgHashPtr, _) <- ContT (unsafeUseByteString bs)
710
753
keyPairPtr <- ContT (withForeignPtr keyPairFPtr)
711
754
lift $ do
712
755
sigBuf <- mallocBytes 64
713
- -- TODO: provide randomness here instead of supplying a null pointer
714
- ret <- Prim. schnorrsigSign ctx sigBuf msgHashPtr keyPairPtr nullPtr
715
- if isSuccess ret
716
- then Just . Signature <$> newForeignPtr finalizerFree sigBuf
717
- else free sigBuf $> Nothing
756
+ gen <- newStdGen
757
+ let randomBytes = BS. pack $ take 32 $ randoms gen
758
+ BS. useAsCStringLen randomBytes $ \ (randomPtr, _) -> do
759
+ ret <- Prim. schnorrsigSign ctx sigBuf msgHashPtr keyPairPtr (castPtr randomPtr)
760
+ if isSuccess ret
761
+ then Just . SchnorrSignature <$> newForeignPtr finalizerFree sigBuf
762
+ else free sigBuf $> Nothing
718
763
719
764
720
765
-- | Verify the authenticity of a schnorr signature. @True@ means the 'Signature' is correct.
721
- schnorrVerify :: PubKeyXO -> ByteString -> Signature -> Bool
722
- schnorrVerify PubKeyXO {.. } bs Signature {.. } = unsafePerformIO . evalContT $ do
766
+ schnorrVerify :: PubKeyXO -> ByteString -> SchnorrSignature -> Bool
767
+ schnorrVerify PubKeyXO {.. } bs SchnorrSignature {.. } = unsafePerformIO . evalContT $ do
723
768
pubKeyPtr <- ContT (withForeignPtr pubKeyXOFPtr)
724
- signaturePtr <- ContT (withForeignPtr signatureFPtr )
769
+ schnorrSignaturePtr <- ContT (withForeignPtr schnorrSignatureFPtr )
725
770
(msgPtr, msgLen) <- ContT (unsafeUseByteString bs)
726
- lift $ isSuccess <$> Prim. schnorrsigSignVerify ctx signaturePtr msgPtr msgLen pubKeyPtr
771
+ lift $ isSuccess <$> Prim. schnorrsigSignVerify ctx schnorrSignaturePtr msgPtr msgLen pubKeyPtr
727
772
728
773
729
774
-- | Generate a tagged sha256 digest as specified in BIP340
0 commit comments