bump package id component to 32 bits

and to make the base64 round even, bump sha256 to 200 bits (up from 192)
This commit is contained in:
Andrew Kelley 2025-02-25 17:58:57 -08:00
parent 0fc7c9f57c
commit ea516f0e81
4 changed files with 26 additions and 27 deletions

View file

@ -12,5 +12,5 @@
},
},
.paths = .{""},
.nonce = 0xc1ce10810000f013,
.nonce = 0xc1ce108124179e16,
}

View file

@ -22,9 +22,11 @@ Zig package namespace.
Must be a valid bare Zig identifier (don't `@` me), limited to 32 bytes.
Together with `nonce`, this represents a globally unique package identifier.
### `nonce`
Together with name, this represents a globally unique package identifier. This
Together with `name`, this represents a globally unique package identifier. This
field is auto-initialized by the toolchain when the package is first created,
and then *never changes*. This allows Zig to unambiguously detect when one
package is an updated version of another.
@ -34,14 +36,14 @@ project is still maintained. Otherwise, the fork is *hostile*, attempting to
take control over the original project's identity. The nonce can be regenerated
by deleting the field and running `zig build`.
This 64-bit integer is the combination of a 16-bit id component, a 32-bit
checksum, and 16 bits of reserved zeroes.
This 64-bit integer is the combination of a 32-bit id component and a 32-bit
checksum.
The id component within the nonce has these restrictions:
`0x0000` is reserved for legacy packages.
`0x00000000` is reserved for legacy packages.
`0xffff` is reserved to represent "naked" packages.
`0xffffffff` is reserved to represent "naked" packages.
The checksum is computed from `name` and serves to protect Zig users from
accidental id collisions.

View file

@ -11,20 +11,19 @@ pub const multihash_hex_digest_len = 2 * multihash_len;
pub const MultiHashHexDigest = [multihash_hex_digest_len]u8;
pub const Nonce = packed struct(u64) {
id: u16,
reserved: u16 = 0,
id: u32,
checksum: u32,
pub fn generate(name: []const u8) Nonce {
return .{
.id = std.crypto.random.intRangeLessThan(u16, 0x0001, 0xffff),
.id = std.crypto.random.intRangeLessThan(u32, 1, 0xffffffff),
.checksum = std.hash.Crc32.hash(name),
};
}
pub fn validate(n: Nonce, name: []const u8) bool {
switch (n.id) {
0x0000, 0xffff => return false,
0x00000000, 0xffffffff => return false,
else => return std.hash.Crc32.hash(name) == n.checksum,
}
}
@ -54,8 +53,8 @@ pub const Hash = struct {
pub const Algo = std.crypto.hash.sha2.Sha256;
pub const Digest = [Algo.digest_length]u8;
/// Example: "nnnn-vvvv-hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh"
pub const max_len = 32 + 1 + 32 + 1 + (16 + 32 + 192) / 6;
/// Example: "nnnn-vvvv-hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh"
pub const max_len = 32 + 1 + 32 + 1 + (32 + 32 + 200) / 6;
pub fn fromSlice(s: []const u8) Hash {
assert(s.len <= max_len);
@ -96,14 +95,12 @@ pub const Hash = struct {
/// bytes and assumed be a valid zig identifier
/// * semver is the version field from build.zig.zon, asserted to be at
/// most 32 bytes
/// * hashplus is the following 39-byte array, base64 encoded using -_ to make
/// * hashplus is the following 33-byte array, base64 encoded using -_ to make
/// it filesystem safe:
/// - (2 bytes) LE u16 Package ID
/// - (4 bytes) LE u32 Package ID
/// - (4 bytes) LE u32 total decompressed size in bytes, overflow saturated
/// - (24 bytes) truncated SHA-256 digest of hashed files of the package
///
/// example: "nasm-2.16.1-3-AAD_ZlwACpGU-c3QXp_yNyn07Q5U9Rq-Cb1ur2G1"
pub fn init(digest: Digest, name: []const u8, ver: []const u8, id: u16, size: u32) Hash {
/// - (25 bytes) truncated SHA-256 digest of hashed files of the package
pub fn init(digest: Digest, name: []const u8, ver: []const u8, id: u32, size: u32) Hash {
assert(name.len <= 32);
assert(ver.len <= 32);
var result: Hash = undefined;
@ -112,11 +109,11 @@ pub const Hash = struct {
buf.appendAssumeCapacity('-');
buf.appendSliceAssumeCapacity(ver);
buf.appendAssumeCapacity('-');
var hashplus: [30]u8 = undefined;
std.mem.writeInt(u16, hashplus[0..2], id, .little);
std.mem.writeInt(u32, hashplus[2..6], size, .little);
hashplus[6..].* = digest[0..24].*;
_ = std.base64.url_safe_no_pad.Encoder.encode(buf.addManyAsArrayAssumeCapacity(40), &hashplus);
var hashplus: [33]u8 = undefined;
std.mem.writeInt(u32, hashplus[0..4], id, .little);
std.mem.writeInt(u32, hashplus[4..8], size, .little);
hashplus[8..].* = digest[0..25].*;
_ = std.base64.url_safe_no_pad.Encoder.encode(buf.addManyAsArrayAssumeCapacity(44), &hashplus);
@memset(buf.unusedCapacitySlice(), 0);
return result;
}
@ -194,8 +191,8 @@ test Hash {
0xc7, 0xf5, 0x71, 0xb7, 0xb4, 0xe7, 0x6f, 0x3c, 0xdb, 0x87, 0x7a, 0x7f, 0xdd, 0xf9, 0x77, 0x87,
0x9d, 0xd3, 0x86, 0xfa, 0x73, 0x57, 0x9a, 0xf7, 0x9d, 0x1e, 0xdb, 0x8f, 0x3a, 0xd9, 0xbd, 0x9f,
};
const result: Hash = .init(example_digest, "nasm", "2.16.1-2", 0xcafe, 10 * 1024 * 1024);
try std.testing.expectEqualStrings("nasm-2.16.1-2-_soAAKAAx_Vxt7Tnbzzbh3p_3fl3h53ThvpzV5r3", result.toSlice());
const result: Hash = .init(example_digest, "nasm", "2.16.1-3", 0xcafebabe, 10 * 1024 * 1024);
try std.testing.expectEqualStrings("nasm-2.16.1-3-vrr-ygAAoADH9XG3tOdvPNuHen_d-XeHndOG-nNXmved", result.toSlice());
}
test {

View file

@ -36,7 +36,7 @@ pub const ErrorMessage = struct {
};
name: []const u8,
id: u16,
id: u32,
version: std.SemanticVersion,
version_node: Ast.Node.Index,
dependencies: std.StringArrayHashMapUnmanaged(Dependency),
@ -149,7 +149,7 @@ const Parse = struct {
errors: std.ArrayListUnmanaged(ErrorMessage),
name: []const u8,
id: u16,
id: u32,
version: std.SemanticVersion,
version_node: Ast.Node.Index,
dependencies: std.StringArrayHashMapUnmanaged(Dependency),