aboutsummaryrefslogtreecommitdiff
path: root/toolsrc/src
diff options
context:
space:
mode:
authorNicole Mazzuca <mazzucan@outlook.com>2019-08-08 16:14:59 -0700
committernicole mazzuca <mazzucan@outlook.com>2019-08-26 12:35:22 -0700
commit782723959399a1a0725ac4921b1b7a7c9d10baf7 (patch)
tree4e611c1b585180f438c692b3bfa367e89bf4a9b6 /toolsrc/src
parente417ff69b746f7842b3b9d0fceacb080498e1c5d (diff)
downloadvcpkg-782723959399a1a0725ac4921b1b7a7c9d10baf7.tar.gz
vcpkg-782723959399a1a0725ac4921b1b7a7c9d10baf7.zip
(#7757) [vcpkg] Switch to internal hash algorithms 📜
On non-Windows platforms, there is no standard way to get the hash of an item -- before this PR, what we did was check for the existence of a few common utility names (shasum, sha1, sha256, sha512), and then call that utility on a file we created containing the contents we wish to hash. This PR adds internal hashers for sha1, sha256, and sha512, and standardizes the interface to allow anyone to implement hashers in the future. These hashers are not extremely optimized, so it's likely that in the future we could get more optimized, but for now we just call out to BCryptHasher on Windows, since it's standard and easy to use (and about 2x faster for sha1 and sha256, and 1.5x faster for sha512). However, they are reasonably fast for being unoptimized. I attempted a few minor optimizations, which actually made the code slower! So as of right now, it's implemented as just a basic conversion of the code on Wikipedia to C++. I have tested these on the standard NIST test vectors (and those test vectors are located in vcpkg-test/hash.cpp).
Diffstat (limited to 'toolsrc/src')
-rw-r--r--toolsrc/src/vcpkg-test/hash.cpp276
-rw-r--r--toolsrc/src/vcpkg/base/downloads.cpp2
-rw-r--r--toolsrc/src/vcpkg/base/hash.cpp830
-rw-r--r--toolsrc/src/vcpkg/base/stringview.cpp6
-rw-r--r--toolsrc/src/vcpkg/build.cpp29
-rw-r--r--toolsrc/src/vcpkg/commands.cpp11
-rw-r--r--toolsrc/src/vcpkg/metrics.cpp2
7 files changed, 984 insertions, 172 deletions
diff --git a/toolsrc/src/vcpkg-test/hash.cpp b/toolsrc/src/vcpkg-test/hash.cpp
new file mode 100644
index 000000000..9f3ccc25e
--- /dev/null
+++ b/toolsrc/src/vcpkg-test/hash.cpp
@@ -0,0 +1,276 @@
+#include <catch2/catch.hpp>
+
+#include <vcpkg/base/hash.h>
+
+#include <algorithm>
+#include <iostream>
+#include <iterator>
+#include <map>
+
+namespace Hash = vcpkg::Hash;
+using vcpkg::StringView;
+
+// Require algorithm: Hash::Algorithm::Tag to be in scope
+#define CHECK_HASH(size, value, real_hash) \
+ do \
+ { \
+ unsigned char data[size]; \
+ std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(value)); \
+ const auto hash = Hash::get_bytes_hash(data, data + size, algorithm); \
+ REQUIRE(hash == real_hash); \
+ } while (0)
+
+#define CHECK_HASH_OF(data, real_hash) \
+ do \
+ { \
+ const auto hash = Hash::get_bytes_hash(std::begin(data), std::end(data), algorithm); \
+ REQUIRE(hash == real_hash); \
+ } while (0)
+
+#define CHECK_HASH_STRING(data, real_hash) \
+ do \
+ { \
+ const auto hash = Hash::get_string_hash(data, algorithm); \
+ REQUIRE(hash == real_hash); \
+ } while (0)
+
+// Requires hasher: std::unique_ptr<Hash::Hasher> to be in scope
+#define CHECK_HASH_LARGE(size, value, real_hash) \
+ do \
+ { \
+ hasher->clear(); \
+ std::uint64_t remaining = size; \
+ unsigned char buffer[512]; \
+ std::fill(std::begin(buffer), std::end(buffer), static_cast<unsigned char>(value)); \
+ while (remaining) \
+ { \
+ if (remaining < 512) \
+ { \
+ hasher->add_bytes(std::begin(buffer), std::begin(buffer) + remaining); \
+ remaining = 0; \
+ } \
+ else \
+ { \
+ hasher->add_bytes(std::begin(buffer), std::end(buffer)); \
+ remaining -= 512; \
+ } \
+ } \
+ REQUIRE(hasher->get_hash() == real_hash); \
+ } while (0)
+
+TEST_CASE ("SHA1: basic tests", "[hash][sha1]")
+{
+ const auto algorithm = Hash::Algorithm::Sha1;
+
+ CHECK_HASH_STRING("", "da39a3ee5e6b4b0d3255bfef95601890afd80709");
+ CHECK_HASH_STRING(";", "2d14ab97cc3dc294c51c0d6814f4ea45f4b4e312");
+ CHECK_HASH_STRING("asdifasdfnas", "b77eb8a1b4c2ef6716d7d302647e4511b1a638a6");
+ CHECK_HASH_STRING("asdfanvoinaoifawenflawenfiwnofvnasfjvnaslkdfjlkasjdfanm,"
+ "werflawoienfowanevoinwai32910u2740918741o;j;wejfqwioaher9283hrpf;asd",
+ "c69bcd30c196c7050906d212722dd7a7659aad04");
+}
+
+TEST_CASE ("SHA1: NIST test cases (small)", "[hash][sha1]")
+{
+ const auto algorithm = Hash::Algorithm::Sha1;
+
+ CHECK_HASH_STRING("abc", "a9993e364706816aba3e25717850c26c9cd0d89d");
+ CHECK_HASH_STRING("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ "84983e441c3bd26ebaae4aa1f95129e5e54670f1");
+}
+
+TEST_CASE ("SHA256: basic tests", "[hash][sha256]")
+{
+ const auto algorithm = Hash::Algorithm::Sha256;
+
+ CHECK_HASH_STRING("", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
+ CHECK_HASH_STRING(";", "41b805ea7ac014e23556e98bb374702a08344268f92489a02f0880849394a1e4");
+ CHECK_HASH_STRING("asdifasdfnas", "2bb1fb910831fdc11d5a3996425a84ace27aeb81c9c20ace9f60ac1b3218b291");
+ CHECK_HASH_STRING("asdfanvoinaoifawenflawenfiwnofvnasfjvnaslkdfjlkasjdfanm,"
+ "werflawoienfowanevoinwai32910u2740918741o;j;wejfqwioaher9283hrpf;asd",
+ "10c98034b424d4e40ca933bc524ea38b4e53290d76e8b38edc4ea2fec7f529aa");
+}
+
+TEST_CASE ("SHA256: NIST test cases (small)", "[hash][sha256]")
+{
+ const auto algorithm = Hash::Algorithm::Sha256;
+
+ CHECK_HASH(1, 0xbd, "68325720aabd7c82f30f554b313d0570c95accbb7dc4b5aae11204c08ffe732b");
+ {
+ const unsigned char data[] = {0xc9, 0x8c, 0x8e, 0x55};
+ CHECK_HASH_OF(data, "7abc22c0ae5af26ce93dbb94433a0e0b2e119d014f8e7f65bd56c61ccccd9504");
+ }
+ CHECK_HASH(55, 0, "02779466cdec163811d078815c633f21901413081449002f24aa3e80f0b88ef7");
+ CHECK_HASH(56, 0, "d4817aa5497628e7c77e6b606107042bbba3130888c5f47a375e6179be789fbb");
+ CHECK_HASH(57, 0, "65a16cb7861335d5ace3c60718b5052e44660726da4cd13bb745381b235a1785");
+ CHECK_HASH(64, 0, "f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b");
+ CHECK_HASH(1000, 0, "541b3e9daa09b20bf85fa273e5cbd3e80185aa4ec298e765db87742b70138a53");
+ CHECK_HASH(1000, 'A', "c2e686823489ced2017f6059b8b239318b6364f6dcd835d0a519105a1eadd6e4");
+ CHECK_HASH(1005, 'U', "f4d62ddec0f3dd90ea1380fa16a5ff8dc4c54b21740650f24afc4120903552b0");
+}
+
+TEST_CASE ("SHA512: NIST test cases (small)", "[hash][sha512]")
+{
+ const auto algorithm = Hash::Algorithm::Sha512;
+
+ CHECK_HASH_STRING("",
+ "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f"
+ "63b931bd47417a81a538327af927da3e");
+
+ CHECK_HASH(111,
+ 0,
+ "77ddd3a542e530fd047b8977c657ba6ce72f1492e360b2b2212cd264e75ec03882e4ff0525517ab4207d14c70c2259ba88d4d33"
+ "5ee0e7e20543d22102ab1788c");
+ CHECK_HASH(112,
+ 0,
+ "2be2e788c8a8adeaa9c89a7f78904cacea6e39297d75e0573a73c756234534d6627ab4156b48a6657b29ab8beb73334040ad39e"
+ "ad81446bb09c70704ec707952");
+ CHECK_HASH(113,
+ 0,
+ "0e67910bcf0f9ccde5464c63b9c850a12a759227d16b040d98986d54253f9f34322318e56b8feb86c5fb2270ed87f31252f7f68"
+ "493ee759743909bd75e4bb544");
+ CHECK_HASH(122,
+ 0,
+ "4f3f095d015be4a7a7cc0b8c04da4aa09e74351e3a97651f744c23716ebd9b3e822e5077a01baa5cc0ed45b9249e88ab343d433"
+ "3539df21ed229da6f4a514e0f");
+ CHECK_HASH(1000,
+ 0,
+ "ca3dff61bb23477aa6087b27508264a6f9126ee3a004f53cb8db942ed345f2f2d229b4b59c859220a1cf1913f34248e3803bab6"
+ "50e849a3d9a709edc09ae4a76");
+ CHECK_HASH(1000,
+ 'A',
+ "329c52ac62d1fe731151f2b895a00475445ef74f50b979c6f7bb7cae349328c1d4cb4f7261a0ab43f936a24b000651d4a824fcd"
+ "d577f211aef8f806b16afe8af");
+ CHECK_HASH(1005,
+ 'U',
+ "59f5e54fe299c6a8764c6b199e44924a37f59e2b56c3ebad939b7289210dc8e4c21b9720165b0f4d4374c90f1bf4fb4a5ace17a"
+ "1161798015052893a48c3d161");
+}
+
+TEST_CASE ("SHA256: NIST test cases (large)", "[.][hash-expensive][sha256-expensive]")
+{
+ auto hasher = Hash::get_hasher_for(Hash::Algorithm::Sha256);
+ CHECK_HASH_LARGE(1'000'000, 0, "d29751f2649b32ff572b5e0a9f541ea660a50f94ff0beedfb0b692b924cc8025");
+ CHECK_HASH_LARGE(0x2000'0000, 'Z', "15a1868c12cc53951e182344277447cd0979536badcc512ad24c67e9b2d4f3dd");
+ CHECK_HASH_LARGE(0x4100'0000, 0, "461c19a93bd4344f9215f5ec64357090342bc66b15a148317d276e31cbc20b53");
+ CHECK_HASH_LARGE(0x6000'003E, 'B', "c23ce8a7895f4b21ec0daf37920ac0a262a220045a03eb2dfed48ef9b05aabea");
+}
+
+TEST_CASE ("SHA512: NIST test cases (large)", "[.][hash-expensive][sha512-expensive]")
+{
+ auto hasher = Hash::get_hasher_for(Hash::Algorithm::Sha512);
+ CHECK_HASH_LARGE(1'000'000,
+ 0,
+ "ce044bc9fd43269d5bbc946cbebc3bb711341115cc4abdf2edbc3ff2c57ad4b15deb699bda257fea5aef9c6e55fcf4cf9"
+ "dc25a8c3ce25f2efe90908379bff7ed");
+ CHECK_HASH_LARGE(0x2000'0000,
+ 'Z',
+ "da172279f3ebbda95f6b6e1e5f0ebec682c25d3d93561a1624c2fa9009d64c7e9923f3b46bcaf11d39a531f43297992ba"
+ "4155c7e827bd0f1e194ae7ed6de4cac");
+ CHECK_HASH_LARGE(0x4100'0000,
+ 0,
+ "14b1be901cb43549b4d831e61e5f9df1c791c85b50e85f9d6bc64135804ad43ce8402750edbe4e5c0fc170b99cf78b9f4"
+ "ecb9c7e02a157911d1bd1832d76784f");
+ CHECK_HASH_LARGE(0x6000'003E,
+ 'B',
+ "fd05e13eb771f05190bd97d62647157ea8f1f6949a52bb6daaedbad5f578ec59b1b8d6c4a7ecb2feca6892b4dc1387716"
+ "70a0f3bd577eea326aed40ab7dd58b1");
+}
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+using Catch::Benchmark::Chronometer;
+void benchmark_hasher(Chronometer& meter, Hash::Hasher& hasher, std::uint64_t size, unsigned char byte) noexcept
+{
+ unsigned char buffer[1024];
+ std::fill(std::begin(buffer), std::end(buffer), byte);
+
+ meter.measure([&] {
+ hasher.clear();
+ std::uint64_t remaining = size;
+ while (remaining)
+ {
+ if (remaining < 512)
+ {
+ hasher.add_bytes(std::begin(buffer), std::begin(buffer) + remaining);
+ remaining = 0;
+ }
+ else
+ {
+ hasher.add_bytes(std::begin(buffer), std::end(buffer));
+ remaining -= 512;
+ }
+ }
+ hasher.get_hash();
+ });
+}
+
+TEST_CASE ("SHA1: benchmark", "[.][hash][sha256][!benchmark]")
+{
+ using Catch::Benchmark::Chronometer;
+
+ auto hasher = Hash::get_hasher_for(Hash::Algorithm::Sha1);
+
+ BENCHMARK_ADVANCED("0 x 1'000'000")(Catch::Benchmark::Chronometer meter)
+ {
+ benchmark_hasher(meter, *hasher, 1'000'000, 0);
+ };
+ BENCHMARK_ADVANCED("'Z' x 0x2000'0000")(Catch::Benchmark::Chronometer meter)
+ {
+ benchmark_hasher(meter, *hasher, 0x2000'0000, 'Z');
+ };
+ BENCHMARK_ADVANCED("0 x 0x4100'0000")(Catch::Benchmark::Chronometer meter)
+ {
+ benchmark_hasher(meter, *hasher, 0x4100'0000, 0);
+ };
+ BENCHMARK_ADVANCED("'B' x 0x6000'003E")(Catch::Benchmark::Chronometer meter)
+ {
+ benchmark_hasher(meter, *hasher, 0x6000'003E, 'B');
+ };
+}
+
+TEST_CASE ("SHA256: benchmark", "[.][hash][sha256][!benchmark]")
+{
+ using Catch::Benchmark::Chronometer;
+
+ auto hasher = Hash::get_hasher_for(Hash::Algorithm::Sha256);
+
+ BENCHMARK_ADVANCED("0 x 1'000'000")(Catch::Benchmark::Chronometer meter)
+ {
+ benchmark_hasher(meter, *hasher, 1'000'000, 0);
+ };
+ BENCHMARK_ADVANCED("'Z' x 0x2000'0000")(Catch::Benchmark::Chronometer meter)
+ {
+ benchmark_hasher(meter, *hasher, 0x2000'0000, 'Z');
+ };
+ BENCHMARK_ADVANCED("0 x 0x4100'0000")(Catch::Benchmark::Chronometer meter)
+ {
+ benchmark_hasher(meter, *hasher, 0x4100'0000, 0);
+ };
+ BENCHMARK_ADVANCED("'B' x 0x6000'003E")(Catch::Benchmark::Chronometer meter)
+ {
+ benchmark_hasher(meter, *hasher, 0x6000'003E, 'B');
+ };
+}
+
+TEST_CASE ("SHA512: large -- benchmark", "[.][hash][sha512][!benchmark]")
+{
+ auto hasher = Hash::get_hasher_for(Hash::Algorithm::Sha512);
+
+ BENCHMARK_ADVANCED("0 x 1'000'000")(Catch::Benchmark::Chronometer meter)
+ {
+ benchmark_hasher(meter, *hasher, 1'000'000, 0);
+ };
+ BENCHMARK_ADVANCED("'Z' x 0x2000'0000")(Catch::Benchmark::Chronometer meter)
+ {
+ benchmark_hasher(meter, *hasher, 0x2000'0000, 'Z');
+ };
+ BENCHMARK_ADVANCED("0 x 0x4100'0000")(Catch::Benchmark::Chronometer meter)
+ {
+ benchmark_hasher(meter, *hasher, 0x4100'0000, 0);
+ };
+ BENCHMARK_ADVANCED("'B' x 0x6000'003E")(Catch::Benchmark::Chronometer meter)
+ {
+ benchmark_hasher(meter, *hasher, 0x6000'003E, 'B');
+ };
+}
+#endif
diff --git a/toolsrc/src/vcpkg/base/downloads.cpp b/toolsrc/src/vcpkg/base/downloads.cpp
index 4bb2178e5..df6b1be09 100644
--- a/toolsrc/src/vcpkg/base/downloads.cpp
+++ b/toolsrc/src/vcpkg/base/downloads.cpp
@@ -127,7 +127,7 @@ namespace vcpkg::Downloads
const fs::path& path,
const std::string& sha512)
{
- std::string actual_hash = vcpkg::Hash::get_file_hash(fs, path, "SHA512");
+ std::string actual_hash = vcpkg::Hash::get_file_hash(VCPKG_LINE_INFO, fs, path, Hash::Algorithm::Sha512);
// <HACK to handle NuGet.org changing nupkg hashes.>
// This is the NEW hash for 7zip
diff --git a/toolsrc/src/vcpkg/base/hash.cpp b/toolsrc/src/vcpkg/base/hash.cpp
index 62a01ed17..11df3e329 100644
--- a/toolsrc/src/vcpkg/base/hash.cpp
+++ b/toolsrc/src/vcpkg/base/hash.cpp
@@ -14,238 +14,758 @@
#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#endif
+
#endif
namespace vcpkg::Hash
{
- static void verify_has_only_allowed_chars(const std::string& s)
+ using uchar = unsigned char;
+
+ Optional<Algorithm> algorithm_from_string(StringView sv) noexcept
+ {
+ if (Strings::case_insensitive_ascii_equals(sv, "SHA1"))
+ {
+ return {Algorithm::Sha1};
+ }
+ if (Strings::case_insensitive_ascii_equals(sv, "SHA256"))
+ {
+ return {Algorithm::Sha256};
+ }
+ if (Strings::case_insensitive_ascii_equals(sv, "SHA512"))
+ {
+ return {Algorithm::Sha512};
+ }
+
+ return {};
+ }
+
+ const char* to_string(Algorithm algo) noexcept
{
- static const std::regex ALLOWED_CHARS{"^[a-zA-Z0-9-]*$"};
- Checks::check_exit(VCPKG_LINE_INFO,
- std::regex_match(s, ALLOWED_CHARS),
- "Only alphanumeric chars and dashes are currently allowed. String was:\n"
- " % s",
- s);
+ switch (algo)
+ {
+ case Algorithm::Sha1: return "SHA1";
+ case Algorithm::Sha256: return "SHA256";
+ case Algorithm::Sha512: return "SHA512";
+ default: vcpkg::Checks::exit_fail(VCPKG_LINE_INFO);
+ }
}
-#if defined(_WIN32)
+
namespace
{
- std::string to_hex(const unsigned char* string, const size_t bytes)
+ struct UInt128
{
- static constexpr char HEX_MAP[] = "0123456789abcdef";
+ std::uint64_t top;
+ std::uint64_t bottom;
- std::string output;
- output.resize(2 * bytes);
+ UInt128() = default;
+ UInt128(std::uint64_t value) : top(0), bottom(value) {}
- size_t current_char = 0;
- for (size_t i = 0; i < bytes; i++)
+ UInt128& operator<<=(int by) noexcept
+ {
+ if (by == 0)
+ {
+ return *this;
+ }
+
+ if (by < 64)
+ {
+ top <<= by;
+ const auto shift_up = bottom >> (64 - by);
+ top |= shift_up;
+ bottom <<= by;
+ }
+ else
+ {
+ top = bottom;
+ top <<= (by - 64);
+ }
+
+ return *this;
+ }
+
+ UInt128& operator>>=(int by) noexcept
+ {
+ if (by == 0)
+ {
+ return *this;
+ }
+
+ if (by < 64)
+ {
+ bottom >>= by;
+ const auto shift_down = top << (64 - by);
+ bottom |= shift_down;
+ top >>= by;
+ }
+ else
+ {
+ bottom = top;
+ bottom >>= (by - 64);
+ }
+
+ return *this;
+ }
+
+ UInt128& operator+=(std::size_t lhs) noexcept
+ {
+ // bottom + lhs > uint64::max
+ if (bottom > std::numeric_limits<std::uint64_t>::max() - lhs)
+ {
+ top += 1;
+ }
+ bottom += lhs;
+ return *this;
+ }
+ };
+
+ }
+ template<class T>
+ void top_bits(T) = delete;
+
+ static constexpr uchar top_bits(uchar x) noexcept { return x; }
+ static constexpr uchar top_bits(std::uint32_t x) noexcept { return (x >> 24) & 0xFF; }
+ static constexpr uchar top_bits(std::uint64_t x) noexcept { return (x >> 56) & 0xFF; }
+ static constexpr uchar top_bits(UInt128 x) noexcept { return top_bits(x.top); }
+
+ // treats UIntTy as big endian for the purpose of this mapping
+ template<class UIntTy>
+ static std::string to_hex(const UIntTy* start, const UIntTy* end) noexcept
+ {
+ static constexpr char HEX_MAP[] = "0123456789abcdef";
+
+ std::string output;
+ output.resize(2 * sizeof(UIntTy) * (end - start));
+
+ std::size_t output_index = 0;
+ for (const UIntTy* it = start; it != end; ++it)
+ {
+ // holds *it in a big-endian buffer, for copying into output
+ uchar buff[sizeof(UIntTy)];
+ UIntTy tmp = *it;
+ for (uchar& ch : buff)
+ {
+ ch = top_bits(tmp);
+ tmp <<= 8;
+ }
+
+ for (const auto byte : buff)
{
// high
- output[current_char] = HEX_MAP[(string[i] & 0xF0) >> 4];
- ++current_char;
+ output[output_index] = HEX_MAP[(byte & 0xF0) >> 4];
+ ++output_index;
// low
- output[current_char] = HEX_MAP[(string[i] & 0x0F)];
- ++current_char;
+ output[output_index] = HEX_MAP[byte & 0x0F];
+ ++output_index;
}
-
- return output;
}
- class BCryptHasher
+ return output;
+ }
+
+ namespace
+ {
+#if defined(_WIN32)
+ BCRYPT_ALG_HANDLE get_alg_handle(LPCWSTR algorithm_identifier) noexcept
{
- struct BCryptAlgorithmHandle : Util::ResourceBase
+ BCRYPT_ALG_HANDLE result;
+ auto error = BCryptOpenAlgorithmProvider(&result, algorithm_identifier, nullptr, 0);
+ if (!NT_SUCCESS(error))
{
- BCRYPT_ALG_HANDLE handle = nullptr;
+ Checks::exit_with_message(VCPKG_LINE_INFO, "Failure to open algorithm: %ls", algorithm_identifier);
+ }
+
+ return result;
+ }
- ~BCryptAlgorithmHandle()
+ struct BCryptHasher : Hasher
+ {
+ static const BCRYPT_ALG_HANDLE sha1_alg_handle;
+ static const BCRYPT_ALG_HANDLE sha256_alg_handle;
+ static const BCRYPT_ALG_HANDLE sha512_alg_handle;
+
+ explicit BCryptHasher(Algorithm algo) noexcept
+ {
+ switch (algo)
{
- if (handle) BCryptCloseAlgorithmProvider(handle, 0);
+ case Algorithm::Sha1: alg_handle = sha1_alg_handle; break;
+ case Algorithm::Sha256: alg_handle = sha256_alg_handle; break;
+ case Algorithm::Sha512: alg_handle = sha512_alg_handle; break;
+ default: Checks::exit_with_message(VCPKG_LINE_INFO, "Unknown algorithm");
}
- };
- struct BCryptHashHandle : Util::ResourceBase
+ clear();
+ }
+
+ virtual void add_bytes(const void* start_, const void* end_) noexcept override
{
- BCRYPT_HASH_HANDLE handle = nullptr;
+ // BCryptHashData takes its input as non-const, but does not modify it
+ uchar* start = const_cast<uchar*>(static_cast<const uchar*>(start_));
+ const uchar* end = static_cast<const uchar*>(end_);
+ Checks::check_exit(VCPKG_LINE_INFO, end - start >= 0);
+
+ // only matters on 64-bit -- BCryptHasher takes an unsigned long
+ // length, so if you have an array bigger than 2**32-1 elements,
+ // you have a problem.
+#if defined(_M_AMD64) || defined(_M_ARM64)
+ constexpr std::ptrdiff_t max = std::numeric_limits<unsigned long>::max();
+ Checks::check_exit(VCPKG_LINE_INFO, end - start <= max);
+#endif
- ~BCryptHashHandle()
- {
- if (handle) BCryptDestroyHash(handle);
- }
- };
+ const auto length = static_cast<unsigned long>(end - start);
+ const NTSTATUS error_code = BCryptHashData(hash_handle, start, length, 0);
+ Checks::check_exit(VCPKG_LINE_INFO, NT_SUCCESS(error_code), "Failed to process a chunk");
+ }
- static void initialize_hash_handle(BCryptHashHandle& hash_handle,
- const BCryptAlgorithmHandle& algorithm_handle)
+ virtual void clear() noexcept override
{
- const NTSTATUS error_code =
- BCryptCreateHash(algorithm_handle.handle, &hash_handle.handle, nullptr, 0, nullptr, 0, 0);
+ if (hash_handle) BCryptDestroyHash(hash_handle);
+ const NTSTATUS error_code = BCryptCreateHash(alg_handle, &hash_handle, nullptr, 0, nullptr, 0, 0);
Checks::check_exit(VCPKG_LINE_INFO, NT_SUCCESS(error_code), "Failed to initialize the hasher");
}
- static void hash_data(BCryptHashHandle& hash_handle, const unsigned char* buffer, const size_t& data_size)
+ virtual std::string get_hash() noexcept override
{
- const NTSTATUS error_code = BCryptHashData(
- hash_handle.handle, const_cast<unsigned char*>(buffer), static_cast<ULONG>(data_size), 0);
- Checks::check_exit(VCPKG_LINE_INFO, NT_SUCCESS(error_code), "Failed to hash data");
+ const auto hash_size = get_hash_buffer_size();
+ const auto buffer = std::make_unique<uchar[]>(hash_size);
+ const auto hash = buffer.get();
+
+ const NTSTATUS error_code = BCryptFinishHash(hash_handle, hash, hash_size, 0);
+ Checks::check_exit(VCPKG_LINE_INFO, NT_SUCCESS(error_code), "Failed to finalize the hash");
+ return to_hex(hash, hash + hash_size);
}
- static std::string finalize_hash_handle(const BCryptHashHandle& hash_handle, const ULONG length_in_bytes)
+ ~BCryptHasher() { BCryptDestroyHash(hash_handle); }
+
+ private:
+ unsigned long get_hash_buffer_size() const
{
- std::unique_ptr<unsigned char[]> hash_buffer = std::make_unique<UCHAR[]>(length_in_bytes);
- const NTSTATUS error_code = BCryptFinishHash(hash_handle.handle, hash_buffer.get(), length_in_bytes, 0);
- Checks::check_exit(VCPKG_LINE_INFO, NT_SUCCESS(error_code), "Failed to finalize the hash");
- return to_hex(hash_buffer.get(), length_in_bytes);
- }
-
- public:
- explicit BCryptHasher(std::string hash_type)
- {
- NTSTATUS error_code = BCryptOpenAlgorithmProvider(
- &this->algorithm_handle.handle,
- Strings::to_utf16(Strings::ascii_to_uppercase(std::move(hash_type))).c_str(),
- nullptr,
- 0);
- Checks::check_exit(VCPKG_LINE_INFO, NT_SUCCESS(error_code), "Failed to open the algorithm provider");
-
- DWORD hash_buffer_bytes;
- DWORD cb_data;
- error_code = BCryptGetProperty(this->algorithm_handle.handle,
- BCRYPT_HASH_LENGTH,
- reinterpret_cast<PUCHAR>(&hash_buffer_bytes),
- sizeof(DWORD),
- &cb_data,
- 0);
+ unsigned long hash_buffer_bytes;
+ unsigned long cb_data;
+ const NTSTATUS error_code = BCryptGetProperty(alg_handle,
+ BCRYPT_HASH_LENGTH,
+ reinterpret_cast<uchar*>(&hash_buffer_bytes),
+ sizeof(hash_buffer_bytes),
+ &cb_data,
+ 0);
Checks::check_exit(VCPKG_LINE_INFO, NT_SUCCESS(error_code), "Failed to get hash length");
- this->length_in_bytes = hash_buffer_bytes;
+
+ return hash_buffer_bytes;
}
- std::string hash_file(const fs::path& path) const
- {
- BCryptHashHandle hash_handle;
- initialize_hash_handle(hash_handle, this->algorithm_handle);
+ BCRYPT_HASH_HANDLE hash_handle = nullptr;
+ BCRYPT_ALG_HANDLE alg_handle = nullptr;
+ };
+
+ const BCRYPT_ALG_HANDLE BCryptHasher::sha1_alg_handle = get_alg_handle(BCRYPT_SHA1_ALGORITHM);
+ const BCRYPT_ALG_HANDLE BCryptHasher::sha256_alg_handle = get_alg_handle(BCRYPT_SHA256_ALGORITHM);
+ const BCRYPT_ALG_HANDLE BCryptHasher::sha512_alg_handle = get_alg_handle(BCRYPT_SHA512_ALGORITHM);
+#else
+
+ template<class WordTy>
+ static WordTy shl(WordTy value, int by) noexcept
+ {
+ return value << by;
+ }
+
+ static std::uint32_t shr32(std::uint32_t value, int by) noexcept { return value >> by; }
+ static std::uint32_t rol32(std::uint32_t value, int by) noexcept
+ {
+ return (value << by) | (value >> (32 - by));
+ }
+ static std::uint32_t ror32(std::uint32_t value, int by) noexcept
+ {
+ return (value >> by) | (value << (32 - by));
+ }
+
+ static std::uint64_t shr64(std::uint64_t value, int by) noexcept { return value >> by; }
+ static std::uint64_t ror64(std::uint64_t value, int by) noexcept
+ {
+ return (value >> by) | (value << (64 - by));
+ }
+
+ template<class ShaAlgorithm>
+ struct ShaHasher final : Hasher
+ {
+ ShaHasher() = default;
- FILE* file = nullptr;
- const auto ec = _wfopen_s(&file, path.c_str(), L"rb");
- Checks::check_exit(VCPKG_LINE_INFO, ec == 0, "Failed to open file: %s", path.u8string());
- if (file != nullptr)
+ virtual void add_bytes(const void* start, const void* end) noexcept override
+ {
+ for (;;)
{
- unsigned char buffer[4096];
- while (const auto actual_size = fread(buffer, 1, sizeof(buffer), file))
+ start = add_to_unprocessed(start, end);
+ if (!start)
{
- hash_data(hash_handle, buffer, actual_size);
+ break; // done
}
- fclose(file);
+
+ m_impl.process_full_chunk(m_chunk);
+ m_current_chunk_size = 0;
}
+ }
+
+ virtual void clear() noexcept override
+ {
+ m_impl.clear();
- return finalize_hash_handle(hash_handle, length_in_bytes);
+ // m_chunk is theoretically uninitialized, so no need to reset it
+ m_current_chunk_size = 0;
+ m_message_length = 0;
}
- std::string hash_string(const std::string& s) const
+ virtual std::string get_hash() noexcept override
{
- BCryptHashHandle hash_handle;
- initialize_hash_handle(hash_handle, this->algorithm_handle);
- hash_data(hash_handle, reinterpret_cast<const unsigned char*>(s.c_str()), s.size());
- return finalize_hash_handle(hash_handle, length_in_bytes);
+ process_last_chunk();
+ return to_hex(m_impl.begin(), m_impl.end());
}
private:
- BCryptAlgorithmHandle algorithm_handle;
- ULONG length_in_bytes;
- };
- }
+ // if unprocessed gets filled,
+ // returns a pointer to the remainder of the block (which might be end)
+ // else, returns nullptr
+ const void* add_to_unprocessed(const void* start_, const void* end_) noexcept
+ {
+ const uchar* start = static_cast<const uchar*>(start_);
+ const uchar* end = static_cast<const uchar*>(end_);
- std::string get_file_hash(const Files::Filesystem& fs, const fs::path& path, const std::string& hash_type)
- {
- Checks::check_exit(VCPKG_LINE_INFO, fs.exists(path), "File %s does not exist", path.u8string());
- return BCryptHasher{hash_type}.hash_file(path);
- }
+ const auto remaining = chunk_size - m_current_chunk_size;
- std::string get_string_hash(const std::string& s, const std::string& hash_type)
- {
- verify_has_only_allowed_chars(s);
- return BCryptHasher{hash_type}.hash_string(s);
- }
+ const std::size_t message_length = end - start;
+ if (message_length >= remaining)
+ {
+ std::copy(start, start + remaining, chunk_begin());
+ m_current_chunk_size += remaining;
+ m_message_length += remaining * 8;
+ return start + remaining;
+ }
+ else
+ {
+ std::copy(start, end, chunk_begin());
+ m_current_chunk_size += message_length;
+ m_message_length += message_length * 8;
+ return nullptr;
+ }
+ }
-#else
- static std::string get_digest_size(const std::string& hash_type)
- {
- if (!Strings::case_insensitive_ascii_starts_with(hash_type, "SHA"))
+ // called before `get_hash`
+ void process_last_chunk() noexcept
+ {
+ const auto message_length = m_message_length;
+
+ // append the bit '1' to the message
+ {
+ const uchar temp = 0x80;
+ add_to_unprocessed(&temp, &temp + 1);
+ }
+
+ // append 0 to the message so that the resulting length is just enough
+ // to add the message length
+ if (chunk_size - m_current_chunk_size < sizeof(m_message_length))
+ {
+ // not enough space to add the message length
+ // just resize and process full chunk
+ std::fill(chunk_begin(), m_chunk.end(), static_cast<uchar>(0));
+ m_impl.process_full_chunk(m_chunk);
+ m_current_chunk_size = 0;
+ }
+
+ const auto before_length = m_chunk.end() - sizeof(m_message_length);
+ std::fill(chunk_begin(), before_length, static_cast<uchar>(0));
+ std::generate(before_length, m_chunk.end(), [length = message_length]() mutable {
+ const auto result = top_bits(length);
+ length <<= 8;
+ return result;
+ });
+
+ m_impl.process_full_chunk(m_chunk);
+ }
+
+ auto chunk_begin() { return m_chunk.begin() + m_current_chunk_size; }
+
+ using underlying_type = typename ShaAlgorithm::underlying_type;
+ using message_length_type = typename ShaAlgorithm::message_length_type;
+ constexpr static std::size_t chunk_size = ShaAlgorithm::chunk_size;
+
+ ShaAlgorithm m_impl{};
+
+ std::array<uchar, chunk_size> m_chunk{};
+ std::size_t m_current_chunk_size = 0;
+ message_length_type m_message_length = 0;
+ };
+ template<class WordTy>
+ inline void sha_fill_initial_words(const uchar* chunk, WordTy* words)
{
- Checks::exit_with_message(
- VCPKG_LINE_INFO, "shasum only supports SHA hashes, but %s was provided", hash_type);
+ // break chunk into 16 N-bit words
+ for (std::size_t word = 0; word < 16; ++word)
+ {
+ words[word] = 0;
+ // big-endian -- so the earliest i becomes the most significant
+ for (std::size_t byte = 0; byte < sizeof(WordTy); ++byte)
+ {
+ const auto bits_to_shift = static_cast<int>(8 * (sizeof(WordTy) - 1 - byte));
+ words[word] |= shl<WordTy>(chunk[word * sizeof(WordTy) + byte], bits_to_shift);
+ }
+ }
}
- return hash_type.substr(3, hash_type.length() - 3);
- }
+ struct Sha1Algorithm
+ {
+ using underlying_type = std::uint32_t;
+ using message_length_type = std::uint64_t;
+ constexpr static std::size_t chunk_size = 64; // = 512 / 8
+ constexpr static std::size_t number_of_rounds = 80;
- static std::string parse_shasum_output(const std::string& shasum_output)
- {
- std::vector<std::string> split = Strings::split(shasum_output, " ");
- // Checking if >= 3 because filenames with spaces will show up as multiple tokens.
- // The hash is the first token so we don't need to parse the filename anyway.
- Checks::check_exit(VCPKG_LINE_INFO,
- split.size() >= 3,
- "Expected output of the form [hash filename\n] (3+ tokens), but got\n"
- "[%s] (%s tokens)",
- shasum_output,
- std::to_string(split.size()));
-
- return split[0];
- }
+ Sha1Algorithm() noexcept { clear(); }
- std::string get_file_hash(const Files::Filesystem& fs, const fs::path& path, const std::string& hash_type)
- {
- const std::string digest_size = get_digest_size(hash_type);
- Checks::check_exit(VCPKG_LINE_INFO, fs.exists(path), "File %s does not exist", path.u8string());
+ void process_full_chunk(const std::array<uchar, chunk_size>& chunk) noexcept
+ {
+ std::uint32_t words[80];
+
+ sha_fill_initial_words(&chunk[0], words);
+ for (std::size_t i = 16; i < number_of_rounds; ++i)
+ {
+ const auto sum = words[i - 3] ^ words[i - 8] ^ words[i - 14] ^ words[i - 16];
+ words[i] = rol32(sum, 1);
+ }
+
+ std::uint32_t a = m_digest[0];
+ std::uint32_t b = m_digest[1];
+ std::uint32_t c = m_digest[2];
+ std::uint32_t d = m_digest[3];
+ std::uint32_t e = m_digest[4];
+
+ for (std::size_t i = 0; i < number_of_rounds; ++i)
+ {
+ std::uint32_t f;
+ std::uint32_t k;
+
+ if (i < 20)
+ {
+ f = (b & c) | (~b & d);
+ k = 0x5A827999;
+ }
+ else if (i < 40)
+ {
+ f = b ^ c ^ d;
+ k = 0x6ED9EBA1;
+ }
+ else if (i < 60)
+ {
+ f = (b & c) | (b & d) | (c & d);
+ k = 0x8F1BBCDC;
+ }
+ else
+ {
+ f = b ^ c ^ d;
+ k = 0xCA62C1D6;
+ }
+
+ auto tmp = rol32(a, 5) + f + e + k + words[i];
+ e = d;
+ d = c;
+ c = rol32(b, 30);
+ b = a;
+ a = tmp;
+ }
+
+ m_digest[0] += a;
+ m_digest[1] += b;
+ m_digest[2] += c;
+ m_digest[3] += d;
+ m_digest[4] += e;
+ }
- // Try hash-specific tools, like sha512sum
+ void clear() noexcept
+ {
+ m_digest[0] = 0x67452301;
+ m_digest[1] = 0xEFCDAB89;
+ m_digest[2] = 0x98BADCFE;
+ m_digest[3] = 0x10325476;
+ m_digest[4] = 0xC3D2E1F0;
+ }
+
+ const std::uint32_t* begin() const noexcept { return &m_digest[0]; }
+ const std::uint32_t* end() const noexcept { return &m_digest[5]; }
+
+ std::uint32_t m_digest[5];
+ };
+
+ struct Sha256Algorithm
{
- const auto ec_data = System::cmd_execute_and_capture_output(
- Strings::format(R"(sha%ssum "%s")", digest_size, path.u8string()));
- if (ec_data.exit_code == 0)
+ using underlying_type = std::uint32_t;
+ using message_length_type = std::uint64_t;
+ constexpr static std::size_t chunk_size = 64;
+
+ constexpr static std::size_t number_of_rounds = 64;
+
+ Sha256Algorithm() noexcept { clear(); }
+
+ void process_full_chunk(const std::array<uchar, chunk_size>& chunk) noexcept
{
- return parse_shasum_output(ec_data.output);
+ std::uint32_t words[64];
+
+ sha_fill_initial_words(&chunk[0], words);
+
+ for (std::size_t i = 16; i < number_of_rounds; ++i)
+ {
+ const auto w0 = words[i - 15];
+ const auto s0 = ror32(w0, 7) ^ ror32(w0, 18) ^ shr32(w0, 3);
+ const auto w1 = words[i - 2];
+ const auto s1 = ror32(w1, 17) ^ ror32(w1, 19) ^ shr32(w1, 10);
+ words[i] = words[i - 16] + s0 + words[i - 7] + s1;
+ }
+
+ std::uint32_t local[8];
+ std::copy(begin(), end(), std::begin(local));
+
+ for (std::size_t i = 0; i < number_of_rounds; ++i)
+ {
+ const auto a = local[0];
+ const auto b = local[1];
+ const auto c = local[2];
+
+ const auto s0 = ror32(a, 2) ^ ror32(a, 13) ^ ror32(a, 22);
+ const auto maj = (a & b) ^ (a & c) ^ (b & c);
+ const auto tmp1 = s0 + maj;
+
+ const auto e = local[4];
+
+ const auto s1 = ror32(e, 6) ^ ror32(e, 11) ^ ror32(e, 25);
+ const auto ch = (e & local[5]) ^ (~e & local[6]);
+ const auto tmp2 = local[7] + s1 + ch + round_constants[i] + words[i];
+
+ for (std::size_t j = 7; j > 0; --j)
+ {
+ local[j] = local[j - 1];
+ }
+ local[4] += tmp2;
+ local[0] = tmp1 + tmp2;
+ }
+
+ for (std::size_t i = 0; i < 8; ++i)
+ {
+ m_digest[i] += local[i];
+ }
}
- }
- // Try shasum
+ void clear() noexcept
+ {
+ m_digest[0] = 0x6a09e667;
+ m_digest[1] = 0xbb67ae85;
+ m_digest[2] = 0x3c6ef372;
+ m_digest[3] = 0xa54ff53a;
+ m_digest[4] = 0x510e527f;
+ m_digest[5] = 0x9b05688c;
+ m_digest[6] = 0x1f83d9ab;
+ m_digest[7] = 0x5be0cd19;
+ }
+
+ constexpr static std::array<std::uint32_t, number_of_rounds> round_constants = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2};
+
+ std::uint32_t* begin() noexcept { return &m_digest[0]; }
+ std::uint32_t* end() noexcept { return &m_digest[8]; }
+
+ std::uint32_t m_digest[8];
+ };
+
+ struct Sha512Algorithm
{
- const auto ec_data = System::cmd_execute_and_capture_output(
- Strings::format(R"(shasum -a %s "%s")", digest_size, path.u8string()));
- if (ec_data.exit_code == 0)
+ using underlying_type = std::uint64_t;
+ using message_length_type = UInt128;
+ constexpr static std::size_t chunk_size = 128; // = 1024 / 8
+
+ constexpr static std::size_t number_of_rounds = 80;
+
+ Sha512Algorithm() noexcept { clear(); }
+
+ void process_full_chunk(const std::array<uchar, chunk_size>& chunk) noexcept
+ {
+ std::uint64_t words[80];
+
+ sha_fill_initial_words(&chunk[0], words);
+
+ for (std::size_t i = 16; i < number_of_rounds; ++i)
+ {
+ const auto w0 = words[i - 15];
+ const auto s0 = ror64(w0, 1) ^ ror64(w0, 8) ^ shr64(w0, 7);
+ const auto w1 = words[i - 2];
+ const auto s1 = ror64(w1, 19) ^ ror64(w1, 61) ^ shr64(w1, 6);
+ words[i] = words[i - 16] + s0 + words[i - 7] + s1;
+ }
+
+ std::uint64_t local[8];
+ std::copy(begin(), end(), std::begin(local));
+
+ for (std::size_t i = 0; i < number_of_rounds; ++i)
+ {
+ const auto a = local[0];
+ const auto b = local[1];
+ const auto c = local[2];
+
+ const auto s0 = ror64(a, 28) ^ ror64(a, 34) ^ ror64(a, 39);
+ const auto maj = (a & b) ^ (a & c) ^ (b & c);
+ const auto tmp0 = s0 + maj;
+
+ const auto e = local[4];
+
+ const auto s1 = ror64(e, 14) ^ ror64(e, 18) ^ ror64(e, 41);
+ const auto ch = (e & local[5]) ^ (~e & local[6]);
+ const auto tmp1 = local[7] + s1 + ch + round_constants[i] + words[i];
+
+ for (std::size_t j = 7; j > 0; --j)
+ {
+ local[j] = local[j - 1];
+ }
+ local[4] += tmp1;
+ local[0] = tmp0 + tmp1;
+ }
+
+ for (std::size_t i = 0; i < 8; ++i)
+ {
+ m_digest[i] += local[i];
+ }
+ }
+
+ void clear() noexcept
{
- return parse_shasum_output(ec_data.output);
+ m_digest[0] = 0x6a09e667f3bcc908;
+ m_digest[1] = 0xbb67ae8584caa73b;
+ m_digest[2] = 0x3c6ef372fe94f82b;
+ m_digest[3] = 0xa54ff53a5f1d36f1;
+ m_digest[4] = 0x510e527fade682d1;
+ m_digest[5] = 0x9b05688c2b3e6c1f;
+ m_digest[6] = 0x1f83d9abfb41bd6b;
+ m_digest[7] = 0x5be0cd19137e2179;
}
- }
- Checks::exit_with_message(VCPKG_LINE_INFO, "Could not hash file %s with %s", path.u8string(), hash_type);
+ constexpr static std::array<std::uint64_t, number_of_rounds> round_constants = {
+ 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, 0x3956c25bf348b538,
+ 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, 0xd807aa98a3030242, 0x12835b0145706fbe,
+ 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235,
+ 0xc19bf174cf692694, 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
+ 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, 0x983e5152ee66dfab,
+ 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4, 0xc6e00bf33da88fc2, 0xd5a79147930aa725,
+ 0x06ca6351e003826f, 0x142929670a0e6e70, 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed,
+ 0x53380d139d95b3df, 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
+ 0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30, 0xd192e819d6ef5218,
+ 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, 0x19a4c116b8d2d0c8, 0x1e376c085141ab53,
+ 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373,
+ 0x682e6ff3d6b2b8a3, 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
+ 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b, 0xca273eceea26619c,
+ 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, 0x06f067aa72176fba, 0x0a637dc5a2c898a6,
+ 0x113f9804bef90dae, 0x1b710b35131c471b, 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc,
+ 0x431d67c49c100d4c, 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817};
+
+ std::uint64_t* begin() noexcept { return &m_digest[0]; }
+ std::uint64_t* end() noexcept { return &m_digest[8]; }
+
+ std::uint64_t m_digest[8];
+ };
+
+ // This is required on older compilers, since it was required in C++14
+ constexpr std::array<std::uint32_t, Sha256Algorithm::number_of_rounds> Sha256Algorithm::round_constants;
+ constexpr std::array<std::uint64_t, Sha512Algorithm::number_of_rounds> Sha512Algorithm::round_constants;
+#endif
}
- std::string get_string_hash(const std::string& s, const std::string& hash_type)
+ std::unique_ptr<Hasher> get_hasher_for(Algorithm algo) noexcept
{
- const std::string digest_size = get_digest_size(hash_type);
- verify_has_only_allowed_chars(s);
+#if defined(_WIN32)
+ return std::make_unique<BCryptHasher>(algo);
+#else
+ switch (algo)
+ {
+ case Algorithm::Sha1: return std::make_unique<ShaHasher<Sha1Algorithm>>();
+ case Algorithm::Sha256: return std::make_unique<ShaHasher<Sha256Algorithm>>();
+ case Algorithm::Sha512: return std::make_unique<ShaHasher<Sha512Algorithm>>();
+ default: vcpkg::Checks::exit_with_message(VCPKG_LINE_INFO, "Unknown hashing algorithm: %s", algo);
+ }
+#endif
+ }
- // Try hash-specific tools, like sha512sum
+ template<class F>
+ static std::string do_hash(Algorithm algo, const F& f) noexcept
+ {
+#if defined(_WIN32)
+ auto hasher = BCryptHasher(algo);
+ return f(hasher);
+#else
+ switch (algo)
{
- const auto ec_data =
- System::cmd_execute_and_capture_output(Strings::format(R"(echo -n "%s" | sha%ssum)", s, digest_size));
- if (ec_data.exit_code == 0)
+ case Algorithm::Sha1:
+ {
+ auto hasher = ShaHasher<Sha1Algorithm>();
+ return f(hasher);
+ }
+ case Algorithm::Sha256:
+ {
+ auto hasher = ShaHasher<Sha256Algorithm>();
+ return f(hasher);
+ }
+ case Algorithm::Sha512:
{
- return parse_shasum_output(ec_data.output);
+ auto hasher = ShaHasher<Sha512Algorithm>();
+ return f(hasher);
}
+ default: vcpkg::Checks::exit_with_message(VCPKG_LINE_INFO, "Unknown hashing algorithm: %s", algo);
}
+#endif
+ }
+
+ std::string get_bytes_hash(const void* first, const void* last, Algorithm algo) noexcept
+ {
+ return do_hash(algo, [first, last](Hasher& hasher) {
+ hasher.add_bytes(first, last);
+ return hasher.get_hash();
+ });
+ }
- // Try shasum
+ std::string get_string_hash(StringView sv, Algorithm algo) noexcept
+ {
+ return get_bytes_hash(sv.data(), sv.data() + sv.size(), algo);
+ }
+
+ // TODO: use Files::Filesystem to open a file
+ std::string get_file_hash(const Files::Filesystem&,
+ const fs::path& path,
+ Algorithm algo,
+ std::error_code& ec) noexcept
+ {
+ auto file = std::fstream(path.c_str(), std::ios_base::in | std::ios_base::binary);
+ if (!file)
{
- const auto ec_data = System::cmd_execute_and_capture_output(
- Strings::format(R"(echo -n "%s" | shasum -a %s)", s, digest_size));
- if (ec_data.exit_code == 0)
- {
- return parse_shasum_output(ec_data.output);
- }
+ ec.assign(ENOENT, std::system_category());
+ return {};
}
- Checks::exit_with_message(VCPKG_LINE_INFO, "Could not hash input string with %s", hash_type);
+ return do_hash(algo, [&file, &ec](Hasher& hasher) {
+ constexpr std::size_t buffer_size = 4096;
+ auto buffer = std::make_unique<char[]>(buffer_size);
+ for (;;)
+ {
+ file.read(buffer.get(), buffer_size);
+ if (file.eof())
+ {
+ hasher.add_bytes(buffer.get(), buffer.get() + file.gcount());
+ return hasher.get_hash();
+ }
+ else if (file)
+ {
+ hasher.add_bytes(buffer.get(), buffer.get() + buffer_size);
+ }
+ else
+ {
+ ec = std::io_errc::stream;
+ return std::string();
+ }
+ }
+ });
}
-#endif
}
diff --git a/toolsrc/src/vcpkg/base/stringview.cpp b/toolsrc/src/vcpkg/base/stringview.cpp
index d0b2cd43a..839d72ce8 100644
--- a/toolsrc/src/vcpkg/base/stringview.cpp
+++ b/toolsrc/src/vcpkg/base/stringview.cpp
@@ -76,8 +76,10 @@ namespace vcpkg
std::string StringView::to_string() const { return std::string(m_ptr, m_size); }
void StringView::to_string(std::string& s) const { s.append(m_ptr, m_size); }
- bool StringView::operator==(StringView other) const
+ bool operator==(StringView lhs, StringView rhs) noexcept
{
- return other.size() == size() && memcmp(data(), other.data(), size()) == 0;
+ return lhs.size() == lhs.size() && memcmp(lhs.data(), rhs.data(), lhs.size()) == 0;
}
+
+ bool operator!=(StringView lhs, StringView rhs) noexcept { return !(lhs == rhs); }
}
diff --git a/toolsrc/src/vcpkg/build.cpp b/toolsrc/src/vcpkg/build.cpp
index a2e90f33b..9245ddfbc 100644
--- a/toolsrc/src/vcpkg/build.cpp
+++ b/toolsrc/src/vcpkg/build.cpp
@@ -486,32 +486,33 @@ namespace vcpkg::Build
}
else
{
- hash = Hash::get_file_hash(fs, triplet_file_path, "SHA1");
+ const auto algo = Hash::Algorithm::Sha1;
+ hash = Hash::get_file_hash(VCPKG_LINE_INFO, fs, triplet_file_path, algo);
if (auto p = pre_build_info.external_toolchain_file.get())
{
hash += "-";
- hash += Hash::get_file_hash(fs, *p, "SHA1");
+ hash += Hash::get_file_hash(VCPKG_LINE_INFO, fs, *p, algo);
}
else if (pre_build_info.cmake_system_name == "Linux")
{
hash += "-";
- hash += Hash::get_file_hash(fs, paths.scripts / "toolchains" / "linux.cmake", "SHA1");
+ hash += Hash::get_file_hash(VCPKG_LINE_INFO, fs, paths.scripts / "toolchains" / "linux.cmake", algo);
}
else if (pre_build_info.cmake_system_name == "Darwin")
{
hash += "-";
- hash += Hash::get_file_hash(fs, paths.scripts / "toolchains" / "osx.cmake", "SHA1");
+ hash += Hash::get_file_hash(VCPKG_LINE_INFO, fs, paths.scripts / "toolchains" / "osx.cmake", algo);
}
else if (pre_build_info.cmake_system_name == "FreeBSD")
{
hash += "-";
- hash += Hash::get_file_hash(fs, paths.scripts / "toolchains" / "freebsd.cmake", "SHA1");
+ hash += Hash::get_file_hash(VCPKG_LINE_INFO, fs, paths.scripts / "toolchains" / "freebsd.cmake", algo);
}
else if (pre_build_info.cmake_system_name == "Android")
{
hash += "-";
- hash += Hash::get_file_hash(fs, paths.scripts / "toolchains" / "android.cmake", "SHA1");
+ hash += Hash::get_file_hash(VCPKG_LINE_INFO, fs, paths.scripts / "toolchains" / "android.cmake", algo);
}
s_hash_cache.emplace(triplet_file_path, hash);
@@ -651,8 +652,9 @@ namespace vcpkg::Build
{
if (fs::is_regular_file(fs.status(VCPKG_LINE_INFO, port_file)))
{
- port_files.emplace_back(port_file.path().filename().u8string(),
- vcpkg::Hash::get_file_hash(fs, port_file, "SHA1"));
+ port_files.emplace_back(
+ port_file.path().filename().u8string(),
+ vcpkg::Hash::get_file_hash(VCPKG_LINE_INFO, fs, port_file, Hash::Algorithm::Sha1));
if (port_files.size() > max_port_file_count)
{
@@ -679,7 +681,10 @@ namespace vcpkg::Build
abi_tag_entries.emplace_back(
"vcpkg_fixup_cmake_targets",
- vcpkg::Hash::get_file_hash(fs, paths.scripts / "cmake" / "vcpkg_fixup_cmake_targets.cmake", "SHA1"));
+ vcpkg::Hash::get_file_hash(VCPKG_LINE_INFO,
+ fs,
+ paths.scripts / "cmake" / "vcpkg_fixup_cmake_targets.cmake",
+ Hash::Algorithm::Sha1));
abi_tag_entries.emplace_back("triplet", pre_build_info.triplet_abi_tag);
abi_tag_entries.emplace_back("features", Strings::join(";", config.feature_list));
@@ -688,7 +693,8 @@ namespace vcpkg::Build
{
abi_tag_entries.emplace_back(
"public_abi_override",
- Hash::get_string_hash(pre_build_info.public_abi_override.value_or_exit(VCPKG_LINE_INFO), "SHA1"));
+ Hash::get_string_hash(pre_build_info.public_abi_override.value_or_exit(VCPKG_LINE_INFO),
+ Hash::Algorithm::Sha1));
}
if (config.build_package_options.use_head_version == UseHeadVersion::YES)
@@ -717,7 +723,8 @@ namespace vcpkg::Build
const auto abi_file_path = paths.buildtrees / name / (triplet.canonical_name() + ".vcpkg_abi_info.txt");
fs.write_contents(abi_file_path, full_abi_info, VCPKG_LINE_INFO);
- return AbiTagAndFile{Hash::get_file_hash(fs, abi_file_path, "SHA1"), abi_file_path};
+ return AbiTagAndFile{Hash::get_file_hash(VCPKG_LINE_INFO, fs, abi_file_path, Hash::Algorithm::Sha1),
+ abi_file_path};
}
System::print2(
diff --git a/toolsrc/src/vcpkg/commands.cpp b/toolsrc/src/vcpkg/commands.cpp
index 3ac568979..962dbd48b 100644
--- a/toolsrc/src/vcpkg/commands.cpp
+++ b/toolsrc/src/vcpkg/commands.cpp
@@ -99,8 +99,15 @@ namespace vcpkg::Commands::Hash
Util::unused(args.parse_arguments(COMMAND_STRUCTURE));
const fs::path file_to_hash = args.command_arguments[0];
- const std::string algorithm = args.command_arguments.size() == 2 ? args.command_arguments[1] : "SHA512";
- const std::string hash = vcpkg::Hash::get_file_hash(paths.get_filesystem(), file_to_hash, algorithm);
+
+ auto algorithm = vcpkg::Hash::Algorithm::Sha512;
+ if (args.command_arguments.size() == 2)
+ {
+ algorithm = vcpkg::Hash::algorithm_from_string(args.command_arguments[1]).value_or_exit(VCPKG_LINE_INFO);
+ }
+
+ const std::string hash =
+ vcpkg::Hash::get_file_hash(VCPKG_LINE_INFO, paths.get_filesystem(), file_to_hash, algorithm);
System::print2(hash, '\n');
Checks::exit_success(VCPKG_LINE_INFO);
}
diff --git a/toolsrc/src/vcpkg/metrics.cpp b/toolsrc/src/vcpkg/metrics.cpp
index 11f613830..b8c55919e 100644
--- a/toolsrc/src/vcpkg/metrics.cpp
+++ b/toolsrc/src/vcpkg/metrics.cpp
@@ -266,7 +266,7 @@ namespace vcpkg::Metrics
const auto match = *next;
if (match[0] != "00-00-00-00-00-00")
{
- return vcpkg::Hash::get_string_hash(match[0], "SHA256");
+ return vcpkg::Hash::get_string_hash(match[0].str(), Hash::Algorithm::Sha256);
}
++next;
}