1use sha2::{Sha256, Digest};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16#[repr(C)]
17pub struct BootAttestation {
18 pub rvf_hash: [u8; 32],
20
21 pub capability_table_hash: [u8; 32],
23
24 pub region_layout_hash: [u8; 32],
26
27 pub boot_timestamp_ns: u64,
29
30 pub boot_sequence: u64,
32
33 pub platform_id: u64,
35
36 pub reserved: [u8; 16],
38}
39
40impl BootAttestation {
41 pub const SIZE: usize = 32 + 32 + 32 + 8 + 8 + 8 + 16; #[must_use]
46 pub fn new(
47 rvf_hash: [u8; 32],
48 capability_table_hash: [u8; 32],
49 region_layout_hash: [u8; 32],
50 boot_timestamp_ns: u64,
51 ) -> Self {
52 Self {
53 rvf_hash,
54 capability_table_hash,
55 region_layout_hash,
56 boot_timestamp_ns,
57 boot_sequence: 0,
58 platform_id: 0,
59 reserved: [0u8; 16],
60 }
61 }
62
63 #[must_use]
65 pub fn with_metadata(
66 rvf_hash: [u8; 32],
67 capability_table_hash: [u8; 32],
68 region_layout_hash: [u8; 32],
69 boot_timestamp_ns: u64,
70 boot_sequence: u64,
71 platform_id: u64,
72 ) -> Self {
73 Self {
74 rvf_hash,
75 capability_table_hash,
76 region_layout_hash,
77 boot_timestamp_ns,
78 boot_sequence,
79 platform_id,
80 reserved: [0u8; 16],
81 }
82 }
83
84 #[must_use]
86 pub fn hash(&self) -> [u8; 32] {
87 let mut hasher = Sha256::new();
88 hasher.update(&self.rvf_hash);
89 hasher.update(&self.capability_table_hash);
90 hasher.update(&self.region_layout_hash);
91 hasher.update(&self.boot_timestamp_ns.to_le_bytes());
92 hasher.update(&self.boot_sequence.to_le_bytes());
93 hasher.update(&self.platform_id.to_le_bytes());
94 hasher.update(&self.reserved);
95
96 let result = hasher.finalize();
97 let mut hash = [0u8; 32];
98 hash.copy_from_slice(&result);
99 hash
100 }
101
102 #[must_use]
104 pub fn to_bytes(&self) -> [u8; Self::SIZE] {
105 let mut bytes = [0u8; Self::SIZE];
106
107 bytes[0..32].copy_from_slice(&self.rvf_hash);
108 bytes[32..64].copy_from_slice(&self.capability_table_hash);
109 bytes[64..96].copy_from_slice(&self.region_layout_hash);
110 bytes[96..104].copy_from_slice(&self.boot_timestamp_ns.to_le_bytes());
111 bytes[104..112].copy_from_slice(&self.boot_sequence.to_le_bytes());
112 bytes[112..120].copy_from_slice(&self.platform_id.to_le_bytes());
113 bytes[120..136].copy_from_slice(&self.reserved);
114
115 bytes
116 }
117
118 pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
120 if bytes.len() < Self::SIZE {
121 return None;
122 }
123
124 let mut rvf_hash = [0u8; 32];
125 rvf_hash.copy_from_slice(&bytes[0..32]);
126
127 let mut capability_table_hash = [0u8; 32];
128 capability_table_hash.copy_from_slice(&bytes[32..64]);
129
130 let mut region_layout_hash = [0u8; 32];
131 region_layout_hash.copy_from_slice(&bytes[64..96]);
132
133 let boot_timestamp_ns = u64::from_le_bytes([
134 bytes[96], bytes[97], bytes[98], bytes[99],
135 bytes[100], bytes[101], bytes[102], bytes[103],
136 ]);
137
138 let boot_sequence = u64::from_le_bytes([
139 bytes[104], bytes[105], bytes[106], bytes[107],
140 bytes[108], bytes[109], bytes[110], bytes[111],
141 ]);
142
143 let platform_id = u64::from_le_bytes([
144 bytes[112], bytes[113], bytes[114], bytes[115],
145 bytes[116], bytes[117], bytes[118], bytes[119],
146 ]);
147
148 let mut reserved = [0u8; 16];
149 reserved.copy_from_slice(&bytes[120..136]);
150
151 Some(Self {
152 rvf_hash,
153 capability_table_hash,
154 region_layout_hash,
155 boot_timestamp_ns,
156 boot_sequence,
157 platform_id,
158 reserved,
159 })
160 }
161
162 #[must_use]
164 pub fn verify(&self, expected_rvf_hash: &[u8; 32]) -> bool {
165 self.rvf_hash == *expected_rvf_hash
166 }
167}
168
169impl Default for BootAttestation {
170 fn default() -> Self {
171 Self {
172 rvf_hash: [0u8; 32],
173 capability_table_hash: [0u8; 32],
174 region_layout_hash: [0u8; 32],
175 boot_timestamp_ns: 0,
176 boot_sequence: 0,
177 platform_id: 0,
178 reserved: [0u8; 16],
179 }
180 }
181}
182
183#[derive(Debug, Clone, Copy, PartialEq, Eq)]
188#[repr(C)]
189pub struct AttestationEntry {
190 pub entry_type: AttestationEntryType,
192
193 pub data_hash: [u8; 32],
195
196 pub timestamp_ns: u64,
198
199 pub task_id: u32,
201
202 pub component_id: u32,
204
205 pub flags: AttestationFlags,
207
208 pub reserved: [u8; 8],
210}
211
212#[derive(Debug, Clone, Copy, PartialEq, Eq)]
214#[repr(u8)]
215pub enum AttestationEntryType {
216 VectorMutation = 0,
218
219 GraphMutation = 1,
221
222 CapabilityDelegate = 2,
224
225 CapabilityRevoke = 3,
227
228 Checkpoint = 4,
230
231 Rollback = 5,
233
234 Custom = 255,
236}
237
238#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
240#[repr(transparent)]
241pub struct AttestationFlags(pub u32);
242
243impl AttestationFlags {
244 pub const NONE: Self = Self(0);
246
247 pub const HIGH_PRIORITY: Self = Self(1 << 0);
249
250 pub const DEADLINE_DRIVEN: Self = Self(1 << 1);
252
253 pub const DEEP_PROOF: Self = Self(1 << 2);
255
256 pub const DURING_ROLLBACK: Self = Self(1 << 3);
258
259 #[inline]
261 #[must_use]
262 pub const fn contains(&self, flag: Self) -> bool {
263 (self.0 & flag.0) != 0
264 }
265
266 #[inline]
268 #[must_use]
269 pub const fn union(self, other: Self) -> Self {
270 Self(self.0 | other.0)
271 }
272}
273
274impl AttestationEntry {
275 pub const SIZE: usize = 1 + 32 + 8 + 4 + 4 + 4 + 8; #[must_use]
280 pub fn new(
281 entry_type: AttestationEntryType,
282 data_hash: [u8; 32],
283 timestamp_ns: u64,
284 task_id: u32,
285 component_id: u32,
286 ) -> Self {
287 Self {
288 entry_type,
289 data_hash,
290 timestamp_ns,
291 task_id,
292 component_id,
293 flags: AttestationFlags::NONE,
294 reserved: [0u8; 8],
295 }
296 }
297
298 #[must_use]
300 pub fn with_flags(
301 entry_type: AttestationEntryType,
302 data_hash: [u8; 32],
303 timestamp_ns: u64,
304 task_id: u32,
305 component_id: u32,
306 flags: AttestationFlags,
307 ) -> Self {
308 Self {
309 entry_type,
310 data_hash,
311 timestamp_ns,
312 task_id,
313 component_id,
314 flags,
315 reserved: [0u8; 8],
316 }
317 }
318
319 #[must_use]
321 pub fn hash(&self) -> [u8; 32] {
322 let mut hasher = Sha256::new();
323 hasher.update(&[self.entry_type as u8]);
324 hasher.update(&self.data_hash);
325 hasher.update(&self.timestamp_ns.to_le_bytes());
326 hasher.update(&self.task_id.to_le_bytes());
327 hasher.update(&self.component_id.to_le_bytes());
328 hasher.update(&self.flags.0.to_le_bytes());
329 hasher.update(&self.reserved);
330
331 let result = hasher.finalize();
332 let mut hash = [0u8; 32];
333 hash.copy_from_slice(&result);
334 hash
335 }
336}
337
338impl Default for AttestationEntry {
339 fn default() -> Self {
340 Self {
341 entry_type: AttestationEntryType::Custom,
342 data_hash: [0u8; 32],
343 timestamp_ns: 0,
344 task_id: 0,
345 component_id: 0,
346 flags: AttestationFlags::NONE,
347 reserved: [0u8; 8],
348 }
349 }
350}
351
352#[cfg(test)]
353mod tests {
354 use super::*;
355
356 #[test]
357 fn test_boot_attestation_creation() {
358 let att = BootAttestation::new(
359 [1u8; 32],
360 [2u8; 32],
361 [3u8; 32],
362 1234567890,
363 );
364
365 assert_eq!(att.rvf_hash, [1u8; 32]);
366 assert_eq!(att.capability_table_hash, [2u8; 32]);
367 assert_eq!(att.region_layout_hash, [3u8; 32]);
368 assert_eq!(att.boot_timestamp_ns, 1234567890);
369 }
370
371 #[test]
372 fn test_boot_attestation_serialization() {
373 let att = BootAttestation::new(
374 [0xAA; 32],
375 [0xBB; 32],
376 [0xCC; 32],
377 999999999,
378 );
379
380 let bytes = att.to_bytes();
381 let recovered = BootAttestation::from_bytes(&bytes).unwrap();
382
383 assert_eq!(att, recovered);
384 }
385
386 #[test]
387 fn test_boot_attestation_hash() {
388 let att1 = BootAttestation::new([1u8; 32], [2u8; 32], [3u8; 32], 100);
389 let att2 = BootAttestation::new([1u8; 32], [2u8; 32], [3u8; 32], 100);
390 let att3 = BootAttestation::new([1u8; 32], [2u8; 32], [4u8; 32], 100);
391
392 assert_eq!(att1.hash(), att2.hash());
394
395 assert_ne!(att1.hash(), att3.hash());
397 }
398
399 #[test]
400 fn test_boot_attestation_verify() {
401 let expected_hash = [0xDEu8; 32];
402 let att = BootAttestation::new(expected_hash, [0u8; 32], [0u8; 32], 0);
403
404 assert!(att.verify(&expected_hash));
405 assert!(!att.verify(&[0u8; 32]));
406 }
407
408 #[test]
409 fn test_attestation_entry_creation() {
410 let entry = AttestationEntry::new(
411 AttestationEntryType::VectorMutation,
412 [0xAB; 32],
413 1234567890,
414 1,
415 2,
416 );
417
418 assert_eq!(entry.entry_type, AttestationEntryType::VectorMutation);
419 assert_eq!(entry.task_id, 1);
420 assert_eq!(entry.component_id, 2);
421 }
422
423 #[test]
424 fn test_attestation_flags() {
425 let flags = AttestationFlags::HIGH_PRIORITY.union(AttestationFlags::DEEP_PROOF);
426
427 assert!(flags.contains(AttestationFlags::HIGH_PRIORITY));
428 assert!(flags.contains(AttestationFlags::DEEP_PROOF));
429 assert!(!flags.contains(AttestationFlags::DEADLINE_DRIVEN));
430 }
431
432 #[test]
433 fn test_boot_attestation_size() {
434 assert_eq!(BootAttestation::SIZE, 136);
435 }
436}