Skip to content

Commit 9b7023d

Browse files
committed
Fuzz HRP of bech32 as well
Also separated the roundtrip testing from the random string decoding for clarity Note that while BIP 173 claims: ``` The human-readable part, which is intended to convey the type of data, or anything else that is relevant to the reader. This part MUST contain 1 to 83 US-ASCII characters, with each character having a value in the range [33-126]. HRP validity may be further restricted by specific applications. ``` bech32::Encode rejects uppercase letters.
1 parent c1a5d5c commit 9b7023d

File tree

1 file changed

+46
-21
lines changed

1 file changed

+46
-21
lines changed

src/test/fuzz/bech32.cpp

Lines changed: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,41 +4,66 @@
44

55
#include <bech32.h>
66
#include <test/fuzz/fuzz.h>
7+
#include <test/fuzz/FuzzedDataProvider.h>
78
#include <test/util/str.h>
89
#include <util/strencodings.h>
910

1011
#include <cassert>
1112
#include <cstdint>
1213
#include <string>
13-
#include <utility>
1414
#include <vector>
1515

16-
FUZZ_TARGET(bech32)
16+
FUZZ_TARGET(bech32_random_decode)
1717
{
18-
const std::string random_string(buffer.begin(), buffer.end());
19-
const auto r1 = bech32::Decode(random_string);
20-
if (r1.hrp.empty()) {
21-
assert(r1.encoding == bech32::Encoding::INVALID);
22-
assert(r1.data.empty());
18+
auto limit = bech32::CharLimit::BECH32;
19+
FuzzedDataProvider fdp(buffer.data(), buffer.size());
20+
auto random_string = fdp.ConsumeRandomLengthString(limit + 1);
21+
auto decoded = bech32::Decode(random_string, limit);
22+
23+
if (decoded.hrp.empty()) {
24+
assert(decoded.encoding == bech32::Encoding::INVALID);
25+
assert(decoded.data.empty());
2326
} else {
24-
assert(r1.encoding != bech32::Encoding::INVALID);
25-
const std::string reencoded = bech32::Encode(r1.encoding, r1.hrp, r1.data);
27+
assert(decoded.encoding != bech32::Encoding::INVALID);
28+
auto reencoded = bech32::Encode(decoded.encoding, decoded.hrp, decoded.data);
2629
assert(CaseInsensitiveEqual(random_string, reencoded));
2730
}
31+
}
32+
33+
// https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki and https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki
34+
std::string GenerateRandomHRP(FuzzedDataProvider& fdp)
35+
{
36+
std::string hrp;
37+
size_t length = fdp.ConsumeIntegralInRange<size_t>(1, 83);
38+
for (size_t i = 0; i < length; ++i) {
39+
// Generate lowercase ASCII characters in ([33-126] - ['A'-'Z']) range
40+
char c = fdp.ConsumeBool()
41+
? fdp.ConsumeIntegralInRange<char>(33, 'A' - 1)
42+
: fdp.ConsumeIntegralInRange<char>('Z' + 1, 126);
43+
hrp += c;
44+
}
45+
return hrp;
46+
}
47+
48+
FUZZ_TARGET(bech32_roundtrip)
49+
{
50+
FuzzedDataProvider fdp(buffer.data(), buffer.size());
51+
auto hrp = GenerateRandomHRP(fdp);
2852

29-
std::vector<unsigned char> input;
30-
ConvertBits<8, 5, true>([&](unsigned char c) { input.push_back(c); }, buffer.begin(), buffer.end());
53+
auto input_chars = fdp.ConsumeBytes<unsigned char>(fdp.ConsumeIntegralInRange<size_t>(0, 82));
54+
std::vector<uint8_t> converted_input;
55+
ConvertBits<8, 5, true>([&](auto c) { converted_input.push_back(c); }, input_chars.begin(), input_chars.end());
3156

32-
// Input data part + 3 characters for the HRP and separator (bc1) + the checksum characters
33-
if (input.size() + 3 + bech32::CHECKSUM_SIZE <= bech32::CharLimit::BECH32) {
34-
// If it's possible to encode input in Bech32(m) without exceeding the bech32-character limit:
35-
for (auto encoding : {bech32::Encoding::BECH32, bech32::Encoding::BECH32M}) {
36-
const std::string encoded = bech32::Encode(encoding, "bc", input);
57+
auto size = converted_input.size() + hrp.length() + std::string({bech32::SEPARATOR}).size() + bech32::CHECKSUM_SIZE;
58+
if (size <= bech32::CharLimit::BECH32) {
59+
for (auto encoding: {bech32::Encoding::BECH32, bech32::Encoding::BECH32M}) {
60+
auto encoded = bech32::Encode(encoding, hrp, converted_input);
3761
assert(!encoded.empty());
38-
const auto r2 = bech32::Decode(encoded);
39-
assert(r2.encoding == encoding);
40-
assert(r2.hrp == "bc");
41-
assert(r2.data == input);
62+
63+
const auto decoded = bech32::Decode(encoded);
64+
assert(decoded.encoding == encoding);
65+
assert(decoded.hrp == hrp);
66+
assert(decoded.data == converted_input);
4267
}
4368
}
44-
}
69+
}

0 commit comments

Comments
 (0)