From bc73844bcdd1ae6acbcde0ee8c2c28b865489385 Mon Sep 17 00:00:00 2001 From: Tristan Ross Date: Thu, 6 Feb 2025 23:29:23 -0800 Subject: [PATCH] feat(libutil): implement hashing in zig Fixes https://github.com/RossComputerGuy/zix/issues/11 Partial on https://github.com/RossComputerGuy/zix/issues/13 --- src/libutil/hash.cc | 64 ++++++++------------------------- src/libutil/hash.hh | 2 +- src/libutil/hash.zig | 79 +++++++++++++++++++++++++++++++++++++++++ src/libutil/libutil.zig | 2 ++ src/libutil/meson.build | 18 ++-------- src/libutil/package.nix | 4 --- 6 files changed, 99 insertions(+), 70 deletions(-) create mode 100644 src/libutil/hash.zig diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc index 6a7a8b092..f41efb47a 100644 --- a/src/libutil/hash.cc +++ b/src/libutil/hash.cc @@ -1,11 +1,6 @@ #include #include -#include -#include -#include -#include - #include "args.hh" #include "hash.hh" #include "archive.hh" @@ -287,55 +282,26 @@ Hash newHashAllowEmpty(std::string_view hashStr, std::optional ha return Hash::parseAny(hashStr, ha); } +struct Ctx {}; -union Ctx -{ - blake3_hasher blake3; - MD5_CTX md5; - SHA_CTX sha1; - SHA256_CTX sha256; - SHA512_CTX sha512; -}; - - -static void start(HashAlgorithm ha, Ctx & ctx) -{ - if (ha == HashAlgorithm::BLAKE3) blake3_hasher_init(&ctx.blake3); - else if (ha == HashAlgorithm::MD5) MD5_Init(&ctx.md5); - else if (ha == HashAlgorithm::SHA1) SHA1_Init(&ctx.sha1); - else if (ha == HashAlgorithm::SHA256) SHA256_Init(&ctx.sha256); - else if (ha == HashAlgorithm::SHA512) SHA512_Init(&ctx.sha512); -} - +extern "C" Ctx* nix_libutil_hash_create(HashAlgorithm type); +extern "C" void nix_libutil_hash_update(Ctx* ctx, const void* data, size_t size); +extern "C" void nix_libutil_hash_finish(Ctx* ctx, uint8_t* hash); -static void update(HashAlgorithm ha, Ctx & ctx, +static void update(Ctx* ctx, std::string_view data) { - if (ha == HashAlgorithm::BLAKE3) blake3_hasher_update(&ctx.blake3, data.data(), data.size()); - else if (ha == HashAlgorithm::MD5) MD5_Update(&ctx.md5, data.data(), data.size()); - else if (ha == HashAlgorithm::SHA1) SHA1_Update(&ctx.sha1, data.data(), data.size()); - else if (ha == HashAlgorithm::SHA256) SHA256_Update(&ctx.sha256, data.data(), data.size()); - else if (ha == HashAlgorithm::SHA512) SHA512_Update(&ctx.sha512, data.data(), data.size()); -} - - -static void finish(HashAlgorithm ha, Ctx & ctx, unsigned char * hash) -{ - if (ha == HashAlgorithm::BLAKE3) blake3_hasher_finalize(&ctx.blake3, hash, BLAKE3_OUT_LEN); - else if (ha == HashAlgorithm::MD5) MD5_Final(hash, &ctx.md5); - else if (ha == HashAlgorithm::SHA1) SHA1_Final(hash, &ctx.sha1); - else if (ha == HashAlgorithm::SHA256) SHA256_Final(hash, &ctx.sha256); - else if (ha == HashAlgorithm::SHA512) SHA512_Final(hash, &ctx.sha512); + nix_libutil_hash_update(ctx, data.data(), data.size()); } Hash hashString( HashAlgorithm ha, std::string_view s, const ExperimentalFeatureSettings & xpSettings) { - Ctx ctx; + Ctx* ctx = nix_libutil_hash_create(ha); Hash hash(ha, xpSettings); - start(ha, ctx); - update(ha, ctx, s); - finish(ha, ctx, hash.hash); + update(ctx, s); + nix_libutil_hash_finish(ctx, &hash.hash[0]); + delete ctx; return hash; } @@ -349,9 +315,8 @@ Hash hashFile(HashAlgorithm ha, const Path & path) HashSink::HashSink(HashAlgorithm ha) : ha(ha) { - ctx = new Ctx; + ctx = nix_libutil_hash_create(ha); bytes = 0; - start(ha, *ctx); } HashSink::~HashSink() @@ -363,23 +328,22 @@ HashSink::~HashSink() void HashSink::writeUnbuffered(std::string_view data) { bytes += data.size(); - update(ha, *ctx, data); + update(ctx, data); } HashResult HashSink::finish() { flush(); Hash hash(ha); - nix::finish(ha, *ctx, hash.hash); + nix_libutil_hash_finish(ctx, &hash.hash[0]); return HashResult(hash, bytes); } HashResult HashSink::currentHash() { flush(); - Ctx ctx2 = *ctx; Hash hash(ha); - nix::finish(ha, ctx2, hash.hash); + nix_libutil_hash_finish(ctx, &hash.hash[0]); return HashResult(hash, bytes); } diff --git a/src/libutil/hash.hh b/src/libutil/hash.hh index 13d526f42..a4ee64588 100644 --- a/src/libutil/hash.hh +++ b/src/libutil/hash.hh @@ -211,7 +211,7 @@ std::optional parseHashAlgoOpt(std::string_view s); std::string_view printHashAlgo(HashAlgorithm ha); -union Ctx; +struct Ctx; struct AbstractHashSink : virtual Sink { diff --git a/src/libutil/hash.zig b/src/libutil/hash.zig new file mode 100644 index 000000000..03c190cab --- /dev/null +++ b/src/libutil/hash.zig @@ -0,0 +1,79 @@ +const std = @import("std"); + +pub const HashAlgorithm = enum(c_char) { + md5 = 42, + sha1, + sha256, + sha512, + blake3, +}; + +pub const Ctx = union(HashAlgorithm) { + md5: std.crypto.hash.Md5, + sha1: std.crypto.hash.Sha1, + sha256: std.crypto.hash.sha2.Sha256, + sha512: std.crypto.hash.sha2.Sha512, + blake3: std.crypto.hash.Blake3, + + pub const Extern = opaque {}; + + pub inline fn toExtern(self: *Ctx) *Extern { + return @ptrCast(@alignCast(self)); + } + + pub inline fn fromExtern(e: *Extern) *Ctx { + return @ptrCast(@alignCast(e)); + } +}; + +pub fn create(t: HashAlgorithm) callconv(.C) *Ctx.Extern { + const self = std.heap.c_allocator.create(Ctx) catch @panic("OOM"); + inline for (comptime std.meta.fields(Ctx)) |field| { + const v = comptime std.meta.stringToEnum(HashAlgorithm, field.name) orelse unreachable; + if (v == t) { + self.* = @unionInit(Ctx, field.name, field.type.init(.{})); + return self.toExtern(); + } + } + unreachable; +} + +pub fn update(e: *Ctx.Extern, raw_data: [*]const u8, size: usize) callconv(.C) void { + const self = Ctx.fromExtern(e); + + const data = raw_data[0..size]; + + inline for (comptime std.meta.fields(Ctx)) |field| { + const v = comptime std.meta.stringToEnum(HashAlgorithm, field.name) orelse unreachable; + if (self.* == v) { + @field(self, field.name).update(data); + return; + } + } + + unreachable; +} + +pub fn finish(e: *Ctx.Extern, raw_hash: [*:0]u8) callconv(.C) void { + const self = Ctx.fromExtern(e); + + inline for (comptime std.meta.fields(Ctx)) |field| { + const v = comptime std.meta.stringToEnum(HashAlgorithm, field.name) orelse unreachable; + const size = field.type.digest_length; + if (self.* == v) { + std.debug.assert(size <= 64); + const hash = raw_hash[0..size]; + @field(self, field.name).final(hash[0..size]); + @memset(raw_hash[size..64], 0); + return; + } + } + + unreachable; +} + +comptime { + @export(&create, .{ .name = "nix_libutil_hash_create" }); + @export(&update, .{ .name = "nix_libutil_hash_update" }); + @export(&finish, .{ .name = "nix_libutil_hash_finish" }); +} diff --git a/src/libutil/libutil.zig b/src/libutil/libutil.zig index 39b058cf1..dc7ce7b16 100644 --- a/src/libutil/libutil.zig +++ b/src/libutil/libutil.zig @@ -1,5 +1,7 @@ pub const cpuid = @import("cpuid.zig"); +pub const hash = @import("hash.zig"); comptime { _ = cpuid; + _ = hash; } diff --git a/src/libutil/meson.build b/src/libutil/meson.build index ff38ed727..58e15c60d 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -64,12 +64,6 @@ elif host_machine.system() == 'sunos' deps_other += [socket, network_service_library] endif -blake3 = dependency( - 'libblake3', - version: '>= 1.5.5', -) -deps_private += blake3 - boost = dependency( 'boost', modules : ['context', 'coroutine'], @@ -79,13 +73,6 @@ boost = dependency( # put in `deps_other`. deps_other += boost -openssl = dependency( - 'libcrypto', - 'openssl', - version : '>= 1.1.1', -) -deps_private += openssl - libarchive = dependency('libarchive', version : '>= 3.1.2') deps_public += libarchive if get_option('default_library') == 'static' @@ -269,11 +256,12 @@ subdir('nix-meson-build-support/windows-version') sources += custom_target( 'zig build-obj', - command: [zig, 'build-lib', '-femit-h=@OUTDIR@/libutil-zig.h', '-femit-bin=@OUTPUT@', '-ofmt=c', zig_args, '@INPUT@'], + command: [zig, 'build-lib', '-femit-h=@OUTDIR@/libutil-zig.h', '-femit-bin=@OUTPUT@', '-ofmt=c', '-lc', zig_args, '@INPUT@'], output: 'libutil-zig.c', input: 'libutil.zig', depend_files: [ - 'cpuid.zig' + 'cpuid.zig', + 'hash.zig', ], ) diff --git a/src/libutil/package.nix b/src/libutil/package.nix index 836967cca..df38a6867 100644 --- a/src/libutil/package.nix +++ b/src/libutil/package.nix @@ -6,10 +6,8 @@ boost, brotli, libarchive, - libblake3, libsodium, nlohmann_json, - openssl, # Configuration Options @@ -46,9 +44,7 @@ mkMesonLibrary (finalAttrs: { buildInputs = [ brotli - libblake3 libsodium - openssl ]; propagatedBuildInputs = [