Commit 24ff8a1a5f

Andrew Kelley <andrew@ziglang.org>
2023-02-02 04:02:35
zig build: use multihash for the hash field
https://multiformats.io/multihash/ Still, only SHA2-256 is supported. This is only intended to future-proof the hash field of the manifest. closes #14284
1 parent ea6e0e3
Changed files (1)
src/Package.zig
@@ -299,16 +299,37 @@ fn fetchAndUnpack(
     // Check if the expected_hash is already present in the global package
     // cache, and thereby avoid both fetching and unpacking.
     if (expected_hash) |h| cached: {
-        if (h.len != 2 * Hash.digest_length) {
+        const hex_multihash_len = 2 * multihash_len;
+        if (h.len >= 2) {
+            const their_multihash_func = std.fmt.parseInt(u8, h[0..2], 16) catch |err| {
+                return reportError(
+                    ini,
+                    comp_directory,
+                    h.ptr,
+                    "invalid multihash value: unable to parse hash function: {s}",
+                    .{@errorName(err)},
+                );
+            };
+            if (@intToEnum(MultihashFunction, their_multihash_func) != multihash_function) {
+                return reportError(
+                    ini,
+                    comp_directory,
+                    h.ptr,
+                    "unsupported hash function: only sha2-256 is supported",
+                    .{},
+                );
+            }
+        }
+        if (h.len != hex_multihash_len) {
             return reportError(
                 ini,
                 comp_directory,
                 h.ptr,
                 "wrong hash size. expected: {d}, found: {d}",
-                .{ Hash.digest_length, h.len },
+                .{ hex_multihash_len, h.len },
             );
         }
-        const hex_digest = h[0 .. 2 * Hash.digest_length];
+        const hex_digest = h[0..hex_multihash_len];
         const pkg_dir_sub_path = "p" ++ s ++ hex_digest;
         var pkg_dir = global_cache_directory.handle.openDir(pkg_dir_sub_path, .{}) catch |err| switch (err) {
             error.FileNotFound => break :cached,
@@ -397,8 +418,8 @@ fn fetchAndUnpack(
     const pkg_dir_sub_path = "p" ++ s ++ hexDigest(actual_hash);
     try renameTmpIntoCache(global_cache_directory.handle, tmp_dir_sub_path, pkg_dir_sub_path);
 
+    const actual_hex = hexDigest(actual_hash);
     if (expected_hash) |h| {
-        const actual_hex = hexDigest(actual_hash);
         if (!mem.eql(u8, h, &actual_hex)) {
             return reportError(
                 ini,
@@ -414,7 +435,7 @@ fn fetchAndUnpack(
             comp_directory,
             url.ptr,
             "url field is missing corresponding hash field: hash={s}",
-            .{std.fmt.fmtSliceHexLower(&actual_hash)},
+            .{&actual_hex},
         );
     }
 
@@ -592,11 +613,30 @@ test hex64 {
     try std.testing.expectEqualStrings("[00efcdab78563412]", s);
 }
 
-fn hexDigest(digest: [Hash.digest_length]u8) [Hash.digest_length * 2]u8 {
-    var result: [Hash.digest_length * 2]u8 = undefined;
+const multihash_function: MultihashFunction = switch (Hash) {
+    std.crypto.hash.sha2.Sha256 => .@"sha2-256",
+    else => @compileError("unreachable"),
+};
+comptime {
+    // We avoid unnecessary uleb128 code in hexDigest by asserting here the
+    // values are small enough to be contained in the one-byte encoding.
+    assert(@enumToInt(multihash_function) < 127);
+    assert(Hash.digest_length < 127);
+}
+const multihash_len = 1 + 1 + Hash.digest_length;
+
+fn hexDigest(digest: [Hash.digest_length]u8) [multihash_len * 2]u8 {
+    var result: [multihash_len * 2]u8 = undefined;
+
+    result[0] = hex_charset[@enumToInt(multihash_function) >> 4];
+    result[1] = hex_charset[@enumToInt(multihash_function) & 15];
+
+    result[2] = hex_charset[Hash.digest_length >> 4];
+    result[3] = hex_charset[Hash.digest_length & 15];
+
     for (digest) |byte, i| {
-        result[i * 2 + 0] = hex_charset[byte >> 4];
-        result[i * 2 + 1] = hex_charset[byte & 15];
+        result[4 + i * 2] = hex_charset[byte >> 4];
+        result[5 + i * 2] = hex_charset[byte & 15];
     }
     return result;
 }
@@ -629,3 +669,21 @@ fn renameTmpIntoCache(
         break;
     }
 }
+
+const MultihashFunction = enum(u16) {
+    identity = 0x00,
+    sha1 = 0x11,
+    @"sha2-256" = 0x12,
+    @"sha2-512" = 0x13,
+    @"sha3-512" = 0x14,
+    @"sha3-384" = 0x15,
+    @"sha3-256" = 0x16,
+    @"sha3-224" = 0x17,
+    @"sha2-384" = 0x20,
+    @"sha2-256-trunc254-padded" = 0x1012,
+    @"sha2-224" = 0x1013,
+    @"sha2-512-224" = 0x1014,
+    @"sha2-512-256" = 0x1015,
+    @"blake2b-256" = 0xb220,
+    _,
+};