| ... | @@ -299,16 +299,37 @@ fn fetchAndUnpack( | ... | @@ -299,16 +299,37 @@ fn fetchAndUnpack( |
| 299 | // Check if the expected_hash is already present in the global package | 299 | // Check if the expected_hash is already present in the global package |
| 300 | // cache, and thereby avoid both fetching and unpacking. | 300 | // cache, and thereby avoid both fetching and unpacking. |
| 301 | if (expected_hash) |h| cached: { | 301 | if (expected_hash) |h| cached: { |
| 302 | if (h.len != 2 * Hash.digest_length) { | 302 | const hex_multihash_len = 2 * multihash_len; |
| | 303 | if (h.len >= 2) { |
| | 304 | const their_multihash_func = std.fmt.parseInt(u8, h[0..2], 16) catch |err| { |
| | 305 | return reportError( |
| | 306 | ini, |
| | 307 | comp_directory, |
| | 308 | h.ptr, |
| | 309 | "invalid multihash value: unable to parse hash function: {s}", |
| | 310 | .{@errorName(err)}, |
| | 311 | ); |
| | 312 | }; |
| | 313 | if (@intToEnum(MultihashFunction, their_multihash_func) != multihash_function) { |
| | 314 | return reportError( |
| | 315 | ini, |
| | 316 | comp_directory, |
| | 317 | h.ptr, |
| | 318 | "unsupported hash function: only sha2-256 is supported", |
| | 319 | .{}, |
| | 320 | ); |
| | 321 | } |
| | 322 | } |
| | 323 | if (h.len != hex_multihash_len) { |
| 303 | return reportError( | 324 | return reportError( |
| 304 | ini, | 325 | ini, |
| 305 | comp_directory, | 326 | comp_directory, |
| 306 | h.ptr, | 327 | h.ptr, |
| 307 | "wrong hash size. expected: {d}, found: {d}", | 328 | "wrong hash size. expected: {d}, found: {d}", |
| 308 | .{ Hash.digest_length, h.len }, | 329 | .{ hex_multihash_len, h.len }, |
| 309 | ); | 330 | ); |
| 310 | } | 331 | } |
| 311 | const hex_digest = h[0 .. 2 * Hash.digest_length]; | 332 | const hex_digest = h[0..hex_multihash_len]; |
| 312 | const pkg_dir_sub_path = "p" ++ s ++ hex_digest; | 333 | const pkg_dir_sub_path = "p" ++ s ++ hex_digest; |
| 313 | var pkg_dir = global_cache_directory.handle.openDir(pkg_dir_sub_path, .{}) catch |err| switch (err) { | 334 | var pkg_dir = global_cache_directory.handle.openDir(pkg_dir_sub_path, .{}) catch |err| switch (err) { |
| 314 | error.FileNotFound => break :cached, | 335 | error.FileNotFound => break :cached, |
| ... | @@ -397,8 +418,8 @@ fn fetchAndUnpack( | ... | @@ -397,8 +418,8 @@ fn fetchAndUnpack( |
| 397 | const pkg_dir_sub_path = "p" ++ s ++ hexDigest(actual_hash); | 418 | const pkg_dir_sub_path = "p" ++ s ++ hexDigest(actual_hash); |
| 398 | try renameTmpIntoCache(global_cache_directory.handle, tmp_dir_sub_path, pkg_dir_sub_path); | 419 | try renameTmpIntoCache(global_cache_directory.handle, tmp_dir_sub_path, pkg_dir_sub_path); |
| 399 | | 420 | |
| | 421 | const actual_hex = hexDigest(actual_hash); |
| 400 | if (expected_hash) |h| { | 422 | if (expected_hash) |h| { |
| 401 | const actual_hex = hexDigest(actual_hash); | | |
| 402 | if (!mem.eql(u8, h, &actual_hex)) { | 423 | if (!mem.eql(u8, h, &actual_hex)) { |
| 403 | return reportError( | 424 | return reportError( |
| 404 | ini, | 425 | ini, |
| ... | @@ -414,7 +435,7 @@ fn fetchAndUnpack( | ... | @@ -414,7 +435,7 @@ fn fetchAndUnpack( |
| 414 | comp_directory, | 435 | comp_directory, |
| 415 | url.ptr, | 436 | url.ptr, |
| 416 | "url field is missing corresponding hash field: hash={s}", | 437 | "url field is missing corresponding hash field: hash={s}", |
| 417 | .{std.fmt.fmtSliceHexLower(&actual_hash)}, | 438 | .{&actual_hex}, |
| 418 | ); | 439 | ); |
| 419 | } | 440 | } |
| 420 | | 441 | |
| ... | @@ -592,11 +613,30 @@ test hex64 { | ... | @@ -592,11 +613,30 @@ test hex64 { |
| 592 | try std.testing.expectEqualStrings("[00efcdab78563412]", s); | 613 | try std.testing.expectEqualStrings("[00efcdab78563412]", s); |
| 593 | } | 614 | } |
| 594 | | 615 | |
| 595 | fn hexDigest(digest: [Hash.digest_length]u8) [Hash.digest_length * 2]u8 { | 616 | const multihash_function: MultihashFunction = switch (Hash) { |
| 596 | var result: [Hash.digest_length * 2]u8 = undefined; | 617 | std.crypto.hash.sha2.Sha256 => .@"sha2-256", |
| | 618 | else => @compileError("unreachable"), |
| | 619 | }; |
| | 620 | comptime { |
| | 621 | // We avoid unnecessary uleb128 code in hexDigest by asserting here the |
| | 622 | // values are small enough to be contained in the one-byte encoding. |
| | 623 | assert(@enumToInt(multihash_function) < 127); |
| | 624 | assert(Hash.digest_length < 127); |
| | 625 | } |
| | 626 | const multihash_len = 1 + 1 + Hash.digest_length; |
| | 627 | |
| | 628 | fn hexDigest(digest: [Hash.digest_length]u8) [multihash_len * 2]u8 { |
| | 629 | var result: [multihash_len * 2]u8 = undefined; |
| | 630 | |
| | 631 | result[0] = hex_charset[@enumToInt(multihash_function) >> 4]; |
| | 632 | result[1] = hex_charset[@enumToInt(multihash_function) & 15]; |
| | 633 | |
| | 634 | result[2] = hex_charset[Hash.digest_length >> 4]; |
| | 635 | result[3] = hex_charset[Hash.digest_length & 15]; |
| | 636 | |
| 597 | for (digest) |byte, i| { | 637 | for (digest) |byte, i| { |
| 598 | result[i * 2 + 0] = hex_charset[byte >> 4]; | 638 | result[4 + i * 2] = hex_charset[byte >> 4]; |
| 599 | result[i * 2 + 1] = hex_charset[byte & 15]; | 639 | result[5 + i * 2] = hex_charset[byte & 15]; |
| 600 | } | 640 | } |
| 601 | return result; | 641 | return result; |
| 602 | } | 642 | } |
| ... | @@ -629,3 +669,21 @@ fn renameTmpIntoCache( | ... | @@ -629,3 +669,21 @@ fn renameTmpIntoCache( |
| 629 | break; | 669 | break; |
| 630 | } | 670 | } |
| 631 | } | 671 | } |
| | 672 | |
| | 673 | const MultihashFunction = enum(u16) { |
| | 674 | identity = 0x00, |
| | 675 | sha1 = 0x11, |
| | 676 | @"sha2-256" = 0x12, |
| | 677 | @"sha2-512" = 0x13, |
| | 678 | @"sha3-512" = 0x14, |
| | 679 | @"sha3-384" = 0x15, |
| | 680 | @"sha3-256" = 0x16, |
| | 681 | @"sha3-224" = 0x17, |
| | 682 | @"sha2-384" = 0x20, |
| | 683 | @"sha2-256-trunc254-padded" = 0x1012, |
| | 684 | @"sha2-224" = 0x1013, |
| | 685 | @"sha2-512-224" = 0x1014, |
| | 686 | @"sha2-512-256" = 0x1015, |
| | 687 | @"blake2b-256" = 0xb220, |
| | 688 | _, |
| | 689 | }; |