Skip to content

Commit

Permalink
Support for Schnorr signatures and integration in SignatureCheckers (…
Browse files Browse the repository at this point in the history
…BIP 340)
  • Loading branch information
sipa committed Aug 7, 2020
1 parent f8d0aee commit 125318b
Show file tree
Hide file tree
Showing 10 changed files with 140 additions and 20 deletions.
2 changes: 1 addition & 1 deletion build_msvc/libsecp256k1/libsecp256k1.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
</ItemGroup>
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions>ENABLE_MODULE_ECDH;ENABLE_MODULE_RECOVERY;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>ENABLE_MODULE_ECDH;ENABLE_MODULE_RECOVERY;ENABLE_MODULE_EXTRAKEYS;ENABLE_MODULE_SCHNORRSIG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\..\src\secp256k1;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
Expand Down
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -1610,7 +1610,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
Expand Down
8 changes: 8 additions & 0 deletions src/pubkey.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include <secp256k1.h>
#include <secp256k1_recovery.h>
#include <secp256k1_schnorrsig.h>

namespace
{
Expand Down Expand Up @@ -166,6 +167,13 @@ static int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1
return 1;
}

bool XOnlyPubKey::VerifySchnorr(const uint256 &hash, const std::vector<unsigned char>& sigbytes) const {
if (sigbytes.size() != 64) return false;
secp256k1_xonly_pubkey pubkey;
if (!secp256k1_xonly_pubkey_parse(secp256k1_context_verify, &pubkey, m_keydata.begin())) return false;
return secp256k1_schnorrsig_verify(secp256k1_context_verify, sigbytes.data(), hash.begin(), &pubkey);
}

bool CPubKey::Verify(const uint256 &hash, const std::vector<unsigned char>& vchSig) const {
if (!IsValid())
return false;
Expand Down
17 changes: 17 additions & 0 deletions src/pubkey.h
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,23 @@ class CPubKey
bool Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const;
};

class XOnlyPubKey {
private:
uint256 m_keydata;

public:
XOnlyPubKey(const uint256& in) : m_keydata(in) {}

/** Verify a 64-byte Schnorr signature.
*
* If the signature is not 64 bytes, or the public key is not fully valid, false is returned.
*/
bool VerifySchnorr(const uint256& hash, const std::vector<unsigned char>& vchSig) 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];
Expand Down
32 changes: 30 additions & 2 deletions src/script/interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1513,11 +1513,17 @@ uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn
}

template <class T>
bool GenericTransactionSignatureChecker<T>::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
bool GenericTransactionSignatureChecker<T>::VerifyECDSASignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
{
return pubkey.Verify(sighash, vchSig);
}

template <class T>
bool GenericTransactionSignatureChecker<T>::VerifySchnorrSignature(const std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey, const uint256& sighash) const
{
return pubkey.VerifySchnorr(sighash, sig);
}

