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 }