Branch data Line data Source code
1 : : // Copyright (c) 2009-2021 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_io.h>
7 : : #include <pubkey.h>
8 : : #include <script/descriptor.h>
9 : : #include <test/fuzz/fuzz.h>
10 : : #include <util/chaintype.h>
11 : : #include <util/strencodings.h>
12 : :
13 : : //! Types are raw (un)compressed pubkeys, raw xonly pubkeys, raw privkeys (WIF), xpubs, xprvs.
14 : : static constexpr uint8_t KEY_TYPES_COUNT{6};
15 : : //! How many keys we'll generate in total.
16 : : static constexpr size_t TOTAL_KEYS_GENERATED{std::numeric_limits<uint8_t>::max() + 1};
17 : :
18 : : /**
19 : : * Converts a mocked descriptor string to a valid one. Every key in a mocked descriptor key is
20 : : * represented by 2 hex characters preceded by the '%' character. We parse the two hex characters
21 : : * as an index in a list of pre-generated keys. This list contains keys of the various types
22 : : * accepted in descriptor keys expressions.
23 : : */
24 : 0 : class MockedDescriptorConverter {
25 : : //! 256 keys of various types.
26 : : std::array<std::string, TOTAL_KEYS_GENERATED> keys_str;
27 : :
28 : : public:
29 : : // We derive the type of key to generate from the 1-byte id parsed from hex.
30 : 0 : bool IdIsCompPubKey(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 0; }
31 : 0 : bool IdIsUnCompPubKey(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 1; }
32 : 0 : bool IdIsXOnlyPubKey(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 2; }
33 : 0 : bool IdIsConstPrivKey(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 3; }
34 : : bool IdIsXpub(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 4; }
35 : 0 : bool IdIsXprv(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 5; }
36 : :
37 : : //! When initializing the target, populate the list of keys.
38 : 0 : void Init() {
39 : : // The data to use as a private key or a seed for an xprv.
40 : 0 : std::array<std::byte, 32> key_data{std::byte{1}};
41 : : // Generate keys of all kinds and store them in the keys array.
42 [ # # ]: 0 : for (size_t i{0}; i < TOTAL_KEYS_GENERATED; i++) {
43 : 0 : key_data[31] = std::byte(i);
44 : :
45 : : // If this is a "raw" key, generate a normal privkey. Otherwise generate
46 : : // an extended one.
47 [ # # ][ # # ]: 0 : if (IdIsCompPubKey(i) || IdIsUnCompPubKey(i) || IdIsXOnlyPubKey(i) || IdIsConstPrivKey(i)) {
[ # # ][ # # ]
48 : 0 : CKey privkey;
49 [ # # ][ # # ]: 0 : privkey.Set(UCharCast(key_data.begin()), UCharCast(key_data.end()), !IdIsUnCompPubKey(i));
[ # # ][ # # ]
50 [ # # ][ # # ]: 0 : if (IdIsCompPubKey(i) || IdIsUnCompPubKey(i)) {
[ # # ][ # # ]
51 [ # # ]: 0 : CPubKey pubkey{privkey.GetPubKey()};
52 [ # # ][ # # ]: 0 : keys_str[i] = HexStr(pubkey);
53 [ # # ][ # # ]: 0 : } else if (IdIsXOnlyPubKey(i)) {
54 [ # # ][ # # ]: 0 : const XOnlyPubKey pubkey{privkey.GetPubKey()};
55 [ # # ][ # # ]: 0 : keys_str[i] = HexStr(pubkey);
56 : 0 : } else {
57 [ # # ]: 0 : keys_str[i] = EncodeSecret(privkey);
58 : : }
59 : 0 : } else {
60 : 0 : CExtKey ext_privkey;
61 [ # # ][ # # ]: 0 : ext_privkey.SetSeed(key_data);
62 [ # # ][ # # ]: 0 : if (IdIsXprv(i)) {
63 [ # # ]: 0 : keys_str[i] = EncodeExtKey(ext_privkey);
64 : 0 : } else {
65 [ # # ]: 0 : const CExtPubKey ext_pubkey{ext_privkey.Neuter()};
66 [ # # ]: 0 : keys_str[i] = EncodeExtPubKey(ext_pubkey);
67 : : }
68 : 0 : }
69 : 0 : }
70 : 0 : }
71 : :
72 : : //! Parse an id in the keys vectors from a 2-characters hex string.
73 : 0 : std::optional<uint8_t> IdxFromHex(std::string_view hex_characters) const {
74 [ # # ]: 0 : if (hex_characters.size() != 2) return {};
75 : 0 : auto idx = ParseHex(hex_characters);
76 [ # # ]: 0 : if (idx.size() != 1) return {};
77 : 0 : return idx[0];
78 : 0 : }
79 : :
80 : : //! Get an actual descriptor string from a descriptor string whose keys were mocked.
81 : 0 : std::optional<std::string> GetDescriptor(std::string_view mocked_desc) const {
82 : : // The smallest fragment would be "pk(%00)"
83 [ # # ]: 0 : if (mocked_desc.size() < 7) return {};
84 : :
85 : : // The actual descriptor string to be returned.
86 : 0 : std::string desc;
87 [ # # ]: 0 : desc.reserve(mocked_desc.size());
88 : :
89 : : // Replace all occurrences of '%' followed by two hex characters with the corresponding key.
90 [ # # ]: 0 : for (size_t i = 0; i < mocked_desc.size();) {
91 [ # # ]: 0 : if (mocked_desc[i] == '%') {
92 [ # # ]: 0 : if (i + 3 >= mocked_desc.size()) return {};
93 [ # # ][ # # ]: 0 : if (const auto idx = IdxFromHex(mocked_desc.substr(i + 1, 2))) {
[ # # ]
94 [ # # ]: 0 : desc += keys_str[*idx];
95 : 0 : i += 3;
96 : 0 : } else {
97 : 0 : return {};
98 : : }
99 : 0 : } else {
100 [ # # ]: 0 : desc += mocked_desc[i++];
101 : : }
102 : : }
103 : :
104 : 0 : return desc;
105 : 0 : }
106 : : };
107 : :
108 : : //! The converter of mocked descriptors, needs to be initialized when the target is.
109 : 2 : MockedDescriptorConverter MOCKED_DESC_CONVERTER;
110 : :
111 : : /** Test a successfully parsed descriptor. */
112 : 0 : static void TestDescriptor(const Descriptor& desc, FlatSigningProvider& sig_provider, std::string& dummy)
113 : : {
114 : : // Trivial helpers.
115 : 0 : (void)desc.IsRange();
116 : 0 : const bool is_solvable{desc.IsSolvable()};
117 : 0 : (void)desc.IsSingleType();
118 : 0 : (void)desc.GetOutputType();
119 : :
120 : : // Serialization to string representation.
121 : 0 : (void)desc.ToString();
122 : 0 : (void)desc.ToPrivateString(sig_provider, dummy);
123 : 0 : (void)desc.ToNormalizedString(sig_provider, dummy);
124 : :
125 : : // Serialization to Script.
126 : 0 : DescriptorCache cache;
127 : 0 : std::vector<CScript> out_scripts;
128 [ # # ]: 0 : (void)desc.Expand(0, sig_provider, out_scripts, sig_provider, &cache);
129 [ # # ]: 0 : (void)desc.ExpandPrivate(0, sig_provider, sig_provider);
130 [ # # ]: 0 : (void)desc.ExpandFromCache(0, cache, out_scripts, sig_provider);
131 : :
132 : : // If we could serialize to script we must be able to infer using the same provider.
133 [ # # ]: 0 : if (!out_scripts.empty()) {
134 [ # # ][ # # ]: 0 : assert(InferDescriptor(out_scripts.back(), sig_provider));
135 : :
136 : : // The ScriptSize() must match the size of the serialized Script. (ScriptSize() is set for all descs but 'combo()'.)
137 [ # # ]: 0 : const bool is_combo{!desc.IsSingleType()};
138 [ # # ][ # # ]: 0 : assert(is_combo || desc.ScriptSize() == out_scripts.back().size());
[ # # ][ # # ]
[ # # ]
139 : 0 : }
140 : :
141 [ # # ]: 0 : const auto max_sat_maxsig{desc.MaxSatisfactionWeight(true)};
142 [ # # ]: 0 : const auto max_sat_nonmaxsig{desc.MaxSatisfactionWeight(true)};
143 [ # # ]: 0 : const auto max_elems{desc.MaxSatisfactionElems()};
144 : : // We must be able to estimate the max satisfaction size for any solvable descriptor (but combo).
145 [ # # ][ # # ]: 0 : const bool is_nontop_or_nonsolvable{!is_solvable || !desc.GetOutputType()};
146 [ # # ][ # # ]: 0 : const bool is_input_size_info_set{max_sat_maxsig && max_sat_nonmaxsig && max_elems};
147 [ # # ][ # # ]: 0 : assert(is_input_size_info_set || is_nontop_or_nonsolvable);
148 : 0 : }
149 : :
150 : 0 : void initialize_descriptor_parse()
151 : : {
152 : 0 : ECC_Start();
153 : 0 : SelectParams(ChainType::MAIN);
154 : 0 : }
155 : :
156 : 0 : void initialize_mocked_descriptor_parse()
157 : : {
158 : 0 : initialize_descriptor_parse();
159 : 0 : MOCKED_DESC_CONVERTER.Init();
160 : 0 : }
161 : :
162 [ + - ]: 4 : FUZZ_TARGET(mocked_descriptor_parse, .init = initialize_mocked_descriptor_parse)
163 : : {
164 [ # # ]: 0 : const std::string mocked_descriptor{buffer.begin(), buffer.end()};
165 [ # # ][ # # ]: 0 : if (const auto descriptor = MOCKED_DESC_CONVERTER.GetDescriptor(mocked_descriptor)) {
166 : 0 : FlatSigningProvider signing_provider;
167 : 0 : std::string error;
168 [ # # ]: 0 : const auto desc = Parse(*descriptor, signing_provider, error);
169 [ # # ][ # # ]: 0 : if (desc) TestDescriptor(*desc, signing_provider, error);
170 : 0 : }
171 : 0 : }
172 : :
173 [ + - ]: 4 : FUZZ_TARGET(descriptor_parse, .init = initialize_descriptor_parse)
174 : : {
175 [ # # ]: 0 : const std::string descriptor(buffer.begin(), buffer.end());
176 : 0 : FlatSigningProvider signing_provider;
177 : 0 : std::string error;
178 [ # # ]: 0 : for (const bool require_checksum : {true, false}) {
179 [ # # ]: 0 : const auto desc = Parse(descriptor, signing_provider, error, require_checksum);
180 [ # # ][ # # ]: 0 : if (desc) TestDescriptor(*desc, signing_provider, error);
181 : 0 : }
182 : 0 : }
|