Skip to content

Commit f4691b6

Browse files
tests: Add fuzzing harness for CKey related functions
1 parent 631df3e commit f4691b6

File tree

3 files changed

+317
-0
lines changed

3 files changed

+317
-0
lines changed

src/Makefile.test.include

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ FUZZ_TARGETS = \
3232
test/fuzz/hex \
3333
test/fuzz/integer \
3434
test/fuzz/inv_deserialize \
35+
test/fuzz/key \
3536
test/fuzz/key_origin_info_deserialize \
3637
test/fuzz/merkle_block_deserialize \
3738
test/fuzz/messageheader_deserialize \
@@ -403,6 +404,12 @@ test_fuzz_inv_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
403404
test_fuzz_inv_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
404405
test_fuzz_inv_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
405406

407+
test_fuzz_key_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
408+
test_fuzz_key_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
409+
test_fuzz_key_LDADD = $(FUZZ_SUITE_LD_COMMON)
410+
test_fuzz_key_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
411+
test_fuzz_key_SOURCES = $(FUZZ_SUITE) test/fuzz/key.cpp
412+
406413
test_fuzz_key_origin_info_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DKEY_ORIGIN_INFO_DESERIALIZE=1
407414
test_fuzz_key_origin_info_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
408415
test_fuzz_key_origin_info_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)

