lib/machine/src/checkpoint/owner/identity.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const canon = @import("../canon/root.zig");
2 const instance_receipt = @import("../../instance/receipt/root.zig");
3 const os = @import("os");
4 const std = @import("std");
5 const types = @import("types.zig");
6
7 const Sha256 = std.crypto.hash.sha2.Sha256;
8 const state_domain = "TINYMACHINECHECKPOINTSTATE1\x00";
9 const fresh_transport_domain = "TINYMACHINEFRESHTRANSPORT1\x00";
10
11 const IdentityError = error{
12 InvalidCpuState,
13 ParentExecutionMismatch,
14 ParentImageMismatch,
15 ParentProfileMismatch,
16 };
17
18 pub const Error = IdentityError || instance_receipt.Error;
19
20 /// Requires a child and its parent to share the profile fingerprint, the
21 /// immutable image descriptor, and the execution fingerprint. The call returns
22 /// the error naming the field that differs: `ParentProfileMismatch`,
23 /// `ParentImageMismatch`, or `ParentExecutionMismatch`.
24 pub fn validateParent(child: types.Material, parent: types.Material) Error!void {
25 if (!std.meta.eql(child.profile, parent.profile)) {
26 return error.ParentProfileMismatch;
27 }
28 if (!std.meta.eql(child.immutable_image, parent.immutable_image)) {
29 return error.ParentImageMismatch;
30 }
31 if (!std.meta.eql(
32 child.receipt.execution_fingerprint,
33 parent.receipt.execution_fingerprint,
34 )) {
35 return error.ParentExecutionMismatch;
36 }
37 }
38
39 /// Derives the state digest from four inputs: the receipt digest, the ten
40 /// registers, the digest of the immutable image, and the digest of memory. The
41 /// call validates the settled receipt and the CPU restart frame before hashing
42 /// anything.
43 pub fn state(
44 receipt: instance_receipt.SemanticReceipt,
45 cpu: types.CpuState,
46 immutable_image: @import("machine_instance_core").provenance.ImmutableImage,
47 memory: types.MemoryDigest,
48 ) Error!types.StateDigest {
49 try validateCpu(
50 cpu,
51 immutable_image.initial,
52 immutable_image.entry_offset,
53 );
54 const receipt_digest = try instance_receipt.semanticReceiptDigest(receipt);
55 var hasher = Sha256.init(.{});
56 hasher.update(state_domain);
57 hasher.update(&receipt_digest);
58 hashInteger(&hasher, cpu.rip);
59 hashInteger(&hasher, cpu.rsp);
60 hashInteger(&hasher, cpu.rdi);
61 hashInteger(&hasher, cpu.rflags);
62 hashInteger(&hasher, cpu.cr0);
63 hashInteger(&hasher, cpu.cr3);
64 hashInteger(&hasher, cpu.cr4);
65 hashInteger(&hasher, cpu.efer);
66 hashInteger(&hasher, cpu.code_selector);
67 hashInteger(&hasher, cpu.data_selector);
68 hasher.update(&immutable_image.digest);
69 hasher.update(fresh_transport_domain);
70 hasher.update(&memory.digest);
71 var output: os.abi.Digest = undefined;
72 hasher.final(&output);
73 return .{ .digest = output };
74 }
75
76 /// Computes the checkpoint root binding one profile fingerprint to one state
77 /// digest. The call returns the fingerprint and the state digest beside the
78 /// digest.
79 pub fn root(
80 profile_fingerprint: anytype,
81 state_digest: types.StateDigest,
82 ) types.Root {
83 return .{
84 .digest = canon.machine(profile_fingerprint.digest, state_digest.digest),
85 .profile = profile_fingerprint,
86 .state = state_digest,
87 };
88 }
89
90 /// Requires `cpu` and `expected` to equal the kernel restart frame the manifest
91 /// fixes for the supplied entry offset. The call compares all ten registers,
92 /// namely `rip`, `rsp`, `rdi`, `rflags`, `cr0`, `cr3`, `cr4`, `efer`, and the
93 /// code and data selectors. Any changed register returns `InvalidCpuState`.
94 pub fn validateCpu(
95 cpu: types.CpuState,
96 expected: types.CpuState,
97 entry_offset: u32,
98 ) Error!void {
99 const manifest = os.boot.kernel.manifest;
100 const profile = os.boot.profile;
101 if (!std.meta.eql(cpu, expected) or
102 expected.rip != profile.kernel_physical_base + entry_offset or
103 expected.rsp != profile.stack_top - manifest.k0_v1_entry_stack_bytes or
104 expected.rdi != os.abi.channel.boot_frame_address or
105 expected.rflags != manifest.k0_v1_rflags or
106 expected.cr0 != manifest.k0_v1_cr0 or
107 expected.cr3 != manifest.k0_v1_pml4_address or
108 expected.cr4 != manifest.k0_v1_cr4 or
109 expected.efer != manifest.k0_v1_efer or
110 expected.code_selector != manifest.k0_v1_code_selector or
111 expected.data_selector != manifest.k0_v1_data_selector)
112 {
113 return error.InvalidCpuState;
114 }
115 }
116
117 fn hashInteger(hasher: *Sha256, value: anytype) void {
118 var encoded: [@sizeOf(@TypeOf(value))]u8 = undefined;
119 std.mem.writeInt(@TypeOf(value), &encoded, value, .little);
120 hasher.update(&encoded);
121 }
122
123 test "checkpoint CPU is the exact admitted restart frame" {
124 const entry_offset: u32 = 0x1234;
125 const expected = os.boot.kernel.manifest.k0V1Facts(
126 entry_offset,
127 1,
128 1,
129 ).initial;
130 try validateCpu(expected, expected, entry_offset);
131
132 var changed = expected;
133 changed.rip += 1;
134 try std.testing.expectError(
135 error.InvalidCpuState,
136 validateCpu(changed, expected, entry_offset),
137 );
138 changed = expected;
139 changed.rsp -= 8;
140 try std.testing.expectError(
141 error.InvalidCpuState,
142 validateCpu(changed, changed, entry_offset),
143 );
144 }