diff --git a/src/key_io.cpp b/src/key_io.cpp index d0015cf51..52a2c371c 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -1,191 +1,199 @@ // Copyright (c) 2014-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_io.h> #include <base58.h> #include <cashaddrenc.h> #include <chainparams.h> #include <config.h> #include <util/strencodings.h> #include <boost/variant/apply_visitor.hpp> #include <boost/variant/static_visitor.hpp> #include <algorithm> #include <cassert> #include <cstring> namespace { class DestinationEncoder : public boost::static_visitor<std::string> { private: const CChainParams &m_params; public: explicit DestinationEncoder(const CChainParams ¶ms) : m_params(params) {} std::string operator()(const PKHash &id) const { std::vector<uint8_t> data = m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS); data.insert(data.end(), id.begin(), id.end()); return EncodeBase58Check(data); } std::string operator()(const ScriptHash &id) const { std::vector<uint8_t> data = m_params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); data.insert(data.end(), id.begin(), id.end()); return EncodeBase58Check(data); } std::string operator()(const CNoDestination &no) const { return {}; } }; CTxDestination DecodeLegacyDestination(const std::string &str, const CChainParams ¶ms) { std::vector<uint8_t> data; uint160 hash; if (!DecodeBase58Check(str, data, 21)) { return CNoDestination(); } // base58-encoded Bitcoin addresses. // Public-key-hash-addresses have version 0 (or 111 testnet). // The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is // the serialized public key. const std::vector<uint8_t> &pubkey_prefix = params.Base58Prefix(CChainParams::PUBKEY_ADDRESS); if (data.size() == hash.size() + pubkey_prefix.size() && std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin())) { std::copy(data.begin() + pubkey_prefix.size(), data.end(), hash.begin()); return PKHash(hash); } // Script-hash-addresses have version 5 (or 196 testnet). // The data vector contains RIPEMD160(SHA256(cscript)), where cscript is // the serialized redemption script. const std::vector<uint8_t> &script_prefix = params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); if (data.size() == hash.size() + script_prefix.size() && std::equal(script_prefix.begin(), script_prefix.end(), data.begin())) { std::copy(data.begin() + script_prefix.size(), data.end(), hash.begin()); return ScriptHash(hash); } return CNoDestination(); } } // namespace CKey DecodeSecret(const std::string &str) { + return DecodeSecret(str, Params()); +} + +CKey DecodeSecret(const std::string &str, const CChainParams ¶ms) { CKey key; std::vector<uint8_t> data; if (DecodeBase58Check(str, data, 34)) { const std::vector<uint8_t> &privkey_prefix = - Params().Base58Prefix(CChainParams::SECRET_KEY); + params.Base58Prefix(CChainParams::SECRET_KEY); if ((data.size() == 32 + privkey_prefix.size() || (data.size() == 33 + privkey_prefix.size() && data.back() == 1)) && std::equal(privkey_prefix.begin(), privkey_prefix.end(), data.begin())) { bool compressed = data.size() == 33 + privkey_prefix.size(); key.Set(data.begin() + privkey_prefix.size(), data.begin() + privkey_prefix.size() + 32, compressed); } } if (!data.empty()) { memory_cleanse(data.data(), data.size()); } return key; } std::string EncodeSecret(const CKey &key) { + return EncodeSecret(key, Params()); +} + +std::string EncodeSecret(const CKey &key, const CChainParams ¶ms) { assert(key.IsValid()); - std::vector<uint8_t> data = Params().Base58Prefix(CChainParams::SECRET_KEY); + std::vector<uint8_t> data = params.Base58Prefix(CChainParams::SECRET_KEY); data.insert(data.end(), key.begin(), key.end()); if (key.IsCompressed()) { data.push_back(1); } std::string ret = EncodeBase58Check(data); memory_cleanse(data.data(), data.size()); return ret; } CExtPubKey DecodeExtPubKey(const std::string &str) { CExtPubKey key; std::vector<uint8_t> data; if (DecodeBase58Check(str, data, 78)) { const std::vector<uint8_t> &prefix = Params().Base58Prefix(CChainParams::EXT_PUBLIC_KEY); if (data.size() == BIP32_EXTKEY_SIZE + prefix.size() && std::equal(prefix.begin(), prefix.end(), data.begin())) { key.Decode(data.data() + prefix.size()); } } return key; } std::string EncodeExtPubKey(const CExtPubKey &key) { std::vector<uint8_t> data = Params().Base58Prefix(CChainParams::EXT_PUBLIC_KEY); size_t size = data.size(); data.resize(size + BIP32_EXTKEY_SIZE); key.Encode(data.data() + size); std::string ret = EncodeBase58Check(data); return ret; } CExtKey DecodeExtKey(const std::string &str) { CExtKey key; std::vector<uint8_t> data; if (DecodeBase58Check(str, data, 78)) { const std::vector<uint8_t> &prefix = Params().Base58Prefix(CChainParams::EXT_SECRET_KEY); if (data.size() == BIP32_EXTKEY_SIZE + prefix.size() && std::equal(prefix.begin(), prefix.end(), data.begin())) { key.Decode(data.data() + prefix.size()); } } return key; } std::string EncodeExtKey(const CExtKey &key) { std::vector<uint8_t> data = Params().Base58Prefix(CChainParams::EXT_SECRET_KEY); size_t size = data.size(); data.resize(size + BIP32_EXTKEY_SIZE); key.Encode(data.data() + size); std::string ret = EncodeBase58Check(data); memory_cleanse(data.data(), data.size()); return ret; } std::string EncodeDestination(const CTxDestination &dest, const Config &config) { const CChainParams ¶ms = config.GetChainParams(); return config.UseCashAddrEncoding() ? EncodeCashAddr(dest, params) : EncodeLegacyAddr(dest, params); } CTxDestination DecodeDestination(const std::string &addr, const CChainParams ¶ms) { CTxDestination dst = DecodeCashAddr(addr, params); if (IsValidDestination(dst)) { return dst; } return DecodeLegacyAddr(addr, params); } bool IsValidDestinationString(const std::string &str, const CChainParams ¶ms) { return IsValidDestination(DecodeDestination(str, params)); } std::string EncodeLegacyAddr(const CTxDestination &dest, const CChainParams ¶ms) { return boost::apply_visitor(DestinationEncoder(params), dest); } CTxDestination DecodeLegacyAddr(const std::string &str, const CChainParams ¶ms) { return DecodeLegacyDestination(str, params); } diff --git a/src/key_io.h b/src/key_io.h index 9e56095b2..86468af67 100644 --- a/src/key_io.h +++ b/src/key_io.h @@ -1,36 +1,38 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-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. #ifndef BITCOIN_KEY_IO_H #define BITCOIN_KEY_IO_H #include <key.h> #include <pubkey.h> #include <script/standard.h> #include <string> class Config; class CChainParams; CKey DecodeSecret(const std::string &str); +CKey DecodeSecret(const std::string &str, const CChainParams ¶ms); std::string EncodeSecret(const CKey &key); +std::string EncodeSecret(const CKey &key, const CChainParams ¶ms); CExtKey DecodeExtKey(const std::string &str); std::string EncodeExtKey(const CExtKey &extkey); CExtPubKey DecodeExtPubKey(const std::string &str); std::string EncodeExtPubKey(const CExtPubKey &extpubkey); std::string EncodeDestination(const CTxDestination &dest, const Config &config); CTxDestination DecodeDestination(const std::string &addr, const CChainParams &); bool IsValidDestinationString(const std::string &str, const CChainParams ¶ms); std::string EncodeLegacyAddr(const CTxDestination &dest, const CChainParams ¶ms); CTxDestination DecodeLegacyAddr(const std::string &str, const CChainParams ¶ms); #endif // BITCOIN_KEY_IO_H diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp index d8a026b10..ef77b5746 100644 --- a/src/test/key_tests.cpp +++ b/src/test/key_tests.cpp @@ -1,366 +1,409 @@ // Copyright (c) 2012-2019 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 <chainparams.h> // For Params() #include <key_io.h> #include <uint256.h> #include <util/strencodings.h> #include <util/system.h> #include <test/util/setup_common.h> #include <boost/test/unit_test.hpp> #include <string> #include <vector> static const std::string strSecret1 = "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj"; static const std::string strSecret2 = "5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3"; static const std::string strSecret1C = "Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw"; +static const std::string strTestSecret1C = + "cND2ZvtabDbJ1gucx9GWH6XT9kgTAqfb6cotPt5Q5CyxVDhid2EN"; 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) static std::vector<uint8_t> get_r_ECDSA(std::vector<uint8_t> sigECDSA) { std::vector<uint8_t> 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() + (4 + rlen), 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(encode_decode_secret_test) { + const auto mainParams = CreateChainParams(CBaseChainParams::MAIN); + const auto testParams = CreateChainParams(CBaseChainParams::TESTNET); + const auto regParams = CreateChainParams(CBaseChainParams::TESTNET); + + { + // Check the mainnet base58 key + CKey mainKey = DecodeSecret(strSecret1C, *mainParams); + BOOST_CHECK(mainKey.IsValid() && mainKey.IsCompressed()); + + CKey testKey = DecodeSecret(strSecret1C, *testParams); + BOOST_CHECK(!testKey.IsValid()); + + CKey regKey = DecodeSecret(strSecret1C, *regParams); + BOOST_CHECK(!regKey.IsValid()); + } + + { + // Check the testnet and regnet base58 key + CKey mainKey = DecodeSecret(strTestSecret1C, *mainParams); + BOOST_CHECK(!mainKey.IsValid()); + + CKey testKey = DecodeSecret(strTestSecret1C, *testParams); + BOOST_CHECK(testKey.IsValid() && testKey.IsCompressed()); + + CKey regKey = DecodeSecret(strTestSecret1C, *regParams); + BOOST_CHECK(regKey.IsValid() && regKey.IsCompressed()); + } + + CKey mainKey = DecodeSecret(strSecret1C, *mainParams); + CKey testKey = DecodeSecret(strTestSecret1C, *testParams); + + // Check key conversion. + BOOST_CHECK_EQUAL(EncodeSecret(mainKey, *mainParams), strSecret1C); + BOOST_CHECK_EQUAL(EncodeSecret(mainKey, *testParams), strTestSecret1C); + BOOST_CHECK_EQUAL(EncodeSecret(mainKey, *regParams), strTestSecret1C); + BOOST_CHECK_EQUAL(EncodeSecret(testKey, *mainParams), strSecret1C); + BOOST_CHECK_EQUAL(EncodeSecret(testKey, *testParams), strTestSecret1C); + BOOST_CHECK_EQUAL(EncodeSecret(testKey, *regParams), strTestSecret1C); +} + BOOST_AUTO_TEST_CASE(key_test1) { CKey key1 = DecodeSecret(strSecret1); BOOST_CHECK(key1.IsValid() && !key1.IsCompressed()); CKey key2 = DecodeSecret(strSecret2); BOOST_CHECK(key2.IsValid() && !key2.IsCompressed()); CKey key1C = DecodeSecret(strSecret1C); BOOST_CHECK(key1C.IsValid() && key1C.IsCompressed()); CKey key2C = DecodeSecret(strSecret2C); BOOST_CHECK(key2C.IsValid() && key2C.IsCompressed()); CKey bad_key = DecodeSecret(strAddressBad); BOOST_CHECK(!bad_key.IsValid()); 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(PKHash(pubkey1))); BOOST_CHECK(DecodeDestination(addr2, chainParams) == CTxDestination(PKHash(pubkey2))); BOOST_CHECK(DecodeDestination(addr1C, chainParams) == CTxDestination(PKHash(pubkey1C))); BOOST_CHECK(DecodeDestination(addr2C, chainParams) == CTxDestination(PKHash(pubkey2C))); 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 ECDSA signatures std::vector<uint8_t> 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 ECDSA signatures (with key recovery) std::vector<uint8_t> 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<uint8_t> 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<uint8_t> rE1 = get_r_ECDSA(sign1); BOOST_CHECK(ssign1.size() == 64); std::vector<uint8_t> rS1(ssign1.begin(), ssign1.begin() + 32); BOOST_CHECK(rE1.size() == 32); BOOST_CHECK(rS1.size() == 32); BOOST_CHECK(rE1 != rS1); std::vector<uint8_t> rE2 = get_r_ECDSA(sign2); BOOST_CHECK(ssign2.size() == 64); std::vector<uint8_t> rS2(ssign2.begin(), ssign2.begin() + 32); BOOST_CHECK(rE2.size() == 32); BOOST_CHECK(rS2.size() == 32); BOOST_CHECK(rE2 != rS2); } // test deterministic signing expected values std::vector<uint8_t> 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("304402200c648ad9936cae4006f0b0d7bcbacdcdf5a14260eb550" "c31ddb1eb1a13b1b58602201b868673bb5926d1610a07cd03692d" "fdcb98ed059314f66b457a794f2c4b8e79")); BOOST_CHECK(key2.SignECDSA(hashMsg, detsig)); BOOST_CHECK(key2C.SignECDSA(hashMsg, detsigc)); BOOST_CHECK(detsig == detsigc); BOOST_CHECK(detsig == ParseHex("304402205fb8ff5dbba6110d877169812f5cd939866d9487c6b62" "5785c6876e4fb8ea69a0220711dc4ecff142f7f808905c04bbd41" "89e3d2c689c4be396ed22883c463d6ad7a")); // 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_CASE(key_signature_tests) { // When entropy is specified, we should see at least one high R signature // within 20 signatures CKey key = DecodeSecret(strSecret1); std::string msg = "A message to be signed"; uint256 msg_hash = Hash(msg.begin(), msg.end()); std::vector<uint8_t> sig; bool found = false; for (int i = 1; i <= 20; ++i) { sig.clear(); BOOST_CHECK(key.SignECDSA(msg_hash, sig, false, i)); found = sig[3] == 0x21 && sig[4] == 0x00; if (found) { break; } } BOOST_CHECK(found); // When entropy is not specified, we should always see low R signatures that // are less than 70 bytes in 256 tries We should see at least one signature // that is less than 70 bytes. found = true; bool found_small = false; for (int i = 0; i < 256; ++i) { sig.clear(); msg = "A message to be signed" + std::to_string(i); msg_hash = Hash(msg.begin(), msg.end()); BOOST_CHECK(key.SignECDSA(msg_hash, sig)); found = sig[3] == 0x20; BOOST_CHECK(sig.size() <= 70); found_small |= sig.size() < 70; } BOOST_CHECK(found); BOOST_CHECK(found_small); } BOOST_AUTO_TEST_CASE(key_key_negation) { // create a dummy hash for signature comparison 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()); // import the static test key CKey key = DecodeSecret(strSecret1C); // create a signature std::vector<uint8_t> vch_sig; std::vector<uint8_t> vch_sig_cmp; key.SignECDSA(hash, vch_sig); // negate the key twice BOOST_CHECK(key.GetPubKey().data()[0] == 0x03); key.Negate(); // after the first negation, the signature must be different key.SignECDSA(hash, vch_sig_cmp); BOOST_CHECK(vch_sig_cmp != vch_sig); BOOST_CHECK(key.GetPubKey().data()[0] == 0x02); key.Negate(); // after the second negation, we should have the original key and thus the // same signature key.SignECDSA(hash, vch_sig_cmp); BOOST_CHECK(vch_sig_cmp == vch_sig); BOOST_CHECK(key.GetPubKey().data()[0] == 0x03); } BOOST_AUTO_TEST_SUITE_END()