Line data Source code
1 : // Copyright (c) 2011-2014 The Bitcoin Core developers
2 : // Copyright (c) 2017-2022 The PIVX Core developers
3 : // Distributed under the MIT/X11 software license, see the accompanying
4 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 :
6 : #include "base58.h"
7 :
8 : #include "data/base58_encode_decode.json.h"
9 : #include "data/base58_keys_invalid.json.h"
10 : #include "data/base58_keys_valid.json.h"
11 :
12 : #include "key.h"
13 : #include "key_io.h"
14 : #include "uint256.h"
15 : #include "utilstrencodings.h"
16 : #include "test/test_pivx.h"
17 : #include "util/vector.h"
18 :
19 : #include <boost/test/unit_test.hpp>
20 :
21 : #include <univalue.h>
22 :
23 : extern UniValue read_json(const std::string& jsondata);
24 :
25 : BOOST_FIXTURE_TEST_SUITE(base58_tests, BasicTestingSetup)
26 :
27 : // Goal: test low-level base58 encoding functionality
28 2 : BOOST_AUTO_TEST_CASE(base58_EncodeBase58)
29 : {
30 3 : UniValue tests = read_json(std::string(json_tests::base58_encode_decode, json_tests::base58_encode_decode + sizeof(json_tests::base58_encode_decode)));
31 13 : for (unsigned int idx = 0; idx < tests.size(); idx++) {
32 12 : UniValue test = tests[idx];
33 24 : std::string strTest = test.write();
34 12 : if (test.size() < 2) // Allow for extra stuff (useful for comments)
35 : {
36 0 : BOOST_ERROR("Bad test: " << strTest);
37 0 : continue;
38 : }
39 24 : std::vector<unsigned char> sourcedata = ParseHex(test[0].get_str());
40 24 : std::string base58string = test[1].get_str();
41 26 : BOOST_CHECK_MESSAGE(
42 : EncodeBase58(sourcedata.data(), sourcedata.data() + sourcedata.size()) == base58string,
43 : strTest);
44 : }
45 1 : }
46 :
47 : // Goal: test low-level base58 decoding functionality
48 2 : BOOST_AUTO_TEST_CASE(base58_DecodeBase58)
49 : {
50 2 : UniValue tests = read_json(std::string(json_tests::base58_encode_decode, json_tests::base58_encode_decode + sizeof(json_tests::base58_encode_decode)));
51 2 : std::vector<unsigned char> result;
52 :
53 13 : for (unsigned int idx = 0; idx < tests.size(); idx++) {
54 12 : UniValue test = tests[idx];
55 24 : std::string strTest = test.write();
56 12 : if (test.size() < 2) // Allow for extra stuff (useful for comments)
57 : {
58 0 : BOOST_ERROR("Bad test: " << strTest);
59 0 : continue;
60 : }
61 24 : std::vector<unsigned char> expected = ParseHex(test[0].get_str());
62 24 : std::string base58string = test[1].get_str();
63 24 : BOOST_CHECK_MESSAGE(DecodeBase58(base58string, result, 256), strTest);
64 24 : BOOST_CHECK_MESSAGE(result.size() == expected.size() && std::equal(result.begin(), result.end(), expected.begin()), strTest);
65 : }
66 :
67 2 : BOOST_CHECK(!DecodeBase58("invalid", result, 100));
68 2 : BOOST_CHECK(!DecodeBase58(std::string("invalid"), result, 100));
69 3 : BOOST_CHECK(!DecodeBase58(std::string("\0invalid", 8), result, 100));
70 :
71 3 : BOOST_CHECK(DecodeBase58(std::string("good", 4), result, 100));
72 3 : BOOST_CHECK(!DecodeBase58(std::string("bad0IOl", 7), result, 100));
73 3 : BOOST_CHECK(!DecodeBase58(std::string("goodbad0IOl", 11), result, 100));
74 3 : BOOST_CHECK(!DecodeBase58(std::string("good\0bad0IOl", 12), result, 100));
75 :
76 : // check that DecodeBase58 skips whitespace, but still fails with unexpected non-whitespace at the end.
77 2 : BOOST_CHECK(!DecodeBase58(" \t\n\v\f\r skip \r\f\v\n\t a", result, 3));
78 2 : BOOST_CHECK( DecodeBase58(" \t\n\v\f\r skip \r\f\v\n\t ", result, 3));
79 2 : std::vector<unsigned char> expected = ParseHex("971a55");
80 2 : BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end());
81 :
82 4 : BOOST_CHECK(DecodeBase58Check(std::string("3vQB7B6MrGQZaxCuFg4oh", 21), result, 100));
83 4 : BOOST_CHECK(!DecodeBase58Check(std::string("3vQB7B6MrGQZaxCuFg4oi", 21), result, 100));
84 4 : BOOST_CHECK(!DecodeBase58Check(std::string("3vQB7B6MrGQZaxCuFg4oh0IOl", 25), result, 100));
85 4 : BOOST_CHECK(!DecodeBase58Check(std::string("3vQB7B6MrGQZaxCuFg4oh\00IOl", 26), result, 100));
86 1 : }
87 :
88 : // Visitor to check address type
89 26 : class TestAddrTypeVisitor : public boost::static_visitor<bool>
90 : {
91 : private:
92 : std::string exp_addrType;
93 : public:
94 26 : explicit TestAddrTypeVisitor(const std::string &exp_addrType) : exp_addrType(exp_addrType) { }
95 13 : bool operator()(const CKeyID &id) const
96 : {
97 13 : return (exp_addrType == "pubkey");
98 : }
99 0 : bool operator()(const CExchangeKeyID &id) const
100 : {
101 0 : return (exp_addrType == "exchangepubkey");
102 : }
103 13 : bool operator()(const CScriptID &id) const
104 : {
105 13 : return (exp_addrType == "script");
106 : }
107 0 : bool operator()(const CNoDestination &no) const
108 : {
109 0 : return (exp_addrType == "none");
110 : }
111 : };
112 :
113 : // Visitor to check address payload
114 : class TestPayloadVisitor : public boost::static_visitor<bool>
115 : {
116 : private:
117 : std::vector<unsigned char> exp_payload;
118 : public:
119 : explicit TestPayloadVisitor(std::vector<unsigned char> &exp_payload) : exp_payload(exp_payload) { }
120 : bool operator()(const CKeyID &id) const
121 : {
122 : uint160 exp_key(exp_payload);
123 : return exp_key == id;
124 : }
125 : bool operator()(const CScriptID &id) const
126 : {
127 : uint160 exp_key(exp_payload);
128 : return exp_key == id;
129 : }
130 : bool operator()(const CNoDestination &no) const
131 : {
132 : return exp_payload.size() == 0;
133 : }
134 : };
135 :
136 : // Goal: check that parsed keys match test payload
137 2 : BOOST_AUTO_TEST_CASE(base58_keys_valid_parse)
138 : {
139 2 : UniValue tests = read_json(std::string(json_tests::base58_keys_valid, json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid)));
140 2 : std::vector<unsigned char> result;
141 2 : CKey privkey;
142 2 : CTxDestination destination;
143 1 : SelectParams(CBaseChainParams::MAIN);
144 :
145 51 : for (unsigned int idx = 0; idx < tests.size(); idx++) {
146 50 : UniValue test = tests[idx];
147 100 : std::string strTest = test.write();
148 50 : if (test.size() < 3) // Allow for extra stuff (useful for comments)
149 : {
150 0 : BOOST_ERROR("Bad test: " << strTest);
151 0 : continue;
152 : }
153 100 : std::string exp_base58string = test[0].get_str();
154 100 : std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str());
155 50 : const UniValue &metadata = test[2].get_obj();
156 50 : bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
157 50 : bool isTestnet = find_value(metadata, "isTestnet").get_bool();
158 50 : if (isTestnet)
159 24 : SelectParams(CBaseChainParams::TESTNET);
160 : else
161 26 : SelectParams(CBaseChainParams::MAIN);
162 50 : if(isPrivkey) {
163 24 : bool isCompressed = find_value(metadata, "isCompressed").get_bool();
164 : // Must be valid private key
165 48 : privkey = KeyIO::DecodeSecret(exp_base58string);
166 72 : BOOST_CHECK_MESSAGE(privkey.IsValid(), "!IsValid:" + strTest);
167 72 : BOOST_CHECK_MESSAGE(privkey.IsCompressed() == isCompressed, "compressed mismatch:" + strTest);
168 96 : BOOST_CHECK_MESSAGE(privkey.size() == exp_payload.size() && std::equal(privkey.begin(), privkey.end(), exp_payload.begin()), "key mismatch:" + strTest);
169 :
170 : // Private key must be invalid public key
171 48 : destination = DecodeDestination(exp_base58string);
172 72 : BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid privkey as pubkey:" + strTest);
173 : } else {
174 78 : std::string exp_addrType = find_value(metadata, "addrType").get_str(); // "script" or "pubkey"
175 : // Must be valid public key
176 52 : destination = DecodeDestination(exp_base58string);
177 78 : BOOST_CHECK_MESSAGE(IsValidDestination(destination), "!IsValid:" + strTest);
178 78 : BOOST_CHECK_MESSAGE((boost::get<CScriptID>(&destination) != nullptr) == (exp_addrType == "script"), "isScript mismatch" + strTest);
179 104 : BOOST_CHECK_MESSAGE(boost::apply_visitor(TestAddrTypeVisitor(exp_addrType), destination), "addrType mismatch" + strTest);
180 :
181 : // Public key must be invalid private key
182 52 : privkey = KeyIO::DecodeSecret(exp_base58string);
183 78 : BOOST_CHECK_MESSAGE(!privkey.IsValid(), "IsValid pubkey as privkey:" + strTest);
184 : }
185 : }
186 1 : }
187 :
188 : // Goal: check that generated keys match test vectors
189 2 : BOOST_AUTO_TEST_CASE(base58_keys_valid_gen)
190 : {
191 2 : UniValue tests = read_json(std::string(json_tests::base58_keys_valid, json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid)));
192 2 : std::vector<unsigned char> result;
193 :
194 51 : for (unsigned int idx = 0; idx < tests.size(); idx++) {
195 50 : UniValue test = tests[idx];
196 100 : std::string strTest = test.write();
197 50 : if (test.size() < 3) // Allow for extra stuff (useful for comments)
198 : {
199 0 : BOOST_ERROR("Bad test: " << strTest);
200 0 : continue;
201 : }
202 100 : std::string exp_base58string = test[0].get_str();
203 100 : std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str());
204 50 : const UniValue &metadata = test[2].get_obj();
205 50 : bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
206 50 : bool isTestnet = find_value(metadata, "isTestnet").get_bool();
207 50 : if (isTestnet)
208 24 : SelectParams(CBaseChainParams::TESTNET);
209 : else
210 26 : SelectParams(CBaseChainParams::MAIN);
211 50 : if(isPrivkey)
212 : {
213 24 : bool isCompressed = find_value(metadata, "isCompressed").get_bool();
214 48 : CKey key;
215 24 : key.Set(exp_payload.begin(), exp_payload.end(), isCompressed);
216 24 : assert(key.IsValid());
217 96 : BOOST_CHECK_MESSAGE(KeyIO::EncodeSecret(key) == exp_base58string, "result mismatch: " + strTest);
218 : }
219 : else
220 : {
221 78 : std::string exp_addrType = find_value(metadata, "addrType").get_str();
222 52 : CTxDestination dest;
223 26 : if(exp_addrType == "pubkey")
224 : {
225 13 : dest = CKeyID(uint160(exp_payload));
226 : }
227 13 : else if(exp_addrType == "script")
228 : {
229 13 : dest = CScriptID(uint160(exp_payload));
230 : }
231 0 : else if(exp_addrType == "none")
232 : {
233 0 : dest = CNoDestination();
234 : }
235 : else
236 : {
237 0 : BOOST_ERROR("Bad addrtype: " << strTest);
238 0 : continue;
239 : }
240 52 : std::string address = EncodeDestination(dest);
241 78 : BOOST_CHECK_MESSAGE(address == exp_base58string, "mismatch: " + strTest);
242 : }
243 : }
244 :
245 1 : SelectParams(CBaseChainParams::MAIN);
246 1 : }
247 :
248 : // Goal: check that base58 parsing code is robust against a variety of corrupted data
249 2 : BOOST_AUTO_TEST_CASE(base58_keys_invalid)
250 : {
251 2 : UniValue tests = read_json(std::string(json_tests::base58_keys_invalid, json_tests::base58_keys_invalid + sizeof(json_tests::base58_keys_invalid))); // Negative testcases
252 2 : std::vector<unsigned char> result;
253 2 : CKey privkey;
254 2 : CTxDestination destination;
255 :
256 51 : for (unsigned int idx = 0; idx < tests.size(); idx++) {
257 50 : UniValue test = tests[idx];
258 100 : std::string strTest = test.write();
259 50 : if (test.size() < 1) // Allow for extra stuff (useful for comments)
260 : {
261 0 : BOOST_ERROR("Bad test: " << strTest);
262 0 : continue;
263 : }
264 100 : std::string exp_base58string = test[0].get_str();
265 :
266 : // must be invalid as public and as private key
267 100 : destination = DecodeDestination(exp_base58string);
268 150 : BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid pubkey:" + strTest);
269 100 : privkey = KeyIO::DecodeSecret(exp_base58string);
270 150 : BOOST_CHECK_MESSAGE(!privkey.IsValid(), "IsValid privkey:" + strTest);
271 : }
272 1 : }
273 :
274 2 : BOOST_AUTO_TEST_CASE(base58_random_encode_decode)
275 : {
276 1001 : for (int n = 0; n < 1000; ++n) {
277 1000 : unsigned int len = 1 + InsecureRandBits(8);
278 1000 : unsigned int zeroes = InsecureRandBool() ? InsecureRandRange(len + 1) : 0;
279 3000 : auto data = Cat(std::vector<unsigned char>(zeroes, '\000'), g_insecure_rand_ctx.randbytes(len - zeroes));
280 2000 : auto encoded = EncodeBase58Check(data);
281 2000 : std::vector<unsigned char> decoded;
282 1000 : auto ok_too_small = DecodeBase58Check(encoded, decoded, InsecureRandRange(len));
283 2000 : BOOST_CHECK(!ok_too_small);
284 1000 : auto ok = DecodeBase58Check(encoded, decoded, len + InsecureRandRange(257 - len));
285 2000 : BOOST_CHECK(ok);
286 2000 : BOOST_CHECK(data == decoded);
287 : }
288 1 : }
289 :
290 : BOOST_AUTO_TEST_SUITE_END()
291 :
|