src/test/fuzz/key.cpp

Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
// Copyright (c) 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 <chainparams.h>
6+
#include <chainparamsbase.h>
7+
#include <key.h>
8+
#include <key_io.h>
9+
#include <outputtype.h>
10+
#include <policy/policy.h>
11+
#include <pubkey.h>
12+
#include <rpc/util.h>
13+
#include <script/keyorigin.h>
14+
#include <script/script.h>
15+
#include <script/sign.h>
16+
#include <script/signingprovider.h>
17+
#include <script/standard.h>
18+
#include <streams.h>
19+
#include <test/fuzz/fuzz.h>
20+
#include <util/memory.h>
21+
#include <util/strencodings.h>
22+
23+
#include <cassert>
24+
#include <cstdint>
25+
#include <numeric>
26+
#include <string>
27+
#include <vector>
28+
29+
void initialize()
30+
{
31+
static const ECCVerifyHandle ecc_verify_handle;
32+
ECC_Start();
33+
SelectParams(CBaseChainParams::REGTEST);
34+
}
35+
36+
void test_one_input(const std::vector<uint8_t>& buffer)
37+
{
38+
const CKey key = [&] {
39+
CKey k;
40+
k.Set(buffer.begin(), buffer.end(), true);
41+
return k;
42+
}();
43+
if (!key.IsValid()) {
44+
return;
45+
}
46+
47+
{
48+
assert(key.begin() + key.size() == key.end());
49+
assert(key.IsCompressed());
50+
assert(key.size() == 32);
51+
assert(DecodeSecret(EncodeSecret(key)) == key);
52+
}
53+
54+
{
55+
CKey invalid_key;
56+
assert(!(invalid_key == key));
57+
assert(!invalid_key.IsCompressed());
58+
assert(!invalid_key.IsValid());
59+
assert(invalid_key.size() == 0);
60+
}
61+
62+
{
63+
CKey uncompressed_key;
64+
uncompressed_key.Set(buffer.begin(), buffer.end(), false);
65+
assert(!(uncompressed_key == key));
66+
assert(!uncompressed_key.IsCompressed());
67+
assert(key.size() == 32);
68+
assert(uncompressed_key.begin() + uncompressed_key.size() == uncompressed_key.end());
69+
assert(uncompressed_key.IsValid());
70+
}
71+
72+
{
73+
CKey copied_key;
74+
copied_key.Set(key.begin(), key.end(), key.IsCompressed());
75+
assert(copied_key == key);
76+
}
77+
78+
{
79+
CKey negated_key = key;
80+
negated_key.Negate();
81+
assert(negated_key.IsValid());
82+
assert(!(negated_key == key));
83+
84+
negated_key.Negate();
85+
assert(negated_key == key);
86+
}
87+
88+
const uint256 random_uint256 = Hash(buffer.begin(), buffer.end());
89+
90+
{
91+
CKey child_key;
92+
ChainCode child_chaincode;
93+
const bool ok = key.Derive(child_key, child_chaincode, 0, random_uint256);
94+
assert(ok);
95+
assert(child_key.IsValid());
96+
assert(!(child_key == key));
97+
assert(child_chaincode != random_uint256);
98+
}
99+
100+
const CPubKey pubkey = key.GetPubKey();
101+
102+
{
103+
assert(pubkey.size() == 33);
104+
assert(key.VerifyPubKey(pubkey));
105+
assert(pubkey.GetHash() != random_uint256);
106+
assert(pubkey.begin() + pubkey.size() == pubkey.end());
107+
assert(pubkey.data() == pubkey.begin());
108+
assert(pubkey.IsCompressed());
109+
assert(pubkey.IsValid());
110+
assert(pubkey.IsFullyValid());
111+
assert(HexToPubKey(HexStr(pubkey.begin(), pubkey.end())) == pubkey);
112+
assert(GetAllDestinationsForKey(pubkey).size() == 3);
113+
}
114+
115+
{
116+
CDataStream data_stream{SER_NETWORK, INIT_PROTO_VERSION};
117+
pubkey.Serialize(data_stream);
118+
119+
CPubKey pubkey_deserialized;
120+
pubkey_deserialized.Unserialize(data_stream);
121+
assert(pubkey_deserialized == pubkey);
122+
}
123+
124+
{
125+
const CScript tx_pubkey_script = GetScriptForRawPubKey(pubkey);
126+
assert(!tx_pubkey_script.IsPayToScriptHash());
127+
assert(!tx_pubkey_script.IsPayToWitnessScriptHash());
128+
assert(!tx_pubkey_script.IsPushOnly());
129+
assert(!tx_pubkey_script.IsUnspendable());
130+
assert(tx_pubkey_script.HasValidOps());
131+
assert(tx_pubkey_script.size() == 35);
132+
133+
const CScript tx_multisig_script = GetScriptForMultisig(1, {pubkey});
134+
assert(!tx_multisig_script.IsPayToScriptHash());
135+
assert(!tx_multisig_script.IsPayToWitnessScriptHash());
136+
assert(!tx_multisig_script.IsPushOnly());
137+
assert(!tx_multisig_script.IsUnspendable());
138+
assert(tx_multisig_script.HasValidOps());
139+
assert(tx_multisig_script.size() == 37);
140+
141+
FillableSigningProvider fillable_signing_provider;
142+
assert(IsSolvable(fillable_signing_provider, tx_pubkey_script));
143+
assert(IsSolvable(fillable_signing_provider, tx_multisig_script));
144+
assert(!IsSegWitOutput(fillable_signing_provider, tx_pubkey_script));
145+
assert(!IsSegWitOutput(fillable_signing_provider, tx_multisig_script));
146+
assert(fillable_signing_provider.GetKeys().size() == 0);
147+
assert(!fillable_signing_provider.HaveKey(pubkey.GetID()));
148+
149+
const bool ok_add_key = fillable_signing_provider.AddKey(key);
150+
assert(ok_add_key);
151+
assert(fillable_signing_provider.HaveKey(pubkey.GetID()));
152+
153+
FillableSigningProvider fillable_signing_provider_pub;
154+
assert(!fillable_signing_provider_pub.HaveKey(pubkey.GetID()));
155+
156+
const bool ok_add_key_pubkey = fillable_signing_provider_pub.AddKeyPubKey(key, pubkey);
157+
assert(ok_add_key_pubkey);
158+
assert(fillable_signing_provider_pub.HaveKey(pubkey.GetID()));
159+
160+
txnouttype which_type_tx_pubkey;
161+
const bool is_standard_tx_pubkey = IsStandard(tx_pubkey_script, which_type_tx_pubkey);
162+
assert(is_standard_tx_pubkey);
163+
assert(which_type_tx_pubkey == txnouttype::TX_PUBKEY);
164+
165+
txnouttype which_type_tx_multisig;
166+
const bool is_standard_tx_multisig = IsStandard(tx_multisig_script, which_type_tx_multisig);
167+
assert(is_standard_tx_multisig);
168+
assert(which_type_tx_multisig == txnouttype::TX_MULTISIG);
169+
170+
std::vector<std::vector<unsigned char>> v_solutions_ret_tx_pubkey;
171+
const txnouttype outtype_tx_pubkey = Solver(tx_pubkey_script, v_solutions_ret_tx_pubkey);
172+
assert(outtype_tx_pubkey == txnouttype::TX_PUBKEY);
173+
assert(v_solutions_ret_tx_pubkey.size() == 1);
174+
assert(v_solutions_ret_tx_pubkey[0].size() == 33);
175+
176+
std::vector<std::vector<unsigned char>> v_solutions_ret_tx_multisig;
177+
const txnouttype outtype_tx_multisig = Solver(tx_multisig_script, v_solutions_ret_tx_multisig);
178+
assert(outtype_tx_multisig == txnouttype::TX_MULTISIG);
179+
assert(v_solutions_ret_tx_multisig.size() == 3);
180+
assert(v_solutions_ret_tx_multisig[0].size() == 1);
181+
assert(v_solutions_ret_tx_multisig[1].size() == 33);
182+
assert(v_solutions_ret_tx_multisig[2].size() == 1);
183+
184+
OutputType output_type{};
185+
const CTxDestination tx_destination = GetDestinationForKey(pubkey, output_type);
186+
assert(output_type == OutputType::LEGACY);
187+
assert(IsValidDestination(tx_destination));
188+
assert(CTxDestination{PKHash{pubkey}} == tx_destination);
189+
190+
const CScript script_for_destination = GetScriptForDestination(tx_destination);
191+
assert(script_for_destination.size() == 25);
192+
193+
const std::string destination_address = EncodeDestination(tx_destination);
194+
assert(DecodeDestination(destination_address) == tx_destination);
195+
196+
const CPubKey pubkey_from_address_string = AddrToPubKey(fillable_signing_provider, destination_address);
197+
assert(pubkey_from_address_string == pubkey);
198+
199+
CKeyID key_id = pubkey.GetID();
200+
assert(!key_id.IsNull());
201+
assert(key_id == CKeyID{key_id});
202+
assert(key_id == GetKeyForDestination(fillable_signing_provider, tx_destination));
203+
204+
CPubKey pubkey_out;
205+
const bool ok_get_pubkey = fillable_signing_provider.GetPubKey(key_id, pubkey_out);
206+
assert(ok_get_pubkey);
207+
208+
CKey key_out;
209+
const bool ok_get_key = fillable_signing_provider.GetKey(key_id, key_out);
210+
assert(ok_get_key);
211+
assert(fillable_signing_provider.GetKeys().size() == 1);
212+
assert(fillable_signing_provider.HaveKey(key_id));
213+
214+
KeyOriginInfo key_origin_info;
215+
const bool ok_get_key_origin = fillable_signing_provider.GetKeyOrigin(key_id, key_origin_info);
216+
assert(!ok_get_key_origin);
217+
}
218+
219+
{
220+
const std::vector<unsigned char> vch_pubkey{pubkey.begin(), pubkey.end()};
221+
assert(CPubKey::ValidSize(vch_pubkey));
222+
assert(!CPubKey::ValidSize({pubkey.begin(), pubkey.begin() + pubkey.size() - 1}));
223+
224+
const CPubKey pubkey_ctor_1{vch_pubkey};
225+
assert(pubkey == pubkey_ctor_1);
226+
227+
const CPubKey pubkey_ctor_2{vch_pubkey.begin(), vch_pubkey.end()};
228+
assert(pubkey == pubkey_ctor_2);
229+
230+
CPubKey pubkey_set;
231+
pubkey_set.Set(vch_pubkey.begin(), vch_pubkey.end());
232+
assert(pubkey == pubkey_set);
233+
}
234+
235+
{
236+
const CPubKey invalid_pubkey{};
237+
assert(!invalid_pubkey.IsValid());
238+
assert(!invalid_pubkey.IsFullyValid());
239+
assert(!(pubkey == invalid_pubkey));
240+
assert(pubkey != invalid_pubkey);
241+
assert(pubkey < invalid_pubkey);
242+
}
243+
244+
{
245+
// Cover CPubKey's operator[](unsigned int pos)
246+
unsigned int sum = 0;
247+
for (size_t i = 0; i < pubkey.size(); ++i) {
248+
sum += pubkey[i];
249+
}
250+
assert(std::accumulate(pubkey.begin(), pubkey.end(), 0U) == sum);
251+
}
252+
253+
{
254+
CPubKey decompressed_pubkey = pubkey;
255+
assert(decompressed_pubkey.IsCompressed());
256+
257+
const bool ok = decompressed_pubkey.Decompress();
258+
assert(ok);
259+
assert(!decompressed_pubkey.IsCompressed());
260+
assert(decompressed_pubkey.size() == 65);
261+
}
262+
263+
{
264+
std::vector<unsigned char> vch_sig;
265+
const bool ok = key.Sign(random_uint256, vch_sig, false);
266+
assert(ok);
267+
assert(pubkey.Verify(random_uint256, vch_sig));
268+
assert(CPubKey::CheckLowS(vch_sig));
269+
270+
const std::vector<unsigned char> vch_invalid_sig{vch_sig.begin(), vch_sig.begin() + vch_sig.size() - 1};
271+
assert(!pubkey.Verify(random_uint256, vch_invalid_sig));
272+
assert(!CPubKey::CheckLowS(vch_invalid_sig));
273+
}
274+
275+
{
276+
std::vector<unsigned char> vch_compact_sig;
277+
const bool ok_sign_compact = key.SignCompact(random_uint256, vch_compact_sig);
278+
assert(ok_sign_compact);
279+
280+
CPubKey recover_pubkey;
281+
const bool ok_recover_compact = recover_pubkey.RecoverCompact(random_uint256, vch_compact_sig);
282+
assert(ok_recover_compact);
283+
assert(recover_pubkey == pubkey);
284+
}
285+
286+
{
287+
CPubKey child_pubkey;
288+
ChainCode child_chaincode;
289+
const bool ok = pubkey.Derive(child_pubkey, child_chaincode, 0, random_uint256);
290+
assert(ok);
291+
assert(child_pubkey != pubkey);
292+
assert(child_pubkey.IsCompressed());
293+
assert(child_pubkey.IsFullyValid());
294+
assert(child_pubkey.IsValid());
295+
assert(child_pubkey.size() == 33);
296+
assert(child_chaincode != random_uint256);
297+
}
298+
299+
const CPrivKey priv_key = key.GetPrivKey();
300+
301+
{
302+
for (const bool skip_check : {true, false}) {
303+
CKey loaded_key;
304+
const bool ok = loaded_key.Load(priv_key, pubkey, skip_check);
305+
assert(ok);
306+
assert(key == loaded_key);
307+
}
308+
}
309+
}

test/fuzz/test_runner.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"flat_file_pos_deserialize",
2626
"hex",
2727
"integer",
28+
"key",
2829
"key_origin_info_deserialize",
2930
"merkle_block_deserialize",
3031
"out_point_deserialize",

0 commit comments

Comments
 (0)