Remove non-standard support

I removed support for all non-standard features:

- ECDH
- Schnorr Signatures
- Signature Recovery

These features are rarely compiled in shared libraries avaliable
from distribution repositories. Also, they are not needed for any
Haskoin project, so there is little incentive for me to continue
maintaining them, especially when I have no good way to test this
code.

Please make your own fork of this library if you wish to add these
back.
This commit is contained in:
Jean-Pierre Rupp 2020-06-13 21:31:21 +01:00
parent f8e9dfbed5
commit 0f862c2198
No known key found for this signature in database
GPG Key ID: 93391726EAFA0C5D
7 changed files with 64 additions and 767 deletions

41
.travis.yml Normal file
View File

@ -0,0 +1,41 @@
# This is the simple Travis configuration, which is intended for use
# on applications which do not require cross-platform and
# multiple-GHC-version support. For more information and other
# options, see:
#
# https://docs.haskellstack.org/en/stable/travis_ci/
#
# Copy these contents into the root directory of your Github project in a file
# named .travis.yml
# Choose a build environment
dist: xenial
# Do not choose a language; we provide our own build tools.
language: generic
# Caching so the next build will be fast too.
cache:
directories:
- $HOME/.stack
# Ensure necessary system libraries are present
addons:
apt:
packages:
- libgmp-dev
- secp256k1
before_install:
# Download and unpack the stack executable
- mkdir -p ~/.local/bin
- export PATH=$HOME/.local/bin:$PATH
- travis_retry curl -L https://get.haskellstack.org/stable/linux-x86_64.tar.gz | tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack'
install:
# Build dependencies
- stack --no-terminal --install-ghc test --only-dependencies
script:
# Build the package, its tests, and its docs and run the tests
- stack --no-terminal test --haddock --no-haddock-deps

View File

@ -4,11 +4,26 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## 0.2.6
### Fixed
- Compiles with all flags now.
### Added
- Script to compile with all flags.
### Removed
- Remove ECDH support.
- Remove Schnorr support.
- Remove Recovery support.
## 0.2.5
### Changed
- Reuse context aggressively.
- Generate context in a single thread.
### Fixed
- Memory deallocation bug.
## 0.2.4
### Changed
- Update Cabal and package version.

View File

@ -1,5 +1,5 @@
name: secp256k1-haskell
version: 0.2.5
version: 0.3.0
synopsis: Bindings for secp256k1
description: Sign and verify signatures using the secp256k1 library.
category: Crypto
@ -23,15 +23,6 @@ dependencies:
- hashable
- QuickCheck
- string-conversions
when:
- condition: flag(ecdh)
cpp-options: -DECDH
- condition: flag(schnorr)
cpp-options: -DSCHNORR
- condition: flag(recovery)
cpp-options: -DRECOVERY
- condition: flag(negate)
cpp-options: -DNEGATE
library:
source-dirs: src
generated-exposed-modules:
@ -55,20 +46,3 @@ tests:
- monad-par
- mtl
- HUnit
flags:
negate:
description: "Enable tweak negate"
manual: true
default: false
ecdh:
description: "Enable (experimental) ECDH APIs"
manual: true
default: false
schnorr:
description: "Enable BIP-340 (Schnorr) APIs"
manual: true
default: false
recovery:
description: "Enable signature key recovery APIs"
manual: true
default: false

View File

@ -4,10 +4,10 @@ cabal-version: 2.0
--
-- see: https://github.com/sol/hpack
--
-- hash: f64a6c7d68d58fab5fc141dd7bda78089fe9403dc39525641eba1ca0fd92593c
-- hash: d55c31829652e236145323f89ee1ec9c61e4f18712778a13011c688d36db8b19
name: secp256k1-haskell
version: 0.2.5
version: 0.3.0
synopsis: Bindings for secp256k1
description: Sign and verify signatures using the secp256k1 library.
category: Crypto
@ -27,26 +27,6 @@ source-repository head
type: git
location: https://github.com/haskoin/secp256k1-haskell.git
flag ecdh
description: Enable (experimental) ECDH APIs
manual: True
default: False
flag negate
description: Enable tweak negate
manual: True
default: False
flag recovery
description: Enable signature key recovery APIs
manual: True
default: False
flag schnorr
description: Enable BIP-340 (Schnorr) APIs
manual: True
default: False
library
exposed-modules:
Crypto.Secp256k1
@ -68,14 +48,6 @@ library
, entropy
, hashable
, string-conversions
if flag(ecdh)
cpp-options: -DECDH
if flag(schnorr)
cpp-options: -DSCHNORR
if flag(recovery)
cpp-options: -DRECOVERY
if flag(negate)
cpp-options: -DNEGATE
default-language: Haskell2010
test-suite spec
@ -103,13 +75,5 @@ test-suite spec
, mtl
, secp256k1-haskell
, string-conversions
if flag(ecdh)
cpp-options: -DECDH
if flag(schnorr)
cpp-options: -DSCHNORR
if flag(recovery)
cpp-options: -DRECOVERY
if flag(negate)
cpp-options: -DNEGATE
default-language: Haskell2010
build-tool-depends: hspec-discover:hspec-discover

