Bitcoin Core 30.99.0
P2P Digital Currency
descriptor.cpp
Go to the documentation of this file.
1// Copyright (c) 2023-present 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
6
7#include <key.h>
8#include <key_io.h>
9#include <pubkey.h>
10#include <util/strencodings.h>
11
12#include <ranges>
13#include <stack>
14
16 // The data to use as a private key or a seed for an xprv.
17 std::array<std::byte, 32> key_data{std::byte{1}};
18 // Generate keys of all kinds and store them in the keys array.
19 for (size_t i{0}; i < TOTAL_KEYS_GENERATED; i++) {
20 key_data[31] = std::byte(i);
21
22 // If this is a "raw" key, generate a normal privkey. Otherwise generate
23 // an extended one.
25 CKey privkey;
26 privkey.Set(key_data.begin(), key_data.end(), !IdIsUnCompPubKey(i));
27 if (IdIsCompPubKey(i) || IdIsUnCompPubKey(i)) {
28 CPubKey pubkey{privkey.GetPubKey()};
29 keys_str[i] = HexStr(pubkey);
30 } else if (IdIsXOnlyPubKey(i)) {
31 const XOnlyPubKey pubkey{privkey.GetPubKey()};
32 keys_str[i] = HexStr(pubkey);
33 } else {
34 keys_str[i] = EncodeSecret(privkey);
35 }
36 } else {
37 CExtKey ext_privkey;
38 ext_privkey.SetSeed(key_data);
39 if (IdIsXprv(i)) {
40 keys_str[i] = EncodeExtKey(ext_privkey);
41 } else {
42 const CExtPubKey ext_pubkey{ext_privkey.Neuter()};
43 keys_str[i] = EncodeExtPubKey(ext_pubkey);
44 }
45 }
46 }
47}
48
49std::optional<uint8_t> MockedDescriptorConverter::IdxFromHex(std::string_view hex_characters) const {
50 if (hex_characters.size() != 2) return {};
51 auto idx = ParseHex(hex_characters);
52 if (idx.size() != 1) return {};
53 return idx[0];
54}
55
56std::optional<std::string> MockedDescriptorConverter::GetDescriptor(std::string_view mocked_desc) const {
57 // The smallest fragment would be "pk(%00)"
58 if (mocked_desc.size() < 7) return {};
59
60 // The actual descriptor string to be returned.
61 std::string desc;
62 desc.reserve(mocked_desc.size());
63
64 // Replace all occurrences of '%' followed by two hex characters with the corresponding key.
65 for (size_t i = 0; i < mocked_desc.size();) {
66 if (mocked_desc[i] == '%') {
67 if (i + 3 >= mocked_desc.size()) return {};
68 if (const auto idx = IdxFromHex(mocked_desc.substr(i + 1, 2))) {
69 desc += keys_str[*idx];
70 i += 3;
71 } else {
72 return {};
73 }
74 } else {
75 desc += mocked_desc[i++];
76 }
77 }
78
79 return desc;
80}
81
82bool HasDeepDerivPath(std::span<const uint8_t> buff, const int max_depth)
83{
84 auto depth{0};
85 for (const auto& ch: buff) {
86 if (ch == ',') {
87 // A comma is always present between two key expressions, so we use that as a delimiter.
88 depth = 0;
89 } else if (ch == '/') {
90 if (++depth > max_depth) return true;
91 }
92 }
93 return false;
94}
95
96bool HasTooManySubFrag(std::span<const uint8_t> buff, const int max_subs, const size_t max_nested_subs)
97{
98 // We use a stack because there may be many nested sub-frags.
99 std::stack<int> counts;
100 for (const auto& ch: buff) {
101 // The fuzzer may generate an input with a ton of parentheses. Rule out pathological cases.
102 if (counts.size() > max_nested_subs) return true;
103
104 if (ch == '(') {
105 // A new fragment was opened, create a new sub-count for it and start as one since any fragment with
106 // parentheses has at least one sub.
107 counts.push(1);
108 } else if (ch == ',' && !counts.empty()) {
109 // When encountering a comma, account for an additional sub in the last opened fragment. If it exceeds the
110 // limit, bail.
111 if (++counts.top() > max_subs) return true;
112 } else if (ch == ')' && !counts.empty()) {
113 // Fragment closed! Drop its sub count and resume to counting the number of subs for its parent.
114 counts.pop();
115 }
116 }
117 return false;
118}
119
120bool HasTooManyWrappers(std::span<const uint8_t> buff, const int max_wrappers)
121{
122 // The number of nested wrappers. Nested wrappers are always characters which follow each other so we don't have to
123 // use a stack as we do above when counting the number of sub-fragments.
124 std::optional<int> count;
125
126 // We want to detect nested wrappers. A wrapper is a character prepended to a fragment, separated by a colon. There
127 // may be more than one wrapper, in which case the colon is not repeated. For instance `jjjjj:pk()`. To count
128 // wrappers we iterate in reverse and use the colon to detect the end of a wrapper expression and count how many
129 // characters there are since the beginning of the expression. We stop counting when we encounter a character
130 // indicating the beginning of a new expression.
131 for (const auto ch: buff | std::views::reverse) {
132 // A colon, start counting.
133 if (ch == ':') {
134 // The colon itself is not a wrapper so we start at 0.
135 count = 0;
136 } else if (count) {
137 // If we are counting wrappers, stop when we crossed the beginning of the wrapper expression. Otherwise keep
138 // counting and bail if we reached the limit.
139 // A wrapper may only ever occur as the first sub of a descriptor/miniscript expression ('('), as the
140 // first Taproot leaf in a pair ('{') or as the nth sub in each case (',').
141 if (ch == ',' || ch == '(' || ch == '{') {
142 count.reset();
143 } else if (++*count > max_wrappers) {
144 return true;
145 }
146 }
147 }
148
149 return false;
150}
151
152bool HasTooLargeLeafSize(std::span<const uint8_t> buff, const uint32_t max_leaf_size)
153{
154 uint32_t leaf_len{0};
155 for (auto c : buff) {
156 if (c == '(' || c == ')' || c == ',' || c == '{' || c == '}') {
157 // Possibly start a fresh leaf, or a fresh function name (with
158 // wrappers), or terminate a prior leaf.
159 leaf_len = 0;
160 } else {
161 // Just treat everything else as a leaf. This will also reject long
162 // function names, but this should be fine if the max_leaf_size is
163 // set large enough.
164 if (++leaf_len > max_leaf_size) {
165 return true;
166 }
167 }
168 }
169 return false;
170}
An encapsulated private key.
Definition: key.h:36
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition: key.cpp:183
void Set(const T pbegin, const T pend, bool fCompressedIn)
Initialize using begin and end iterators to byte data.
Definition: key.h:104
An encapsulated public key.
Definition: pubkey.h:34
bool IdIsUnCompPubKey(uint8_t idx) const
Definition: descriptor.h:35
bool IdIsXOnlyPubKey(uint8_t idx) const
Definition: descriptor.h:36
void Init()
When initializing the target, populate the list of keys.
Definition: descriptor.cpp:15
static constexpr size_t TOTAL_KEYS_GENERATED
How many keys we'll generate in total.
Definition: descriptor.h:28
std::optional< std::string > GetDescriptor(std::string_view mocked_desc) const
Get an actual descriptor string from a descriptor string whose keys were mocked.
Definition: descriptor.cpp:56
bool IdIsXprv(uint8_t idx) const
Definition: descriptor.h:39
bool IdIsCompPubKey(uint8_t idx) const
Definition: descriptor.h:34
std::array< std::string, TOTAL_KEYS_GENERATED > keys_str
256 keys of various types.
Definition: descriptor.h:30
bool IdIsConstPrivKey(uint8_t idx) const
Definition: descriptor.h:37
std::optional< uint8_t > IdxFromHex(std::string_view hex_characters) const
Parse an id in the keys vectors from a 2-characters hex string.
Definition: descriptor.cpp:49
std::string HexStr(const std::span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
Definition: hex_base.cpp:30
std::string EncodeExtKey(const CExtKey &key)
Definition: key_io.cpp:283
std::string EncodeSecret(const CKey &key)
Definition: key_io.cpp:231
std::string EncodeExtPubKey(const CExtPubKey &key)
Definition: key_io.cpp:257
std::vector< Byte > ParseHex(std::string_view hex_str)
Like TryParseHex, but returns an empty vector on invalid input.
Definition: strencodings.h:68
Definition: key.h:231
CExtPubKey Neuter() const
Definition: key.cpp:503
void SetSeed(std::span< const std::byte > seed)
Definition: key.cpp:491
bool HasTooManySubFrag(std::span< const uint8_t > buff, const int max_subs, const size_t max_nested_subs)
Whether the buffer, if it represents a valid descriptor, contains a fragment with more sub-fragments ...
Definition: descriptor.cpp:96
bool HasTooLargeLeafSize(std::span< const uint8_t > buff, const uint32_t max_leaf_size)
Whether the expanded buffer (after calling GetDescriptor() in MockedDescriptorConverter) has a leaf s...
Definition: descriptor.cpp:152
bool HasDeepDerivPath(std::span< const uint8_t > buff, const int max_depth)
Whether the buffer, if it represents a valid descriptor, contains a derivation path deeper than a giv...
Definition: descriptor.cpp:82
bool HasTooManyWrappers(std::span< const uint8_t > buff, const int max_wrappers)
Whether the buffer, if it represents a valid descriptor, contains a fragment with more wrappers than ...
Definition: descriptor.cpp:120
static int count