Coverage Report

Created: 2025-02-21 14:37

/root/bitcoin/src/test/fuzz/key.cpp
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) 2020-2022 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 <key.h>
7
#include <key_io.h>
8
#include <outputtype.h>
9
#include <policy/policy.h>
10
#include <pubkey.h>
11
#include <rpc/util.h>
12
#include <script/keyorigin.h>
13
#include <script/script.h>
14
#include <script/sign.h>
15
#include <script/signingprovider.h>
16
#include <script/solver.h>
17
#include <streams.h>
18
#include <test/fuzz/FuzzedDataProvider.h>
19
#include <test/fuzz/fuzz.h>
20
#include <test/fuzz/util.h>
21
#include <test/util/random.h>
22
#include <util/chaintype.h>
23
#include <util/strencodings.h>
24
25
#include <array>
26
#include <cassert>
27
#include <cstddef>
28
#include <cstdint>
29
#include <numeric>
30
#include <optional>
31
#include <string>
32
#include <vector>
33
34
void initialize_key()
35
0
{
36
0
    static ECC_Context ecc_context{};
37
0
    SelectParams(ChainType::REGTEST);
38
0
}
39
40
FUZZ_TARGET(key, .init = initialize_key)
41
0
{
42
0
    SeedRandomStateForTest(SeedRand::ZEROS);
43
0
    const CKey key = [&] {
44
0
        CKey k;
45
0
        k.Set(buffer.begin(), buffer.end(), true);
46
0
        return k;
47
0
    }();
48
0
    if (!key.IsValid()) {
49
0
        return;
50
0
    }
51
52
0
    {
53
0
        assert(key.begin() + key.size() == key.end());
54
0
        assert(key.IsCompressed());
55
0
        assert(key.size() == 32);
56
0
        assert(DecodeSecret(EncodeSecret(key)) == key);
57
0
    }
58
59
0
    {
60
0
        CKey invalid_key;
61
0
        assert(!(invalid_key == key));
62
0
        assert(!invalid_key.IsCompressed());
63
0
        assert(!invalid_key.IsValid());
64
0
        assert(invalid_key.size() == 0);
65
0
    }
66
67
0
    {
68
0
        CKey uncompressed_key;
69
0
        uncompressed_key.Set(buffer.begin(), buffer.end(), false);
70
0
        assert(!(uncompressed_key == key));
71
0
        assert(!uncompressed_key.IsCompressed());
72
0
        assert(key.size() == 32);
73
0
        assert(uncompressed_key.begin() + uncompressed_key.size() == uncompressed_key.end());
74
0
        assert(uncompressed_key.IsValid());
75
0
    }
76
77
0
    {
78
0
        CKey copied_key;
79
0
        copied_key.Set(key.begin(), key.end(), key.IsCompressed());
80
0
        assert(copied_key == key);
81
0
    }
82
83
0
    const uint256 random_uint256 = Hash(buffer);
84
85
0
    {
86
0
        CKey child_key;
87
0
        ChainCode child_chaincode;
88
0
        const bool ok = key.Derive(child_key, child_chaincode, 0, random_uint256);
89
0
        assert(ok);
90
0
        assert(child_key.IsValid());
91
0
        assert(!(child_key == key));
92
0
        assert(child_chaincode != random_uint256);
93
0
    }
94
95
0
    const CPubKey pubkey = key.GetPubKey();
96
97
0
    {
98
0
        assert(pubkey.size() == 33);
99
0
        assert(key.VerifyPubKey(pubkey));
100
0
        assert(pubkey.GetHash() != random_uint256);
101
0
        assert(pubkey.begin() + pubkey.size() == pubkey.end());
102
0
        assert(pubkey.data() == pubkey.begin());
103
0
        assert(pubkey.IsCompressed());
104
0
        assert(pubkey.IsValid());
105
0
        assert(pubkey.IsFullyValid());
106
0
        assert(HexToPubKey(HexStr(pubkey)) == pubkey);
107
0
        assert(GetAllDestinationsForKey(pubkey).size() == 3);
108
0
    }
109
110
0
    {
111
0
        DataStream data_stream{};
112
0
        pubkey.Serialize(data_stream);
113
114
0
        CPubKey pubkey_deserialized;
115
0
        pubkey_deserialized.Unserialize(data_stream);
116
0
        assert(pubkey_deserialized == pubkey);
117
0
    }
118
119
0
    {
120
0
        const CScript tx_pubkey_script = GetScriptForRawPubKey(pubkey);
121
0
        assert(!tx_pubkey_script.IsPayToScriptHash());
122
0
        assert(!tx_pubkey_script.IsPayToWitnessScriptHash());
123
0
        assert(!tx_pubkey_script.IsPushOnly());
124
0
        assert(!tx_pubkey_script.IsUnspendable());
125
0
        assert(tx_pubkey_script.HasValidOps());
126
0
        assert(tx_pubkey_script.size() == 35);
127
128
0
        const CScript tx_multisig_script = GetScriptForMultisig(1, {pubkey});
129
0
        assert(!tx_multisig_script.IsPayToScriptHash());
130
0
        assert(!tx_multisig_script.IsPayToWitnessScriptHash());
131
0
        assert(!tx_multisig_script.IsPushOnly());
132
0
        assert(!tx_multisig_script.IsUnspendable());
133
0
        assert(tx_multisig_script.HasValidOps());
134
0
        assert(tx_multisig_script.size() == 37);
135
136
0
        FillableSigningProvider fillable_signing_provider;
137
0
        assert(!IsSegWitOutput(fillable_signing_provider, tx_pubkey_script));
138
0
        assert(!IsSegWitOutput(fillable_signing_provider, tx_multisig_script));
139
0
        assert(fillable_signing_provider.GetKeys().size() == 0);
140
0
        assert(!fillable_signing_provider.HaveKey(pubkey.GetID()));
141
142
0
        const bool ok_add_key = fillable_signing_provider.AddKey(key);
143
0
        assert(ok_add_key);
144
0
        assert(fillable_signing_provider.HaveKey(pubkey.GetID()));
145
146
0
        FillableSigningProvider fillable_signing_provider_pub;
147
0
        assert(!fillable_signing_provider_pub.HaveKey(pubkey.GetID()));
148
149
0
        const bool ok_add_key_pubkey = fillable_signing_provider_pub.AddKeyPubKey(key, pubkey);
150
0
        assert(ok_add_key_pubkey);
151
0
        assert(fillable_signing_provider_pub.HaveKey(pubkey.GetID()));
152
153
0
        TxoutType which_type_tx_pubkey;
154
0
        const bool is_standard_tx_pubkey = IsStandard(tx_pubkey_script, std::nullopt, which_type_tx_pubkey);
155
0
        assert(is_standard_tx_pubkey);
156
0
        assert(which_type_tx_pubkey == TxoutType::PUBKEY);
157
158
0
        TxoutType which_type_tx_multisig;
159
0
        const bool is_standard_tx_multisig = IsStandard(tx_multisig_script, std::nullopt, which_type_tx_multisig);
160
0
        assert(is_standard_tx_multisig);
161
0
        assert(which_type_tx_multisig == TxoutType::MULTISIG);
162
163
0
        std::vector<std::vector<unsigned char>> v_solutions_ret_tx_pubkey;
164
0
        const TxoutType outtype_tx_pubkey = Solver(tx_pubkey_script, v_solutions_ret_tx_pubkey);
165
0
        assert(outtype_tx_pubkey == TxoutType::PUBKEY);
166
0
        assert(v_solutions_ret_tx_pubkey.size() == 1);
167
0
        assert(v_solutions_ret_tx_pubkey[0].size() == 33);
168
169
0
        std::vector<std::vector<unsigned char>> v_solutions_ret_tx_multisig;
170
0
        const TxoutType outtype_tx_multisig = Solver(tx_multisig_script, v_solutions_ret_tx_multisig);
171
0
        assert(outtype_tx_multisig == TxoutType::MULTISIG);
172
0
        assert(v_solutions_ret_tx_multisig.size() == 3);
173
0
        assert(v_solutions_ret_tx_multisig[0].size() == 1);
174
0
        assert(v_solutions_ret_tx_multisig[1].size() == 33);
175
0
        assert(v_solutions_ret_tx_multisig[2].size() == 1);
176
177
0
        OutputType output_type{};
178
0
        const CTxDestination tx_destination = GetDestinationForKey(pubkey, output_type);
179
0
        assert(output_type == OutputType::LEGACY);
180
0
        assert(IsValidDestination(tx_destination));
181
0
        assert(PKHash{pubkey} == *std::get_if<PKHash>(&tx_destination));
182
183
0
        const CScript script_for_destination = GetScriptForDestination(tx_destination);
184
0
        assert(script_for_destination.size() == 25);
185
186
0
        const std::string destination_address = EncodeDestination(tx_destination);
187
0
        assert(DecodeDestination(destination_address) == tx_destination);
188
189
0
        const CPubKey pubkey_from_address_string = AddrToPubKey(fillable_signing_provider, destination_address);
190
0
        assert(pubkey_from_address_string == pubkey);
191
192
0
        CKeyID key_id = pubkey.GetID();
193
0
        assert(!key_id.IsNull());
194
0
        assert(key_id == CKeyID{key_id});
195
0
        assert(key_id == GetKeyForDestination(fillable_signing_provider, tx_destination));
196
197
0
        CPubKey pubkey_out;
198
0
        const bool ok_get_pubkey = fillable_signing_provider.GetPubKey(key_id, pubkey_out);
199
0
        assert(ok_get_pubkey);
200
201
0
        CKey key_out;
202
0
        const bool ok_get_key = fillable_signing_provider.GetKey(key_id, key_out);
203
0
        assert(ok_get_key);
204
0
        assert(fillable_signing_provider.GetKeys().size() == 1);
205
0
        assert(fillable_signing_provider.HaveKey(key_id));
206
207
0
        KeyOriginInfo key_origin_info;
208
0
        const bool ok_get_key_origin = fillable_signing_provider.GetKeyOrigin(key_id, key_origin_info);
209
0
        assert(!ok_get_key_origin);
210
0
    }
211
212
0
    {
213
0
        const std::vector<unsigned char> vch_pubkey{pubkey.begin(), pubkey.end()};
214
0
        assert(CPubKey::ValidSize(vch_pubkey));
215
0
        assert(!CPubKey::ValidSize({pubkey.begin(), pubkey.begin() + pubkey.size() - 1}));
216
217
0
        const CPubKey pubkey_ctor_1{vch_pubkey};
218
0
        assert(pubkey == pubkey_ctor_1);
219
220
0
        const CPubKey pubkey_ctor_2{vch_pubkey.begin(), vch_pubkey.end()};
221
0
        assert(pubkey == pubkey_ctor_2);
222
223
0
        CPubKey pubkey_set;
224
0
        pubkey_set.Set(vch_pubkey.begin(), vch_pubkey.end());
225
0
        assert(pubkey == pubkey_set);
226
0
    }
227
228
0
    {
229
0
        const CPubKey invalid_pubkey{};
230
0
        assert(!invalid_pubkey.IsValid());
231
0
        assert(!invalid_pubkey.IsFullyValid());
232
0
        assert(!(pubkey == invalid_pubkey));
233
0
        assert(pubkey != invalid_pubkey);
234
0
        assert(pubkey < invalid_pubkey);
235
0
    }
236
237
0
    {
238
        // Cover CPubKey's operator[](unsigned int pos)
239
0
        unsigned int sum = 0;
240
0
        for (size_t i = 0; i < pubkey.size(); ++i) {
241
0
            sum += pubkey[i];
242
0
        }
243
0
        assert(std::accumulate(pubkey.begin(), pubkey.end(), 0U) == sum);
244
0
    }
245
246
0
    {
247
0
        CPubKey decompressed_pubkey = pubkey;
248
0
        assert(decompressed_pubkey.IsCompressed());
249
250
0
        const bool ok = decompressed_pubkey.Decompress();
251
0
        assert(ok);
252
0
        assert(!decompressed_pubkey.IsCompressed());
253
0
        assert(decompressed_pubkey.size() == 65);
254
0
    }
255
256
0
    {
257
0
        std::vector<unsigned char> vch_sig;
258
0
        const bool ok = key.Sign(random_uint256, vch_sig, false);
259
0
        assert(ok);
260
0
        assert(pubkey.Verify(random_uint256, vch_sig));
261
0
        assert(CPubKey::CheckLowS(vch_sig));
262
263
0
        const std::vector<unsigned char> vch_invalid_sig{vch_sig.begin(), vch_sig.begin() + vch_sig.size() - 1};
264
0
        assert(!pubkey.Verify(random_uint256, vch_invalid_sig));
265
0
        assert(!CPubKey::CheckLowS(vch_invalid_sig));
266
0
    }
267
268
0
    {
269
0
        std::vector<unsigned char> vch_compact_sig;
270
0
        const bool ok_sign_compact = key.SignCompact(random_uint256, vch_compact_sig);
271
0
        assert(ok_sign_compact);
272
273
0
        CPubKey recover_pubkey;
274
0
        const bool ok_recover_compact = recover_pubkey.RecoverCompact(random_uint256, vch_compact_sig);
275
0
        assert(ok_recover_compact);
276
0
        assert(recover_pubkey == pubkey);
277
0
    }
278
279
0
    {
280
0
        CPubKey child_pubkey;
281
0
        ChainCode child_chaincode;
282
0
        const bool ok = pubkey.Derive(child_pubkey, child_chaincode, 0, random_uint256);
283
0
        assert(ok);
284
0
        assert(child_pubkey != pubkey);
285
0
        assert(child_pubkey.IsCompressed());
286
0
        assert(child_pubkey.IsFullyValid());
287
0
        assert(child_pubkey.IsValid());
288
0
        assert(child_pubkey.size() == 33);
289
0
        assert(child_chaincode != random_uint256);
290
0
    }
291
292
0
    const CPrivKey priv_key = key.GetPrivKey();
293
294
0
    {
295
0
        for (const bool skip_check : {true, false}) {
296
0
            CKey loaded_key;
297
0
            const bool ok = loaded_key.Load(priv_key, pubkey, skip_check);
298
0
            assert(ok);
299
0
            assert(key == loaded_key);
300
0
        }
301
0
    }
302
0
}
303
304
FUZZ_TARGET(ellswift_roundtrip, .init = initialize_key)
305
0
{
306
0
    FuzzedDataProvider fdp{buffer.data(), buffer.size()};
307
308
0
    CKey key = ConsumePrivateKey(fdp, /*compressed=*/true);
309
0
    if (!key.IsValid()) return;
310
311
0
    auto ent32 = fdp.ConsumeBytes<std::byte>(32);
312
0
    ent32.resize(32);
313
314
0
    auto encoded_ellswift = key.EllSwiftCreate(ent32);
315
0
    auto decoded_pubkey = encoded_ellswift.Decode();
316
317
0
    uint256 hash{ConsumeUInt256(fdp)};
318
0
    std::vector<unsigned char> sig;
319
0
    key.Sign(hash, sig);
320
0
    assert(decoded_pubkey.Verify(hash, sig));
321
0
}
322
323
FUZZ_TARGET(bip324_ecdh, .init = initialize_key)
324
0
{
325
0
    FuzzedDataProvider fdp{buffer.data(), buffer.size()};
326
327
    // We generate private key, k1.
328
0
    CKey k1 = ConsumePrivateKey(fdp, /*compressed=*/true);
329
0
    if (!k1.IsValid()) return;
330
331
    // They generate private key, k2.
332
0
    CKey k2 = ConsumePrivateKey(fdp, /*compressed=*/true);
333
0
    if (!k2.IsValid()) return;
334
335
    // We construct an ellswift encoding for our key, k1_ellswift.
336
0
    auto ent32_1 = fdp.ConsumeBytes<std::byte>(32);
337
0
    ent32_1.resize(32);
338
0
    auto k1_ellswift = k1.EllSwiftCreate(ent32_1);
339
340
    // They construct an ellswift encoding for their key, k2_ellswift.
341
0
    auto ent32_2 = fdp.ConsumeBytes<std::byte>(32);
342
0
    ent32_2.resize(32);
343
0
    auto k2_ellswift = k2.EllSwiftCreate(ent32_2);
344
345
    // They construct another (possibly distinct) ellswift encoding for their key, k2_ellswift_bad.
346
0
    auto ent32_2_bad = fdp.ConsumeBytes<std::byte>(32);
347
0
    ent32_2_bad.resize(32);
348
0
    auto k2_ellswift_bad = k2.EllSwiftCreate(ent32_2_bad);
349
0
    assert((ent32_2_bad == ent32_2) == (k2_ellswift_bad == k2_ellswift));
350
351
    // Determine who is who.
352
0
    bool initiating = fdp.ConsumeBool();
353
354
    // We compute our shared secret using our key and their public key.
355
0
    auto ecdh_secret_1 = k1.ComputeBIP324ECDHSecret(k2_ellswift, k1_ellswift, initiating);
356
    // They compute their shared secret using their key and our public key.
357
0
    auto ecdh_secret_2 = k2.ComputeBIP324ECDHSecret(k1_ellswift, k2_ellswift, !initiating);
358
    // Those must match, as everyone is behaving correctly.
359
0
    assert(ecdh_secret_1 == ecdh_secret_2);
360
361
0
    if (k1_ellswift != k2_ellswift) {
362
        // Unless the two keys are exactly identical, acting as the wrong party breaks things.
363
0
        auto ecdh_secret_bad = k1.ComputeBIP324ECDHSecret(k2_ellswift, k1_ellswift, !initiating);
364
0
        assert(ecdh_secret_bad != ecdh_secret_1);
365
0
    }
366
367
0
    if (k2_ellswift_bad != k2_ellswift) {
368
        // Unless both encodings created by them are identical, using the second one breaks things.
369
0
        auto ecdh_secret_bad = k1.ComputeBIP324ECDHSecret(k2_ellswift_bad, k1_ellswift, initiating);
370
0
        assert(ecdh_secret_bad != ecdh_secret_1);
371
0
    }
372
0
}