From fabdf81983e2542d60542b80fb94ccb1acdd204a Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Tue, 9 Nov 2021 20:17:48 +0100 Subject: [PATCH 1/3] test: Add test for embedded null in hex string Also, fix style in the corresponding function. The style change can be reviewed with "--word-diff-regex=." --- src/test/util_tests.cpp | 10 +++++++++- src/util/strencodings.cpp | 1 - 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 1ca20fd848..0b2f796078 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -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, 0x5f }; -BOOST_AUTO_TEST_CASE(util_ParseHex) +BOOST_AUTO_TEST_CASE(parse_hex) { std::vector result; std::vector expected(ParseHex_expected, ParseHex_expected + sizeof(ParseHex_expected)); @@ -169,6 +169,14 @@ BOOST_AUTO_TEST_CASE(util_ParseHex) result = ParseHex(" 89 34 56 78"); 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 result = ParseHex("1234 invalid 1234"); BOOST_CHECK(result.size() == 2 && result[0] == 0x12 && result[1] == 0x34); diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp index 35f62f0422..3556c0f4b0 100644 --- a/src/util/strencodings.cpp +++ b/src/util/strencodings.cpp @@ -78,7 +78,6 @@ bool IsHexNumber(std::string_view str) std::vector ParseHex(std::string_view str) { - // convert hex dump to vector std::vector vch; auto it = str.begin(); while (it != str.end() && it + 1 != str.end()) { From fae1006019188700e0c497a63fc1550fe00ca8bb Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Wed, 10 Nov 2021 10:49:58 +0100 Subject: [PATCH 2/3] util: Add ParseHex() helper --- src/test/fuzz/hex.cpp | 2 ++ src/util/strencodings.cpp | 9 ++++++--- src/util/strencodings.h | 4 +++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/test/fuzz/hex.cpp b/src/test/fuzz/hex.cpp index cc1bc1c8cf..e637975b48 100644 --- a/src/test/fuzz/hex.cpp +++ b/src/test/fuzz/hex.cpp @@ -25,6 +25,8 @@ FUZZ_TARGET_INIT(hex, initialize_hex) { const std::string random_hex_string(buffer.begin(), buffer.end()); const std::vector data = ParseHex(random_hex_string); + const std::vector bytes{ParseHex(random_hex_string)}; + assert(AsBytes(Span{data}) == Span{bytes}); const std::string hex_data = HexStr(data); if (IsHex(random_hex_string)) { assert(ToLower(random_hex_string) == hex_data); diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp index 3556c0f4b0..6f034c65a2 100644 --- a/src/util/strencodings.cpp +++ b/src/util/strencodings.cpp @@ -76,9 +76,10 @@ bool IsHexNumber(std::string_view str) return str.size() > 0; } -std::vector ParseHex(std::string_view str) +template +std::vector ParseHex(std::string_view str) { - std::vector vch; + std::vector vch; auto it = str.begin(); while (it != str.end() && it + 1 != str.end()) { if (IsSpace(*it)) { @@ -88,10 +89,12 @@ std::vector ParseHex(std::string_view str) auto c1 = HexDigit(*(it++)); auto c2 = HexDigit(*(it++)); if (c1 < 0 || c2 < 0) break; - vch.push_back(uint8_t(c1 << 4) | c2); + vch.push_back(Byte(c1 << 4) | Byte(c2)); } return vch; } +template std::vector ParseHex(std::string_view); +template std::vector ParseHex(std::string_view); void SplitHostPort(std::string_view in, uint16_t& portOut, std::string& hostOut) { diff --git a/src/util/strencodings.h b/src/util/strencodings.h index ebb6d88952..ee58383528 100644 --- a/src/util/strencodings.h +++ b/src/util/strencodings.h @@ -55,7 +55,9 @@ enum class ByteUnit : uint64_t { * @return A new string without unsafe chars */ std::string SanitizeString(std::string_view str, int rule = SAFE_CHARS_DEFAULT); -std::vector ParseHex(std::string_view str); +/** Parse the hex string into bytes (uint8_t or std::byte). Ignores whitespace. */ +template +std::vector ParseHex(std::string_view str); signed char HexDigit(char c); /* Returns true if each character in str is a hex character, and has an even * number of hex digits.*/ From facd1fb911abfc595a3484ee53397eff515d4c40 Mon Sep 17 00:00:00 2001 From: MarcoFalke Date: Mon, 8 Nov 2021 14:27:35 +0100 Subject: [PATCH 3/3] refactor: Use Span of std::byte in CExtKey::SetSeed --- src/key.cpp | 4 ++-- src/key.h | 4 ++-- src/test/bip32_tests.cpp | 5 +++-- src/test/key_io_tests.cpp | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/key.cpp b/src/key.cpp index d1d521f97d..9b0971a2dd 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -340,11 +340,11 @@ bool CExtKey::Derive(CExtKey &out, unsigned int _nChild) const { return key.Derive(out.key, out.chaincode, _nChild, chaincode); } -void CExtKey::SetSeed(Span seed) +void CExtKey::SetSeed(Span seed) { static const unsigned char hashkey[] = {'B','i','t','c','o','i','n',' ','s','e','e','d'}; std::vector> 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); memcpy(chaincode.begin(), vout.data() + 32, 32); nDepth = 0; diff --git a/src/key.h b/src/key.h index b21e658107..12d03778a0 100644 --- a/src/key.h +++ b/src/key.h @@ -85,7 +85,7 @@ public: //! Simple read-only vector-like interface. 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(keydata.data()); } const unsigned char* begin() const { return keydata.data(); } const unsigned char* end() const { return keydata.data() + size(); } @@ -178,7 +178,7 @@ struct CExtKey { void Decode(const unsigned char code[BIP32_EXTKEY_SIZE]); bool Derive(CExtKey& out, unsigned int nChild) const; CExtPubKey Neuter() const; - void SetSeed(Span seed); + void SetSeed(Span seed); }; /** Initialize the elliptic curve support. May not be called twice without calling ECC_Stop first. */ diff --git a/src/test/bip32_tests.cpp b/src/test/bip32_tests.cpp index 0fa6b7784f..64cc924239 100644 --- a/src/test/bip32_tests.cpp +++ b/src/test/bip32_tests.cpp @@ -120,8 +120,9 @@ const std::vector TEST5 = { "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHL" }; -void RunTest(const TestVector &test) { - std::vector seed = ParseHex(test.strHexMaster); +void RunTest(const TestVector& test) +{ + std::vector seed{ParseHex(test.strHexMaster)}; CExtKey key; CExtPubKey pubkey; key.SetSeed(seed); diff --git a/src/test/key_io_tests.cpp b/src/test/key_io_tests.cpp index b06157e99f..e70b8b3dfd 100644 --- a/src/test/key_io_tests.cpp +++ b/src/test/key_io_tests.cpp @@ -35,7 +35,7 @@ BOOST_AUTO_TEST_CASE(key_io_valid_parse) continue; } std::string exp_base58string = test[0].get_str(); - std::vector exp_payload = ParseHex(test[1].get_str()); + const std::vector exp_payload{ParseHex(test[1].get_str())}; const UniValue &metadata = test[2].get_obj(); bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); SelectParams(find_value(metadata, "chain").get_str());