Merge bitcoin/bitcoin#23595: util: Add ParseHex<std::byte>() helper

facd1fb911 refactor: Use Span of std::byte in CExtKey::SetSeed (MarcoFalke)
fae1006019 util: Add ParseHex<std::byte>() helper (MarcoFalke)
fabdf81983 test: Add test for embedded null in hex string (MarcoFalke)

Pull request description:

  This adds the hex->`std::byte` helper after the `std::byte`->hex helper was added in commit 9394964f6b

ACKs for top commit:
  pk-b2:
    ACK https://github.com/bitcoin/bitcoin/pull/23595/commits/facd1fb911abfc595a3484ee53397eff515d4c40
  laanwj:
    Code review ACK facd1fb911

Tree-SHA512: e2329fbdea2e580bd1618caab31f5d0e59c245a028e1236662858e621929818870b76ab6834f7ac6a46d7874dfec63f498380ad99da6efe4218f720a60e859be
This commit is contained in:
laanwj 2022-05-20 10:47:26 +02:00
commit 0cd1a2eff9
No known key found for this signature in database
GPG key ID: 1E4AED62986CD25D
8 changed files with 28 additions and 13 deletions

View file

@ -340,11 +340,11 @@ bool CExtKey::Derive(CExtKey &out, unsigned int _nChild) const {
return key.Derive(out.key, out.chaincode, _nChild, chaincode); return key.Derive(out.key, out.chaincode, _nChild, chaincode);
} }
void CExtKey::SetSeed(Span<const uint8_t> seed) void CExtKey::SetSeed(Span<const std::byte> seed)
{ {
static const unsigned char hashkey[] = {'B','i','t','c','o','i','n',' ','s','e','e','d'}; static const unsigned char hashkey[] = {'B','i','t','c','o','i','n',' ','s','e','e','d'};
std::vector<unsigned char, secure_allocator<unsigned char>> vout(64); std::vector<unsigned char, secure_allocator<unsigned char>> vout(64);
CHMAC_SHA512{hashkey, sizeof(hashkey)}.Write(seed.data(), seed.size()).Finalize(vout.data()); CHMAC_SHA512{hashkey, sizeof(hashkey)}.Write(UCharCast(seed.data()), seed.size()).Finalize(vout.data());
key.Set(vout.data(), vout.data() + 32, true); key.Set(vout.data(), vout.data() + 32, true);
memcpy(chaincode.begin(), vout.data() + 32, 32); memcpy(chaincode.begin(), vout.data() + 32, 32);
nDepth = 0; nDepth = 0;

View file

@ -85,7 +85,7 @@ public:
//! Simple read-only vector-like interface. //! Simple read-only vector-like interface.
unsigned int size() const { return (fValid ? keydata.size() : 0); } unsigned int size() const { return (fValid ? keydata.size() : 0); }
const unsigned char* data() const { return keydata.data(); } const std::byte* data() const { return reinterpret_cast<const std::byte*>(keydata.data()); }
const unsigned char* begin() const { return keydata.data(); } const unsigned char* begin() const { return keydata.data(); }
const unsigned char* end() const { return keydata.data() + size(); } const unsigned char* end() const { return keydata.data() + size(); }
@ -178,7 +178,7 @@ struct CExtKey {
void Decode(const unsigned char code[BIP32_EXTKEY_SIZE]); void Decode(const unsigned char code[BIP32_EXTKEY_SIZE]);
bool Derive(CExtKey& out, unsigned int nChild) const; bool Derive(CExtKey& out, unsigned int nChild) const;
CExtPubKey Neuter() const; CExtPubKey Neuter() const;
void SetSeed(Span<const uint8_t> seed); void SetSeed(Span<const std::byte> seed);
}; };
/** Initialize the elliptic curve support. May not be called twice without calling ECC_Stop first. */ /** Initialize the elliptic curve support. May not be called twice without calling ECC_Stop first. */

View file

@ -120,8 +120,9 @@ const std::vector<std::string> TEST5 = {
"xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHL" "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHL"
}; };
void RunTest(const TestVector &test) { void RunTest(const TestVector& test)
std::vector<unsigned char> seed = ParseHex(test.strHexMaster); {
std::vector<std::byte> seed{ParseHex<std::byte>(test.strHexMaster)};
CExtKey key; CExtKey key;
CExtPubKey pubkey; CExtPubKey pubkey;
key.SetSeed(seed); key.SetSeed(seed);

View file

@ -25,6 +25,8 @@ FUZZ_TARGET_INIT(hex, initialize_hex)
{ {
const std::string random_hex_string(buffer.begin(), buffer.end()); const std::string random_hex_string(buffer.begin(), buffer.end());
const std::vector<unsigned char> data = ParseHex(random_hex_string); const std::vector<unsigned char> data = ParseHex(random_hex_string);
const std::vector<std::byte> bytes{ParseHex<std::byte>(random_hex_string)};
assert(AsBytes(Span{data}) == Span{bytes});
const std::string hex_data = HexStr(data); const std::string hex_data = HexStr(data);
if (IsHex(random_hex_string)) { if (IsHex(random_hex_string)) {
assert(ToLower(random_hex_string) == hex_data); assert(ToLower(random_hex_string) == hex_data);

View file

@ -35,7 +35,7 @@ BOOST_AUTO_TEST_CASE(key_io_valid_parse)
continue; continue;
} }
std::string exp_base58string = test[0].get_str(); std::string exp_base58string = test[0].get_str();
std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str()); const std::vector<std::byte> exp_payload{ParseHex<std::byte>(test[1].get_str())};
const UniValue &metadata = test[2].get_obj(); const UniValue &metadata = test[2].get_obj();
bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); bool isPrivkey = find_value(metadata, "isPrivkey").get_bool();
SelectParams(find_value(metadata, "chain").get_str()); SelectParams(find_value(metadata, "chain").get_str());

View file

@ -153,7 +153,7 @@ static const unsigned char ParseHex_expected[65] = {
0xde, 0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b, 0x8d, 0x57, 0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, 0x1d, 0xde, 0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b, 0x8d, 0x57, 0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, 0x1d,
0x5f 0x5f
}; };
BOOST_AUTO_TEST_CASE(util_ParseHex) BOOST_AUTO_TEST_CASE(parse_hex)
{ {
std::vector<unsigned char> result; std::vector<unsigned char> result;
std::vector<unsigned char> expected(ParseHex_expected, ParseHex_expected + sizeof(ParseHex_expected)); std::vector<unsigned char> expected(ParseHex_expected, ParseHex_expected + sizeof(ParseHex_expected));
@ -169,6 +169,14 @@ BOOST_AUTO_TEST_CASE(util_ParseHex)
result = ParseHex(" 89 34 56 78"); result = ParseHex(" 89 34 56 78");
BOOST_CHECK(result.size() == 4 && result[0] == 0x89 && result[1] == 0x34 && result[2] == 0x56 && result[3] == 0x78); BOOST_CHECK(result.size() == 4 && result[0] == 0x89 && result[1] == 0x34 && result[2] == 0x56 && result[3] == 0x78);
// Embedded null is treated as end
const std::string with_embedded_null{" 11 "s
" \0 "
" 22 "s};
BOOST_CHECK_EQUAL(with_embedded_null.size(), 11);
result = ParseHex(with_embedded_null);
BOOST_CHECK(result.size() == 1 && result[0] == 0x11);
// Stop parsing at invalid value // Stop parsing at invalid value
result = ParseHex("1234 invalid 1234"); result = ParseHex("1234 invalid 1234");
BOOST_CHECK(result.size() == 2 && result[0] == 0x12 && result[1] == 0x34); BOOST_CHECK(result.size() == 2 && result[0] == 0x12 && result[1] == 0x34);

View file

@ -77,10 +77,10 @@ bool IsHexNumber(std::string_view str)
return str.size() > 0; return str.size() > 0;
} }
std::vector<unsigned char> ParseHex(std::string_view str) template <typename Byte>
std::vector<Byte> ParseHex(std::string_view str)
{ {
// convert hex dump to vector std::vector<Byte> vch;
std::vector<unsigned char> vch;
auto it = str.begin(); auto it = str.begin();
while (it != str.end() && it + 1 != str.end()) { while (it != str.end() && it + 1 != str.end()) {
if (IsSpace(*it)) { if (IsSpace(*it)) {
@ -90,10 +90,12 @@ std::vector<unsigned char> ParseHex(std::string_view str)
auto c1 = HexDigit(*(it++)); auto c1 = HexDigit(*(it++));
auto c2 = HexDigit(*(it++)); auto c2 = HexDigit(*(it++));
if (c1 < 0 || c2 < 0) break; if (c1 < 0 || c2 < 0) break;
vch.push_back(uint8_t(c1 << 4) | c2); vch.push_back(Byte(c1 << 4) | Byte(c2));
} }
return vch; return vch;
} }
template std::vector<std::byte> ParseHex(std::string_view);
template std::vector<uint8_t> ParseHex(std::string_view);
void SplitHostPort(std::string_view in, uint16_t& portOut, std::string& hostOut) void SplitHostPort(std::string_view in, uint16_t& portOut, std::string& hostOut)
{ {

View file

@ -55,7 +55,9 @@ enum class ByteUnit : uint64_t {
* @return A new string without unsafe chars * @return A new string without unsafe chars
*/ */
std::string SanitizeString(std::string_view str, int rule = SAFE_CHARS_DEFAULT); std::string SanitizeString(std::string_view str, int rule = SAFE_CHARS_DEFAULT);
std::vector<unsigned char> ParseHex(std::string_view str); /** Parse the hex string into bytes (uint8_t or std::byte). Ignores whitespace. */
template <typename Byte = uint8_t>
std::vector<Byte> ParseHex(std::string_view str);
signed char HexDigit(char c); signed char HexDigit(char c);
/* Returns true if each character in str is a hex character, and has an even /* Returns true if each character in str is a hex character, and has an even
* number of hex digits.*/ * number of hex digits.*/