lib/machine/src/checkpoint/canon/digest.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const core = @import("machine_instance_core");
2 const os = @import("os");
3 const std = @import("std");
4
5 const Sha256 = std.crypto.hash.sha2.Sha256;
6 const page_domain = "TINYMACHINECHECKPOINTPAGE1\x00";
7 const leaf_domain = "TINYMACHINECHECKPOINTLEAF1\x00";
8 const node_domain = "TINYMACHINECHECKPOINTNODE1\x00";
9 const memory_domain = "TINYMACHINECHECKPOINTMEMORY1\x00";
10 const machine_domain = "TINYMACHINEROOT1\x00";
11
12 pub const page_bytes: usize = core.layout.page_bytes;
13 pub const page_count: usize = core.layout.ram_bytes / page_bytes;
14 pub const tree_levels: usize = std.math.log2_int(usize, page_count);
15
16 /// Computes the SHA-256 digest of one complete 4096-byte page. The call hashes
17 /// the page domain tag before the page bytes, which keeps this digest apart
18 /// from a digest of the same bytes taken for another purpose.
19 pub fn page(value: *const [page_bytes]u8) os.abi.Digest {
20 var hasher = Sha256.init(.{});
21 hasher.update(page_domain);
22 hasher.update(value);
23 var output: os.abi.Digest = undefined;
24 hasher.final(&output);
25 return output;
26 }
27
28 /// Ties one page digest to the index of the page it came from. The call
29 /// requires the index to be below 16,384, checked by an assertion. A caller
30 /// holds a page digest at one position in the page tree.
31 pub fn leaf(index: usize, page_digest: os.abi.Digest) os.abi.Digest {
32 std.debug.assert(index < page_count);
33 var hasher = Sha256.init(.{});
34 hasher.update(leaf_domain);
35 hashInteger(&hasher, @as(u32, @intCast(index)));
36 hasher.update(&page_digest);
37 var output: os.abi.Digest = undefined;
38 hasher.final(&output);
39 return output;
40 }
41
42 /// Binds one tree level to its two ordered child digests. Level zero joins two
43 /// leaves. The call requires the level to be below 14, checked by an assertion.
44 pub fn node(
45 level: u8,
46 left: os.abi.Digest,
47 right: os.abi.Digest,
48 ) os.abi.Digest {
49 std.debug.assert(level < tree_levels);
50 var hasher = Sha256.init(.{});
51 hasher.update(node_domain);
52 hashInteger(&hasher, level);
53 hasher.update(&left);
54 hasher.update(&right);
55 var output: os.abi.Digest = undefined;
56 hasher.final(&output);
57 return output;
58 }
59
60 /// Binds a page tree's root digest to the fixed memory geometry, namely base
61 /// address zero, 67,108,864 bytes, 4096-byte pages, and 16,384 pages.
62 pub fn memory(tree_root: os.abi.Digest) os.abi.Digest {
63 var hasher = Sha256.init(.{});
64 hasher.update(memory_domain);
65 hashInteger(&hasher, @as(u64, 0));
66 hashInteger(&hasher, @as(u64, core.layout.ram_bytes));
67 hashInteger(&hasher, @as(u32, page_bytes));
68 hashInteger(&hasher, @as(u32, page_count));
69 hasher.update(&tree_root);
70 var output: os.abi.Digest = undefined;
71 hasher.final(&output);
72 return output;
73 }
74
75 /// Computes one machine root from a profile fingerprint digest and a state
76 /// digest.
77 pub fn machine(
78 profile_digest: os.abi.Digest,
79 state_digest: os.abi.Digest,
80 ) os.abi.Digest {
81 var hasher = Sha256.init(.{});
82 hasher.update(machine_domain);
83 hasher.update(&profile_digest);
84 hasher.update(&state_digest);
85 var output: os.abi.Digest = undefined;
86 hasher.final(&output);
87 return output;
88 }
89
90 fn hashInteger(hasher: *Sha256, value: anytype) void {
91 var encoded: [@sizeOf(@TypeOf(value))]u8 = undefined;
92 std.mem.writeInt(@TypeOf(value), &encoded, value, .little);
93 hasher.update(&encoded);
94 }
95
96 comptime {
97 std.debug.assert(std.math.isPowerOfTwo(page_count));
98 std.debug.assert(page_count == 16_384);
99 std.debug.assert(tree_levels == 14);
100 }