libsecp256k1-0.1.2: src/Crypto/Secp256k1.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -fno-show-valid-hole-fits #-}
-- |
-- Module : Crypto.Secp256k1
-- License : UNLICENSE
-- Maintainer : Keagan McClelland <keagan.mcclelland@gmail.com>
-- Stability : experimental
-- Portability : POSIX
--
-- Crytpographic functions from Bitcoin’s secp256k1 library.
module Crypto.Secp256k1 (
-- * Core Types
SecKey,
PubKeyXY,
PubKeyXO,
KeyPair,
Signature,
RecoverableSignature,
Tweak,
-- * Parsing and Serialization
importSecKey,
exportSecKey,
importPubKeyXY,
exportPubKeyXY,
importPubKeyXO,
exportPubKeyXO,
importSignature,
exportSignatureCompact,
exportSignatureDer,
importRecoverableSignature,
exportRecoverableSignature,
importTweak,
-- * ECDSA Operations
ecdsaVerify,
ecdsaSign,
ecdsaSignRecoverable,
ecdsaRecover,
ecdsaNormalizeSignature,
-- * Conversions
recSigToSig,
derivePubKey,
keyPairCreate,
keyPairSecKey,
keyPairPubKeyXY,
keyPairPubKeyXO,
xyToXO,
-- * Tweaks
secKeyTweakAdd,
secKeyTweakMul,
keyPairPubKeyXOTweakAdd,
pubKeyCombine,
pubKeyNegate,
secKeyNegate,
tweakNegate,
pubKeyTweakAdd,
pubKeyTweakMul,
pubKeyXOTweakAdd,
pubKeyXOTweakAddCheck,
-- * Schnorr Operations
schnorrSign,
schnorrVerify,
-- * Other
taggedSha256,
ecdh,
) where
import Control.DeepSeq (NFData (..))
import Control.Monad (replicateM, unless, (<=<))
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.Cont (ContT (..), evalContT)
import Crypto.Secp256k1.Internal
import Crypto.Secp256k1.Prim (flagsEcUncompressed)
import Crypto.Secp256k1.Prim qualified as Prim
import Data.ByteArray.Encoding qualified as BA
import Data.ByteArray.Sized
import Data.ByteString (ByteString)
import Data.ByteString qualified as BS
import Data.ByteString.Char8 qualified as B8
import Data.ByteString.Unsafe (unsafePackCStringLen, unsafePackMallocCStringLen)
import Data.Foldable (for_)
import Data.Functor (($>))
import Data.Hashable (Hashable (..))
import Data.Maybe (fromJust, fromMaybe, isJust, maybeToList)
import Data.Memory.PtrMethods (memCompare)
import Data.String (IsString (..))
import Foreign (
Bits (..),
ForeignPtr,
FunPtr,
Ptr,
Storable,
Word32,
Word64,
Word8,
alloca,
allocaArray,
allocaBytes,
castForeignPtr,
castPtr,
finalizerFree,
free,
freeHaskellFunPtr,
malloc,
mallocBytes,
newForeignPtr,
newForeignPtr_,
nullFunPtr,
nullPtr,
peek,
peekByteOff,
peekElemOff,
plusPtr,
poke,
pokeArray,
pokeByteOff,
pokeElemOff,
sizeOf,
withForeignPtr,
)
import Foreign.C (CInt (..), CSize (..))
import Foreign.Storable (Storable (..))
import GHC.Generics (Generic)
import GHC.IO.Handle.Text (memcpy)
import System.IO.Unsafe (unsafePerformIO)
import Text.Read (
Lexeme (String),
lexP,
parens,
pfail,
readPrec,
)
-- | Secret Key
newtype SecKey = SecKey {secKeyFPtr :: ForeignPtr Prim.Seckey32}
instance Show SecKey where
show SecKey{..} = unsafePerformIO . evalContT $ do
secKeyPtr <- ContT (withForeignPtr secKeyFPtr)
-- avoid allocating a new bytestring because we are only reading from this pointer
bs <- lift (Data.ByteString.Unsafe.unsafePackCStringLen (castPtr secKeyPtr, 32))
pure $ "0x" <> B8.unpack (BA.convertToBase BA.Base16 bs)
instance Read SecKey where
readsPrec i s = case s of
('0' : 'x' : bytes) -> case decodeBase16 $ B8.pack bytes of
Left e -> []
Right a -> maybeToList $ (,"") <$> importSecKey a
_ -> []
instance Eq SecKey where
sk == sk' = unsafePerformIO . evalContT $ do
skp <- ContT $ withForeignPtr (secKeyFPtr sk)
skp' <- ContT $ withForeignPtr (secKeyFPtr sk')
(EQ ==) <$> lift (memCompare (castPtr skp) (castPtr skp') 32)
instance Ord SecKey where
sk `compare` sk' = unsafePerformIO . evalContT $ do
skp <- ContT $ withForeignPtr (secKeyFPtr sk)
skp' <- ContT $ withForeignPtr (secKeyFPtr sk')
lift (memCompare (castPtr skp) (castPtr skp') 32)
instance Hashable SecKey where
hashWithSalt i k = hashWithSalt i $ exportSecKey k
instance NFData SecKey where
rnf SecKey{..} = seq secKeyFPtr ()
-- | Public Key with both X and Y coordinates
newtype PubKeyXY = PubKeyXY {pubKeyXYFPtr :: ForeignPtr Prim.Pubkey64}
instance Show PubKeyXY where
show pk = "0x" <> B8.unpack (BA.convertToBase BA.Base16 (exportPubKeyXY True pk))
instance Read PubKeyXY where
readsPrec i s = case s of
('0' : 'x' : bytes) -> case decodeBase16 $ B8.pack bytes of
Left e -> []
Right a -> maybeToList $ (,"") <$> importPubKeyXY a
_ -> []
instance Eq PubKeyXY where
pk == pk' = unsafePerformIO . evalContT $ do
pkp <- ContT . withForeignPtr . pubKeyXYFPtr $ pk
pkp' <- ContT . withForeignPtr . pubKeyXYFPtr $ pk'
res <- lift (Prim.ecPubkeyCmp ctx pkp pkp')
pure $ res == 0
instance Ord PubKeyXY where
pk `compare` pk' = unsafePerformIO . evalContT $ do
pkp <- ContT . withForeignPtr . pubKeyXYFPtr $ pk
pkp' <- ContT . withForeignPtr . pubKeyXYFPtr $ pk'
res <- lift (Prim.ecPubkeyCmp ctx pkp pkp')
pure $ compare res 0
instance Hashable PubKeyXY where
hashWithSalt i k = hashWithSalt i $ exportPubKeyXY True k
instance NFData PubKeyXY where
rnf PubKeyXY{..} = seq pubKeyXYFPtr ()
-- | Public Key with only an X coordinate.
newtype PubKeyXO = PubKeyXO {pubKeyXOFPtr :: ForeignPtr Prim.XonlyPubkey64}
instance Show PubKeyXO where
show pk = "0x" <> B8.unpack (BA.convertToBase BA.Base16 (exportPubKeyXO pk))
instance Read PubKeyXO where
readsPrec i s = case s of
('0' : 'x' : bytes) -> case decodeBase16 $ B8.pack bytes of
Left e -> []
Right a -> maybeToList $ (,"") <$> importPubKeyXO a
_ -> []
instance Eq PubKeyXO where
pk == pk' = unsafePerformIO . evalContT $ do
pkp <- ContT . withForeignPtr . pubKeyXOFPtr $ pk
pkp' <- ContT . withForeignPtr . pubKeyXOFPtr $ pk'
res <- lift (Prim.xonlyPubkeyCmp ctx pkp pkp')
pure $ res == 0
instance Ord PubKeyXO where
pk `compare` pk' = unsafePerformIO . evalContT $ do
pkp <- ContT . withForeignPtr . pubKeyXOFPtr $ pk
pkp' <- ContT . withForeignPtr . pubKeyXOFPtr $ pk'
res <- lift (Prim.xonlyPubkeyCmp ctx pkp pkp')
pure $ compare res 0
instance Hashable PubKeyXO where
hashWithSalt i k = hashWithSalt i $ exportPubKeyXO k
instance NFData PubKeyXO where
rnf PubKeyXO{..} = seq pubKeyXOFPtr ()
-- | Structure containing information equivalent to 'SecKey' and 'PubKeyXY'
newtype KeyPair = KeyPair {keyPairFPtr :: ForeignPtr Prim.Keypair96}
instance Eq KeyPair where
kp == kp' = unsafePerformIO . evalContT $ do
kpp <- ContT $ withForeignPtr (keyPairFPtr kp)
kpp' <- ContT $ withForeignPtr (keyPairFPtr kp')
(EQ ==) <$> lift (memCompare (castPtr kpp) (castPtr kpp') 32)
instance NFData KeyPair where
rnf KeyPair{..} = seq keyPairFPtr ()
-- | Structure containing Signature (R,S) data.
newtype Signature = Signature {signatureFPtr :: ForeignPtr Prim.Sig64}
instance Show Signature where
show sig = "0x" <> (B8.unpack . encodeBase16) (exportSignatureCompact sig)
instance Eq Signature where
sig == sig' = unsafePerformIO . evalContT $ do
sigp <- ContT $ withForeignPtr (signatureFPtr sig)
sigp' <- ContT $ withForeignPtr (signatureFPtr sig')
(EQ ==) <$> lift (memCompare (castPtr sigp) (castPtr sigp') 32)
instance NFData Signature where
rnf Signature{..} = seq signatureFPtr ()
-- | Structure containing Signature AND recovery ID
newtype RecoverableSignature = RecoverableSignature {recoverableSignatureFPtr :: ForeignPtr Prim.RecSig65}
instance Show RecoverableSignature where
show recSig = "0x" <> (B8.unpack . encodeBase16) (exportRecoverableSignature recSig)
instance Eq RecoverableSignature where
rs == rs' = unsafePerformIO . evalContT $ do
rsp <- ContT $ withForeignPtr (recoverableSignatureFPtr rs)
rsp' <- ContT $ withForeignPtr (recoverableSignatureFPtr rs')
(EQ ==) <$> lift (memCompare (castPtr rsp) (castPtr rsp') 32)
instance NFData RecoverableSignature where
rnf RecoverableSignature{..} = seq recoverableSignatureFPtr ()
-- | Isomorphic to 'SecKey' but specifically used for tweaking (EC Group operations) other keys
newtype Tweak = Tweak {tweakFPtr :: ForeignPtr Prim.Tweak32}
instance Show Tweak where
show (Tweak fptr) = show (SecKey $ castForeignPtr fptr)
instance Eq Tweak where
sk == sk' = unsafePerformIO . evalContT $ do
skp <- ContT $ withForeignPtr (tweakFPtr sk)
skp' <- ContT $ withForeignPtr (tweakFPtr sk')
(EQ ==) <$> lift (memCompare (castPtr skp) (castPtr skp') 32)
instance Ord Tweak where
sk `compare` sk' = unsafePerformIO . evalContT $ do
skp <- ContT $ withForeignPtr (tweakFPtr sk)
skp' <- ContT $ withForeignPtr (tweakFPtr sk')
lift (memCompare (castPtr skp) (castPtr skp') 32)
instance NFData Tweak where
rnf Tweak{..} = seq tweakFPtr ()
-- | Preinitialized context for signing and verification
ctx :: Prim.Ctx
ctx = unsafePerformIO $ Prim.contextCreate (Prim.flagsContextSign .|. Prim.flagsContextVerify)
{-# NOINLINE ctx #-}
-- | Parses 'SecKey', will be @Nothing@ if the @ByteString@ corresponds to 0{32} or is not 32 bytes in length
importSecKey :: ByteString -> Maybe SecKey
importSecKey bs
| BS.length bs /= 32 = Nothing
| otherwise = unsafePerformIO $ do
unsafeUseByteString bs $ \(ptr, len) -> do
ret <- Prim.ecSecKeyVerify ctx ptr
if isSuccess ret
then do
newPtr <- mallocBytes 32
memcpy newPtr ptr 32
Just . SecKey <$> newForeignPtr finalizerFree newPtr
else pure Nothing
exportSecKey :: SecKey -> ByteString
exportSecKey SecKey{..} = unsafePerformIO . evalContT $ do
secKeyPtr <- ContT (withForeignPtr secKeyFPtr)
lift $ packByteString (secKeyPtr, 32)
-- | Parses a 33 or 65 byte 'PubKeyXY', all other lengths will result in @Nothing@
importPubKeyXY :: ByteString -> Maybe PubKeyXY
importPubKeyXY bs = unsafePerformIO . evalContT $ do
(input, len) <- ContT (unsafeUseByteString bs)
lift $ do
if len == 33 || len == 65
then do
pubkeyOutputBuf <- mallocBytes 64
ret <- Prim.ecPubkeyParse ctx pubkeyOutputBuf input len
if isSuccess ret
then Just . PubKeyXY <$> newForeignPtr finalizerFree pubkeyOutputBuf
else free pubkeyOutputBuf $> Nothing
else pure Nothing
-- | Serialize 'PubKeyXY'. First argument @True@ for compressed output (33 bytes), @False@ for uncompressed (65 bytes).
exportPubKeyXY :: Bool -> PubKeyXY -> ByteString
exportPubKeyXY compress PubKeyXY{..} = unsafePerformIO . evalContT $ do
let flags = if compress then Prim.flagsEcCompressed else Prim.flagsEcUncompressed
let sz = if compress then 33 else 65
ptr <- ContT (withForeignPtr pubKeyXYFPtr)
written <- ContT alloca
lift $ do
poke written (fromIntegral sz)
buf <- mallocBytes sz
-- always succeeds so we don't need to check
_ret <- Prim.ecPubkeySerialize ctx buf written ptr flags
len <- peek written
unsafePackMallocCStringLen (castPtr buf, fromIntegral len)
-- | Parses 'PubKeyXO' from @ByteString@, will be @Nothing@ if the pubkey corresponds to the Point at Infinity or the
-- the @ByteString@ is not 32 bytes long
importPubKeyXO :: ByteString -> Maybe PubKeyXO
importPubKeyXO bs
| BS.length bs /= 32 = Nothing
| otherwise = unsafePerformIO $ do
outBuf <- mallocBytes 64
unsafeUseByteString bs $ \(ptr, _) -> do
ret <- Prim.xonlyPubkeyParse ctx outBuf ptr
if isSuccess ret
then Just . PubKeyXO <$> newForeignPtr finalizerFree outBuf
else free outBuf $> Nothing
-- | Serializes 'PubKeyXO' to 32 byte @ByteString@
exportPubKeyXO :: PubKeyXO -> ByteString
exportPubKeyXO (PubKeyXO pkFPtr) = unsafePerformIO $ do
outBuf <- mallocBytes 32
_ret <- withForeignPtr pkFPtr $ Prim.xonlyPubkeySerialize ctx outBuf
unsafePackByteString (outBuf, 32)
-- | Parses 'Signature' from DER (71 | 72 | 73 bytes) or Compact (64 bytes) representations.
importSignature :: ByteString -> Maybe Signature
importSignature bs = unsafePerformIO $
unsafeUseByteString bs $ \(inBuf, len) -> do
outBuf <- mallocBytes 64
ret <-
if
-- compact
| len == 64 -> Prim.ecdsaSignatureParseCompact ctx outBuf inBuf
-- der
| len >= 69 && len <= 73 -> Prim.ecdsaSignatureParseDer ctx outBuf inBuf len
-- invalid
| otherwise -> pure 0
if isSuccess ret
then Just . Signature <$> newForeignPtr finalizerFree outBuf
else free outBuf $> Nothing
-- | Serializes 'Signature' to Compact (64 byte) representation
exportSignatureCompact :: Signature -> ByteString
exportSignatureCompact (Signature fptr) = unsafePerformIO $ do
outBuf <- mallocBytes 64
-- always succeeds
_ret <- withForeignPtr fptr $ Prim.ecdsaSignatureSerializeCompact ctx outBuf
unsafePackByteString (outBuf, 64)
-- | Serializes 'Signature' to DER (71 | 72 bytes) representation
exportSignatureDer :: Signature -> ByteString
exportSignatureDer (Signature fptr) = unsafePerformIO $ do
-- as of Q4'2015 73 byte sigs became nonstandard so we will never create one that big
outBuf <- mallocBytes 72
alloca $ \written -> do
poke written 72
-- always succeeds
_ret <- withForeignPtr fptr $ Prim.ecdsaSignatureSerializeDer ctx outBuf written
len <- peek written
unsafePackByteString (outBuf, len)
-- | Parses 'RecoverableSignature' from Compact (65 byte) representation
importRecoverableSignature :: ByteString -> Maybe RecoverableSignature
importRecoverableSignature bs
| BS.length bs /= 65 = Nothing
| otherwise = unsafePerformIO . evalContT $ do
outBuf <- lift (mallocBytes 65)
(ptr, len) <- ContT (unsafeUseByteString bs)
recId <- lift (peekByteOff @Word8 ptr 64)
let recIdCInt = fromIntegral recId
ret <- lift (Prim.ecdsaRecoverableSignatureParseCompact ctx outBuf ptr recIdCInt)
lift $
if isSuccess ret
then Just . RecoverableSignature <$> newForeignPtr finalizerFree outBuf
else free outBuf $> Nothing
-- | Serializes 'RecoverableSignature' to Compact (65 byte) representation
exportRecoverableSignature :: RecoverableSignature -> ByteString
exportRecoverableSignature RecoverableSignature{..} = unsafePerformIO . evalContT $ do
recSigPtr <- ContT (withForeignPtr recoverableSignatureFPtr)
lift $ do
outBuf <- mallocBytes 65
recIdPtr <- malloc
_ret <- Prim.ecdsaRecoverableSignatureSerializeCompact ctx outBuf recIdPtr recSigPtr
recId <- peek recIdPtr
pokeByteOff outBuf 64 recId
unsafePackByteString (outBuf, 65)
-- | Parses 'Tweak' from 32 byte @ByteString@. If the @ByteString@ is an invalid 'SecKey' then this will yield @Nothing@
importTweak :: ByteString -> Maybe Tweak
importTweak = fmap (Tweak . castForeignPtr . secKeyFPtr) . importSecKey
-- | Verify message signature. 'True' means that the signature is correct.
ecdsaVerify :: ByteString -> PubKeyXY -> Signature -> Bool
ecdsaVerify msgHash (PubKeyXY pkFPtr) (Signature sigFPtr) = unsafePerformIO $
evalContT $ do
pkPtr <- ContT (withForeignPtr pkFPtr)
sigPtr <- ContT (withForeignPtr sigFPtr)
(msgHashPtr, n) <- ContT (unsafeUseByteString msgHash)
lift $ isSuccess <$> Prim.ecdsaVerify ctx sigPtr msgHashPtr pkPtr
-- | Signs @ByteString@ with 'SecKey' only if @ByteString@ is 32 bytes.
ecdsaSign :: SecKey -> ByteString -> Maybe Signature
ecdsaSign (SecKey skFPtr) msgHash
| BS.length msgHash /= 32 = Nothing
| otherwise = unsafePerformIO . evalContT $ do
skPtr <- ContT (withForeignPtr skFPtr)
(msgHashPtr, _) <- ContT (unsafeUseByteString msgHash)
lift $ do
sigBuf <- mallocBytes 64
ret <- Prim.ecdsaSign ctx sigBuf msgHashPtr skPtr Prim.nonceFunctionDefault nullPtr
if isSuccess ret
then Just . Signature <$> newForeignPtr finalizerFree sigBuf
else free sigBuf $> Nothing
-- | Signs @ByteString@ with 'SecKey' only if @ByteString@ is 32 bytes. Retains ability to compute 'PubKeyXY' from the
-- 'RecoverableSignature' and the original message (@ByteString@)
ecdsaSignRecoverable :: SecKey -> ByteString -> Maybe RecoverableSignature
ecdsaSignRecoverable SecKey{..} bs
| BS.length bs /= 32 = Nothing
| otherwise = unsafePerformIO . evalContT $ do
(msgHashPtr, _) <- ContT (unsafeUseByteString bs)
secKeyPtr <- ContT (withForeignPtr secKeyFPtr)
lift $ do
recSigBuf <- mallocBytes 65
ret <- Prim.ecdsaSignRecoverable ctx recSigBuf msgHashPtr secKeyPtr Prim.nonceFunctionDefault nullPtr
if isSuccess ret
then Just . RecoverableSignature <$> newForeignPtr finalizerFree recSigBuf
else free recSigBuf $> Nothing
-- | Computes 'PubKeyXY' from 'RecoverableSignature' and the original message that was signed (must be 32 bytes).
ecdsaRecover :: RecoverableSignature -> ByteString -> Maybe PubKeyXY
ecdsaRecover RecoverableSignature{..} msgHash
| BS.length msgHash /= 32 = Nothing
| otherwise = unsafePerformIO . evalContT $ do
recSigPtr <- ContT (withForeignPtr recoverableSignatureFPtr)
(msgHashPtr, _) <- ContT (unsafeUseByteString msgHash)
lift $ do
pubKeyBuf <- mallocBytes 64
ret <- Prim.ecdsaRecover ctx pubKeyBuf recSigPtr msgHashPtr
if isSuccess ret
then Just . PubKeyXY <$> newForeignPtr finalizerFree pubKeyBuf
else free pubKeyBuf $> Nothing
-- | Convert a 'Signature' to a normalized lower-S form. If the 'Signature' was already in its lower-S form it will
-- be equal to the input.
ecdsaNormalizeSignature :: Signature -> Signature
ecdsaNormalizeSignature Signature{..} = unsafePerformIO . evalContT $ do
sigPtr <- ContT (withForeignPtr signatureFPtr)
lift $ do
outBuf <- mallocBytes 64
_ret <- Prim.ecdsaSignatureNormalize ctx outBuf sigPtr
Signature <$> newForeignPtr finalizerFree outBuf
-- | Forgets the recovery id of a signature
recSigToSig :: RecoverableSignature -> Signature
recSigToSig RecoverableSignature{..} = unsafePerformIO . evalContT $ do
recSigPtr <- ContT (withForeignPtr recoverableSignatureFPtr)
lift $ do
sigBuf <- mallocBytes 64
_ret <- Prim.ecdsaRecoverableSignatureConvert ctx sigBuf recSigPtr
Signature <$> newForeignPtr finalizerFree sigBuf
-- | Use 'SecKey' to compute the corresponding 'PubKeyXY'
derivePubKey :: SecKey -> PubKeyXY
derivePubKey (SecKey skFPtr) = unsafePerformIO $ do
outBuf <- mallocBytes 64
ret <- withForeignPtr skFPtr $ Prim.ecPubkeyCreate ctx outBuf
unless (isSuccess ret) $ do
free outBuf
error "Bug: Invalid SecKey Constructed"
PubKeyXY <$> newForeignPtr finalizerFree outBuf
-- | Compute a shared secret using ECDH and SHA256. This algorithm uses your own 'SecKey', your counterparty's 'PubKeyXY'
-- and results in a 32 byte SHA256 Digest.
ecdh :: SecKey -> PubKeyXY -> SizedByteArray 32 ByteString
ecdh SecKey{..} PubKeyXY{..} = unsafePerformIO . evalContT $ do
outBuf <- lift (mallocBytes 32)
sk <- ContT (withForeignPtr secKeyFPtr)
pk <- ContT (withForeignPtr pubKeyXYFPtr)
ret <- lift (Prim.ecdh ctx outBuf pk sk Prim.ecdhHashFunctionSha256 nullPtr)
if isSuccess ret
then do
bs <- lift $ unsafePackByteString (outBuf, 32)
let Just digest = sizedByteArray bs
pure digest
else lift (free outBuf) *> error "Bug: Invalid Scalar or Overflow"
-- | Add 'Tweak' to 'SecKey'.
secKeyTweakAdd :: SecKey -> Tweak -> Maybe SecKey
secKeyTweakAdd SecKey{..} Tweak{..} = unsafePerformIO . evalContT $ do
skPtr <- ContT (withForeignPtr secKeyFPtr)
skOut <- lift (mallocBytes 32)
lift (memcpy skOut skPtr 32)
twkPtr <- ContT (withForeignPtr tweakFPtr)
ret <- lift (Prim.ecSeckeyTweakAdd ctx skOut twkPtr)
lift $
if isSuccess ret
then Just . SecKey <$> newForeignPtr finalizerFree skOut
else free skOut $> Nothing
-- | Multiply 'SecKey' by 'Tweak'.
secKeyTweakMul :: SecKey -> Tweak -> Maybe SecKey
secKeyTweakMul SecKey{..} Tweak{..} = unsafePerformIO . evalContT $ do
skPtr <- ContT (withForeignPtr secKeyFPtr)
skOut <- lift (mallocBytes 32)
lift (memcpy skOut skPtr 32)
twkPtr <- ContT (withForeignPtr tweakFPtr)
ret <- lift (Prim.ecSeckeyTweakMul ctx skOut twkPtr)
lift $
if isSuccess ret
then Just . SecKey <$> newForeignPtr finalizerFree skOut
else free skOut $> Nothing
-- | Compute 'KeyPair' structure from 'SecKey'
keyPairCreate :: SecKey -> KeyPair
keyPairCreate SecKey{..} = unsafePerformIO $ do
keyPairBuf <- mallocBytes 96
ret <- withForeignPtr secKeyFPtr $ Prim.keypairCreate ctx keyPairBuf
unless (isSuccess ret) $ do
free keyPairBuf
error "Bug: Invalid SecKey Constructed"
KeyPair <$> newForeignPtr finalizerFree keyPairBuf
-- | Project 'PubKeyXY' from 'KeyPair'
keyPairPubKeyXY :: KeyPair -> PubKeyXY
keyPairPubKeyXY KeyPair{..} = unsafePerformIO $ do
pubKeyBuf <- mallocBytes 64
ret <- withForeignPtr keyPairFPtr $ Prim.keypairPub ctx pubKeyBuf
unless (isSuccess ret) $ do
free pubKeyBuf
error "Bug: Invalid KeyPair Constructed"
PubKeyXY <$> newForeignPtr finalizerFree pubKeyBuf
-- | Project 'SecKey' from 'KeyPair'
keyPairSecKey :: KeyPair -> SecKey
keyPairSecKey KeyPair{..} = unsafePerformIO $ do
secKeyBuf <- mallocBytes 32
ret <- withForeignPtr keyPairFPtr $ Prim.keypairSec ctx secKeyBuf
unless (isSuccess ret) $ do
free secKeyBuf
error "Bug: Invalid KeyPair Constructed"
SecKey <$> newForeignPtr finalizerFree secKeyBuf
-- | Project 'PubKeyXO' from 'KeyPair' as well as parity bit. @True@ indicates that the public key is the same as it
-- would be if you had serialized the 'PubKeyXO' and it was prefixed with 'Prim.flagsTagPubkeyOdd'. @False@ indicates
-- it would be prefixed by 'Prim.flagsTagPubkeyEven'
keyPairPubKeyXO :: KeyPair -> (PubKeyXO, Bool)
keyPairPubKeyXO KeyPair{..} = unsafePerformIO $ do
pubKeyBuf <- mallocBytes 64
parityPtr <- malloc
ret <- withForeignPtr keyPairFPtr $ Prim.keypairXonlyPub ctx pubKeyBuf parityPtr
unless (isSuccess ret) $ do
free pubKeyBuf
free parityPtr
error "Bug: Invalid KeyPair Constructed"
parity <- peek parityPtr
negated <- case parity of
0 -> pure False
1 -> pure True
_ -> do
free pubKeyBuf
free parityPtr
error "Bug: Invalid pk_parity result from Prim"
(,negated) . PubKeyXO <$> newForeignPtr finalizerFree pubKeyBuf
-- | Tweak a 'KeyPair' with a 'Tweak'. If the resulting 'KeyPair' is invalid (0, Infinity), then the result is @Nothing@
keyPairPubKeyXOTweakAdd :: KeyPair -> Tweak -> Maybe KeyPair
keyPairPubKeyXOTweakAdd KeyPair{..} Tweak{..} = unsafePerformIO . evalContT $ do
keyPairPtr <- ContT (withForeignPtr keyPairFPtr)
tweakPtr <- ContT (withForeignPtr tweakFPtr)
lift $ do
keyPairOut <- mallocBytes 96
_ <- memcpy keyPairOut keyPairPtr 96
ret <- Prim.keypairXonlyTweakAdd ctx keyPairOut tweakPtr
if isSuccess ret
then Just . KeyPair <$> newForeignPtr finalizerFree keyPairOut
else free keyPairOut $> Nothing
-- | Compute a schnorr signature using a 'KeyPair'. The @ByteString@ must be 32 bytes long to get a @Just@ out of this
-- function
schnorrSign :: KeyPair -> ByteString -> Maybe Signature
schnorrSign KeyPair{..} bs
| BS.length bs /= 32 = Nothing
| otherwise = unsafePerformIO . evalContT $ do
(msgHashPtr, _) <- ContT (unsafeUseByteString bs)
keyPairPtr <- ContT (withForeignPtr keyPairFPtr)
lift $ do
sigBuf <- mallocBytes 64
-- TODO: provide randomness here instead of supplying a null pointer
ret <- Prim.schnorrsigSign ctx sigBuf msgHashPtr keyPairPtr nullPtr
if isSuccess ret
then Just . Signature <$> newForeignPtr finalizerFree sigBuf
else free sigBuf $> Nothing
-- | Verify the authenticity of a schnorr signature. @True@ means the 'Signature' is correct.
schnorrVerify :: PubKeyXO -> ByteString -> Signature -> Bool
schnorrVerify PubKeyXO{..} bs Signature{..} = unsafePerformIO . evalContT $ do
pubKeyPtr <- ContT (withForeignPtr pubKeyXOFPtr)
signaturePtr <- ContT (withForeignPtr signatureFPtr)
(msgPtr, msgLen) <- ContT (unsafeUseByteString bs)
lift $ isSuccess <$> Prim.schnorrsigSignVerify ctx signaturePtr msgPtr msgLen pubKeyPtr
-- | Generate a tagged sha256 digest as specified in BIP340
taggedSha256 :: ByteString -> ByteString -> SizedByteArray 32 ByteString
taggedSha256 tag msg = unsafePerformIO . evalContT $ do
(tagBuf, tagLen) <- ContT (unsafeUseByteString tag)
(msgBuf, msgLen) <- ContT (unsafeUseByteString msg)
lift $ do
hashBuf <- mallocBytes 32
ret <- Prim.taggedSha256 ctx hashBuf tagBuf tagLen msgBuf msgLen
unless (isSuccess ret) $ do
free hashBuf
error "Bug: Invalid use of C Lib"
bs <- unsafePackByteString (hashBuf, 32)
let Just digest = sizedByteArray bs
pure digest
-- | Combine a list of 'PubKeyXY's into a single 'PubKeyXY'. This will result in @Nothing@ if the group operation results
-- in the Point at Infinity
pubKeyCombine :: [PubKeyXY] -> Maybe PubKeyXY
pubKeyCombine keys@(_ : _) = unsafePerformIO $ do
let n = length keys
keysBuf <- mallocBytes (64 * n)
for_ (zip [0 ..] keys) $ \(i, PubKeyXY{..}) ->
withForeignPtr pubKeyXYFPtr $ pokeElemOff keysBuf i
outBuf <- mallocBytes 64
ret <- Prim.ecPubkeyCombine ctx outBuf keysBuf (fromIntegral n)
if isSuccess ret
then Just . PubKeyXY <$> newForeignPtr finalizerFree outBuf
else free outBuf $> Nothing
pubKeyCombine [] = Nothing
-- | Negate a 'PubKeyXY'
pubKeyNegate :: PubKeyXY -> PubKeyXY
pubKeyNegate PubKeyXY{..} = unsafePerformIO $ do
outBuf <- mallocBytes 64
withForeignPtr pubKeyXYFPtr $ flip (memcpy outBuf) 64
_ret <- Prim.ecPubkeyNegate ctx outBuf
PubKeyXY <$> newForeignPtr finalizerFree outBuf
-- | Add 'Tweak' to 'PubKeyXY'. This will result in @Nothing@ if the group operation results in the Point at Infinity
pubKeyTweakAdd :: PubKeyXY -> Tweak -> Maybe PubKeyXY
pubKeyTweakAdd PubKeyXY{..} Tweak{..} = unsafePerformIO . evalContT $ do
pubKeyPtr <- ContT (withForeignPtr pubKeyXYFPtr)
tweakPtr <- ContT (withForeignPtr tweakFPtr)
lift $ do
pubKeyOutBuf <- mallocBytes 64
memcpy pubKeyOutBuf pubKeyPtr 64
ret <- Prim.ecPubkeyTweakAdd ctx pubKeyOutBuf tweakPtr
if isSuccess ret
then Just . PubKeyXY <$> newForeignPtr finalizerFree pubKeyOutBuf
else free pubKeyOutBuf $> Nothing
-- | Multiply 'PubKeyXY' by 'Tweak'. This will result in @Nothing@ if the group operation results in the Point at Infinity
pubKeyTweakMul :: PubKeyXY -> Tweak -> Maybe PubKeyXY
pubKeyTweakMul PubKeyXY{..} Tweak{..} = unsafePerformIO . evalContT $ do
pubKeyPtr <- ContT (withForeignPtr pubKeyXYFPtr)
tweakPtr <- ContT (withForeignPtr tweakFPtr)
lift $ do
pubKeyOutBuf <- mallocBytes 64
memcpy pubKeyOutBuf pubKeyPtr 64
ret <- Prim.ecPubkeyTweakMul ctx pubKeyOutBuf tweakPtr
if isSuccess ret
then Just . PubKeyXY <$> newForeignPtr finalizerFree pubKeyOutBuf
else free pubKeyOutBuf $> Nothing
-- | Negate a 'SecKey'
secKeyNegate :: SecKey -> SecKey
secKeyNegate SecKey{..} = unsafePerformIO $ do
outBuf <- mallocBytes 32
withForeignPtr secKeyFPtr $ flip (memcpy outBuf) 32
_ret <- Prim.ecSeckeyNegate ctx outBuf
SecKey <$> newForeignPtr finalizerFree outBuf
-- | Negate a 'Tweak'
tweakNegate :: Tweak -> Tweak
tweakNegate Tweak{..} = unsafePerformIO $ do
outBuf <- mallocBytes 32
let asKey = castForeignPtr tweakFPtr
withForeignPtr asKey $ flip (memcpy outBuf) 32
_ret <- Prim.ecSeckeyNegate ctx outBuf
Tweak <$> newForeignPtr finalizerFree (castPtr outBuf)
-- | Convert 'PubKeyXY' to 'PubKeyXO'. See 'keyPairPubKeyXO' for more information on how to interpret the parity bit.
xyToXO :: PubKeyXY -> (PubKeyXO, Bool)
xyToXO PubKeyXY{..} = unsafePerformIO $ do
outBuf <- mallocBytes 64
parityPtr <- malloc
ret <- withForeignPtr pubKeyXYFPtr $ Prim.xonlyPubkeyFromPubkey ctx outBuf parityPtr
unless (isSuccess ret) $ do
free outBuf
error "Bug: Couldn't convert xy to xo"
parity <- peek parityPtr
negated <- case parity of
0 -> pure False
1 -> pure True
_ -> free outBuf *> error "Bug: Invalid pk_parity from Prim"
(,negated) . PubKeyXO <$> newForeignPtr finalizerFree outBuf
-- | Add 'Tweak' to 'PubKeyXO'. This will result in @Nothing@ if the group operation results in the Point at Infinity
pubKeyXOTweakAdd :: PubKeyXO -> Tweak -> Maybe PubKeyXY
pubKeyXOTweakAdd PubKeyXO{..} Tweak{..} = unsafePerformIO . evalContT $ do
pubKeyXOPtr <- ContT (withForeignPtr pubKeyXOFPtr)
tweakPtr <- ContT (withForeignPtr tweakFPtr)
lift $ do
outBuf <- mallocBytes 64
ret <- Prim.xonlyPubkeyTweakAdd ctx outBuf pubKeyXOPtr tweakPtr
if isSuccess ret
then Just . PubKeyXY <$> newForeignPtr finalizerFree outBuf
else free outBuf $> Nothing
-- | Check that a 'PubKeyXO' is the result of the specified tweak operation. @True@ means it was.
pubKeyXOTweakAddCheck :: PubKeyXO -> Bool -> PubKeyXO -> Tweak -> Bool
pubKeyXOTweakAddCheck PubKeyXO{pubKeyXOFPtr = tweakedFPtr} parity PubKeyXO{pubKeyXOFPtr = origFPtr} Tweak{..} =
unsafePerformIO . evalContT $ do
tweakedPtr <- ContT (withForeignPtr tweakedFPtr)
origPtr <- ContT (withForeignPtr origFPtr)
tweakPtr <- ContT (withForeignPtr tweakFPtr)
let parityInt = if parity then 1 else 0
lift $ isSuccess <$> Prim.xonlyPubkeyTweakAddCheck ctx tweakedPtr parityInt origPtr tweakPtr
foreign import ccall "wrapper"
mkNonceFunHardened :: Prim.NonceFunHardened a -> IO (FunPtr (Prim.NonceFunHardened a))