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 : 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 : }