template <class T>
bool GenericTransactionSignatureChecker<T>::CheckSig(const std::vector<unsigned char>& vchSigIn, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const
{
Expand All @@ -1534,12 +1540,34 @@ bool GenericTransactionSignatureChecker<T>::CheckSig(const std::vector<unsigned

uint256 sighash = SignatureHash(scriptCode, *txTo, nIn, nHashType, amount, sigversion, this->txdata);

if (!VerifySignature(vchSig, pubkey, sighash))
if (!VerifyECDSASignature(vchSig, pubkey, sighash))
return false;

return true;
}

template <class T>
bool GenericTransactionSignatureChecker<T>::CheckSigSchnorr(const std::vector<unsigned char>& sig_in, const std::vector<unsigned char>& pubkey_in, SigVersion sigversion) const
{
std::vector<unsigned char> sig(sig_in);
if (sig.empty()) return false;

if (pubkey_in.size() != 32) return false;
XOnlyPubKey pubkey{uint256(pubkey_in)};

uint8_t hashtype = SIGHASH_DEFAULT;
if (sig.size() == 65) {
hashtype = sig.back();
if (hashtype == SIGHASH_DEFAULT) return false;
sig.pop_back();
}
if (sig.size() != 64) return false;
uint256 sighash;
bool ret = SignatureHashSchnorr(sighash, *txTo, nIn, hashtype, sigversion, this->txdata);
if (!ret) return false;
return VerifySchnorrSignature(sig, pubkey, sighash);
}

template <class T>
bool GenericTransactionSignatureChecker<T>::CheckLockTime(const CScriptNum& nLockTime) const
{
Expand Down
10 changes: 9 additions & 1 deletion src/script/interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <stdint.h>

class CPubKey;
class XOnlyPubKey;
class CScript;
class CTransaction;
class CTxOut;
Expand Down Expand Up @@ -175,6 +176,11 @@ class BaseSignatureChecker
return false;
}

virtual bool CheckSigSchnorr(const std::vector<unsigned char>& sig, const std::vector<unsigned char>& pubkey, SigVersion sigversion) const
{
return false;
}

virtual bool CheckLockTime(const CScriptNum& nLockTime) const
{
return false;
Expand All @@ -198,12 +204,14 @@ class GenericTransactionSignatureChecker : public BaseSignatureChecker
const PrecomputedTransactionData* txdata;

protected:
virtual bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
virtual bool VerifyECDSASignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const;
virtual bool VerifySchnorrSignature(const std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey, const uint256& sighash) const;

public:
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(nullptr) {}
GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {}
bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override;
bool CheckSigSchnorr(const std::vector<unsigned char>& sig, const std::vector<unsigned char>& pubkey, SigVersion sigversion) const override;
bool CheckLockTime(const CScriptNum& nLockTime) const override;
bool CheckSequence(const CScriptNum& nSequence) const override;
};
Expand Down
43 changes: 33 additions & 10 deletions src/script/sigcache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ namespace {
class CSignatureCache
{
private:
//! Entries are SHA256(nonce || signature hash || public key || signature):
CSHA256 m_salted_hasher;
//! Entries are SHA256(nonce || 'E' or 'S' || signature hash || public key || signature):
CSHA256 m_salted_hasher_ecdsa;
CSHA256 m_salted_hasher_schnorr;
typedef CuckooCache::cache<uint256, SignatureCacheHasher> map_type;
map_type setValid;
boost::shared_mutex cs_sigcache;
Expand All @@ -34,18 +35,30 @@ class CSignatureCache
uint256 nonce = GetRandHash();
// We want the nonce to be 64 bytes long to force the hasher to process
// this chunk, which makes later hash computations more efficient. We
// just write our 32-byte entropy twice to fill the 64 bytes.
m_salted_hasher.Write(nonce.begin(), 32);
m_salted_hasher.Write(nonce.begin(), 32);
// just write our 32-byte entropy, and then pad with 'E' for ECDSA and
// 'S' for Schnorr (followed by 0 bytes).
static const unsigned char PADDING_ECDSA[32] = {'E'};
static const unsigned char PADDING_SCHNORR[32] = {'S'};
m_salted_hasher_ecdsa.Write(nonce.begin(), 32);
m_salted_hasher_ecdsa.Write(PADDING_ECDSA, 32);
m_salted_hasher_schnorr.Write(nonce.begin(), 32);
m_salted_hasher_schnorr.Write(PADDING_SCHNORR, 32);
}

void
ComputeEntry(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubkey)
ComputeEntryECDSA(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& vchSig, const CPubKey& pubkey)
{
CSHA256 hasher = m_salted_hasher;
CSHA256 hasher = m_salted_hasher_ecdsa;
hasher.Write(hash.begin(), 32).Write(&pubkey[0], pubkey.size()).Write(&vchSig[0], vchSig.size()).Finalize(entry.begin());
}

void
ComputeEntrySchnorr(uint256& entry, const uint256 &hash, const std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey)
{
CSHA256 hasher = m_salted_hasher_schnorr;
hasher.Write(hash.begin(), 32).Write(&pubkey[0], pubkey.size()).Write(&sig[0], sig.size()).Finalize(entry.begin());
}

bool
Get(const uint256& entry, const bool erase)
{
Expand Down Expand Up @@ -85,15 +98,25 @@ void InitSignatureCache()
(nElems*sizeof(uint256)) >>20, (nMaxCacheSize*2)>>20, nElems);
}

bool CachingTransactionSignatureChecker::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
bool CachingTransactionSignatureChecker::VerifyECDSASignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const
{
uint256 entry;
signatureCache.ComputeEntry(entry, sighash, vchSig, pubkey);
signatureCache.ComputeEntryECDSA(entry, sighash, vchSig, pubkey);
if (signatureCache.Get(entry, !store))
return true;
if (!TransactionSignatureChecker::VerifySignature(vchSig, pubkey, sighash))
if (!TransactionSignatureChecker::VerifyECDSASignature(vchSig, pubkey, sighash))
return false;
if (store)
signatureCache.Set(entry);
return true;
}

bool CachingTransactionSignatureChecker::VerifySchnorrSignature(const std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey, const uint256& sighash) const
{
uint256 entry;
signatureCache.ComputeEntrySchnorr(entry, sighash, sig, pubkey);
if (signatureCache.Get(entry, !store)) return true;
if (!TransactionSignatureChecker::VerifySchnorrSignature(sig, pubkey, sighash)) return false;
if (store) signatureCache.Set(entry);
return true;
}
3 changes: 2 additions & 1 deletion src/script/sigcache.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ class CachingTransactionSignatureChecker : public TransactionSignatureChecker
public:
CachingTransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, bool storeIn, PrecomputedTransactionData& txdataIn) : TransactionSignatureChecker(txToIn, nInIn, amountIn, txdataIn), store(storeIn) {}

bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const override;
bool VerifyECDSASignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const override;
bool VerifySchnorrSignature(const std::vector<unsigned char>& sig, const XOnlyPubKey& pubkey, const uint256& sighash) const override;
};

