From 0664f5fe1f77f08d235aa3750b59428257b0b91d Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 11 Sep 2020 14:33:30 -0700 Subject: [PATCH] Support for Schnorr signatures and integration in SignatureCheckers (BIP 340) This enables the schnorrsig module in libsecp256k1, adds the relevant types and functions to src/pubkey, as well as in higher-level `SignatureChecker` classes. The (verification side of the) BIP340 test vectors is also added. --- build_msvc/libsecp256k1/libsecp256k1.vcxproj | 2 +- configure.ac | 2 +- src/pubkey.cpp | 15 +++++++++ src/pubkey.h | 20 +++++++++++ src/script/interpreter.cpp | 30 +++++++++++++++++ src/script/interpreter.h | 9 +++++ src/script/script_error.cpp | 6 ++++ src/script/script_error.h | 5 +++ src/script/sigcache.cpp | 35 ++++++++++++++++---- src/script/sigcache.h | 2 ++ src/script/sign.h | 1 + src/test/fuzz/script_sigcache.cpp | 18 +++++++--- src/test/fuzz/signature_checker.cpp | 5 +++ src/test/key_tests.cpp | 28 ++++++++++++++++ 14 files changed, 165 insertions(+), 13 deletions(-) diff --git a/build_msvc/libsecp256k1/libsecp256k1.vcxproj b/build_msvc/libsecp256k1/libsecp256k1.vcxproj index 99fb63fb02..c42918d6e1 100644 --- a/build_msvc/libsecp256k1/libsecp256k1.vcxproj +++ b/build_msvc/libsecp256k1/libsecp256k1.vcxproj @@ -12,7 +12,7 @@ - ENABLE_MODULE_ECDH;ENABLE_MODULE_RECOVERY;%(PreprocessorDefinitions) + ENABLE_MODULE_ECDH;ENABLE_MODULE_RECOVERY;ENABLE_MODULE_EXTRAKEYS;ENABLE_MODULE_SCHNORRSIG;%(PreprocessorDefinitions) ..\..\src\secp256k1;%(AdditionalIncludeDirectories) diff --git a/configure.ac b/configure.ac index fbf56443f1..3df59e0b7b 100644 --- a/configure.ac +++ b/configure.ac @@ -1645,7 +1645,7 @@ if test x$need_bundled_univalue = xyes; then AC_CONFIG_SUBDIRS([src/univalue]) fi -ac_configure_args="${ac_configure_args} --disable-shared --with-pic --enable-benchmark=no --with-bignum=no --enable-module-recovery" +ac_configure_args="${ac_configure_args} --disable-shared --with-pic --enable-benchmark=no --with-bignum=no --enable-module-recovery --enable-module-schnorrsig --enable-experimental" AC_CONFIG_SUBDIRS([src/secp256k1]) AC_OUTPUT diff --git a/src/pubkey.cpp b/src/pubkey.cpp index fc14f41a0c..69e3d91392 100644 --- a/src/pubkey.cpp +++ b/src/pubkey.cpp @@ -7,6 +7,7 @@ #include #include +#include namespace { @@ -166,6 +167,20 @@ int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_ return 1; } +XOnlyPubKey::XOnlyPubKey(Span bytes) +{ + assert(bytes.size() == 32); + std::copy(bytes.begin(), bytes.end(), m_keydata.begin()); +} + +bool XOnlyPubKey::VerifySchnorr(const uint256& msg, Span sigbytes) const +{ + assert(sigbytes.size() == 64); + secp256k1_xonly_pubkey pubkey; + if (!secp256k1_xonly_pubkey_parse(secp256k1_context_verify, &pubkey, m_keydata.data())) return false; + return secp256k1_schnorrsig_verify(secp256k1_context_verify, sigbytes.data(), msg.begin(), &pubkey); +} + bool CPubKey::Verify(const uint256 &hash, const std::vector& vchSig) const { if (!IsValid()) return false; diff --git a/src/pubkey.h b/src/pubkey.h index cd1049f66f..1a818037d1 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -206,6 +207,25 @@ public: bool Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const; }; +class XOnlyPubKey +{ +private: + uint256 m_keydata; + +public: + /** Construct an x-only pubkey from exactly 32 bytes. */ + XOnlyPubKey(Span bytes); + + /** Verify a Schnorr signature against this public key. + * + * sigbytes must be exactly 64 bytes. + */ + bool VerifySchnorr(const uint256& msg, Span sigbytes) const; + + const unsigned char& operator[](int pos) const { return *(m_keydata.begin() + pos); } + size_t size() const { return m_keydata.size(); } +}; + struct CExtPubKey { unsigned char nDepth; unsigned char vchFingerprint[4]; diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index af8fd4912e..aef6e28118 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1521,6 +1521,12 @@ bool GenericTransactionSignatureChecker::VerifyECDSASignature(const std::vect return pubkey.Verify(sighash, vchSig); } +template +bool GenericTransactionSignatureChecker::VerifySchnorrSignature(Span sig, const XOnlyPubKey& pubkey, const uint256& sighash) const +{ + return pubkey.VerifySchnorr(sighash, sig); +} + template bool GenericTransactionSignatureChecker::CheckECDSASignature(const std::vector& vchSigIn, const std::vector& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const { @@ -1543,6 +1549,30 @@ bool GenericTransactionSignatureChecker::CheckECDSASignature(const std::vecto return true; } +template +bool GenericTransactionSignatureChecker::CheckSchnorrSignature(Span sig, Span pubkey_in, SigVersion sigversion, ScriptError* serror) const +{ + assert(sigversion == SigVersion::TAPROOT); + // Schnorr signatures have 32-byte public keys. The caller is responsible for enforcing this. + assert(pubkey_in.size() == 32); + if (sig.size() != 64 && sig.size() != 65) return set_error(serror, SCRIPT_ERR_SCHNORR_SIG_SIZE); + + XOnlyPubKey pubkey{pubkey_in}; + + uint8_t hashtype = SIGHASH_DEFAULT; + if (sig.size() == 65) { + hashtype = SpanPopBack(sig); + if (hashtype == SIGHASH_DEFAULT) return set_error(serror, SCRIPT_ERR_SCHNORR_SIG_HASHTYPE); + } + uint256 sighash; + assert(this->txdata); + if (!SignatureHashSchnorr(sighash, *txTo, nIn, hashtype, sigversion, *this->txdata)) { + return set_error(serror, SCRIPT_ERR_SCHNORR_SIG_HASHTYPE); + } + if (!VerifySchnorrSignature(sig, pubkey, sighash)) return set_error(serror, SCRIPT_ERR_SCHNORR_SIG); + return true; +} + template bool GenericTransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) const { diff --git a/src/script/interpreter.h b/src/script/interpreter.h index b739528f0f..e54243c8f0 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -7,12 +7,14 @@ #define BITCOIN_SCRIPT_INTERPRETER_H #include