diff --git a/src/key.cpp b/src/key.cpp index cf39bd3ee..a7dbeaa69 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -1,358 +1,381 @@ // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "key.h" #include "arith_uint256.h" #include "crypto/common.h" #include "crypto/hmac_sha512.h" #include "pubkey.h" #include "random.h" #include #include +#include static secp256k1_context *secp256k1_context_sign = nullptr; /** These functions are taken from the libsecp256k1 distribution and are very * ugly. */ static int ec_privkey_import_der(const secp256k1_context *ctx, uint8_t *out32, const uint8_t *privkey, size_t privkeylen) { const uint8_t *end = privkey + privkeylen; int lenb = 0; int len = 0; memset(out32, 0, 32); /* sequence header */ if (end < privkey + 1 || *privkey != 0x30) { return 0; } privkey++; /* sequence length constructor */ if (end < privkey + 1 || !(*privkey & 0x80)) { return 0; } lenb = *privkey & ~0x80; privkey++; if (lenb < 1 || lenb > 2) { return 0; } if (end < privkey + lenb) { return 0; } /* sequence length */ len = privkey[lenb - 1] | (lenb > 1 ? privkey[lenb - 2] << 8 : 0); privkey += lenb; if (end < privkey + len) { return 0; } /* sequence element 0: version number (=1) */ if (end < privkey + 3 || privkey[0] != 0x02 || privkey[1] != 0x01 || privkey[2] != 0x01) { return 0; } privkey += 3; /* sequence element 1: octet string, up to 32 bytes */ if (end < privkey + 2 || privkey[0] != 0x04 || privkey[1] > 0x20 || end < privkey + 2 + privkey[1]) { return 0; } memcpy(out32 + 32 - privkey[1], privkey + 2, privkey[1]); if (!secp256k1_ec_seckey_verify(ctx, out32)) { memset(out32, 0, 32); return 0; } return 1; } static int ec_privkey_export_der(const secp256k1_context *ctx, uint8_t *privkey, size_t *privkeylen, const uint8_t *key32, int compressed) { secp256k1_pubkey pubkey; size_t pubkeylen = 0; if (!secp256k1_ec_pubkey_create(ctx, &pubkey, key32)) { *privkeylen = 0; return 0; } if (compressed) { static const uint8_t begin[] = {0x30, 0x81, 0xD3, 0x02, 0x01, 0x01, 0x04, 0x20}; static const uint8_t middle[] = { 0xA0, 0x81, 0x85, 0x30, 0x81, 0x82, 0x02, 0x01, 0x01, 0x30, 0x2C, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x01, 0x01, 0x02, 0x21, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x2F, 0x30, 0x06, 0x04, 0x01, 0x00, 0x04, 0x01, 0x07, 0x04, 0x21, 0x02, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, 0x98, 0x02, 0x21, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41, 0x02, 0x01, 0x01, 0xA1, 0x24, 0x03, 0x22, 0x00}; uint8_t *ptr = privkey; memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin); memcpy(ptr, key32, 32); ptr += 32; memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle); pubkeylen = 33; secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED); ptr += pubkeylen; *privkeylen = ptr - privkey; } else { static const uint8_t begin[] = {0x30, 0x82, 0x01, 0x13, 0x02, 0x01, 0x01, 0x04, 0x20}; static const uint8_t middle[] = { 0xA0, 0x81, 0xA5, 0x30, 0x81, 0xA2, 0x02, 0x01, 0x01, 0x30, 0x2C, 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x01, 0x01, 0x02, 0x21, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x2F, 0x30, 0x06, 0x04, 0x01, 0x00, 0x04, 0x01, 0x07, 0x04, 0x41, 0x04, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, 0x98, 0x48, 0x3A, 0xDA, 0x77, 0x26, 0xA3, 0xC4, 0x65, 0x5D, 0xA4, 0xFB, 0xFC, 0x0E, 0x11, 0x08, 0xA8, 0xFD, 0x17, 0xB4, 0x48, 0xA6, 0x85, 0x54, 0x19, 0x9C, 0x47, 0xD0, 0x8F, 0xFB, 0x10, 0xD4, 0xB8, 0x02, 0x21, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41, 0x02, 0x01, 0x01, 0xA1, 0x44, 0x03, 0x42, 0x00}; uint8_t *ptr = privkey; memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin); memcpy(ptr, key32, 32); ptr += 32; memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle); pubkeylen = 65; secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_UNCOMPRESSED); ptr += pubkeylen; *privkeylen = ptr - privkey; } return 1; } bool CKey::Check(const uint8_t *vch) { return secp256k1_ec_seckey_verify(secp256k1_context_sign, vch); } void CKey::MakeNewKey(bool fCompressedIn) { do { GetStrongRandBytes(keydata.data(), keydata.size()); } while (!Check(keydata.data())); fValid = true; fCompressed = fCompressedIn; } CPrivKey CKey::GetPrivKey() const { assert(fValid); CPrivKey privkey; int ret; size_t privkeylen; privkey.resize(279); privkeylen = 279; ret = ec_privkey_export_der( secp256k1_context_sign, (uint8_t *)&privkey[0], &privkeylen, begin(), fCompressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED); assert(ret); privkey.resize(privkeylen); return privkey; } CPubKey CKey::GetPubKey() const { assert(fValid); secp256k1_pubkey pubkey; size_t clen = 65; CPubKey result; int ret = secp256k1_ec_pubkey_create(secp256k1_context_sign, &pubkey, begin()); assert(ret); secp256k1_ec_pubkey_serialize( secp256k1_context_sign, (uint8_t *)result.begin(), &clen, &pubkey, fCompressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED); assert(result.size() == clen); assert(result.IsValid()); return result; } bool CKey::SignECDSA(const uint256 &hash, std::vector &vchSig, uint32_t test_case) const { - if (!fValid) return false; + if (!fValid) { + return false; + } vchSig.resize(72); size_t nSigLen = 72; uint8_t extra_entropy[32] = {0}; WriteLE32(extra_entropy, test_case); secp256k1_ecdsa_signature sig; int ret = secp256k1_ecdsa_sign(secp256k1_context_sign, &sig, hash.begin(), begin(), secp256k1_nonce_function_rfc6979, test_case ? extra_entropy : nullptr); assert(ret); secp256k1_ecdsa_signature_serialize_der( secp256k1_context_sign, (uint8_t *)&vchSig[0], &nSigLen, &sig); vchSig.resize(nSigLen); return true; } +bool CKey::SignSchnorr(const uint256 &hash, std::vector &vchSig, + uint32_t test_case) const { + if (!fValid) { + return false; + } + vchSig.resize(64); + uint8_t extra_entropy[32] = {0}; + WriteLE32(extra_entropy, test_case); + + int ret = secp256k1_schnorr_sign( + secp256k1_context_sign, &vchSig[0], hash.begin(), begin(), + secp256k1_nonce_function_rfc6979, test_case ? extra_entropy : nullptr); + assert(ret); + return true; +} + bool CKey::VerifyPubKey(const CPubKey &pubkey) const { if (pubkey.IsCompressed() != fCompressed) { return false; } uint8_t rnd[8]; std::string str = "Bitcoin key verification\n"; GetRandBytes(rnd, sizeof(rnd)); uint256 hash; CHash256() .Write((uint8_t *)str.data(), str.size()) .Write(rnd, sizeof(rnd)) .Finalize(hash.begin()); std::vector vchSig; SignECDSA(hash, vchSig); return pubkey.VerifyECDSA(hash, vchSig); } bool CKey::SignCompact(const uint256 &hash, std::vector &vchSig) const { - if (!fValid) return false; + if (!fValid) { + return false; + } vchSig.resize(65); int rec = -1; secp256k1_ecdsa_recoverable_signature sig; int ret = secp256k1_ecdsa_sign_recoverable( secp256k1_context_sign, &sig, hash.begin(), begin(), secp256k1_nonce_function_rfc6979, nullptr); assert(ret); secp256k1_ecdsa_recoverable_signature_serialize_compact( secp256k1_context_sign, (uint8_t *)&vchSig[1], &rec, &sig); assert(ret); assert(rec != -1); vchSig[0] = 27 + rec + (fCompressed ? 4 : 0); return true; } bool CKey::Load(CPrivKey &privkey, CPubKey &vchPubKey, bool fSkipCheck = false) { if (!ec_privkey_import_der(secp256k1_context_sign, (uint8_t *)begin(), &privkey[0], privkey.size())) return false; fCompressed = vchPubKey.IsCompressed(); fValid = true; - if (fSkipCheck) return true; + if (fSkipCheck) { + return true; + } return VerifyPubKey(vchPubKey); } bool CKey::Derive(CKey &keyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode &cc) const { assert(IsValid()); assert(IsCompressed()); std::vector> vout(64); if ((nChild >> 31) == 0) { CPubKey pubkey = GetPubKey(); assert(pubkey.begin() + 33 == pubkey.end()); BIP32Hash(cc, nChild, *pubkey.begin(), pubkey.begin() + 1, vout.data()); } else { assert(begin() + 32 == end()); BIP32Hash(cc, nChild, 0, begin(), vout.data()); } memcpy(ccChild.begin(), vout.data() + 32, 32); memcpy((uint8_t *)keyChild.begin(), begin(), 32); bool ret = secp256k1_ec_privkey_tweak_add( secp256k1_context_sign, (uint8_t *)keyChild.begin(), vout.data()); keyChild.fCompressed = true; keyChild.fValid = ret; return ret; } bool CExtKey::Derive(CExtKey &out, unsigned int _nChild) const { out.nDepth = nDepth + 1; CKeyID id = key.GetPubKey().GetID(); memcpy(&out.vchFingerprint[0], &id, 4); out.nChild = _nChild; return key.Derive(out.key, out.chaincode, _nChild, chaincode); } void CExtKey::SetMaster(const uint8_t *seed, unsigned int nSeedLen) { static const uint8_t hashkey[] = {'B', 'i', 't', 'c', 'o', 'i', 'n', ' ', 's', 'e', 'e', 'd'}; std::vector> vout(64); CHMAC_SHA512(hashkey, sizeof(hashkey)) .Write(seed, nSeedLen) .Finalize(vout.data()); key.Set(&vout[0], &vout[32], true); memcpy(chaincode.begin(), &vout[32], 32); nDepth = 0; nChild = 0; memset(vchFingerprint, 0, sizeof(vchFingerprint)); } CExtPubKey CExtKey::Neuter() const { CExtPubKey ret; ret.nDepth = nDepth; memcpy(&ret.vchFingerprint[0], &vchFingerprint[0], 4); ret.nChild = nChild; ret.pubkey = key.GetPubKey(); ret.chaincode = chaincode; return ret; } void CExtKey::Encode(uint8_t code[BIP32_EXTKEY_SIZE]) const { code[0] = nDepth; memcpy(code + 1, vchFingerprint, 4); code[5] = (nChild >> 24) & 0xFF; code[6] = (nChild >> 16) & 0xFF; code[7] = (nChild >> 8) & 0xFF; code[8] = (nChild >> 0) & 0xFF; memcpy(code + 9, chaincode.begin(), 32); code[41] = 0; assert(key.size() == 32); memcpy(code + 42, key.begin(), 32); } void CExtKey::Decode(const uint8_t code[BIP32_EXTKEY_SIZE]) { nDepth = code[0]; memcpy(vchFingerprint, code + 1, 4); nChild = (code[5] << 24) | (code[6] << 16) | (code[7] << 8) | code[8]; memcpy(chaincode.begin(), code + 9, 32); key.Set(code + 42, code + BIP32_EXTKEY_SIZE, true); } bool ECC_InitSanityCheck() { CKey key; key.MakeNewKey(true); CPubKey pubkey = key.GetPubKey(); return key.VerifyPubKey(pubkey); } void ECC_Start() { assert(secp256k1_context_sign == nullptr); secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); assert(ctx != nullptr); { // Pass in a random blinding seed to the secp256k1 context. std::vector> vseed(32); GetRandBytes(vseed.data(), 32); bool ret = secp256k1_context_randomize(ctx, vseed.data()); assert(ret); } secp256k1_context_sign = ctx; } void ECC_Stop() { secp256k1_context *ctx = secp256k1_context_sign; secp256k1_context_sign = nullptr; if (ctx) { secp256k1_context_destroy(ctx); } } diff --git a/src/key.h b/src/key.h index ca4f928cd..8bd35b5d3 100644 --- a/src/key.h +++ b/src/key.h @@ -1,190 +1,197 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_KEY_H #define BITCOIN_KEY_H #include "pubkey.h" #include "serialize.h" #include "support/allocators/secure.h" #include "uint256.h" #include #include /** * secp256k1: * const unsigned int PRIVATE_KEY_SIZE = 279; * const unsigned int PUBLIC_KEY_SIZE = 65; * const unsigned int SIGNATURE_SIZE = 72; * * see www.keylength.com * script supports up to 75 for single byte push */ /** * secure_allocator is defined in allocators.h * CPrivKey is a serialized private key, with all parameters included (279 * bytes) */ typedef std::vector> CPrivKey; /** An encapsulated secp256k1 private key. */ class CKey { private: //! Whether this private key is valid. We check for correctness when //! modifying the key data, so fValid should always correspond to the actual //! state. bool fValid; //! Whether the public key corresponding to this private key is (to be) //! compressed. bool fCompressed; //! The actual byte data std::vector> keydata; //! Check whether the 32-byte array pointed to be vch is valid keydata. static bool Check(const uint8_t *vch); public: //! Construct an invalid private key. CKey() : fValid(false), fCompressed(false) { // Important: vch must be 32 bytes in length to not break serialization keydata.resize(32); } //! Destructor (again necessary because of memlocking). ~CKey() {} friend bool operator==(const CKey &a, const CKey &b) { return a.fCompressed == b.fCompressed && a.size() == b.size() && memcmp(a.keydata.data(), b.keydata.data(), a.size()) == 0; } //! Initialize using begin and end iterators to byte data. template void Set(const T pbegin, const T pend, bool fCompressedIn) { if (size_t(pend - pbegin) != keydata.size()) { fValid = false; } else if (Check(&pbegin[0])) { memcpy(keydata.data(), (uint8_t *)&pbegin[0], keydata.size()); fValid = true; fCompressed = fCompressedIn; } else { fValid = false; } } //! Simple read-only vector-like interface. unsigned int size() const { return (fValid ? keydata.size() : 0); } const uint8_t *begin() const { return keydata.data(); } const uint8_t *end() const { return keydata.data() + size(); } //! Check whether this private key is valid. bool IsValid() const { return fValid; } //! Check whether the public key corresponding to this private key is (to //! be) compressed. bool IsCompressed() const { return fCompressed; } //! Generate a new private key using a cryptographic PRNG. void MakeNewKey(bool fCompressed); /** * Convert the private key to a CPrivKey (serialized OpenSSL private key * data). * This is expensive. */ CPrivKey GetPrivKey() const; /** * Compute the public key from a private key. * This is expensive. */ CPubKey GetPubKey() const; /** * Create a DER-serialized ECDSA signature. * The test_case parameter tweaks the deterministic nonce. */ bool SignECDSA(const uint256 &hash, std::vector &vchSig, uint32_t test_case = 0) const; + /** + * Create a Schnorr signature. + * The test_case parameter tweaks the deterministic nonce. + */ + bool SignSchnorr(const uint256 &hash, std::vector &vchSig, + uint32_t test_case = 0) const; + /** * Create a compact ECDSA signature (65 bytes), which allows reconstructing * the used public key. * The format is one header byte, followed by two times 32 bytes for the * serialized r and s values. * The header byte: 0x1B = first key with even y, 0x1C = first key with odd * y, * 0x1D = second key with even y, 0x1E = second key with * odd y, * add 0x04 for compressed keys. */ bool SignCompact(const uint256 &hash, std::vector &vchSig) const; //! Derive BIP32 child key. bool Derive(CKey &keyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode &cc) const; /** * Verify thoroughly whether a private key and a public key match. * This is done using a different mechanism than just regenerating it. * (An ECDSA signature is created then verified.) */ bool VerifyPubKey(const CPubKey &vchPubKey) const; //! Load private key and check that public key matches. bool Load(CPrivKey &privkey, CPubKey &vchPubKey, bool fSkipCheck); }; struct CExtKey { uint8_t nDepth; uint8_t vchFingerprint[4]; unsigned int nChild; ChainCode chaincode; CKey key; friend bool operator==(const CExtKey &a, const CExtKey &b) { return a.nDepth == b.nDepth && memcmp(&a.vchFingerprint[0], &b.vchFingerprint[0], sizeof(vchFingerprint)) == 0 && a.nChild == b.nChild && a.chaincode == b.chaincode && a.key == b.key; } void Encode(uint8_t code[BIP32_EXTKEY_SIZE]) const; void Decode(const uint8_t code[BIP32_EXTKEY_SIZE]); bool Derive(CExtKey &out, unsigned int nChild) const; CExtPubKey Neuter() const; void SetMaster(const uint8_t *seed, unsigned int nSeedLen); template void Serialize(Stream &s) const { unsigned int len = BIP32_EXTKEY_SIZE; ::WriteCompactSize(s, len); uint8_t code[BIP32_EXTKEY_SIZE]; Encode(code); s.write((const char *)&code[0], len); } template void Unserialize(Stream &s) { unsigned int len = ::ReadCompactSize(s); uint8_t code[BIP32_EXTKEY_SIZE]; s.read((char *)&code[0], len); Decode(code); } }; /** Initialize the elliptic curve support. May not be called twice without * calling ECC_Stop first. */ void ECC_Start(void); /** Deinitialize the elliptic curve support. No-op if ECC_Start wasn't called * first. */ void ECC_Stop(void); /** Check that required EC support is available at runtime. */ bool ECC_InitSanityCheck(void); #endif // BITCOIN_KEY_H diff --git a/src/pubkey.cpp b/src/pubkey.cpp index 9fb51ac4a..ab05348a7 100644 --- a/src/pubkey.cpp +++ b/src/pubkey.cpp @@ -1,333 +1,354 @@ // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "pubkey.h" #include #include +#include namespace { /* Global secp256k1_context object used for verification. */ secp256k1_context *secp256k1_context_verify = nullptr; } // namespace /** * This function is taken from the libsecp256k1 distribution and implements DER * parsing for ECDSA signatures, while supporting an arbitrary subset of format * violations. * * Supported violations include negative integers, excessive padding, garbage at * the end, and overly long length descriptors. This is safe to use in Bitcoin * because since the activation of BIP66, signatures are verified to be strict * DER before being passed to this module, and we know it supports all * violations present in the blockchain before that point. */ static int ecdsa_signature_parse_der_lax(const secp256k1_context *ctx, secp256k1_ecdsa_signature *sig, const uint8_t *input, size_t inputlen) { size_t rpos, rlen, spos, slen; size_t pos = 0; size_t lenbyte; uint8_t tmpsig[64] = {0}; int overflow = 0; /* Hack to initialize sig with a correctly-parsed but invalid signature. */ secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig); /* Sequence tag byte */ if (pos == inputlen || input[pos] != 0x30) { return 0; } pos++; /* Sequence length bytes */ if (pos == inputlen) { return 0; } lenbyte = input[pos++]; if (lenbyte & 0x80) { lenbyte -= 0x80; if (pos + lenbyte > inputlen) { return 0; } pos += lenbyte; } /* Integer tag byte for R */ if (pos == inputlen || input[pos] != 0x02) { return 0; } pos++; /* Integer length for R */ if (pos == inputlen) { return 0; } lenbyte = input[pos++]; if (lenbyte & 0x80) { lenbyte -= 0x80; if (pos + lenbyte > inputlen) { return 0; } while (lenbyte > 0 && input[pos] == 0) { pos++; lenbyte--; } if (lenbyte >= sizeof(size_t)) { return 0; } rlen = 0; while (lenbyte > 0) { rlen = (rlen << 8) + input[pos]; pos++; lenbyte--; } } else { rlen = lenbyte; } if (rlen > inputlen - pos) { return 0; } rpos = pos; pos += rlen; /* Integer tag byte for S */ if (pos == inputlen || input[pos] != 0x02) { return 0; } pos++; /* Integer length for S */ if (pos == inputlen) { return 0; } lenbyte = input[pos++]; if (lenbyte & 0x80) { lenbyte -= 0x80; if (pos + lenbyte > inputlen) { return 0; } while (lenbyte > 0 && input[pos] == 0) { pos++; lenbyte--; } if (lenbyte >= sizeof(size_t)) { return 0; } slen = 0; while (lenbyte > 0) { slen = (slen << 8) + input[pos]; pos++; lenbyte--; } } else { slen = lenbyte; } if (slen > inputlen - pos) { return 0; } spos = pos; /* Ignore leading zeroes in R */ while (rlen > 0 && input[rpos] == 0) { rlen--; rpos++; } /* Copy R value */ if (rlen > 32) { overflow = 1; } else { memcpy(tmpsig + 32 - rlen, input + rpos, rlen); } /* Ignore leading zeroes in S */ while (slen > 0 && input[spos] == 0) { slen--; spos++; } /* Copy S value */ if (slen > 32) { overflow = 1; } else { memcpy(tmpsig + 64 - slen, input + spos, slen); } if (!overflow) { overflow = !secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig); } if (overflow) { /* Overwrite the result again with a correctly-parsed but invalid signature if parsing failed. */ memset(tmpsig, 0, 64); secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig); } return 1; } bool CPubKey::VerifyECDSA(const uint256 &hash, const std::vector &vchSig) const { if (!IsValid()) { return false; } secp256k1_pubkey pubkey; secp256k1_ecdsa_signature sig; if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, &(*this)[0], size())) { return false; } if (vchSig.size() == 0) { return false; } if (!ecdsa_signature_parse_der_lax(secp256k1_context_verify, &sig, &vchSig[0], vchSig.size())) { return false; } /** * libsecp256k1's ECDSA verification requires lower-S signatures, which have * not historically been enforced in Bitcoin, so normalize them first. */ secp256k1_ecdsa_signature_normalize(secp256k1_context_verify, &sig, &sig); return secp256k1_ecdsa_verify(secp256k1_context_verify, &sig, hash.begin(), &pubkey); } +bool CPubKey::VerifySchnorr(const uint256 &hash, + const std::vector &vchSig) const { + if (!IsValid()) { + return false; + } + + if (vchSig.size() != 64) { + return false; + } + + secp256k1_pubkey pubkey; + if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, + &(*this)[0], size())) { + return false; + } + + return secp256k1_schnorr_verify(secp256k1_context_verify, &vchSig[0], + hash.begin(), &pubkey); +} + bool CPubKey::RecoverCompact(const uint256 &hash, const std::vector &vchSig) { if (vchSig.size() != 65) { return false; } int recid = (vchSig[0] - 27) & 3; bool fComp = ((vchSig[0] - 27) & 4) != 0; secp256k1_pubkey pubkey; secp256k1_ecdsa_recoverable_signature sig; if (!secp256k1_ecdsa_recoverable_signature_parse_compact( secp256k1_context_verify, &sig, &vchSig[1], recid)) { return false; } if (!secp256k1_ecdsa_recover(secp256k1_context_verify, &pubkey, &sig, hash.begin())) { return false; } uint8_t pub[65]; size_t publen = 65; secp256k1_ec_pubkey_serialize( secp256k1_context_verify, pub, &publen, &pubkey, fComp ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED); Set(pub, pub + publen); return true; } bool CPubKey::IsFullyValid() const { if (!IsValid()) { return false; } secp256k1_pubkey pubkey; return secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, &(*this)[0], size()); } bool CPubKey::Decompress() { if (!IsValid()) { return false; } secp256k1_pubkey pubkey; if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, &(*this)[0], size())) { return false; } uint8_t pub[65]; size_t publen = 65; secp256k1_ec_pubkey_serialize(secp256k1_context_verify, pub, &publen, &pubkey, SECP256K1_EC_UNCOMPRESSED); Set(pub, pub + publen); return true; } bool CPubKey::Derive(CPubKey &pubkeyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode &cc) const { assert(IsValid()); assert((nChild >> 31) == 0); assert(begin() + 33 == end()); uint8_t out[64]; BIP32Hash(cc, nChild, *begin(), begin() + 1, out); memcpy(ccChild.begin(), out + 32, 32); secp256k1_pubkey pubkey; if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, &(*this)[0], size())) { return false; } if (!secp256k1_ec_pubkey_tweak_add(secp256k1_context_verify, &pubkey, out)) { return false; } uint8_t pub[33]; size_t publen = 33; secp256k1_ec_pubkey_serialize(secp256k1_context_verify, pub, &publen, &pubkey, SECP256K1_EC_COMPRESSED); pubkeyChild.Set(pub, pub + publen); return true; } void CExtPubKey::Encode(uint8_t code[BIP32_EXTKEY_SIZE]) const { code[0] = nDepth; memcpy(code + 1, vchFingerprint, 4); code[5] = (nChild >> 24) & 0xFF; code[6] = (nChild >> 16) & 0xFF; code[7] = (nChild >> 8) & 0xFF; code[8] = (nChild >> 0) & 0xFF; memcpy(code + 9, chaincode.begin(), 32); assert(pubkey.size() == 33); memcpy(code + 41, pubkey.begin(), 33); } void CExtPubKey::Decode(const uint8_t code[BIP32_EXTKEY_SIZE]) { nDepth = code[0]; memcpy(vchFingerprint, code + 1, 4); nChild = (code[5] << 24) | (code[6] << 16) | (code[7] << 8) | code[8]; memcpy(chaincode.begin(), code + 9, 32); pubkey.Set(code + 41, code + BIP32_EXTKEY_SIZE); } bool CExtPubKey::Derive(CExtPubKey &out, unsigned int _nChild) const { out.nDepth = nDepth + 1; CKeyID id = pubkey.GetID(); memcpy(&out.vchFingerprint[0], &id, 4); out.nChild = _nChild; return pubkey.Derive(out.pubkey, out.chaincode, _nChild, chaincode); } bool CPubKey::CheckLowS( const boost::sliced_range> &vchSig) { secp256k1_ecdsa_signature sig; if (!ecdsa_signature_parse_der_lax(secp256k1_context_verify, &sig, &vchSig[0], vchSig.size())) { return false; } return (!secp256k1_ecdsa_signature_normalize(secp256k1_context_verify, nullptr, &sig)); } /* static */ int ECCVerifyHandle::refcount = 0; ECCVerifyHandle::ECCVerifyHandle() { if (refcount == 0) { assert(secp256k1_context_verify == nullptr); secp256k1_context_verify = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); assert(secp256k1_context_verify != nullptr); } refcount++; } ECCVerifyHandle::~ECCVerifyHandle() { refcount--; if (refcount == 0) { assert(secp256k1_context_verify != nullptr); secp256k1_context_destroy(secp256k1_context_verify); secp256k1_context_verify = nullptr; } } diff --git a/src/pubkey.h b/src/pubkey.h index 375b5713d..9f1e63bf3 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -1,219 +1,226 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef BITCOIN_PUBKEY_H #define BITCOIN_PUBKEY_H #include "hash.h" #include "serialize.h" #include "uint256.h" #include #include #include /** * secp256k1: * const unsigned int PRIVATE_KEY_SIZE = 279; * const unsigned int PUBLIC_KEY_SIZE = 65; * const unsigned int SIGNATURE_SIZE = 72; * * see www.keylength.com * script supports up to 75 for single byte push */ const unsigned int BIP32_EXTKEY_SIZE = 74; /** A reference to a CKey: the Hash160 of its serialized public key */ class CKeyID : public uint160 { public: CKeyID() : uint160() {} explicit CKeyID(const uint160 &in) : uint160(in) {} }; typedef uint256 ChainCode; /** An encapsulated secp256k1 public key. */ class CPubKey { private: /** * Just store the serialized data. * Its length can very cheaply be computed from the first byte. */ uint8_t vch[65]; //! Compute the length of a pubkey with a given first byte. static unsigned int GetLen(uint8_t chHeader) { if (chHeader == 2 || chHeader == 3) return 33; if (chHeader == 4 || chHeader == 6 || chHeader == 7) return 65; return 0; } //! Set this key data to be invalid void Invalidate() { vch[0] = 0xFF; } public: //! Construct an invalid public key. CPubKey() { Invalidate(); } //! Initialize a public key using begin/end iterators to byte data. template void Set(const T pbegin, const T pend) { int len = pend == pbegin ? 0 : GetLen(pbegin[0]); if (len && len == (pend - pbegin)) memcpy(vch, (uint8_t *)&pbegin[0], len); else Invalidate(); } //! Construct a public key using begin/end iterators to byte data. template CPubKey(const T pbegin, const T pend) { Set(pbegin, pend); } //! Construct a public key from a byte vector. explicit CPubKey(const std::vector &_vch) { Set(_vch.begin(), _vch.end()); } //! Simple read-only vector-like interface to the pubkey data. unsigned int size() const { return GetLen(vch[0]); } const uint8_t *begin() const { return vch; } const uint8_t *end() const { return vch + size(); } const uint8_t &operator[](unsigned int pos) const { return vch[pos]; } //! Comparator implementation. friend bool operator==(const CPubKey &a, const CPubKey &b) { return a.vch[0] == b.vch[0] && memcmp(a.vch, b.vch, a.size()) == 0; } friend bool operator!=(const CPubKey &a, const CPubKey &b) { return !(a == b); } friend bool operator<(const CPubKey &a, const CPubKey &b) { return a.vch[0] < b.vch[0] || (a.vch[0] == b.vch[0] && memcmp(a.vch, b.vch, a.size()) < 0); } //! Implement serialization, as if this was a byte vector. template void Serialize(Stream &s) const { unsigned int len = size(); ::WriteCompactSize(s, len); s.write((char *)vch, len); } template void Unserialize(Stream &s) { unsigned int len = ::ReadCompactSize(s); if (len <= 65) { s.read((char *)vch, len); } else { // invalid pubkey, skip available data char dummy; while (len--) s.read(&dummy, 1); Invalidate(); } } //! Get the KeyID of this public key (hash of its serialization) CKeyID GetID() const { return CKeyID(Hash160(vch, vch + size())); } //! Get the 256-bit hash of this public key. uint256 GetHash() const { return Hash(vch, vch + size()); } /* * Check syntactic correctness. * * Note that this is consensus critical as CheckSig() calls it! */ bool IsValid() const { return size() > 0; } //! fully validate whether this is a valid public key (more expensive than //! IsValid()) bool IsFullyValid() const; //! Check whether this is a compressed public key. bool IsCompressed() const { return size() == 33; } /** * Verify a DER-serialized ECDSA signature (~72 bytes). * If this public key is not fully valid, the return value will be false. */ bool VerifyECDSA(const uint256 &hash, const std::vector &vchSig) const; + /** + * Verify a Schnorr signature (=64 bytes). + * If this public key is not fully valid, the return value will be false. + */ + bool VerifySchnorr(const uint256 &hash, + const std::vector &vchSig) const; + /** * Check whether a DER-serialized ECDSA signature is normalized (lower-S). */ static bool CheckLowS(const boost::sliced_range> &vchSig); static bool CheckLowS(const std::vector &vchSig) { return CheckLowS(vchSig | boost::adaptors::sliced(0, vchSig.size())); } //! Recover a public key from a compact ECDSA signature. bool RecoverCompact(const uint256 &hash, const std::vector &vchSig); //! Turn this public key into an uncompressed public key. bool Decompress(); //! Derive BIP32 child pubkey. bool Derive(CPubKey &pubkeyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode &cc) const; }; struct CExtPubKey { uint8_t nDepth; uint8_t vchFingerprint[4]; unsigned int nChild; ChainCode chaincode; CPubKey pubkey; friend bool operator==(const CExtPubKey &a, const CExtPubKey &b) { return a.nDepth == b.nDepth && memcmp(&a.vchFingerprint[0], &b.vchFingerprint[0], sizeof(vchFingerprint)) == 0 && a.nChild == b.nChild && a.chaincode == b.chaincode && a.pubkey == b.pubkey; } void Encode(uint8_t code[BIP32_EXTKEY_SIZE]) const; void Decode(const uint8_t code[BIP32_EXTKEY_SIZE]); bool Derive(CExtPubKey &out, unsigned int nChild) const; void Serialize(CSizeComputer &s) const { // Optimized implementation for ::GetSerializeSize that avoids copying. // add one byte for the size (compact int) s.seek(BIP32_EXTKEY_SIZE + 1); } template void Serialize(Stream &s) const { unsigned int len = BIP32_EXTKEY_SIZE; ::WriteCompactSize(s, len); uint8_t code[BIP32_EXTKEY_SIZE]; Encode(code); s.write((const char *)&code[0], len); } template void Unserialize(Stream &s) { unsigned int len = ::ReadCompactSize(s); uint8_t code[BIP32_EXTKEY_SIZE]; if (len != BIP32_EXTKEY_SIZE) throw std::runtime_error("Invalid extended key size\n"); s.read((char *)&code[0], len); Decode(code); } }; /** * Users of this module must hold an ECCVerifyHandle. The constructor and * destructor of these are not allowed to run in parallel, though. */ class ECCVerifyHandle { static int refcount; public: ECCVerifyHandle(); ~ECCVerifyHandle(); }; #endif // BITCOIN_PUBKEY_H diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp index a8308f0d8..0beab1069 100644 --- a/src/test/key_tests.cpp +++ b/src/test/key_tests.cpp @@ -1,185 +1,300 @@ // Copyright (c) 2012-2015 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "key.h" #include "base58.h" #include "dstencode.h" #include "script/script.h" #include "test/test_bitcoin.h" #include "uint256.h" #include "util.h" #include "utilstrencodings.h" #include #include #include static const std::string strSecret1 = "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj"; static const std::string strSecret2 = "5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3"; static const std::string strSecret1C = "Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw"; static const std::string strSecret2C = "L3Hq7a8FEQwJkW1M2GNKDW28546Vp5miewcCzSqUD9kCAXrJdS3g"; static const std::string addr1 = "1QFqqMUD55ZV3PJEJZtaKCsQmjLT6JkjvJ"; static const std::string addr2 = "1F5y5E5FMc5YzdJtB9hLaUe43GDxEKXENJ"; static const std::string addr1C = "1NoJrossxPBKfCHuJXT4HadJrXRE9Fxiqs"; static const std::string addr2C = "1CRj2HyM1CXWzHAXLQtiGLyggNT9WQqsDs"; static const std::string strAddressBad = "1HV9Lc3sNHZxwj4Zk6fB38tEmBryq2cBiF"; +// get r value produced by ECDSA signing algorithm +// (assumes ECDSA r is encoded in the canonical manner) +std::vector get_r_ECDSA(std::vector sigECDSA) { + std::vector ret(32, 0); + + assert(sigECDSA[2] == 2); + int rlen = sigECDSA[3]; + assert(rlen <= 33); + assert(sigECDSA[4 + rlen] == 2); + if (rlen == 33) { + assert(sigECDSA[4] == 0); + std::copy(sigECDSA.begin() + 5, sigECDSA.begin() + 37, ret.begin()); + } else { + std::copy(sigECDSA.begin() + 4, sigECDSA.begin() + 36, + ret.begin() + (32 - rlen)); + } + return ret; +} + BOOST_FIXTURE_TEST_SUITE(key_tests, BasicTestingSetup) +BOOST_AUTO_TEST_CASE(internal_test) { + // test get_r_ECDSA (defined above) to make sure it's working properly + BOOST_CHECK(get_r_ECDSA(ParseHex( + "3045022100c6ab5f8acfccc114da39dd5ad0b1ef4d39df6a721e8" + "24c22e00b7bc7944a1f7802206ff23df3802e241ee234a8b66c40" + "c82e56a6cc37f9b50463111c9f9229b8f3b3")) == + ParseHex("c6ab5f8acfccc114da39dd5ad0b1ef4d39df6a721e8" + "24c22e00b7bc7944a1f78")); + BOOST_CHECK(get_r_ECDSA(ParseHex( + "3045022046ab5f8acfccc114da39dd5ad0b1ef4d39df6a721e8" + "24c22e00b7bc7944a1f7802206ff23df3802e241ee234a8b66c40" + "c82e56a6cc37f9b50463111c9f9229b8f3b3")) == + ParseHex("46ab5f8acfccc114da39dd5ad0b1ef4d39df6a721e8" + "24c22e00b7bc7944a1f78")); + BOOST_CHECK(get_r_ECDSA(ParseHex( + "3045021f4b5f8acfccc114da39dd5ad0b1ef4d39df6a721e8" + "24c22e00b7bc7944a1f7802206ff23df3802e241ee234a8b66c40" + "c82e56a6cc37f9b50463111c9f9229b8f3b3")) == + ParseHex("004b5f8acfccc114da39dd5ad0b1ef4d39df6a721e8" + "24c22e00b7bc7944a1f78")); + BOOST_CHECK(get_r_ECDSA(ParseHex( + "3045021e5f8acfccc114da39dd5ad0b1ef4d39df6a721e8" + "24c22e00b7bc7944a1f7802206ff23df3802e241ee234a8b66c40" + "c82e56a6cc37f9b50463111c9f9229b8f3b3")) == + ParseHex("00005f8acfccc114da39dd5ad0b1ef4d39df6a721e8" + "24c22e00b7bc7944a1f78")); +} + BOOST_AUTO_TEST_CASE(key_test1) { CBitcoinSecret bsecret1, bsecret2, bsecret1C, bsecret2C, baddress1; BOOST_CHECK(bsecret1.SetString(strSecret1)); BOOST_CHECK(bsecret2.SetString(strSecret2)); BOOST_CHECK(bsecret1C.SetString(strSecret1C)); BOOST_CHECK(bsecret2C.SetString(strSecret2C)); BOOST_CHECK(!baddress1.SetString(strAddressBad)); CKey key1 = bsecret1.GetKey(); BOOST_CHECK(key1.IsCompressed() == false); CKey key2 = bsecret2.GetKey(); BOOST_CHECK(key2.IsCompressed() == false); CKey key1C = bsecret1C.GetKey(); BOOST_CHECK(key1C.IsCompressed() == true); CKey key2C = bsecret2C.GetKey(); BOOST_CHECK(key2C.IsCompressed() == true); CPubKey pubkey1 = key1.GetPubKey(); CPubKey pubkey2 = key2.GetPubKey(); CPubKey pubkey1C = key1C.GetPubKey(); CPubKey pubkey2C = key2C.GetPubKey(); BOOST_CHECK(key1.VerifyPubKey(pubkey1)); BOOST_CHECK(!key1.VerifyPubKey(pubkey1C)); BOOST_CHECK(!key1.VerifyPubKey(pubkey2)); BOOST_CHECK(!key1.VerifyPubKey(pubkey2C)); BOOST_CHECK(!key1C.VerifyPubKey(pubkey1)); BOOST_CHECK(key1C.VerifyPubKey(pubkey1C)); BOOST_CHECK(!key1C.VerifyPubKey(pubkey2)); BOOST_CHECK(!key1C.VerifyPubKey(pubkey2C)); BOOST_CHECK(!key2.VerifyPubKey(pubkey1)); BOOST_CHECK(!key2.VerifyPubKey(pubkey1C)); BOOST_CHECK(key2.VerifyPubKey(pubkey2)); BOOST_CHECK(!key2.VerifyPubKey(pubkey2C)); BOOST_CHECK(!key2C.VerifyPubKey(pubkey1)); BOOST_CHECK(!key2C.VerifyPubKey(pubkey1C)); BOOST_CHECK(!key2C.VerifyPubKey(pubkey2)); BOOST_CHECK(key2C.VerifyPubKey(pubkey2C)); const CChainParams &chainParams = Params(); BOOST_CHECK(DecodeDestination(addr1, chainParams) == CTxDestination(pubkey1.GetID())); BOOST_CHECK(DecodeDestination(addr2, chainParams) == CTxDestination(pubkey2.GetID())); BOOST_CHECK(DecodeDestination(addr1C, chainParams) == CTxDestination(pubkey1C.GetID())); BOOST_CHECK(DecodeDestination(addr2C, chainParams) == CTxDestination(pubkey2C.GetID())); for (int n = 0; n < 16; n++) { std::string strMsg = strprintf("Very secret message %i: 11", n); uint256 hashMsg = Hash(strMsg.begin(), strMsg.end()); - // normal signatures + // normal ECDSA signatures std::vector sign1, sign2, sign1C, sign2C; BOOST_CHECK(key1.SignECDSA(hashMsg, sign1)); BOOST_CHECK(key2.SignECDSA(hashMsg, sign2)); BOOST_CHECK(key1C.SignECDSA(hashMsg, sign1C)); BOOST_CHECK(key2C.SignECDSA(hashMsg, sign2C)); BOOST_CHECK(pubkey1.VerifyECDSA(hashMsg, sign1)); BOOST_CHECK(!pubkey1.VerifyECDSA(hashMsg, sign2)); BOOST_CHECK(pubkey1.VerifyECDSA(hashMsg, sign1C)); BOOST_CHECK(!pubkey1.VerifyECDSA(hashMsg, sign2C)); BOOST_CHECK(!pubkey2.VerifyECDSA(hashMsg, sign1)); BOOST_CHECK(pubkey2.VerifyECDSA(hashMsg, sign2)); BOOST_CHECK(!pubkey2.VerifyECDSA(hashMsg, sign1C)); BOOST_CHECK(pubkey2.VerifyECDSA(hashMsg, sign2C)); BOOST_CHECK(pubkey1C.VerifyECDSA(hashMsg, sign1)); BOOST_CHECK(!pubkey1C.VerifyECDSA(hashMsg, sign2)); BOOST_CHECK(pubkey1C.VerifyECDSA(hashMsg, sign1C)); BOOST_CHECK(!pubkey1C.VerifyECDSA(hashMsg, sign2C)); BOOST_CHECK(!pubkey2C.VerifyECDSA(hashMsg, sign1)); BOOST_CHECK(pubkey2C.VerifyECDSA(hashMsg, sign2)); BOOST_CHECK(!pubkey2C.VerifyECDSA(hashMsg, sign1C)); BOOST_CHECK(pubkey2C.VerifyECDSA(hashMsg, sign2C)); - // compact signatures (with key recovery) + // compact ECDSA signatures (with key recovery) std::vector csign1, csign2, csign1C, csign2C; BOOST_CHECK(key1.SignCompact(hashMsg, csign1)); BOOST_CHECK(key2.SignCompact(hashMsg, csign2)); BOOST_CHECK(key1C.SignCompact(hashMsg, csign1C)); BOOST_CHECK(key2C.SignCompact(hashMsg, csign2C)); CPubKey rkey1, rkey2, rkey1C, rkey2C; BOOST_CHECK(rkey1.RecoverCompact(hashMsg, csign1)); BOOST_CHECK(rkey2.RecoverCompact(hashMsg, csign2)); BOOST_CHECK(rkey1C.RecoverCompact(hashMsg, csign1C)); BOOST_CHECK(rkey2C.RecoverCompact(hashMsg, csign2C)); BOOST_CHECK(rkey1 == pubkey1); BOOST_CHECK(rkey2 == pubkey2); BOOST_CHECK(rkey1C == pubkey1C); BOOST_CHECK(rkey2C == pubkey2C); + + // Schnorr signatures + + std::vector ssign1, ssign2, ssign1C, ssign2C; + + BOOST_CHECK(key1.SignSchnorr(hashMsg, ssign1)); + BOOST_CHECK(key2.SignSchnorr(hashMsg, ssign2)); + BOOST_CHECK(key1C.SignSchnorr(hashMsg, ssign1C)); + BOOST_CHECK(key2C.SignSchnorr(hashMsg, ssign2C)); + + BOOST_CHECK(pubkey1.VerifySchnorr(hashMsg, ssign1)); + BOOST_CHECK(!pubkey1.VerifySchnorr(hashMsg, ssign2)); + BOOST_CHECK(pubkey1.VerifySchnorr(hashMsg, ssign1C)); + BOOST_CHECK(!pubkey1.VerifySchnorr(hashMsg, ssign2C)); + + BOOST_CHECK(!pubkey2.VerifySchnorr(hashMsg, ssign1)); + BOOST_CHECK(pubkey2.VerifySchnorr(hashMsg, ssign2)); + BOOST_CHECK(!pubkey2.VerifySchnorr(hashMsg, ssign1C)); + BOOST_CHECK(pubkey2.VerifySchnorr(hashMsg, ssign2C)); + + BOOST_CHECK(pubkey1C.VerifySchnorr(hashMsg, ssign1)); + BOOST_CHECK(!pubkey1C.VerifySchnorr(hashMsg, ssign2)); + BOOST_CHECK(pubkey1C.VerifySchnorr(hashMsg, ssign1C)); + BOOST_CHECK(!pubkey1C.VerifySchnorr(hashMsg, ssign2C)); + + BOOST_CHECK(!pubkey2C.VerifySchnorr(hashMsg, ssign1)); + BOOST_CHECK(pubkey2C.VerifySchnorr(hashMsg, ssign2)); + BOOST_CHECK(!pubkey2C.VerifySchnorr(hashMsg, ssign1C)); + BOOST_CHECK(pubkey2C.VerifySchnorr(hashMsg, ssign2C)); + + // check deterministicity of ECDSA & Schnorr + BOOST_CHECK(sign1 == sign1C); + BOOST_CHECK(sign2 == sign2C); + BOOST_CHECK(ssign1 == ssign1C); + BOOST_CHECK(ssign2 == ssign2C); + + // Extract r value from ECDSA and Schnorr. Make sure they are + // distinct (nonce reuse would be dangerous and can leak private key). + std::vector rE1 = get_r_ECDSA(sign1); + BOOST_CHECK(ssign1.size() == 64); + std::vector rS1(ssign1.begin(), ssign1.begin() + 32); + BOOST_CHECK(rE1.size() == 32); + BOOST_CHECK(rS1.size() == 32); + BOOST_CHECK(rE1 != rS1); + + std::vector rE2 = get_r_ECDSA(sign2); + BOOST_CHECK(ssign2.size() == 64); + std::vector rS2(ssign2.begin(), ssign2.begin() + 32); + BOOST_CHECK(rE2.size() == 32); + BOOST_CHECK(rS2.size() == 32); + BOOST_CHECK(rE2 != rS2); } - // test deterministic signing + // test deterministic signing expected values std::vector detsig, detsigc; std::string strMsg = "Very deterministic message"; uint256 hashMsg = Hash(strMsg.begin(), strMsg.end()); + // ECDSA BOOST_CHECK(key1.SignECDSA(hashMsg, detsig)); BOOST_CHECK(key1C.SignECDSA(hashMsg, detsigc)); BOOST_CHECK(detsig == detsigc); BOOST_CHECK(detsig == ParseHex("3045022100c6ab5f8acfccc114da39dd5ad0b1ef4d39df6a721e8" "24c22e00b7bc7944a1f7802206ff23df3802e241ee234a8b66c40" "c82e56a6cc37f9b50463111c9f9229b8f3b3")); BOOST_CHECK(key2.SignECDSA(hashMsg, detsig)); BOOST_CHECK(key2C.SignECDSA(hashMsg, detsigc)); BOOST_CHECK(detsig == detsigc); BOOST_CHECK(detsig == ParseHex("304502210094dc5a77b8d5db6b42b66c29d7033cd873fac7a1272" "4a90373726f60bb9f852a02204eb4c98b9a2f5c017f9417ba7c43" "279c20c84bb058dc05b3beeb9333016b15bb")); + // Compact BOOST_CHECK(key1.SignCompact(hashMsg, detsig)); BOOST_CHECK(key1C.SignCompact(hashMsg, detsigc)); BOOST_CHECK(detsig == ParseHex("1b8c56f224d51415e6ce329144aa1e1c1563e297a005f450df015" "14f3d047681760277e79d57502df27b8feebb001a588aa3a8c2bc" "f5b2367273c15f840638cfc8")); BOOST_CHECK(detsigc == ParseHex("1f8c56f224d51415e6ce329144aa1e1c1563e297a005f450df015" "14f3d047681760277e79d57502df27b8feebb001a588aa3a8c2bc" "f5b2367273c15f840638cfc8")); BOOST_CHECK(key2.SignCompact(hashMsg, detsig)); BOOST_CHECK(key2C.SignCompact(hashMsg, detsigc)); BOOST_CHECK(detsig == ParseHex("1c9ffc56b38fbfc0e3eb2c42dff99d2375982449f35019c1b3d56" "ca62bef187c5103e483a0ad481eaacc224fef4ee2995027300d5f" "2457f7a20c43547aeddbae6e")); BOOST_CHECK(detsigc == ParseHex("209ffc56b38fbfc0e3eb2c42dff99d2375982449f35019c1b3d56" "ca62bef187c5103e483a0ad481eaacc224fef4ee2995027300d5f" "2457f7a20c43547aeddbae6e")); + // Schnorr + BOOST_CHECK(key1.SignSchnorr(hashMsg, detsig)); + BOOST_CHECK(key1C.SignSchnorr(hashMsg, detsigc)); + BOOST_CHECK(detsig == detsigc); + BOOST_CHECK(detsig == + ParseHex("2c56731ac2f7a7e7f11518fc7722a166b02438924ca9d8b4d1113" + "47b81d0717571846de67ad3d913a8fdf9d8f3f73161a4c48ae81c" + "b183b214765feb86e255ce")); + BOOST_CHECK(key2.SignSchnorr(hashMsg, detsig)); + BOOST_CHECK(key2C.SignSchnorr(hashMsg, detsigc)); + BOOST_CHECK(detsig == detsigc); + BOOST_CHECK(detsig == + ParseHex("e7167ae0afbba6019b4c7fcfe6de79165d555e8295bd72da1b8aa" + "1a5b54305880517cace1bcb0cb515e2eeaffd49f1e4dd49fd7282" + "6b4b1573c84da49a38405d")); } BOOST_AUTO_TEST_SUITE_END()