void InitSignatureCache();
Expand Down
15 changes: 11 additions & 4 deletions src/test/fuzz/script_sigcache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,18 @@ void test_one_input(const std::vector<uint8_t>& buffer)
const bool store = fuzzed_data_provider.ConsumeBool();
PrecomputedTransactionData tx_data;
CachingTransactionSignatureChecker caching_transaction_signature_checker{mutable_transaction ? &tx : nullptr, n_in, amount, store, tx_data};
const std::optional<CPubKey> pub_key = ConsumeDeserializable<CPubKey>(fuzzed_data_provider);
if (pub_key) {
const std::vector<uint8_t> random_bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider);
const std::vector<uint8_t> random_bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider);
if (fuzzed_data_provider.ConsumeBool()) {
XOnlyPubKey pub_key(ConsumeUInt256(fuzzed_data_provider));
if (!random_bytes.empty()) {
(void)caching_transaction_signature_checker.VerifySignature(random_bytes, *pub_key, ConsumeUInt256(fuzzed_data_provider));
(void)caching_transaction_signature_checker.VerifySchnorrSignature(random_bytes, pub_key, ConsumeUInt256(fuzzed_data_provider));
}
} else {
const std::optional<CPubKey> pub_key = ConsumeDeserializable<CPubKey>(fuzzed_data_provider);
if (pub_key) {
if (!random_bytes.empty()) {
(void)caching_transaction_signature_checker.VerifyECDSASignature(random_bytes, *pub_key, ConsumeUInt256(fuzzed_data_provider));
}
}
}
}
28 changes: 28 additions & 0 deletions src/test/key_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,4 +264,32 @@ BOOST_AUTO_TEST_CASE(pubkey_unserialize)
}
}

