mirror of
https://github.com/NixOS/nix.git
synced 2025-11-30 22:20:59 +01:00
Merge pull request #13693 from obsidiansystems/more-base-files
Clean up Base* code
This commit is contained in:
commit
e25ab029ae
18 changed files with 357 additions and 244 deletions
|
|
@ -2,6 +2,7 @@
|
||||||
#include "nix/fetchers/git-lfs-fetch.hh"
|
#include "nix/fetchers/git-lfs-fetch.hh"
|
||||||
#include "nix/fetchers/cache.hh"
|
#include "nix/fetchers/cache.hh"
|
||||||
#include "nix/fetchers/fetch-settings.hh"
|
#include "nix/fetchers/fetch-settings.hh"
|
||||||
|
#include "nix/util/base-n.hh"
|
||||||
#include "nix/util/finally.hh"
|
#include "nix/util/finally.hh"
|
||||||
#include "nix/util/processes.hh"
|
#include "nix/util/processes.hh"
|
||||||
#include "nix/util/signals.hh"
|
#include "nix/util/signals.hh"
|
||||||
|
|
@ -608,7 +609,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
|
||||||
// Calculate sha256 fingerprint from public key and escape the regex symbol '+' to match the key literally
|
// Calculate sha256 fingerprint from public key and escape the regex symbol '+' to match the key literally
|
||||||
std::string keyDecoded;
|
std::string keyDecoded;
|
||||||
try {
|
try {
|
||||||
keyDecoded = base64Decode(k.key);
|
keyDecoded = base64::decode(k.key);
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace({}, "while decoding public key '%s' used for git signature", k.key);
|
e.addTrace({}, "while decoding public key '%s' used for git signature", k.key);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include "nix/util/base-n.hh"
|
||||||
#include "nix/store/machines.hh"
|
#include "nix/store/machines.hh"
|
||||||
#include "nix/store/globals.hh"
|
#include "nix/store/globals.hh"
|
||||||
#include "nix/store/store-open.hh"
|
#include "nix/store/store-open.hh"
|
||||||
|
|
@ -158,7 +159,7 @@ static Machine parseBuilderLine(const StringSet & defaultSystems, const std::str
|
||||||
auto ensureBase64 = [&](size_t fieldIndex) {
|
auto ensureBase64 = [&](size_t fieldIndex) {
|
||||||
const auto & str = tokens[fieldIndex];
|
const auto & str = tokens[fieldIndex];
|
||||||
try {
|
try {
|
||||||
base64Decode(str);
|
base64::decode(str);
|
||||||
} catch (FormatError & e) {
|
} catch (FormatError & e) {
|
||||||
e.addTrace({}, "while parsing machine specification at a column #%lu in a row: '%s'", fieldIndex, line);
|
e.addTrace({}, "while parsing machine specification at a column #%lu in a row: '%s'", fieldIndex, line);
|
||||||
throw;
|
throw;
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,14 @@
|
||||||
#include "nix/util/environment-variables.hh"
|
#include "nix/util/environment-variables.hh"
|
||||||
#include "nix/util/util.hh"
|
#include "nix/util/util.hh"
|
||||||
#include "nix/util/exec.hh"
|
#include "nix/util/exec.hh"
|
||||||
|
#include "nix/util/base-n.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
static std::string parsePublicHostKey(std::string_view host, std::string_view sshPublicHostKey)
|
static std::string parsePublicHostKey(std::string_view host, std::string_view sshPublicHostKey)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
return base64Decode(sshPublicHostKey);
|
return base64::decode(sshPublicHostKey);
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace({}, "while decoding ssh public host key for host '%s'", host);
|
e.addTrace({}, "while decoding ssh public host key for host '%s'", host);
|
||||||
throw;
|
throw;
|
||||||
|
|
|
||||||
68
src/libutil-tests/base-n.cc
Normal file
68
src/libutil-tests/base-n.cc
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <numeric>
|
||||||
|
|
||||||
|
#include "nix/util/base-n.hh"
|
||||||
|
#include "nix/util/error.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
static const std::span<const std::byte> stringToByteSpan(const std::string_view s)
|
||||||
|
{
|
||||||
|
return {(const std::byte *) s.data(), s.size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
* base64::encode
|
||||||
|
* --------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
TEST(base64Encode, emptyString)
|
||||||
|
{
|
||||||
|
ASSERT_EQ(base64::encode(stringToByteSpan("")), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(base64Encode, encodesAString)
|
||||||
|
{
|
||||||
|
ASSERT_EQ(base64::encode(stringToByteSpan("quod erat demonstrandum")), "cXVvZCBlcmF0IGRlbW9uc3RyYW5kdW0=");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(base64Encode, encodeAndDecode)
|
||||||
|
{
|
||||||
|
auto s = "quod erat demonstrandum";
|
||||||
|
auto encoded = base64::encode(stringToByteSpan(s));
|
||||||
|
auto decoded = base64::decode(encoded);
|
||||||
|
|
||||||
|
ASSERT_EQ(decoded, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(base64Encode, encodeAndDecodeNonPrintable)
|
||||||
|
{
|
||||||
|
char s[256];
|
||||||
|
std::iota(std::rbegin(s), std::rend(s), 0);
|
||||||
|
|
||||||
|
auto encoded = base64::encode(std::as_bytes(std::span<const char>{std::string_view{s}}));
|
||||||
|
auto decoded = base64::decode(encoded);
|
||||||
|
|
||||||
|
EXPECT_EQ(decoded.length(), 255u);
|
||||||
|
ASSERT_EQ(decoded, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
* base64::decode
|
||||||
|
* --------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
TEST(base64Decode, emptyString)
|
||||||
|
{
|
||||||
|
ASSERT_EQ(base64::decode(""), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(base64Decode, decodeAString)
|
||||||
|
{
|
||||||
|
ASSERT_EQ(base64::decode("cXVvZCBlcmF0IGRlbW9uc3RyYW5kdW0="), "quod erat demonstrandum");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(base64Decode, decodeThrowsOnInvalidChar)
|
||||||
|
{
|
||||||
|
ASSERT_THROW(base64::decode("cXVvZCBlcm_0IGRlbW9uc3RyYW5kdW0="), Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace nix
|
||||||
|
|
@ -44,6 +44,7 @@ subdir('nix-meson-build-support/common')
|
||||||
|
|
||||||
sources = files(
|
sources = files(
|
||||||
'args.cc',
|
'args.cc',
|
||||||
|
'base-n.cc',
|
||||||
'canon-path.cc',
|
'canon-path.cc',
|
||||||
'checked-arithmetic.cc',
|
'checked-arithmetic.cc',
|
||||||
'chunked-vector.cc',
|
'chunked-vector.cc',
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#include "nix/util/file-system.hh"
|
#include "nix/util/file-system.hh"
|
||||||
#include "nix/util/terminal.hh"
|
#include "nix/util/terminal.hh"
|
||||||
#include "nix/util/strings.hh"
|
#include "nix/util/strings.hh"
|
||||||
|
#include "nix/util/base-n.hh"
|
||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
@ -48,60 +49,6 @@ TEST(hasSuffix, trivialCase)
|
||||||
ASSERT_TRUE(hasSuffix("foobar", "bar"));
|
ASSERT_TRUE(hasSuffix("foobar", "bar"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
|
||||||
* base64Encode
|
|
||||||
* --------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
TEST(base64Encode, emptyString)
|
|
||||||
{
|
|
||||||
ASSERT_EQ(base64Encode(""), "");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(base64Encode, encodesAString)
|
|
||||||
{
|
|
||||||
ASSERT_EQ(base64Encode("quod erat demonstrandum"), "cXVvZCBlcmF0IGRlbW9uc3RyYW5kdW0=");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(base64Encode, encodeAndDecode)
|
|
||||||
{
|
|
||||||
auto s = "quod erat demonstrandum";
|
|
||||||
auto encoded = base64Encode(s);
|
|
||||||
auto decoded = base64Decode(encoded);
|
|
||||||
|
|
||||||
ASSERT_EQ(decoded, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(base64Encode, encodeAndDecodeNonPrintable)
|
|
||||||
{
|
|
||||||
char s[256];
|
|
||||||
std::iota(std::rbegin(s), std::rend(s), 0);
|
|
||||||
|
|
||||||
auto encoded = base64Encode(s);
|
|
||||||
auto decoded = base64Decode(encoded);
|
|
||||||
|
|
||||||
EXPECT_EQ(decoded.length(), 255u);
|
|
||||||
ASSERT_EQ(decoded, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
|
||||||
* base64Decode
|
|
||||||
* --------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
TEST(base64Decode, emptyString)
|
|
||||||
{
|
|
||||||
ASSERT_EQ(base64Decode(""), "");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(base64Decode, decodeAString)
|
|
||||||
{
|
|
||||||
ASSERT_EQ(base64Decode("cXVvZCBlcmF0IGRlbW9uc3RyYW5kdW0="), "quod erat demonstrandum");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(base64Decode, decodeThrowsOnInvalidChar)
|
|
||||||
{
|
|
||||||
ASSERT_THROW(base64Decode("cXVvZCBlcm_0IGRlbW9uc3RyYW5kdW0="), Error);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------------
|
/* ----------------------------------------------------------------------------
|
||||||
* getLine
|
* getLine
|
||||||
* --------------------------------------------------------------------------*/
|
* --------------------------------------------------------------------------*/
|
||||||
|
|
|
||||||
114
src/libutil/base-n.cc
Normal file
114
src/libutil/base-n.cc
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#include "nix/util/array-from-string-literal.hh"
|
||||||
|
#include "nix/util/util.hh"
|
||||||
|
#include "nix/util/base-n.hh"
|
||||||
|
|
||||||
|
using namespace std::literals;
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
constexpr static const std::array<char, 16> base16Chars = "0123456789abcdef"_arrayNoNull;
|
||||||
|
|
||||||
|
std::string base16::encode(std::span<const std::byte> b)
|
||||||
|
{
|
||||||
|
std::string buf;
|
||||||
|
buf.reserve(b.size() * 2);
|
||||||
|
for (size_t i = 0; i < b.size(); i++) {
|
||||||
|
buf.push_back(base16Chars[(uint8_t) b.data()[i] >> 4]);
|
||||||
|
buf.push_back(base16Chars[(uint8_t) b.data()[i] & 0x0f]);
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string base16::decode(std::string_view s)
|
||||||
|
{
|
||||||
|
auto parseHexDigit = [&](char c) {
|
||||||
|
if (c >= '0' && c <= '9')
|
||||||
|
return c - '0';
|
||||||
|
if (c >= 'A' && c <= 'F')
|
||||||
|
return c - 'A' + 10;
|
||||||
|
if (c >= 'a' && c <= 'f')
|
||||||
|
return c - 'a' + 10;
|
||||||
|
throw FormatError("invalid character in Base16 string: '%c'", c);
|
||||||
|
};
|
||||||
|
|
||||||
|
assert(s.size() % 2 == 0);
|
||||||
|
auto decodedSize = s.size() / 2;
|
||||||
|
|
||||||
|
std::string res;
|
||||||
|
res.reserve(decodedSize);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < decodedSize; i++) {
|
||||||
|
res.push_back(parseHexDigit(s[i * 2]) << 4 | parseHexDigit(s[i * 2 + 1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr static const std::array<char, 64> base64Chars =
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"_arrayNoNull;
|
||||||
|
|
||||||
|
std::string base64::encode(std::span<const std::byte> s)
|
||||||
|
{
|
||||||
|
std::string res;
|
||||||
|
res.reserve((s.size() + 2) / 3 * 4);
|
||||||
|
int data = 0, nbits = 0;
|
||||||
|
|
||||||
|
for (std::byte c : s) {
|
||||||
|
data = data << 8 | (uint8_t) c;
|
||||||
|
nbits += 8;
|
||||||
|
while (nbits >= 6) {
|
||||||
|
nbits -= 6;
|
||||||
|
res.push_back(base64Chars[data >> nbits & 0x3f]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nbits)
|
||||||
|
res.push_back(base64Chars[data << (6 - nbits) & 0x3f]);
|
||||||
|
while (res.size() % 4)
|
||||||
|
res.push_back('=');
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string base64::decode(std::string_view s)
|
||||||
|
{
|
||||||
|
constexpr char npos = -1;
|
||||||
|
constexpr std::array<char, 256> base64DecodeChars = [&] {
|
||||||
|
std::array<char, 256> result{};
|
||||||
|
for (auto & c : result)
|
||||||
|
c = npos;
|
||||||
|
for (int i = 0; i < 64; i++)
|
||||||
|
result[base64Chars[i]] = i;
|
||||||
|
return result;
|
||||||
|
}();
|
||||||
|
|
||||||
|
std::string res;
|
||||||
|
// Some sequences are missing the padding consisting of up to two '='.
|
||||||
|
// vvv
|
||||||
|
res.reserve((s.size() + 2) / 4 * 3);
|
||||||
|
unsigned int d = 0, bits = 0;
|
||||||
|
|
||||||
|
for (char c : s) {
|
||||||
|
if (c == '=')
|
||||||
|
break;
|
||||||
|
if (c == '\n')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
char digit = base64DecodeChars[(unsigned char) c];
|
||||||
|
if (digit == npos)
|
||||||
|
throw FormatError("invalid character in Base64 string: '%c'", c);
|
||||||
|
|
||||||
|
bits += 6;
|
||||||
|
d = d << 6 | digit;
|
||||||
|
if (bits >= 8) {
|
||||||
|
res.push_back(d >> (bits - 8) & 0xff);
|
||||||
|
bits -= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace nix
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
#include "nix/util/base-nix-32.hh"
|
#include "nix/util/base-nix-32.hh"
|
||||||
|
#include "nix/util/util.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
@ -16,12 +17,12 @@ constexpr const std::array<unsigned char, 256> BaseNix32::reverseMap = [] {
|
||||||
return map;
|
return map;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
std::string BaseNix32::encode(std::span<const uint8_t> originalData)
|
std::string BaseNix32::encode(std::span<const std::byte> bs)
|
||||||
{
|
{
|
||||||
if (originalData.size() == 0)
|
if (bs.size() == 0)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
size_t len = encodedLength(originalData.size());
|
size_t len = encodedLength(bs.size());
|
||||||
assert(len);
|
assert(len);
|
||||||
|
|
||||||
std::string s;
|
std::string s;
|
||||||
|
|
@ -31,12 +32,42 @@ std::string BaseNix32::encode(std::span<const uint8_t> originalData)
|
||||||
unsigned int b = n * 5;
|
unsigned int b = n * 5;
|
||||||
unsigned int i = b / 8;
|
unsigned int i = b / 8;
|
||||||
unsigned int j = b % 8;
|
unsigned int j = b % 8;
|
||||||
unsigned char c =
|
std::byte c = (bs.data()[i] >> j) | (i >= bs.size() - 1 ? std::byte{0} : bs.data()[i + 1] << (8 - j));
|
||||||
(originalData.data()[i] >> j) | (i >= originalData.size() - 1 ? 0 : originalData.data()[i + 1] << (8 - j));
|
s.push_back(characters[uint8_t(c & std::byte{0x1f})]);
|
||||||
s.push_back(characters[c & 0x1f]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string BaseNix32::decode(std::string_view s)
|
||||||
|
{
|
||||||
|
std::string res;
|
||||||
|
res.reserve((s.size() * 5 + 7) / 8); // ceiling(size * 5/8)
|
||||||
|
|
||||||
|
for (unsigned int n = 0; n < s.size(); ++n) {
|
||||||
|
char c = s[s.size() - n - 1];
|
||||||
|
auto digit_opt = BaseNix32::lookupReverse(c);
|
||||||
|
|
||||||
|
if (!digit_opt)
|
||||||
|
throw FormatError("invalid character in Nix32 (Nix's Base32 variation) string: '%c'", c);
|
||||||
|
|
||||||
|
uint8_t digit = *digit_opt;
|
||||||
|
|
||||||
|
unsigned int b = n * 5;
|
||||||
|
unsigned int i = b / 8;
|
||||||
|
unsigned int j = b % 8;
|
||||||
|
|
||||||
|
// Ensure res has enough space
|
||||||
|
res.resize(i + 1);
|
||||||
|
res[i] |= digit << j;
|
||||||
|
|
||||||
|
if (digit >> (8 - j)) {
|
||||||
|
res.resize(i + 2);
|
||||||
|
res[i + 1] |= digit >> (8 - j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace nix
|
} // namespace nix
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
#include "nix/util/archive.hh"
|
#include "nix/util/archive.hh"
|
||||||
#include "nix/util/configuration.hh"
|
#include "nix/util/configuration.hh"
|
||||||
#include "nix/util/split.hh"
|
#include "nix/util/split.hh"
|
||||||
|
#include "nix/util/base-n.hh"
|
||||||
#include "nix/util/base-nix-32.hh"
|
#include "nix/util/base-nix-32.hh"
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
@ -59,25 +60,6 @@ std::strong_ordering Hash::operator<=>(const Hash & h) const noexcept
|
||||||
return std::strong_ordering::equivalent;
|
return std::strong_ordering::equivalent;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string base16Chars = "0123456789abcdef";
|
|
||||||
|
|
||||||
static std::string printHash16(const Hash & hash)
|
|
||||||
{
|
|
||||||
std::string buf;
|
|
||||||
buf.reserve(hash.hashSize * 2);
|
|
||||||
for (unsigned int i = 0; i < hash.hashSize; i++) {
|
|
||||||
buf.push_back(base16Chars[hash.hash[i] >> 4]);
|
|
||||||
buf.push_back(base16Chars[hash.hash[i] & 0x0f]);
|
|
||||||
}
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string printHash32(const Hash & hash)
|
|
||||||
{
|
|
||||||
assert(hash.hashSize);
|
|
||||||
return BaseNix32::encode({&hash.hash[0], hash.hashSize});
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string printHash16or32(const Hash & hash)
|
std::string printHash16or32(const Hash & hash)
|
||||||
{
|
{
|
||||||
assert(static_cast<char>(hash.algo));
|
assert(static_cast<char>(hash.algo));
|
||||||
|
|
@ -91,16 +73,20 @@ std::string Hash::to_string(HashFormat hashFormat, bool includeAlgo) const
|
||||||
s += printHashAlgo(algo);
|
s += printHashAlgo(algo);
|
||||||
s += hashFormat == HashFormat::SRI ? '-' : ':';
|
s += hashFormat == HashFormat::SRI ? '-' : ':';
|
||||||
}
|
}
|
||||||
|
const auto bytes = std::as_bytes(std::span<const uint8_t>{&hash[0], hashSize});
|
||||||
switch (hashFormat) {
|
switch (hashFormat) {
|
||||||
case HashFormat::Base16:
|
case HashFormat::Base16:
|
||||||
s += printHash16(*this);
|
assert(hashSize);
|
||||||
|
s += base16::encode(bytes);
|
||||||
break;
|
break;
|
||||||
case HashFormat::Nix32:
|
case HashFormat::Nix32:
|
||||||
s += printHash32(*this);
|
assert(hashSize);
|
||||||
|
s += BaseNix32::encode(bytes);
|
||||||
break;
|
break;
|
||||||
case HashFormat::Base64:
|
case HashFormat::Base64:
|
||||||
case HashFormat::SRI:
|
case HashFormat::SRI:
|
||||||
s += base64Encode(std::string_view((const char *) hash, hashSize));
|
assert(hashSize);
|
||||||
|
s += base64::encode(bytes);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return s;
|
return s;
|
||||||
|
|
@ -180,65 +166,40 @@ Hash Hash::parseNonSRIUnprefixed(std::string_view s, HashAlgorithm algo)
|
||||||
Hash::Hash(std::string_view rest, HashAlgorithm algo, bool isSRI)
|
Hash::Hash(std::string_view rest, HashAlgorithm algo, bool isSRI)
|
||||||
: Hash(algo)
|
: Hash(algo)
|
||||||
{
|
{
|
||||||
if (!isSRI && rest.size() == base16Len()) {
|
auto [decode, formatName] = [&]() -> std::pair<decltype(base16::decode) *, std::string_view> {
|
||||||
|
if (isSRI) {
|
||||||
auto parseHexDigit = [&](char c) {
|
/* In the SRI case, we always are using Base64. If the
|
||||||
if (c >= '0' && c <= '9')
|
length is wrong, get an error later. */
|
||||||
return c - '0';
|
return {base64::decode, "SRI"};
|
||||||
if (c >= 'A' && c <= 'F')
|
|
||||||
return c - 'A' + 10;
|
|
||||||
if (c >= 'a' && c <= 'f')
|
|
||||||
return c - 'a' + 10;
|
|
||||||
throw BadHash("invalid base-16 hash '%s'", rest);
|
|
||||||
};
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < hashSize; i++) {
|
|
||||||
hash[i] = parseHexDigit(rest[i * 2]) << 4 | parseHexDigit(rest[i * 2 + 1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (!isSRI && rest.size() == base32Len()) {
|
|
||||||
|
|
||||||
for (unsigned int n = 0; n < rest.size(); ++n) {
|
|
||||||
char c = rest[rest.size() - n - 1];
|
|
||||||
auto digit_opt = BaseNix32::lookupReverse(c);
|
|
||||||
|
|
||||||
if (!digit_opt)
|
|
||||||
throw BadHash("invalid base-32 hash: '%s'", rest);
|
|
||||||
|
|
||||||
uint8_t digit = std::move(*digit_opt);
|
|
||||||
|
|
||||||
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 {
|
} else {
|
||||||
if (digit >> (8 - j))
|
/* Otherwise, decide via the length of the hash (for the
|
||||||
throw BadHash("invalid base-32 hash '%s'", rest);
|
given algorithm) what base encoding it is. */
|
||||||
}
|
|
||||||
}
|
if (rest.size() == base16::encodedLength(hashSize))
|
||||||
|
return {base16::decode, "base16"};
|
||||||
|
|
||||||
|
if (rest.size() == BaseNix32::encodedLength(hashSize))
|
||||||
|
return {BaseNix32::decode, "nix32"};
|
||||||
|
|
||||||
|
if (rest.size() == base64::encodedLength(hashSize))
|
||||||
|
return {base64::decode, "Base64"};
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (isSRI || rest.size() == base64Len()) {
|
throw BadHash("hash '%s' has wrong length for hash algorithm '%s'", rest, printHashAlgo(this->algo));
|
||||||
|
}();
|
||||||
|
|
||||||
std::string d;
|
std::string d;
|
||||||
try {
|
try {
|
||||||
d = base64Decode(rest);
|
d = decode(rest);
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace({}, "While decoding hash '%s'", rest);
|
e.addTrace({}, "While decoding hash '%s'", rest);
|
||||||
}
|
}
|
||||||
if (d.size() != hashSize)
|
if (d.size() != hashSize)
|
||||||
throw BadHash("invalid %s hash '%s'", isSRI ? "SRI" : "base-64", rest);
|
throw BadHash("invalid %s hash '%s' %d %d", formatName, rest);
|
||||||
assert(hashSize);
|
assert(hashSize);
|
||||||
memcpy(hash, d.data(), hashSize);
|
memcpy(hash, d.data(), hashSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
|
||||||
throw BadHash("hash '%s' has wrong length for hash algorithm '%s'", rest, printHashAlgo(this->algo));
|
|
||||||
}
|
|
||||||
|
|
||||||
Hash Hash::random(HashAlgorithm algo)
|
Hash Hash::random(HashAlgorithm algo)
|
||||||
{
|
{
|
||||||
Hash hash(algo);
|
Hash hash(algo);
|
||||||
|
|
|
||||||
27
src/libutil/include/nix/util/array-from-string-literal.hh
Normal file
27
src/libutil/include/nix/util/array-from-string-literal.hh
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
#pragma once
|
||||||
|
///@file
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
template<size_t sizeWithNull>
|
||||||
|
struct ArrayNoNullAdaptor
|
||||||
|
{
|
||||||
|
std::array<char, sizeWithNull - 1> data;
|
||||||
|
|
||||||
|
constexpr ArrayNoNullAdaptor(const char (&init)[sizeWithNull])
|
||||||
|
{
|
||||||
|
static_assert(sizeWithNull > 0);
|
||||||
|
std::copy_n(init, sizeWithNull - 1, data.data());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<ArrayNoNullAdaptor str>
|
||||||
|
constexpr auto operator""_arrayNoNull()
|
||||||
|
{
|
||||||
|
return str.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace nix
|
||||||
53
src/libutil/include/nix/util/base-n.hh
Normal file
53
src/libutil/include/nix/util/base-n.hh
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
#pragma once
|
||||||
|
///@file
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <span>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
namespace base16 {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the length of a base-16 representation of this many bytes.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] constexpr static inline size_t encodedLength(size_t origSize)
|
||||||
|
{
|
||||||
|
return origSize * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode arbitrary bytes as Base16.
|
||||||
|
*/
|
||||||
|
std::string encode(std::span<const std::byte> b);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode arbitrary Base16 string to bytes.
|
||||||
|
*/
|
||||||
|
std::string decode(std::string_view s);
|
||||||
|
|
||||||
|
} // namespace base16
|
||||||
|
|
||||||
|
namespace base64 {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the length of a base-64 representation of this many bytes.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] constexpr static inline size_t encodedLength(size_t origSize)
|
||||||
|
{
|
||||||
|
return ((4 * origSize / 3) + 3) & ~3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode arbitrary bytes as Base64.
|
||||||
|
*/
|
||||||
|
std::string encode(std::span<const std::byte> b);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode arbitrary Base64 string to bytes.
|
||||||
|
*/
|
||||||
|
std::string decode(std::string_view s);
|
||||||
|
|
||||||
|
} // namespace base64
|
||||||
|
|
||||||
|
} // namespace nix
|
||||||
|
|
@ -7,14 +7,14 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <span>
|
#include <span>
|
||||||
|
|
||||||
|
#include "nix/util/array-from-string-literal.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
struct BaseNix32
|
struct BaseNix32
|
||||||
{
|
{
|
||||||
/// omitted: E O U T
|
/// omitted: E O U T
|
||||||
constexpr static std::array<char, 32> characters = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a',
|
constexpr static std::array<char, 32> characters = "0123456789abcdfghijklmnpqrsvwxyz"_arrayNoNull;
|
||||||
'b', 'c', 'd', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
|
|
||||||
'n', 'p', 'q', 'r', 's', 'v', 'w', 'x', 'y', 'z'};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const std::array<uint8_t, 256> reverseMap;
|
static const std::array<uint8_t, 256> reverseMap;
|
||||||
|
|
@ -34,12 +34,14 @@ public:
|
||||||
/**
|
/**
|
||||||
* Returns the length of a base-32 representation of this hash.
|
* Returns the length of a base-32 representation of this hash.
|
||||||
*/
|
*/
|
||||||
static size_t encodedLength(size_t originalLength)
|
[[nodiscard]] constexpr static inline size_t encodedLength(size_t originalLength)
|
||||||
{
|
{
|
||||||
return (originalLength * 8 - 1) / 5 + 1;
|
return (originalLength * 8 - 1) / 5 + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string encode(std::span<const uint8_t> originalData);
|
static std::string encode(std::span<const std::byte> originalData);
|
||||||
|
|
||||||
|
static std::string decode(std::string_view s);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace nix
|
} // namespace nix
|
||||||
|
|
|
||||||
|
|
@ -110,30 +110,6 @@ public:
|
||||||
*/
|
*/
|
||||||
std::strong_ordering operator<=>(const Hash & h2) const noexcept;
|
std::strong_ordering operator<=>(const Hash & h2) const noexcept;
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the length of a base-16 representation of this hash.
|
|
||||||
*/
|
|
||||||
[[nodiscard]] size_t base16Len() const
|
|
||||||
{
|
|
||||||
return hashSize * 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the length of a base-32 representation of this hash.
|
|
||||||
*/
|
|
||||||
[[nodiscard]] size_t base32Len() const
|
|
||||||
{
|
|
||||||
return (hashSize * 8 - 1) / 5 + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the length of a base-64 representation of this hash.
|
|
||||||
*/
|
|
||||||
[[nodiscard]] size_t base64Len() const
|
|
||||||
{
|
|
||||||
return ((4 * hashSize / 3) + 3) & ~3;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a string representation of the hash, in base-16, base-32
|
* Return a string representation of the hash, in base-16, base-32
|
||||||
* or base-64. By default, this is prefixed by the hash algo
|
* or base-64. By default, this is prefixed by the hash algo
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ headers = files(
|
||||||
'archive.hh',
|
'archive.hh',
|
||||||
'args.hh',
|
'args.hh',
|
||||||
'args/root.hh',
|
'args/root.hh',
|
||||||
|
'array-from-string-literal.hh',
|
||||||
|
'base-n.hh',
|
||||||
'base-nix-32.hh',
|
'base-nix-32.hh',
|
||||||
'callback.hh',
|
'callback.hh',
|
||||||
'canon-path.hh',
|
'canon-path.hh',
|
||||||
|
|
|
||||||
|
|
@ -179,16 +179,6 @@ constexpr char treeLast[] = "└───";
|
||||||
constexpr char treeLine[] = "│ ";
|
constexpr char treeLine[] = "│ ";
|
||||||
constexpr char treeNull[] = " ";
|
constexpr char treeNull[] = " ";
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode arbitrary bytes as Base64.
|
|
||||||
*/
|
|
||||||
std::string base64Encode(std::string_view s);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decode arbitrary bytes to Base64.
|
|
||||||
*/
|
|
||||||
std::string base64Decode(std::string_view s);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove common leading whitespace from the lines in the string
|
* Remove common leading whitespace from the lines in the string
|
||||||
* 's'. For example, if every line is indented by at least 3 spaces,
|
* 's'. For example, if every line is indented by at least 3 spaces,
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,7 @@ subdir('nix-meson-build-support/common')
|
||||||
sources = [config_priv_h] + files(
|
sources = [config_priv_h] + files(
|
||||||
'archive.cc',
|
'archive.cc',
|
||||||
'args.cc',
|
'args.cc',
|
||||||
|
'base-n.cc',
|
||||||
'base-nix-32.cc',
|
'base-nix-32.cc',
|
||||||
'canon-path.cc',
|
'canon-path.cc',
|
||||||
'compression.cc',
|
'compression.cc',
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "nix/util/signature/local-keys.hh"
|
#include "nix/util/signature/local-keys.hh"
|
||||||
|
|
||||||
#include "nix/util/file-system.hh"
|
#include "nix/util/file-system.hh"
|
||||||
|
#include "nix/util/base-n.hh"
|
||||||
#include "nix/util/util.hh"
|
#include "nix/util/util.hh"
|
||||||
#include <sodium.h>
|
#include <sodium.h>
|
||||||
|
|
||||||
|
|
@ -25,7 +26,7 @@ Key::Key(std::string_view s, bool sensitiveValue)
|
||||||
if (name == "" || key == "")
|
if (name == "" || key == "")
|
||||||
throw FormatError("key is corrupt");
|
throw FormatError("key is corrupt");
|
||||||
|
|
||||||
key = base64Decode(key);
|
key = base64::decode(key);
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
std::string extra;
|
std::string extra;
|
||||||
if (!sensitiveValue)
|
if (!sensitiveValue)
|
||||||
|
|
@ -37,7 +38,7 @@ Key::Key(std::string_view s, bool sensitiveValue)
|
||||||
|
|
||||||
std::string Key::to_string() const
|
std::string Key::to_string() const
|
||||||
{
|
{
|
||||||
return name + ":" + base64Encode(key);
|
return name + ":" + base64::encode(std::as_bytes(std::span<const char>{key}));
|
||||||
}
|
}
|
||||||
|
|
||||||
SecretKey::SecretKey(std::string_view s)
|
SecretKey::SecretKey(std::string_view s)
|
||||||
|
|
@ -52,7 +53,7 @@ std::string SecretKey::signDetached(std::string_view data) const
|
||||||
unsigned char sig[crypto_sign_BYTES];
|
unsigned char sig[crypto_sign_BYTES];
|
||||||
unsigned long long sigLen;
|
unsigned long long sigLen;
|
||||||
crypto_sign_detached(sig, &sigLen, (unsigned char *) data.data(), data.size(), (unsigned char *) key.data());
|
crypto_sign_detached(sig, &sigLen, (unsigned char *) data.data(), data.size(), (unsigned char *) key.data());
|
||||||
return name + ":" + base64Encode(std::string((char *) sig, sigLen));
|
return name + ":" + base64::encode(std::as_bytes(std::span<const unsigned char>{sig, sigLen}));
|
||||||
}
|
}
|
||||||
|
|
||||||
PublicKey SecretKey::toPublicKey() const
|
PublicKey SecretKey::toPublicKey() const
|
||||||
|
|
@ -93,7 +94,7 @@ bool PublicKey::verifyDetachedAnon(std::string_view data, std::string_view sig)
|
||||||
{
|
{
|
||||||
std::string sig2;
|
std::string sig2;
|
||||||
try {
|
try {
|
||||||
sig2 = base64Decode(sig);
|
sig2 = base64::decode(sig);
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace({}, "while decoding signature '%s'", sig);
|
e.addTrace({}, "while decoding signature '%s'", sig);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -204,70 +204,6 @@ void ignoreExceptionExceptInterrupt(Verbosity lvl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr char base64Chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
||||||
|
|
||||||
std::string base64Encode(std::string_view s)
|
|
||||||
{
|
|
||||||
std::string res;
|
|
||||||
res.reserve((s.size() + 2) / 3 * 4);
|
|
||||||
int data = 0, nbits = 0;
|
|
||||||
|
|
||||||
for (char c : s) {
|
|
||||||
data = data << 8 | (unsigned char) c;
|
|
||||||
nbits += 8;
|
|
||||||
while (nbits >= 6) {
|
|
||||||
nbits -= 6;
|
|
||||||
res.push_back(base64Chars[data >> nbits & 0x3f]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nbits)
|
|
||||||
res.push_back(base64Chars[data << (6 - nbits) & 0x3f]);
|
|
||||||
while (res.size() % 4)
|
|
||||||
res.push_back('=');
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string base64Decode(std::string_view s)
|
|
||||||
{
|
|
||||||
constexpr char npos = -1;
|
|
||||||
constexpr std::array<char, 256> base64DecodeChars = [&] {
|
|
||||||
std::array<char, 256> result{};
|
|
||||||
for (auto & c : result)
|
|
||||||
c = npos;
|
|
||||||
for (int i = 0; i < 64; i++)
|
|
||||||
result[base64Chars[i]] = i;
|
|
||||||
return result;
|
|
||||||
}();
|
|
||||||
|
|
||||||
std::string res;
|
|
||||||
// Some sequences are missing the padding consisting of up to two '='.
|
|
||||||
// vvv
|
|
||||||
res.reserve((s.size() + 2) / 4 * 3);
|
|
||||||
unsigned int d = 0, bits = 0;
|
|
||||||
|
|
||||||
for (char c : s) {
|
|
||||||
if (c == '=')
|
|
||||||
break;
|
|
||||||
if (c == '\n')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
char digit = base64DecodeChars[(unsigned char) c];
|
|
||||||
if (digit == npos)
|
|
||||||
throw FormatError("invalid character in Base64 string: '%c'", c);
|
|
||||||
|
|
||||||
bits += 6;
|
|
||||||
d = d << 6 | digit;
|
|
||||||
if (bits >= 8) {
|
|
||||||
res.push_back(d >> (bits - 8) & 0xff);
|
|
||||||
bits -= 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string stripIndentation(std::string_view s)
|
std::string stripIndentation(std::string_view s)
|
||||||
{
|
{
|
||||||
size_t minIndent = 10000;
|
size_t minIndent = 10000;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue