Branch data Line data Source code
1 : : // Copyright (c) 2011-2022 The Bitcoin Core developers 2 : : // Distributed under the MIT software license, see the accompanying 3 : 0 : // file COPYING or http://www.opensource.org/licenses/mit-license.php. 4 : : 5 : : #include <test/data/key_io_invalid.json.h> 6 : : #include <test/data/key_io_valid.json.h> 7 : : 8 : : #include <key.h> 9 : : #include <key_io.h> 10 : : #include <script/script.h> 11 : : #include <test/util/json.h> 12 : : #include <test/util/setup_common.h> 13 : : #include <util/chaintype.h> 14 : : #include <util/strencodings.h> 15 : : 16 : : #include <boost/test/unit_test.hpp> 17 : : 18 : : #include <univalue.h> 19 : : 20 : 0 : BOOST_FIXTURE_TEST_SUITE(key_io_tests, BasicTestingSetup) 21 : : 22 : : // Goal: check that parsed keys match test payload 23 : 0 : BOOST_AUTO_TEST_CASE(key_io_valid_parse) 24 : : { 25 : 0 : UniValue tests = read_json(json_tests::key_io_valid); 26 : 0 : CKey privkey; 27 : 0 : CTxDestination destination; 28 : 0 : SelectParams(ChainType::MAIN); 29 : : 30 : 0 : for (unsigned int idx = 0; idx < tests.size(); idx++) { 31 : 0 : const UniValue& test = tests[idx]; 32 : 0 : std::string strTest = test.write(); 33 : 0 : if (test.size() < 3) { // Allow for extra stuff (useful for comments) 34 : 0 : BOOST_ERROR("Bad test: " << strTest); 35 : 0 : continue; 36 : : } 37 : 0 : std::string exp_base58string = test[0].get_str(); 38 : 0 : const std::vector<std::byte> exp_payload{ParseHex<std::byte>(test[1].get_str())}; 39 : 0 : const UniValue &metadata = test[2].get_obj(); 40 : 0 : bool isPrivkey = metadata.find_value("isPrivkey").get_bool(); 41 : 0 : SelectParams(ChainTypeFromString(metadata.find_value("chain").get_str()).value()); 42 : 0 : bool try_case_flip = metadata.find_value("tryCaseFlip").isNull() ? false : metadata.find_value("tryCaseFlip").get_bool(); 43 : 0 : if (isPrivkey) { 44 : 0 : bool isCompressed = metadata.find_value("isCompressed").get_bool(); 45 : : // Must be valid private key 46 : 0 : privkey = DecodeSecret(exp_base58string); 47 : 0 : BOOST_CHECK_MESSAGE(privkey.IsValid(), "!IsValid:" + strTest); 48 : 0 : BOOST_CHECK_MESSAGE(privkey.IsCompressed() == isCompressed, "compressed mismatch:" + strTest); 49 : 0 : BOOST_CHECK_MESSAGE(Span{privkey} == Span{exp_payload}, "key mismatch:" + strTest); 50 : : 51 : : // Private key must be invalid public key 52 : 0 : destination = DecodeDestination(exp_base58string); 53 : 0 : BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid privkey as pubkey:" + strTest); 54 : 0 : } else { 55 : : // Must be valid public key 56 : 0 : destination = DecodeDestination(exp_base58string); 57 : 0 : CScript script = GetScriptForDestination(destination); 58 : 0 : BOOST_CHECK_MESSAGE(IsValidDestination(destination), "!IsValid:" + strTest); 59 : 0 : BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload)); 60 : : 61 : : // Try flipped case version 62 : 0 : for (char& c : exp_base58string) { 63 : 0 : if (c >= 'a' && c <= 'z') { 64 : 0 : c = (c - 'a') + 'A'; 65 : 0 : } else if (c >= 'A' && c <= 'Z') { 66 : 0 : c = (c - 'A') + 'a'; 67 : 0 : } 68 : : } 69 : 0 : destination = DecodeDestination(exp_base58string); 70 : 0 : BOOST_CHECK_MESSAGE(IsValidDestination(destination) == try_case_flip, "!IsValid case flipped:" + strTest); 71 : 0 : if (IsValidDestination(destination)) { 72 : 0 : script = GetScriptForDestination(destination); 73 : 0 : BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload)); 74 : 0 : } 75 : : 76 : : // Public key must be invalid private key 77 : 0 : privkey = DecodeSecret(exp_base58string); 78 : 0 : BOOST_CHECK_MESSAGE(!privkey.IsValid(), "IsValid pubkey as privkey:" + strTest); 79 : 0 : } 80 : 0 : } 81 : 0 : } 82 : : 83 : : // Goal: check that generated keys match test vectors 84 : 0 : BOOST_AUTO_TEST_CASE(key_io_valid_gen) 85 : : { 86 : 0 : UniValue tests = read_json(json_tests::key_io_valid); 87 : : 88 : 0 : for (unsigned int idx = 0; idx < tests.size(); idx++) { 89 : 0 : const UniValue& test = tests[idx]; 90 : 0 : std::string strTest = test.write(); 91 : 0 : if (test.size() < 3) // Allow for extra stuff (useful for comments) 92 : : { 93 : 0 : BOOST_ERROR("Bad test: " << strTest); 94 : 0 : continue; 95 : : } 96 : 0 : std::string exp_base58string = test[0].get_str(); 97 : 0 : std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str()); 98 : 0 : const UniValue &metadata = test[2].get_obj(); 99 : 0 : bool isPrivkey = metadata.find_value("isPrivkey").get_bool(); 100 : 0 : SelectParams(ChainTypeFromString(metadata.find_value("chain").get_str()).value()); 101 : 0 : if (isPrivkey) { 102 : 0 : bool isCompressed = metadata.find_value("isCompressed").get_bool(); 103 : 0 : CKey key; 104 : 0 : key.Set(exp_payload.begin(), exp_payload.end(), isCompressed); 105 : 0 : assert(key.IsValid()); 106 : 0 : BOOST_CHECK_MESSAGE(EncodeSecret(key) == exp_base58string, "result mismatch: " + strTest); 107 : 0 : } else { 108 : 0 : CTxDestination dest; 109 : 0 : CScript exp_script(exp_payload.begin(), exp_payload.end()); 110 : 0 : BOOST_CHECK(ExtractDestination(exp_script, dest)); 111 : 0 : std::string address = EncodeDestination(dest); 112 : : 113 : 0 : BOOST_CHECK_EQUAL(address, exp_base58string); 114 : 0 : } 115 : 0 : } 116 : : 117 : 0 : SelectParams(ChainType::MAIN); 118 : 0 : } 119 : : 120 : : 121 : : // Goal: check that base58 parsing code is robust against a variety of corrupted data 122 : 0 : BOOST_AUTO_TEST_CASE(key_io_invalid) 123 : : { 124 : 0 : UniValue tests = read_json(json_tests::key_io_invalid); // Negative testcases 125 : 0 : CKey privkey; 126 : 0 : CTxDestination destination; 127 : : 128 : 0 : for (unsigned int idx = 0; idx < tests.size(); idx++) { 129 : 0 : const UniValue& test = tests[idx]; 130 : 0 : std::string strTest = test.write(); 131 : 0 : if (test.size() < 1) // Allow for extra stuff (useful for comments) 132 : : { 133 : 0 : BOOST_ERROR("Bad test: " << strTest); 134 : 0 : continue; 135 : : } 136 : 0 : std::string exp_base58string = test[0].get_str(); 137 : : 138 : : // must be invalid as public and as private key 139 : 0 : for (const auto& chain : {ChainType::MAIN, ChainType::TESTNET, ChainType::SIGNET, ChainType::REGTEST}) { 140 : 0 : SelectParams(chain); 141 : 0 : destination = DecodeDestination(exp_base58string); 142 : 0 : BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid pubkey in mainnet:" + strTest); 143 : 0 : privkey = DecodeSecret(exp_base58string); 144 : 0 : BOOST_CHECK_MESSAGE(!privkey.IsValid(), "IsValid privkey in mainnet:" + strTest); 145 : : } 146 : 0 : } 147 : 0 : } 148 : : 149 : 0 : BOOST_AUTO_TEST_SUITE_END()