BOOST_AUTO_TEST_CASE(bip340_test_vectors)
{
static const std::vector<std::pair<std::array<std::string, 3>, bool>> VECTORS = {
{{"F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9","0000000000000000000000000000000000000000000000000000000000000000","067E337AD551B2276EC705E43F0920926A9CE08AC68159F9D258C9BBA412781C9F059FCDF4824F13B3D7C1305316F956704BB3FEA2C26142E18ACD90A90C947E"},true},
{{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659","243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89","0E12B8C520948A776753A96F21ABD7FDC2D7D0C0DDC90851BE17B04E75EF86A47EF0DA46C4DC4D0D1BCB8668C2CE16C54C7C23A6716EDE303AF86774917CF928"},true},
{{"DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8","7E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C","FC012F9FB8FE00A358F51EF93DCE0DC0C895F6E9A87C6C4905BC820B0C3677616B8737D14E703AF8E16E22E5B8F26227D41E5128F82D86F747244CC289C74D1D"},true},
{{"25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160D8F517","FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF","FC132D4E426DFF535AEC0FA7083AC5118BC1D5FFFD848ABD8290C23F271CA0DD11AEDCEA3F55DA9BD677FE29C9DDA0CF878BCE43FDE0E313D69D1AF7A5AE8369"},true},
{{"D69C3509BB99E412E68B0FE8544E72837DFA30746D8BE2AA65975F29D22DC7B9","4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703","00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C630EC50E5363E227ACAC6F542CE1C0B186657E0E0D1A6FFE283A33438DE4738419"},true},
{{"EEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34","243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89","7036D6BFE1837AE919631039A2CF652A295DFAC9A8BBB0806014B2F48DD7C807941607B563ABBA414287F374A332BA3636DE009EE1EF551A17796B72B68B8A24"},false},
{{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659","243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89","F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F995A579DA959FA739FCE39E8BD16FECB5CDCF97060B2C73CDE60E87ABCA1AA5D9"},false},
{{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659","243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89","F8704654F4687B7365ED32E796DE92761390A3BCC495179BFE073817B7ED32824E76B987F7C1F9A751EF5C343F7645D3CFFC7D570B9A7192EBF1898E1344E3BF"},false},
{{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659","243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89","7036D6BFE1837AE919631039A2CF652A295DFAC9A8BBB0806014B2F48DD7C8076BE9F84A9C5445BEBD780C8B5CCD45C883D0DC47CD594B21A858F31A19AAB71D"},false},
{{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659","243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89","00000000000000000000000000000000000000000000000000000000000000009915EE59F07F9DBBAEDC31BFCC9B34AD49DE669CD24773BCED77DDA36D073EC8"},false},
{{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659","243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89","0000000000000000000000000000000000000000000000000000000000000001C7EC918B2B9CF34071BB54BED7EB4BB6BAB148E9A7E36E6B228F95DFA08B43EC"},false},
{{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659","243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89","4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D941607B563ABBA414287F374A332BA3636DE009EE1EF551A17796B72B68B8A24"},false},
{{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659","243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89","FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F941607B563ABBA414287F374A332BA3636DE009EE1EF551A17796B72B68B8A24"},false},
{{"DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659","243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89","7036D6BFE1837AE919631039A2CF652A295DFAC9A8BBB0806014B2F48DD7C807FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"},false},
{{"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30","243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89","7036D6BFE1837AE919631039A2CF652A295DFAC9A8BBB0806014B2F48DD7C807941607B563ABBA414287F374A332BA3636DE009EE1EF551A17796B72B68B8A24"},false}
};

for (const auto& test : VECTORS) {
auto pubkey = XOnlyPubKey(uint256(ParseHex(test.first[0])));
auto msg = uint256(ParseHex(test.first[1]));
auto sig = ParseHex(test.first[2]);
BOOST_CHECK_EQUAL(pubkey.VerifySchnorr(msg, sig), test.second);
}
}

BOOST_AUTO_TEST_SUITE_END()

0 comments on commit 125318b

Please sign in to comment.