View File

@ -39,16 +39,6 @@ module Crypto.Secp256k1
, CompactSig(..)
, exportCompactSig
, importCompactSig
#ifdef RECOVERY
-- ** Recovery
, RecSig
, CompactRecSig(..)
, importCompactRecSig
, exportCompactRecSig
, convertRecSig
, signRecMsg
, recover
#endif
-- * Addition & Multiplication
, Tweak
@ -59,29 +49,7 @@ module Crypto.Secp256k1
, tweakAddPubKey
, tweakMulPubKey
, combinePubKeys
#ifdef NEGATE
, tweakNegate
#endif
#ifdef ECDH
-- * 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, (<=<))
@ -93,9 +61,8 @@ import Data.Hashable (Hashable (..))
import Data.Maybe (fromJust, fromMaybe, isJust)
import Data.String (IsString (..))
import Data.String.Conversions (ConvertibleStrings, cs)
import Foreign (ForeignPtr, alloca,
allocaArray, allocaBytes,
mallocForeignPtr,
import Foreign (ForeignPtr, alloca, allocaArray,
allocaBytes, mallocForeignPtr,
nullPtr, peek, poke, pokeArray,
withForeignPtr)
import System.IO.Unsafe (unsafePerformIO)
@ -113,10 +80,6 @@ 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` ()
@ -185,30 +148,6 @@ instance Hashable Sig where
instance Show Sig where
showsPrec _ = shows . B16.encode . exportSig
#ifdef RECOVERY
recSigFromString :: String -> Maybe RecSig
recSigFromString str = do
bs <- decodeHex str
rs <- either (const Nothing) Just $ decode bs
importCompactRecSig rs
instance Hashable RecSig where
i `hashWithSalt` s = i `hashWithSalt` encode (exportCompactRecSig s)
instance Read RecSig where
readPrec = parens $ do
String str <- lexP
maybe pfail return $ recSigFromString str
instance IsString RecSig where
fromString = fromMaybe e . recSigFromString
where
e = error "Could not decode signature from hex string"
instance Show RecSig where
showsPrec _ = shows . B16.encode . encode . exportCompactRecSig
#endif
instance Read SecKey where
readPrec = parens $ do
String str <- lexP
@ -248,61 +187,12 @@ instance Eq Msg where
instance Eq Sig where
fg1 == fg2 = exportCompactSig fg1 == exportCompactSig fg2
#ifdef RECOVERY
instance Eq RecSig where
fg1 == fg2 = exportCompactRecSig fg1 == exportCompactRecSig fg2
#endif
instance Eq SecKey where
fk1 == fk2 = getSecKey fk1 == getSecKey fk2
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
@ -500,65 +390,8 @@ combinePubKeys pubs = unsafePerformIO $ pointers [] pubs $ \ps ->
pointers ps (PubKey fp : pubs') f =
withForeignPtr fp $ \p -> pointers (p:ps) pubs' f
#ifdef RECOVERY
-- | Parse a compact ECDSA signature (64 bytes + recovery id).
importCompactRecSig :: CompactRecSig -> Maybe RecSig
importCompactRecSig cr =
if getCompactRecSigV cr `notElem` [0,1,2,3]
then Nothing
else withContext $ \ctx -> alloca $ \pc -> do
let
c = CompactSig (getCompactRecSigR cr) (getCompactRecSigS cr)
recid = fromIntegral $ getCompactRecSigV cr
poke pc c
fg <- mallocForeignPtr
ret <- withForeignPtr fg $ \pg ->
ecdsaRecoverableSignatureParseCompact ctx pg pc recid
if isSuccess ret then return $ Just $ RecSig fg else return Nothing
-- | Serialize an ECDSA signature in compact format (64 bytes + recovery id).
exportCompactRecSig :: RecSig -> CompactRecSig
exportCompactRecSig (RecSig fg) = withContext $ \ctx ->
withForeignPtr fg $ \pg -> alloca $ \pc -> alloca $ \pr -> do
ret <- ecdsaRecoverableSignatureSerializeCompact ctx pc pr pg
unless (isSuccess ret) $ error "Could not obtain compact signature"
CompactSig r s <- peek pc
v <- fromIntegral <$> peek pr
return $ CompactRecSig r s v
-- | Convert a recoverable signature into a normal signature.
convertRecSig :: RecSig -> Sig
convertRecSig (RecSig frg) = withContext $ \ctx ->
withForeignPtr frg $ \prg -> do
fg <- mallocForeignPtr
ret <- withForeignPtr fg $ \pg ->
ecdsaRecoverableSignatureConvert ctx pg prg
unless (isSuccess ret) $
error "Could not convert a recoverable signature"
return $ Sig fg
-- | Create a recoverable ECDSA signature.
signRecMsg :: SecKey -> Msg -> RecSig
signRecMsg (SecKey fk) (Msg fm) = withContext $ \ctx ->
withForeignPtr fk $ \k -> withForeignPtr fm $ \m -> do
fg <- mallocForeignPtr
ret <- withForeignPtr fg $ \g ->
ecdsaSignRecoverable ctx g m k nullPtr nullPtr
unless (isSuccess ret) $ error "could not sign message"
return $ RecSig fg
-- | Recover an ECDSA public key from a signature.
recover :: RecSig -> Msg -> Maybe PubKey
recover (RecSig frg) (Msg fm) = withContext $ \ctx ->
withForeignPtr frg $ \prg -> withForeignPtr fm $ \pm -> do
fp <- mallocForeignPtr
ret <- withForeignPtr fp $ \pp -> ecdsaRecover ctx pp prg pm
if isSuccess ret then return $ Just $ PubKey fp else return Nothing
#endif
#ifdef NEGATE
tweakNegate :: Tweak -> Maybe Tweak
tweakNegate (Tweak fk) = withContext $ \ctx -> do
tweakNegate (Tweak fk) = unsafePerformIO $ do
fnew <- mallocForeignPtr
peeked <- withForeignPtr fk peek
ret <- withForeignPtr fnew $ \n -> do
@ -568,121 +401,6 @@ tweakNegate (Tweak fk) = withContext $ \ctx -> do
if isSuccess ret
then Just (Tweak fnew)
else Nothing
#endif
#ifdef ECDH
-- | Compute Diffie-Hellman secret.
ecdh :: PubKey -> SecKey -> ByteString
ecdh (PubKey pk) (SecKey sk) = withContext $ \ctx ->
withForeignPtr pk $ \pkPtr -> withForeignPtr sk $ \skPtr ->
allocaBytes size $ \o -> do
ret <- ecEcdh ctx o pkPtr skPtr nullPtr nullPtr
unless (isSuccess ret) $ error "ecdh failed"
packByteString (o, size)
where
size :: Integral a => a
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 nullPtr 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
@ -699,10 +417,3 @@ instance Arbitrary SecKey where
instance Arbitrary PubKey where
arbitrary = derivePubKey <$> arbitrary
#ifdef SCHNORR
instance Arbitrary XOnlyPubKey where
arbitrary = do
key <- arbitrary
return $ deriveXOnlyPubKey key
#endif

View File

@ -23,8 +23,8 @@ import Data.Serialize (Serialize (..))
import qualified Data.Serialize.Get as Get
import qualified Data.Serialize.Put as Put
import Data.Void (Void)
import Foreign (FunPtr, Ptr, Storable (..),
alloca, castPtr, copyArray)
import Foreign (FunPtr, Ptr, Storable (..), alloca,
castPtr, copyArray)
import Foreign.C (CInt (..), CSize (..), CString, CUChar,
CUInt (..))
import GHC.Generics (Generic)
@ -52,16 +52,6 @@ data CompactSig =
newtype RecSig65 = RecSig65 { getRecSig65 :: ShortByteString }
deriving (Read, Show, Eq, Ord, Generic, NFData)
#ifdef RECOVERY
data CompactRecSig =
CompactRecSig
{ getCompactRecSigR :: !ShortByteString
, getCompactRecSigS :: !ShortByteString
, getCompactRecSigV :: !Word8
}
deriving (Show, Eq, Ord, Generic, NFData)
#endif
newtype Seed32 = Seed32 { getSeed32 :: ShortByteString }
deriving (Read, Show, Eq, Ord, Generic, NFData)
@ -86,14 +76,6 @@ 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
verify :: CtxFlags
verify = CtxFlags 0x0101
@ -116,22 +98,6 @@ 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
@ -173,47 +139,6 @@ instance Serialize CompactSig where
Put.putShortByteString r
Put.putShortByteString s
#ifdef RECOVERY
instance Storable RecSig65 where
sizeOf _ = 65
alignment _ = 1
peek p = RecSig65 . toShort <$> packByteString (castPtr p, 65)
poke p (RecSig65 k) = useByteString (fromShort k) $
\(b, _) -> copyArray (castPtr p) b 65
instance Storable CompactRecSig where
sizeOf _ = 65
alignment _ = 1
peek p = do
bs <- BS.packCStringLen (castPtr p, 65)
let (r, s) = BS.splitAt 32 $ BS.take 64 bs
v = BS.last bs
return CompactRecSig { getCompactRecSigR = toShort r
, getCompactRecSigS = toShort s
, getCompactRecSigV = v
}
poke p CompactRecSig{..} =
useByteString bs $ \(b, _) -> copyArray (castPtr p) b 65
where
bs = fromShort getCompactRecSigR `BS.append`
fromShort getCompactRecSigS `BS.snoc`
getCompactRecSigV
instance Serialize CompactRecSig where
get = do
r <- Get.getByteString 32
s <- Get.getByteString 32
v <- Get.getWord8
return CompactRecSig { getCompactRecSigR = toShort r
, getCompactRecSigS = toShort s
, getCompactRecSigV = v
}
put (CompactRecSig r s v) = do
Put.putShortByteString r
Put.putShortByteString s
Put.putWord8 v
#endif
instance Storable Msg32 where
sizeOf _ = 32
alignment _ = 1
@ -410,14 +335,12 @@ foreign import ccall
-> Ptr Tweak32
-> IO Ret
#ifdef NEGATE
foreign import ccall
"secp256k1.h secp256k1_ec_privkey_negate"
ecTweakNegate
:: Ptr Ctx
-> Ptr Tweak32
-> IO Ret
#endif
foreign import ccall
"secp256k1.h secp256k1_ec_pubkey_tweak_add"
@ -458,156 +381,3 @@ foreign import ccall
-> Ptr (Ptr PubKey64) -- ^ pointer to array of public keys
-> CInt -- ^ number of public keys
-> IO Ret
#ifdef RECOVERY
foreign import ccall
"secp256k1_recovery.h secp256k1_ecdsa_recoverable_signature_parse_compact"
ecdsaRecoverableSignatureParseCompact
:: Ptr Ctx
-> Ptr RecSig65
-> Ptr CompactSig
-> CInt
-> IO Ret
foreign import ccall
"secp256k1_recovery.h secp256k1_ecdsa_recoverable_signature_convert"
ecdsaRecoverableSignatureConvert
:: Ptr Ctx
-> Ptr Sig64
-> Ptr RecSig65
-> IO Ret
foreign import ccall
"secp256k1_recovery.h secp256k1_ecdsa_recoverable_signature_serialize_compact"
ecdsaRecoverableSignatureSerializeCompact
:: Ptr Ctx
-> Ptr CompactSig
-> Ptr CInt
-> Ptr RecSig65
-> IO Ret
foreign import ccall
"secp256k1_recovery.h secp256k1_ecdsa_sign_recoverable"
ecdsaSignRecoverable
:: Ptr Ctx
-> Ptr RecSig65
-> Ptr Msg32
-> Ptr SecKey32
-> Ptr Void
-> Ptr a -- ^ nonce data
-> IO Ret
foreign import ccall
"secp256k1_recovery.h secp256k1_ecdsa_recover"
ecdsaRecover
:: Ptr Ctx
-> Ptr PubKey64
-> Ptr RecSig65
-> Ptr Msg32
-> IO Ret
#endif
#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.
-> Ptr Void
-> 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"
ecEcdh
:: Ptr Ctx
-> Ptr CUChar
-> Ptr PubKey64
-> Ptr SecKey32
-> Ptr a
-> Ptr b
-> IO Ret
#endif

View File

@ -61,53 +61,7 @@ spec = do
it "multiply public key" $ property tweakMulPubKeyTest
it "combine public keys" $ property combinePubKeyTest
it "can't combine 0 public keys" $ property combinePubKeyEmptyListTest
#ifdef RECOVERY
describe "recovery" $ do
it "recovers public keys" $
property recoverTest
it "recovers key from signed message" $
property signRecMsgTest
it "does not recover bad public keys" $
property badRecoverTest
it "detects bad recoverable signature" $
property badRecSignatureTest
it "serializes compact recoverable signature" $
property serializeCompactRecSigTest
it "shows and reads recoverable signature" $
property (showReadRecSig :: (SecKey, Msg) -> Bool)
it "reads recoverable signature from string" $ property $ isStringRecSig
#endif
#ifdef NEGATE
describe "negate" $
it "negates tweak" $ property negateTweakTest
#endif
#ifdef ECDH
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
@ -121,13 +75,6 @@ isStringSig (k, m) = g == fromString (cs hex) where
g = signMsg k m
hex = B16.encode $ exportSig g
#ifdef RECOVERY
isStringRecSig :: (SecKey, Msg) -> Bool
isStringRecSig (k, m) = g == fromString (cs hex) where
g = signRecMsg k m
hex = B16.encode . S.encode $ exportCompactRecSig g
#endif
isStringMsg :: Msg -> Bool
isStringMsg m = m == fromString (cs m') where
m' = B16.encode $ getMsg m
@ -150,12 +97,6 @@ showReadSig :: (SecKey, Msg) -> Bool
showReadSig (k, m) = showRead sig where
sig = signMsg k m
#ifdef RECOVERY
showReadRecSig :: (SecKey, Msg) -> Bool
showReadRecSig (k, m) = showRead recSig where
recSig = signRecMsg k m
#endif
showRead :: (Show a, Read a, Eq a) => a -> Bool
showRead x = read (show x) == x
@ -169,36 +110,10 @@ signMsgParTest xs = P.runPar $ do
ys <- mapM (P.spawnP . signMsgTest) xs
and <$> mapM P.get ys
#ifdef RECOVERY
signRecMsgTest :: (Msg, SecKey) -> Bool
signRecMsgTest (fm, fk) = verifySig fp fg fm where
fp = derivePubKey fk
fg = convertRecSig $ signRecMsg fk fm
recoverTest :: (Msg, SecKey) -> Bool
recoverTest (fm, fk) = recover fg fm == Just fp where
fp = derivePubKey fk
fg = signRecMsg fk fm
badRecoverTest :: (Msg, SecKey, Msg) -> Property
badRecoverTest (fm, fk, fm') =
fm' /= fm ==> fp' /= Nothing ==> fp' /= Just fp
where
fg = signRecMsg fk fm
fp = derivePubKey fk
fp' = recover fg fm'
#endif
badSignatureTest :: (Msg, SecKey, PubKey) -> Bool
badSignatureTest (fm, fk, fp) = not $ verifySig fp fg fm where
fg = signMsg fk fm
#ifdef RECOVERY
badRecSignatureTest :: (Msg, SecKey, PubKey) -> Bool
badRecSignatureTest (fm, fk, fp) = not $ verifySig fp fg fm where
fg = convertRecSig $ signRecMsg fk fm
#endif
normalizeSigTest :: (Msg, SecKey) -> Bool
normalizeSigTest (fm, fk) = not norm && sig == fg where
fg = signMsg fk fm
@ -226,16 +141,6 @@ serializeCompactSigTest (fm, fk) =
where
fg = signMsg fk fm
#ifdef RECOVERY
serializeCompactRecSigTest :: (Msg, SecKey) -> Bool
serializeCompactRecSigTest (fm, fk) =
case importCompactRecSig $ exportCompactRecSig fg of
Just fg' -> fg == fg'
Nothing -> False
where
fg = signRecMsg fk fm
#endif
serializeSecKeyTest :: SecKey -> Bool
serializeSecKeyTest fk =
case secKey $ getSecKey fk of
@ -316,7 +221,6 @@ combinePubKeyEmptyListTest =
expected = Nothing
combined = combinePubKeys []
#ifdef NEGATE
negateTweakTest :: Assertion
negateTweakTest =
assertEqual "can recover secret key 1 after adding tweak 1" oneKey subtracted
@ -328,85 +232,3 @@ negateTweakTest =
Just minusOneTwk = tweakNegate oneTwk
Just twoKey = tweakAddSecKey oneKey oneTwk
Just subtracted = tweakAddSecKey twoKey minusOneTwk
#endif
#ifdef ECDH
computeDhSecret :: Assertion
computeDhSecret =
assertEqual "ecdh computes known secret" expected computed
where
computed = do
pub <- importPubKey $ hexToBytes
"028d7500dd4c12685d1f568b4c2b5048e8534b873319f3a8daa612b469132ec7f7"
sec <- secKey $ hexToBytes
"1212121212121212121212121212121212121212121212121212121212121212"
pure $ ecdh pub sec
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