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