diff --git a/lix/libutil/hash.cc b/lix/libutil/hash.cc index 68cb67dad..d4771a177 100644 --- a/lix/libutil/hash.cc +++ b/lix/libutil/hash.cc @@ -80,6 +80,33 @@ static std::string printHash16(const Hash & hash) } +// omitted: E O U T +const std::string base32Chars = "0123456789abcdfghijklmnpqrsvwxyz"; + + +static std::string printHash32(const Hash & hash) +{ + assert(hash.hashSize); + size_t len = hash.base32Len(); + assert(len); + + std::string s; + s.reserve(len); + + for (int n = (int) len - 1; n >= 0; n--) { + unsigned int b = n * 5; + unsigned int i = b / 8; + unsigned int j = b % 8; + unsigned char c = + (hash.hash[i] >> j) + | (i >= hash.hashSize - 1 ? 0 : hash.hash[i + 1] << (8 - j)); + s.push_back(base32Chars[c & 0x1f]); + } + + return s; +} + + std::string printHash16or32(const Hash & hash) { return hash.to_string(hash.type == HashType::MD5 ? Base::Base16 : Base::Base32, false); @@ -98,7 +125,7 @@ std::string Hash::to_string(Base base, bool includeType) const s += printHash16(*this); break; case Base::Base32: - s += base32Encode(std::string_view(charptr_cast(hash), hashSize)); + s += printHash32(*this); break; case Base::Base64: case Base::SRI: @@ -198,8 +225,26 @@ Hash::Hash(std::string_view rest, HashType type, bool isSRI) } else if (!isSRI && rest.size() == base32Len()) { - auto d = base32Decode(rest); - memcpy(hash, d.data(), hashSize); + + for (unsigned int n = 0; n < rest.size(); ++n) { + char c = rest[rest.size() - n - 1]; + size_t digit; + for (digit = 0; digit < base32Chars.size(); ++digit) /* !!! slow */ + if (base32Chars[digit] == c) break; + if (digit >= 32) + throw BadHash("invalid base-32 hash '%s'", rest); + unsigned int b = n * 5; + unsigned int i = b / 8; + unsigned int j = b % 8; + hash[i] |= digit << j; + + if (i < hashSize - 1) { + hash[i + 1] |= digit >> (8 - j); + } else { + if (digit >> (8 - j)) + throw BadHash("invalid base-32 hash '%s'", rest); + } + } } else if (isSRI || rest.size() == base64Len()) { diff --git a/lix/libutil/hash.hh b/lix/libutil/hash.hh index 79858fd99..7e8910ed0 100644 --- a/lix/libutil/hash.hh +++ b/lix/libutil/hash.hh @@ -32,6 +32,8 @@ const int sha512HashSize = 64; extern std::set hashTypes; +extern const std::string base32Chars; + enum class Base : int { Base64, Base32, Base16, SRI }; diff --git a/lix/libutil/references.cc b/lix/libutil/references.cc index 5055d122b..1b9a2a955 100644 --- a/lix/libutil/references.cc +++ b/lix/libutil/references.cc @@ -1,5 +1,4 @@ #include "lix/libutil/references.hh" -#include "lix/libutil/strings.hh" #include "lix/libutil/hash.hh" #include "lix/libutil/logging.hh" diff --git a/lix/libutil/strings.cc b/lix/libutil/strings.cc index 9349c1e25..1c32b871f 100644 --- a/lix/libutil/strings.cc +++ b/lix/libutil/strings.cc @@ -195,66 +195,6 @@ std::string base64Decode(std::string_view s) return res; } -// omitted: E O U T -const std::string base32Chars = "0123456789abcdfghijklmnpqrsvwxyz"; - -std::string base32Encode(std::string_view s) -{ - if (s.empty()) { - return ""; - } - - size_t len = (s.size() * 8 - 1) / 5 + 1; - - std::string res; - res.reserve(len); - - for (int n = (int) len - 1; n >= 0; n--) { - unsigned int b = n * 5; - unsigned int i = b / 8; - unsigned int j = b % 8; - unsigned char c = (static_cast(s[i]) >> j) | (i >= s.size() - 1 ? 0 : static_cast(s[i + 1]) << (8 - j)); - res.push_back(base32Chars[c & 0x1f]); - } - - return res; -} - -std::string base32Decode(std::string_view s) -{ - if (s.empty()) { - return ""; - } - - std::string res(((s.size() - 1) * 5) / 8 + 1, 0); - - for (unsigned int n = 0; n < s.size(); ++n) { - char c = s[s.size() - n - 1]; - size_t digit; - for (digit = 0; digit < base32Chars.size(); ++digit) /* !!! slow */ { - if (base32Chars[digit] == c) { - break; - } - } - if (digit >= 32) { - throw Error("invalid character in base-32 string '%s'", s); - } - unsigned int b = n * 5; - unsigned int i = b / 8; - unsigned int j = b % 8; - res[i] |= digit << j; - - if (i < s.size() - 1) { - res[i + 1] |= digit >> (8 - j); - } else { - if (digit >> (8 - j)) { - throw Error("invalid base-32 string '%s'", s); - } - } - } - - return res; -} std::string stripIndentation(std::string_view s) { diff --git a/lix/libutil/strings.hh b/lix/libutil/strings.hh index de575b463..67238824a 100644 --- a/lix/libutil/strings.hh +++ b/lix/libutil/strings.hh @@ -197,12 +197,6 @@ std::string shellEscape(const std::string_view s); std::string base64Encode(std::string_view s); std::string base64Decode(std::string_view s); -/** - * Base32 encoding/decoding. - */ -extern const std::string base32Chars; -std::string base32Encode(std::string_view s); -std::string base32Decode(std::string_view s); /** * Remove common leading whitespace from the lines in the string diff --git a/tests/unit/libutil/tests.cc b/tests/unit/libutil/tests.cc index 7982b87c3..263fd7834 100644 --- a/tests/unit/libutil/tests.cc +++ b/tests/unit/libutil/tests.cc @@ -364,53 +364,6 @@ namespace nix { ASSERT_THROW(base64Decode("cXVvZCBlcm_0IGRlbW9uc3RyYW5kdW0="), Error); } - /* ---------------------------------------------------------------------------- - * base32Encode - * --------------------------------------------------------------------------*/ - - TEST(base32Encode, emptyString) { - ASSERT_EQ(base32Encode(""), ""); - } - - TEST(base32Encode, encodesAString) { - ASSERT_EQ(base32Encode("quod erat demonstrandum"), "6sxb4drhp4x3kdrpnsrb441s62wk541j6yxbi"); - } - - TEST(base32Encode, encodeAndDecode) { - auto s = "quod erat demonstrandum"; - auto encoded = base32Encode(s); - auto decoded = base32Decode(encoded); - - ASSERT_EQ(decoded, s); - } - - TEST(base32Encode, encodeAndDecodeNonPrintable) { - char s[256]; - std::iota(std::rbegin(s), std::rend(s), 0); - - auto encoded = base32Encode(s); - auto decoded = base32Decode(encoded); - - EXPECT_EQ(decoded.length(), 255); - ASSERT_EQ(decoded, s); - } - - /* ---------------------------------------------------------------------------- - * base32Decode - * --------------------------------------------------------------------------*/ - - TEST(base32Decode, emptyString) { - ASSERT_EQ(base32Decode(""), ""); - } - - TEST(base32Decode, decodeAString) { - ASSERT_EQ(base32Decode("6sxb4drhp4x3kdrpnsrb441s62wk541j6yxbi"), "quod erat demonstrandum"); - } - - TEST(base32Decode, decodeThrowsOnInvalidChar) { - ASSERT_THROW(base32Decode("6sxb4drhp4x3kdrpnsrb441s62wk541j6yxbe"), Error); - } - /* ---------------------------------------------------------------------------- * getLine * --------------------------------------------------------------------------*/