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 : : 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 : 428 : bool IdIsCompPubKey(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 0; } 31 : 514 : bool IdIsUnCompPubKey(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 1; } 32 : 256 : bool IdIsXOnlyPubKey(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 2; } 33 : 127 : 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 : 84 : bool IdIsXprv(uint8_t idx) const { return idx % KEY_TYPES_COUNT == 5; } 36 : : 37 : : //! When initializing the target, populate the list of keys. 38 : 1 : void Init() { 39 : : // The data to use as a private key or a seed for an xprv. 40 : 1 : std::array<std::byte, 32> key_data{std::byte{1}}; 41 : : // Generate keys of all kinds and store them in the keys array. 42 [ + + ]: 257 : for (size_t i{0}; i < TOTAL_KEYS_GENERATED; i++) { 43 : 256 : 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 [ + + + + : 256 : if (IdIsCompPubKey(i) || IdIsUnCompPubKey(i) || IdIsXOnlyPubKey(i) || IdIsConstPrivKey(i)) { + + + + ] 48 : 172 : CKey privkey; 49 [ + - + - : 172 : privkey.Set(UCharCast(key_data.begin()), UCharCast(key_data.end()), !IdIsUnCompPubKey(i)); + - + - ] 50 [ + - + + : 172 : if (IdIsCompPubKey(i) || IdIsUnCompPubKey(i)) { + - + + ] 51 [ + - ]: 86 : CPubKey pubkey{privkey.GetPubKey()}; 52 [ + - + - ]: 86 : keys_str[i] = HexStr(pubkey); 53 [ + - + + ]: 172 : } else if (IdIsXOnlyPubKey(i)) { 54 [ + - + - ]: 43 : const XOnlyPubKey pubkey{privkey.GetPubKey()}; 55 [ + - + - ]: 43 : keys_str[i] = HexStr(pubkey); 56 : 43 : } else { 57 [ + - ]: 43 : keys_str[i] = EncodeSecret(privkey); 58 : : } 59 : 172 : } else { 60 : 84 : CExtKey ext_privkey; 61 [ + - + - ]: 84 : ext_privkey.SetSeed(key_data); 62 [ + - + + ]: 84 : if (IdIsXprv(i)) { 63 [ + - ]: 42 : keys_str[i] = EncodeExtKey(ext_privkey); 64 : 42 : } else { 65 [ + - ]: 42 : const CExtPubKey ext_pubkey{ext_privkey.Neuter()}; 66 [ + - ]: 42 : keys_str[i] = EncodeExtPubKey(ext_pubkey); 67 : : } 68 : 84 : } 69 : 256 : } 70 : 1 : } 71 : : 72 : : //! Parse an id in the keys vectors from a 2-characters hex string. 73 : 6632 : std::optional<uint8_t> IdxFromHex(std::string_view hex_characters) const { 74 [ - + ]: 6632 : if (hex_characters.size() != 2) return {}; 75 : 6632 : auto idx = ParseHex(hex_characters); 76 [ + + ]: 6632 : if (idx.size() != 1) return {}; 77 [ + - ]: 6619 : return idx[0]; 78 : 6632 : } 79 : : 80 : : //! Get an actual descriptor string from a descriptor string whose keys were mocked. 81 : 881 : std::optional<std::string> GetDescriptor(std::string_view mocked_desc) const { 82 : : // The smallest fragment would be "pk(%00)" 83 [ + + ]: 881 : if (mocked_desc.size() < 7) return {}; 84 : : 85 : : // The actual descriptor string to be returned. 86 : 880 : std::string desc; 87 [ + - ]: 880 : desc.reserve(mocked_desc.size()); 88 : : 89 : : // Replace all occurrences of '%' followed by two hex characters with the corresponding key. 90 [ + + ]: 297484 : for (size_t i = 0; i < mocked_desc.size();) { 91 [ + + ]: 296618 : if (mocked_desc[i] == '%') { 92 [ + + ]: 6633 : if (i + 3 >= mocked_desc.size()) return {}; 93 [ + - + - : 6632 : if (const auto idx = IdxFromHex(mocked_desc.substr(i + 1, 2))) { + + ] 94 [ + - + - ]: 6619 : desc += keys_str[*idx]; 95 : 6619 : i += 3; 96 : 6619 : } else { 97 : 13 : return {}; 98 : : } 99 : 6619 : } else { 100 [ + - ]: 289985 : desc += mocked_desc[i++]; 101 : : } 102 : : } 103 : : 104 [ + - ]: 866 : return desc; 105 : 881 : } 106 : : }; 107 : : 108 : : //! The converter of mocked descriptors, needs to be initialized when the target is. 109 : 173 : MockedDescriptorConverter MOCKED_DESC_CONVERTER; 110 : : 111 : : /** Test a successfully parsed descriptor. */ 112 : 587 : static void TestDescriptor(const Descriptor& desc, FlatSigningProvider& sig_provider, std::string& dummy) 113 : : { 114 : : // Trivial helpers. 115 : 587 : (void)desc.IsRange(); 116 : 587 : const bool is_solvable{desc.IsSolvable()}; 117 : 587 : (void)desc.IsSingleType(); 118 : 587 : (void)desc.GetOutputType(); 119 : : 120 : : // Serialization to string representation. 121 : 587 : (void)desc.ToString(); 122 : 587 : (void)desc.ToPrivateString(sig_provider, dummy); 123 : 587 : (void)desc.ToNormalizedString(sig_provider, dummy); 124 : : 125 : : // Serialization to Script. 126 : 587 : DescriptorCache cache; 127 : 587 : std::vector<CScript> out_scripts; 128 [ + - ]: 587 : (void)desc.Expand(0, sig_provider, out_scripts, sig_provider, &cache); 129 [ + - ]: 587 : (void)desc.ExpandPrivate(0, sig_provider, sig_provider); 130 [ + - ]: 587 : (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 [ + + ]: 587 : if (!out_scripts.empty()) { 134 [ + - + - ]: 550 : 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 [ + - ]: 550 : const bool is_combo{!desc.IsSingleType()}; 138 [ + + + - : 550 : assert(is_combo || desc.ScriptSize() == out_scripts.back().size()); + - + - - + ] 139 : 550 : } 140 : : 141 [ + - ]: 587 : const auto max_sat_maxsig{desc.MaxSatisfactionWeight(true)}; 142 [ + - ]: 587 : const auto max_sat_nonmaxsig{desc.MaxSatisfactionWeight(true)}; 143 [ + - ]: 587 : const auto max_elems{desc.MaxSatisfactionElems()}; 144 : : // We must be able to estimate the max satisfaction size for any solvable descriptor (but combo). 145 [ + + + - ]: 587 : const bool is_nontop_or_nonsolvable{!is_solvable || !desc.GetOutputType()}; 146 [ + + - + ]: 587 : const bool is_input_size_info_set{max_sat_maxsig && max_sat_nonmaxsig && max_elems}; 147 [ + + + - ]: 587 : assert(is_input_size_info_set || is_nontop_or_nonsolvable); 148 : 587 : } 149 : : 150 : 2 : void initialize_descriptor_parse() 151 : : { 152 : 2 : ECC_Start(); 153 : 2 : SelectParams(ChainType::MAIN); 154 : 2 : } 155 : : 156 : 1 : void initialize_mocked_descriptor_parse() 157 : : { 158 : 1 : initialize_descriptor_parse(); 159 : 1 : MOCKED_DESC_CONVERTER.Init(); 160 : 1 : } 161 : : 162 [ + - - + ]: 1227 : FUZZ_TARGET(mocked_descriptor_parse, .init = initialize_mocked_descriptor_parse) 163 : : { 164 [ + - ]: 881 : const std::string mocked_descriptor{buffer.begin(), buffer.end()}; 165 [ + - + + ]: 1747 : if (const auto descriptor = MOCKED_DESC_CONVERTER.GetDescriptor(mocked_descriptor)) { 166 : 866 : FlatSigningProvider signing_provider; 167 : 866 : std::string error; 168 [ + - + - ]: 866 : const auto desc = Parse(*descriptor, signing_provider, error); 169 [ + + + - : 866 : if (desc) TestDescriptor(*desc, signing_provider, error); + - ] 170 : 866 : } 171 : 881 : } 172 : : 173 [ + - - + ]: 1396 : FUZZ_TARGET(descriptor_parse, .init = initialize_descriptor_parse) 174 : : { 175 [ + - ]: 1050 : const std::string descriptor(buffer.begin(), buffer.end()); 176 : 1050 : FlatSigningProvider signing_provider; 177 : 1050 : std::string error; 178 [ + + ]: 3150 : for (const bool require_checksum : {true, false}) { 179 [ + - ]: 2100 : const auto desc = Parse(descriptor, signing_provider, error, require_checksum); 180 [ + + + - : 2100 : if (desc) TestDescriptor(*desc, signing_provider, error); + - ] 181 : 2100 : } 182 : 1050 : }