Skip to content

Commit 3f82659

Browse files
author
MarcoFalke
committed
Merge #17972: tests: Add fuzzing harness for CKey and key related functions
f4691b6 tests: Add fuzzing harness for CKey related functions (practicalswift) Pull request description: Add fuzzing harness for `CKey` and key related functions. **How to test this PR** ``` $ make distclean $ ./autogen.sh $ CC=clang CXX=clang++ ./configure --enable-fuzz \ --with-sanitizers=address,fuzzer,undefined $ make $ src/test/fuzz/key … #4096 pulse cov: 5736 ft: 6960 corp: 27/833b lim: 67 exec/s: 2048 rss: 122Mb #8192 pulse cov: 5736 ft: 6960 corp: 27/833b lim: 103 exec/s: 2048 rss: 143Mb #13067 NEW cov: 5736 ft: 6965 corp: 28/865b lim: 154 exec/s: 2177 rss: 166Mb L: 32/32 MS: 1 ChangeBit- #16384 pulse cov: 5736 ft: 6965 corp: 28/865b lim: 182 exec/s: 2048 rss: 181Mb #32768 pulse cov: 5736 ft: 6965 corp: 28/865b lim: 347 exec/s: 2184 rss: 258Mb … ``` Top commit has no ACKs. Tree-SHA512: 5b17ffb70c31966d3eac06d2258c127ae671d28d6cdf4e6ac20b45cd59ad32f80952c9c749930b97d317c72d5f840a3b75d466fd28fb6c351424a72c3e41bcbc
2 parents a2b5aae + f4691b6 commit 3f82659

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
@@ -35,6 +35,7 @@ FUZZ_TARGETS = \
3535
test/fuzz/hex \
3636
test/fuzz/integer \
3737
test/fuzz/inv_deserialize \
38+
test/fuzz/key \
3839
test/fuzz/key_origin_info_deserialize \
3940
test/fuzz/merkle_block_deserialize \
4041
test/fuzz/messageheader_deserialize \
@@ -430,6 +431,12 @@ test_fuzz_inv_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
430431
test_fuzz_inv_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
431432
test_fuzz_inv_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp
432433

434+
test_fuzz_key_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
435+
test_fuzz_key_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
436+
test_fuzz_key_LDADD = $(FUZZ_SUITE_LD_COMMON)
437+
test_fuzz_key_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
438+
test_fuzz_key_SOURCES = $(FUZZ_SUITE) test/fuzz/key.cpp
439+
433440
test_fuzz_key_origin_info_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DKEY_ORIGIN_INFO_DESERIALIZE=1
434441
test_fuzz_key_origin_info_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
435442
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
@@ -28,6 +28,7 @@
2828
"float",
2929
"hex",
3030
"integer",
31+
"key",
3132
"key_origin_info_deserialize",
3233
"merkle_block_deserialize",
3334
"out_point_deserialize",

0 commit comments

Comments
 (0)