From 0664f5fe1f77f08d235aa3750b59428257b0b91d Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Fri, 11 Sep 2020 14:33:30 -0700 Subject: 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. --- 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 ++++++++++++++++++++++++++++ 12 files changed, 163 insertions(+), 11 deletions(-) (limited to 'src') 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