1use crate::error::Error;
5use crate::logger::VmgsLogEvent;
6use crate::logger::VmgsLogger;
7use crate::storage::VmgsStorage;
8use cvm_tracing::CVM_ALLOWED;
9use disk_backend::Disk;
10#[cfg(feature = "inspect")]
11use inspect::Inspect;
12#[cfg(feature = "inspect")]
13use inspect_counters::Counter;
14use std::collections::BTreeMap;
15use std::collections::HashMap;
16use std::num::NonZeroU32;
17use std::sync::Arc;
18use vmgs_format::EncryptionAlgorithm;
19use vmgs_format::FileAttribute;
20use vmgs_format::FileId;
21use vmgs_format::VMGS_BYTES_PER_BLOCK;
22use vmgs_format::VMGS_ENCRYPTION_KEY_SIZE;
23use vmgs_format::VMGS_FILE_TABLE_BLOCK_SIZE;
24use vmgs_format::VMGS_MIN_FILE_BLOCK_OFFSET;
25use vmgs_format::VMGS_NONCE_SIZE;
26use vmgs_format::VMGS_SIGNATURE;
27use vmgs_format::VMGS_VERSION_3_0;
28use vmgs_format::VmgsAuthTag;
29use vmgs_format::VmgsDatastoreKey;
30use vmgs_format::VmgsEncryptionKey;
31use vmgs_format::VmgsExtendedFileEntry;
32use vmgs_format::VmgsExtendedFileTable;
33use vmgs_format::VmgsFileEntry;
34use vmgs_format::VmgsFileTable;
35use vmgs_format::VmgsHeader;
36use vmgs_format::VmgsMarkers;
37use vmgs_format::VmgsNonce;
38use vmgs_format::VmgsProvisioningMarker;
39use vmgs_format::VmgsProvisioningReason;
40use zerocopy::FromBytes;
41use zerocopy::FromZeros;
42use zerocopy::IntoBytes;
43
44#[derive(Debug)]
46enum LogOpType {
47 VmgsProvision,
48}
49
50#[derive(Debug)]
52#[cfg_attr(feature = "mesh", derive(mesh_protobuf::Protobuf))]
53pub struct VmgsFileInfo {
54 pub allocated_bytes: u64,
56 pub valid_bytes: u64,
58 pub encrypted: bool,
60}
61
62#[derive(Debug, Clone, Copy)]
64pub enum GspType {
65 None,
67 GspById,
69 GspKey,
71}
72
73#[derive(Clone, PartialEq, Eq, Debug)]
76#[cfg_attr(feature = "inspect", derive(Inspect))]
77struct ResolvedFileControlBlock {
78 block_offset: u32,
81 #[cfg_attr(feature = "inspect", inspect(with = "|x| x.get()"))]
82 allocated_blocks: NonZeroU32,
83 valid_bytes: u64,
84
85 nonce: VmgsNonce,
86 authentication_tag: VmgsAuthTag,
87
88 attributes: FileAttribute,
91 encryption_key: VmgsDatastoreKey,
92}
93
94impl ResolvedFileControlBlock {
95 fn new(block_offset: u32, block_count: u32, valid_bytes: usize, encrypt: bool) -> Self {
96 let (attributes, encryption_key, nonce) = if encrypt {
97 (
98 FileAttribute::new()
99 .with_encrypted(true)
100 .with_authenticated(true),
101 {
102 let mut encryption_key = VmgsDatastoreKey::new_zeroed();
103 getrandom::fill(&mut encryption_key).expect("rng failure");
104 encryption_key
105 },
106 generate_nonce(),
107 )
108 } else {
109 (
110 FileAttribute::new(),
111 VmgsDatastoreKey::new_zeroed(),
112 VmgsNonce::new_zeroed(),
113 )
114 };
115
116 ResolvedFileControlBlock {
117 block_offset,
118 allocated_blocks: NonZeroU32::new(block_count).unwrap(),
119 valid_bytes: valid_bytes as u64,
120
121 nonce,
122 authentication_tag: VmgsAuthTag::new_zeroed(),
123
124 attributes,
125 encryption_key,
126 }
127 }
128
129 fn file_info(&self) -> VmgsFileInfo {
130 VmgsFileInfo {
131 allocated_bytes: block_count_to_byte_count(self.allocated_blocks.get()),
132 valid_bytes: self.valid_bytes,
133 encrypted: self.encrypted(),
134 }
135 }
136
137 fn encrypted(&self) -> bool {
138 self.attributes.encrypted() || self.attributes.authenticated()
139 }
140
141 fn fill_file_entry(&self, version: u32, file_entry: &mut VmgsFileEntry) {
142 file_entry.offset = self.block_offset;
143 file_entry.allocation_size = self.allocated_blocks.get();
144 file_entry.valid_data_size = self.valid_bytes;
145
146 if version >= VMGS_VERSION_3_0 {
147 file_entry.nonce.copy_from_slice(&self.nonce);
148 file_entry
149 .authentication_tag
150 .copy_from_slice(&self.authentication_tag);
151 file_entry.attributes = self.attributes;
152 }
153 }
154
155 fn fill_extended_file_entry(&self, extended_file_entry: &mut VmgsExtendedFileEntry) {
156 extended_file_entry.attributes = self.attributes;
157 extended_file_entry
158 .encryption_key
159 .copy_from_slice(&self.encryption_key);
160 }
161
162 fn from_file_entry(version: u32, file_entry: &VmgsFileEntry) -> Self {
163 let (nonce, authentication_tag, attributes) = if version >= VMGS_VERSION_3_0 {
164 (
165 file_entry.nonce,
166 file_entry.authentication_tag,
167 file_entry.attributes,
168 )
169 } else {
170 Default::default()
171 };
172
173 ResolvedFileControlBlock {
174 block_offset: file_entry.offset,
175 allocated_blocks: NonZeroU32::new(file_entry.allocation_size).unwrap(),
176 valid_bytes: file_entry.valid_data_size,
177
178 nonce,
179 authentication_tag,
180
181 attributes,
182 encryption_key: VmgsDatastoreKey::new_zeroed(),
183 }
184 }
185
186 #[cfg_attr(not(feature = "encryption"), expect(dead_code))]
187 fn update_extended_data(&mut self, extended_file_entry: &VmgsExtendedFileEntry) {
188 self.attributes = extended_file_entry.attributes;
189 self.encryption_key = extended_file_entry.encryption_key;
190 }
191
192 #[cfg_attr(not(feature = "encryption"), expect(unused_variables))]
193 fn encrypt(&mut self, data: &[u8]) -> Result<Vec<u8>, Error> {
194 #[cfg(not(feature = "encryption"))]
195 unreachable!("Encryption requires the encryption feature");
196 #[cfg(feature = "encryption")]
197 {
198 let encrypted = crate::encrypt::vmgs_encrypt(
199 &self.encryption_key,
200 &self.nonce,
201 data,
202 &mut self.authentication_tag,
203 )?;
204
205 if encrypted.len() as u64 != self.valid_bytes {
206 return Err(Error::UnexpectedLength(
207 "encrypted data",
208 self.valid_bytes as usize,
209 encrypted.len(),
210 ));
211 }
212
213 Ok(encrypted)
214 }
215 }
216
217 #[cfg_attr(not(feature = "encryption"), expect(unused_variables))]
218 fn decrypt(&self, data: &[u8]) -> Result<Vec<u8>, Error> {
219 #[cfg(not(feature = "encryption"))]
220 unreachable!("Encryption requires the encryption feature");
221 #[cfg(feature = "encryption")]
222 {
223 if data.iter().all(|x| *x == 0) {
227 return Err(Error::InvalidFormat("encrypted data is all-zeros".into()));
228 }
229
230 let decrypted = crate::encrypt::vmgs_decrypt(
231 &self.encryption_key,
232 &self.nonce,
233 data,
234 &self.authentication_tag,
235 )?;
236
237 if decrypted.len() as u64 != self.valid_bytes {
238 return Err(Error::UnexpectedLength(
239 "decrypted data",
240 self.valid_bytes as usize,
241 decrypted.len(),
242 ));
243 }
244
245 Ok(decrypted)
246 }
247 }
248}
249
250enum RefOrOwned<'a> {
251 Ref(&'a [u8]),
252 Owned(Vec<u8>),
253}
254
255impl<'a> RefOrOwned<'a> {
256 fn placeholder<T>() -> Self {
257 RefOrOwned::Owned(vec![0; size_of::<T>()])
258 }
259
260 fn len(&self) -> usize {
261 match self {
262 RefOrOwned::Ref(x) => x.len(),
263 RefOrOwned::Owned(x) => x.len(),
264 }
265 }
266
267 fn copy_from_slice(&mut self, src: &[u8]) {
268 match self {
269 RefOrOwned::Ref(_) => panic!("cannot modify ref"),
270 RefOrOwned::Owned(x) => x.copy_from_slice(src),
271 }
272 }
273
274 fn get(&self) -> &[u8] {
275 match self {
276 RefOrOwned::Ref(x) => x,
277 RefOrOwned::Owned(x) => x,
278 }
279 }
280
281 fn replace(&mut self, new_value: Self) {
282 assert_eq!(self.len(), new_value.len());
283 *self = new_value;
284 }
285}
286
287struct AllocRequest<'a> {
288 data: RefOrOwned<'a>,
289 encrypt: bool,
290}
291
292impl<'a> AllocRequest<'a> {
293 fn new(data: RefOrOwned<'a>, encrypt: bool) -> Self {
294 Self { data, encrypt }
295 }
296
297 fn allocate(
298 self,
299 allocation_list: &mut Vec<AllocationBlock>,
300 block_capacity: u32,
301 ) -> Result<AllocResult<'a>, Error> {
302 let valid_bytes = self.data.len();
303
304 let mut block_count = (round_up_count(valid_bytes, VMGS_BYTES_PER_BLOCK)
305 / VMGS_BYTES_PER_BLOCK as u64) as u32;
306 if block_count == 0 {
308 block_count = 1;
309 }
310 if block_count as u64 > vmgs_format::VMGS_MAX_FILE_SIZE_BLOCKS {
311 return Err(Error::WriteFileBlocks);
312 }
313
314 let block_offset = allocate_helper(allocation_list, block_count, block_capacity)?;
315
316 let fcb =
317 ResolvedFileControlBlock::new(block_offset, block_count, valid_bytes, self.encrypt);
318
319 Ok(AllocResult {
320 fcb,
321 data: self.data,
322 })
323 }
324}
325
326struct AllocResult<'a> {
327 fcb: ResolvedFileControlBlock,
328 data: RefOrOwned<'a>,
329}
330
331impl<'a> AllocResult<'a> {
332 fn encrypt(&mut self) -> Result<(), Error> {
333 self.data
334 .replace(RefOrOwned::Owned(self.fcb.encrypt(self.data.get())?));
335 Ok(())
336 }
337
338 fn encrypt_from(&mut self, data: &[u8]) -> Result<(), Error> {
339 self.data
340 .replace(RefOrOwned::Owned(self.fcb.encrypt(data)?));
341 Ok(())
342 }
343}
344
345#[cfg_attr(feature = "inspect", derive(Inspect))]
348pub struct Vmgs {
349 storage: VmgsStorage,
350
351 #[cfg(feature = "inspect")]
352 stats: vmgs_inspect::VmgsStats,
353
354 state: VmgsState,
355
356 #[cfg_attr(feature = "inspect", inspect(skip))]
357 logger: Option<Arc<dyn VmgsLogger>>,
358}
359
360#[cfg_attr(feature = "inspect", derive(Inspect))]
361#[derive(Clone)]
362struct VmgsState {
363 active_header_index: usize,
364 active_header_sequence_number: u32,
365 version: u32,
366 #[cfg_attr(feature = "inspect", inspect(with = "vmgs_inspect::fcbs"))]
367 fcbs: HashMap<FileId, ResolvedFileControlBlock>,
368 encryption_algorithm: EncryptionAlgorithm,
369 datastore_key_count: u8,
370 active_datastore_key_index: Option<usize>,
371 #[cfg_attr(feature = "inspect", inspect(iter_by_index))]
372 datastore_keys: [VmgsDatastoreKey; 2],
373 unused_metadata_key: VmgsDatastoreKey,
375 #[cfg_attr(feature = "inspect", inspect(iter_by_index))]
376 encrypted_metadata_keys: [VmgsEncryptionKey; 2],
377 reprovisioned: bool,
378 provisioning_reason: Option<VmgsProvisioningReason>,
379}
380
381#[cfg(feature = "inspect")]
382mod vmgs_inspect {
383 use super::*;
384
385 #[derive(Default)]
386 pub struct IoStat {
387 pub attempt: Counter,
388 pub resolved: Counter,
389 }
390
391 impl Inspect for IoStat {
394 fn inspect(&self, req: inspect::Request<'_>) {
395 let mut resp = req.respond();
396 resp.counter("ok", self.resolved.get())
397 .counter("err", self.attempt.get() - self.resolved.get());
398 }
399 }
400
401 #[derive(Inspect, Default)]
402 pub struct VmgsStats {
403 #[inspect(with = "stat_map")]
404 pub read: HashMap<FileId, IoStat>,
405 #[inspect(with = "stat_map")]
406 pub write: HashMap<FileId, IoStat>,
407 }
408
409 pub(super) fn fcbs(fcbs: &HashMap<FileId, ResolvedFileControlBlock>) -> impl Inspect + '_ {
410 inspect::adhoc(|req| {
411 let mut res = req.respond();
412 for (id, fcb) in fcbs.iter() {
413 res.field(&format!("{}-{:?}", id.0, id), fcb);
414 }
415 })
416 }
417
418 pub fn stat_map(map: &HashMap<FileId, IoStat>) -> impl Inspect + '_ {
419 inspect::iter_by_key(map).map_key(|x| format!("{:?}", x))
420 }
421}
422
423impl Vmgs {
424 pub async fn try_open(
427 disk: Disk,
428 logger: Option<Arc<dyn VmgsLogger>>,
429 format_on_empty: bool,
430 format_on_failure: bool,
431 ) -> Result<Self, Error> {
432 match Self::open(disk.clone(), logger.clone()).await {
433 Ok(vmgs) => Ok(vmgs),
434 Err(Error::EmptyFile) if format_on_empty => {
435 tracing::info!(CVM_ALLOWED, "empty vmgs file, formatting");
436 Self::format_new_with_reason(disk, VmgsProvisioningReason::Empty, logger).await
437 }
438 Err(err) if format_on_failure => {
439 tracing::warn!(CVM_ALLOWED, ?err, "vmgs initialization error, reformatting");
440 Self::format_new_with_reason(disk, VmgsProvisioningReason::Failure, logger).await
441 }
442 Err(err) => {
443 let event_log_id = match err {
444 Error::InvalidFormat(_) => VmgsLogEvent::InvalidFormat,
446 Error::CorruptFormat(_) => VmgsLogEvent::CorruptFormat,
448 _ => VmgsLogEvent::InitFailed,
450 };
451
452 logger.log_event_fatal(event_log_id).await;
453 Err(err)
454 }
455 }
456 }
457
458 pub async fn open(disk: Disk, logger: Option<Arc<dyn VmgsLogger>>) -> Result<Self, Error> {
460 tracing::debug!(CVM_ALLOWED, "opening VMGS datastore");
461 let storage = VmgsStorage::new_validated(disk).map_err(Error::Initialization)?;
462 Self::open_inner(storage, logger).await
463 }
464
465 pub async fn format_new(
467 disk: Disk,
468 logger: Option<Arc<dyn VmgsLogger>>,
469 ) -> Result<Self, Error> {
470 Self::format_new_with_reason(disk, VmgsProvisioningReason::Request, logger).await
471 }
472
473 pub async fn format_new_with_reason(
475 disk: Disk,
476 reason: VmgsProvisioningReason,
477 logger: Option<Arc<dyn VmgsLogger>>,
478 ) -> Result<Self, Error> {
479 tracing::info!(
480 CVM_ALLOWED,
481 op_type = ?LogOpType::VmgsProvision,
482 ?reason,
483 "formatting and initializing VMGS datastore"
484 );
485 let storage = VmgsStorage::new_validated(disk).map_err(Error::Initialization)?;
486 Self::format_new_inner(storage, VMGS_VERSION_3_0, reason, logger).await
487 }
488
489 pub async fn request_format(
491 disk: Disk,
492 logger: Option<Arc<dyn VmgsLogger>>,
493 ) -> Result<Self, Error> {
494 let mut storage = VmgsStorage::new_validated(disk).map_err(Error::Initialization)?;
495
496 match Self::open_header(&mut storage).await {
497 Ok((active_header, active_header_index)) if active_header.markers.reprovisioned() => {
498 tracing::info!(CVM_ALLOWED, "reprovisioned marker found, skipping format");
499 Self::finish_open(storage, active_header, active_header_index, logger).await
500 }
501 _ => {
502 tracing::info!(CVM_ALLOWED, "formatting vmgs file on request");
503 let mut vmgs = Vmgs::format_new_inner(
504 storage,
505 VMGS_VERSION_3_0,
506 VmgsProvisioningReason::Request,
507 logger,
508 )
509 .await?;
510
511 vmgs.set_reprovisioned(true).await?;
514
515 Ok(vmgs)
516 }
517 }
518 }
519
520 async fn open_inner(
521 mut storage: VmgsStorage,
522 logger: Option<Arc<dyn VmgsLogger>>,
523 ) -> Result<Self, Error> {
524 let (active_header, active_header_index) = Self::open_header(&mut storage).await?;
525
526 let mut vmgs =
527 Self::finish_open(storage, active_header, active_header_index, logger).await?;
528
529 vmgs.set_reprovisioned(false).await?;
532
533 Ok(vmgs)
534 }
535
536 async fn open_header(storage: &mut VmgsStorage) -> Result<(VmgsHeader, usize), Error> {
537 let (header_1, header_2) = read_headers_inner(storage).await.map_err(|(e, _)| e)?;
538
539 let active_header_index =
540 get_active_header(validate_header(&header_1), validate_header(&header_2))?;
541
542 let active_header = if active_header_index == 0 {
543 header_1
544 } else {
545 header_2
546 };
547
548 Ok((active_header, active_header_index))
549 }
550
551 async fn finish_open(
552 storage: VmgsStorage,
553 active_header: VmgsHeader,
554 active_header_index: usize,
555 logger: Option<Arc<dyn VmgsLogger>>,
556 ) -> Result<Vmgs, Error> {
557 let mut vmgs = Self {
558 storage,
559
560 state: VmgsState::from_header(active_header, active_header_index),
561
562 #[cfg(feature = "inspect")]
563 stats: Default::default(),
564
565 logger,
566 };
567
568 let file_table_buffer = vmgs
569 .read_file_internal(FileId::FILE_TABLE, false, None)
570 .await?;
571 vmgs.state.fcbs = initialize_file_metadata(
572 VmgsFileTable::ref_from_bytes(&file_table_buffer)
573 .map_err(|_| Error::InvalidFormat("incorrect file table size".into()))?,
574 vmgs.state.version,
575 vmgs.storage.block_capacity(),
576 )?;
577
578 Ok(vmgs)
579 }
580
581 fn new(
582 storage: VmgsStorage,
583 version: u32,
584 reason: VmgsProvisioningReason,
585 logger: Option<Arc<dyn VmgsLogger>>,
586 ) -> Vmgs {
587 Self {
588 storage,
589
590 state: VmgsState::new(version, Some(reason)),
591
592 #[cfg(feature = "inspect")]
593 stats: Default::default(),
594
595 logger,
596 }
597 }
598
599 async fn format_new_inner(
601 storage: VmgsStorage,
602 version: u32,
603 reason: VmgsProvisioningReason,
604 logger: Option<Arc<dyn VmgsLogger>>,
605 ) -> Result<Vmgs, Error> {
606 tracing::info!(CVM_ALLOWED, "Formatting new VMGS file.");
607
608 let mut vmgs = Self::new(storage, version, reason, logger);
609
610 vmgs.write_header_internal(&VmgsHeader::new_zeroed(), vmgs.state.active_header_index)
612 .await?;
613
614 let files = if version >= VMGS_VERSION_3_0 {
616 [(
617 FileId::EXTENDED_FILE_TABLE,
618 AllocRequest::new(RefOrOwned::placeholder::<VmgsExtendedFileTable>(), false),
619 )]
620 .into()
621 } else {
622 BTreeMap::new()
623 };
624
625 vmgs.write_files_internal(files, None).await?;
627
628 let (new_header, index) = vmgs.state.make_header();
630 vmgs.write_header_internal(&new_header, index).await?;
631
632 vmgs.storage.flush().await.map_err(Error::FlushDisk)?;
634
635 Ok(vmgs)
636 }
637
638 pub fn get_file_info(&self, file_id: FileId) -> Result<VmgsFileInfo, Error> {
642 Ok(self
643 .state
644 .fcbs
645 .get(&file_id)
646 .ok_or(Error::FileInfoNotAllocated(file_id))?
647 .file_info())
648 }
649
650 pub fn check_file_allocated(&self, file_id: FileId) -> bool {
652 self.state.fcbs.contains_key(&file_id)
653 }
654
655 pub fn dump_file_table(&self) -> Vec<(FileId, VmgsFileInfo)> {
657 let mut file_table = self
658 .state
659 .fcbs
660 .iter()
661 .map(|(file_id, fcb)| (*file_id, fcb.file_info()))
662 .collect::<Vec<_>>();
663 file_table.sort_by_key(|(file_id, _)| *file_id);
664 file_table
665 }
666
667 async fn write_file_inner(
670 &mut self,
671 file_id: FileId,
672 buf: &[u8],
673 encrypt: bool,
674 overwrite_encrypted: bool,
675 ) -> Result<(), Error> {
676 #[cfg(feature = "inspect")]
677 self.stats
678 .write
679 .entry(file_id)
680 .or_default()
681 .attempt
682 .increment();
683
684 if matches!(file_id, FileId::FILE_TABLE | FileId::EXTENDED_FILE_TABLE) {
685 return Err(Error::FileId);
686 }
687 if buf.len() > vmgs_format::VMGS_MAX_FILE_SIZE_BYTES as usize {
688 return Err(Error::WriteFileLength);
689 }
690
691 let mut temp_state = self.temp_state();
692
693 if encrypt && !temp_state.encrypted_and_unlocked() {
694 tracing::trace!(
695 CVM_ALLOWED,
696 "VMGS file not encrypted and unlocked, performing plaintext write"
697 );
698 }
699
700 let encrypt = encrypt && temp_state.encrypted_and_unlocked();
701 let existing_encrypted = temp_state
702 .fcbs
703 .get(&file_id)
704 .is_some_and(|fcb| fcb.encrypted());
705
706 if !encrypt && existing_encrypted {
707 if overwrite_encrypted {
708 tracing::warn!(
709 CVM_ALLOWED,
710 "overwriting encrypted file with plaintext data!"
711 )
712 } else {
713 return Err(Error::OverwriteEncrypted);
714 }
715 }
716
717 self.write_files_internal(
718 [(file_id, AllocRequest::new(RefOrOwned::Ref(buf), encrypt))].into(),
719 Some(&mut temp_state),
720 )
721 .await?;
722
723 self.write_header_and_apply(temp_state).await?;
725
726 #[cfg(feature = "inspect")]
727 self.stats
728 .write
729 .entry(file_id)
730 .or_default()
731 .resolved
732 .increment();
733
734 Ok(())
735 }
736
737 async fn write_files_internal<'a>(
739 &mut self,
740 mut files: BTreeMap<FileId, AllocRequest<'a>>,
742 temp_state: Option<&mut VmgsState>,
743 ) -> Result<(), Error> {
744 let state = temp_state.unwrap_or(&mut self.state);
745
746 files.insert(
748 FileId::FILE_TABLE,
749 AllocRequest::new(RefOrOwned::placeholder::<VmgsFileTable>(), false),
750 );
751 if state.encrypted_and_unlocked() {
752 files.insert(
753 FileId::EXTENDED_FILE_TABLE,
754 AllocRequest::new(RefOrOwned::placeholder::<VmgsExtendedFileTable>(), true),
755 );
756 }
757
758 let mut files = state.allocate_space(files, self.storage.block_capacity())?;
760
761 for (file_id, res) in files.iter_mut() {
764 if *file_id != FileId::EXTENDED_FILE_TABLE {
765 if res.fcb.encrypted() {
766 res.encrypt()?;
767 }
768 state.fcbs.insert(*file_id, res.fcb.clone());
769 }
770 }
771
772 if let Some(res) = files.get_mut(&FileId::EXTENDED_FILE_TABLE) {
774 if state.encrypted_and_unlocked() {
775 let new_extended_file_table = state.make_extended_file_table()?;
776 res.encrypt_from(new_extended_file_table.as_bytes())?;
777 }
778 state
780 .fcbs
781 .insert(FileId::EXTENDED_FILE_TABLE, res.fcb.clone());
782 if state.encrypted_and_unlocked() {
783 state.encrypt_metadata_key()?;
784 }
785 }
786
787 let new_file_table = state.make_file_table()?;
790 files
791 .get_mut(&FileId::FILE_TABLE)
792 .unwrap()
793 .data
794 .copy_from_slice(new_file_table.as_bytes());
795
796 for res in files.values() {
798 self.write_file_internal(&res.fcb, res.data.get()).await?;
799 }
800
801 Ok(())
802 }
803
804 async fn write_file_internal(
807 &mut self,
808 fcb: &ResolvedFileControlBlock,
809 buf: &[u8],
810 ) -> Result<(), Error> {
811 if let Err(e) = self
812 .storage
813 .write_block(block_count_to_byte_count(fcb.block_offset), buf)
814 .await
815 {
816 self.logger
817 .log_event_fatal(VmgsLogEvent::AccessFailed)
818 .await;
819
820 return Err(Error::WriteDisk(e));
821 }
822
823 Ok(())
824 }
825
826 async fn write_header_internal(
828 &mut self,
829 header: &VmgsHeader,
830 index: usize,
831 ) -> Result<(), Error> {
832 assert!(index < 2);
833 self.storage
834 .write_block(
835 index as u64 * self.storage.aligned_header_size(),
836 header.as_bytes(),
837 )
838 .await
839 .map_err(Error::WriteDisk)?;
840 Ok(())
841 }
842
843 pub async fn read_file(&mut self, file_id: FileId) -> Result<Vec<u8>, Error> {
845 self.read_file_inner(file_id, true).await
846 }
847
848 pub async fn read_file_raw(&mut self, file_id: FileId) -> Result<Vec<u8>, Error> {
850 self.read_file_inner(file_id, false).await
851 }
852
853 async fn read_file_inner(&mut self, file_id: FileId, decrypt: bool) -> Result<Vec<u8>, Error> {
855 #[cfg(feature = "inspect")]
856 self.stats
857 .read
858 .entry(file_id)
859 .or_default()
860 .attempt
861 .increment();
862
863 if matches!(file_id, FileId::FILE_TABLE | FileId::EXTENDED_FILE_TABLE) {
864 return Err(Error::FileId);
865 }
866
867 let buf = self.read_file_internal(file_id, decrypt, None).await?;
868
869 #[cfg(feature = "inspect")]
870 self.stats
871 .read
872 .entry(file_id)
873 .or_default()
874 .resolved
875 .increment();
876
877 Ok(buf)
878 }
879
880 async fn read_file_internal(
882 &mut self,
883 file_id: FileId,
884 decrypt: bool,
885 temp_state: Option<&VmgsState>,
886 ) -> Result<Vec<u8>, Error> {
887 let state = temp_state.unwrap_or(&self.state);
888
889 let fcb = state
890 .fcbs
891 .get(&file_id)
892 .ok_or(Error::FileInfoNotAllocated(file_id))?;
893
894 let buf = {
896 let mut buf = vec![0; fcb.valid_bytes as usize];
897
898 if let Err(e) = self
899 .storage
900 .read_block(block_count_to_byte_count(fcb.block_offset), &mut buf)
901 .await
902 {
903 self.logger
904 .log_event_fatal(VmgsLogEvent::AccessFailed)
905 .await;
906
907 return Err(Error::ReadDisk(e));
908 }
909
910 buf
911 };
912
913 if decrypt
915 && state.version >= VMGS_VERSION_3_0
916 && state.encrypted_and_unlocked()
917 && fcb.encrypted()
918 {
919 match fcb.decrypt(&buf) {
920 Err(e) => {
921 self.logger
922 .log_event_fatal(VmgsLogEvent::AccessFailed)
923 .await;
924
925 Err(e)
926 }
927 Ok(b) => Ok(b),
928 }
929 } else if fcb.encrypted() && decrypt {
930 Err(Error::NeedsUnlock)
931 } else {
932 Ok(buf)
933 }
934 }
935
936 pub async fn write_file(&mut self, file_id: FileId, buf: &[u8]) -> Result<(), Error> {
944 self.write_file_inner(file_id, buf, false, false).await
945 }
946
947 pub async fn write_file_allow_overwrite_encrypted(
950 &mut self,
951 file_id: FileId,
952 buf: &[u8],
953 ) -> Result<(), Error> {
954 self.write_file_inner(file_id, buf, false, true).await
955 }
956
957 #[cfg(feature = "encryption")]
960 pub async fn write_file_encrypted(&mut self, file_id: FileId, buf: &[u8]) -> Result<(), Error> {
961 self.write_file_inner(file_id, buf, true, true).await
962 }
963
964 pub async fn move_file(
966 &mut self,
967 src: FileId,
968 dst: FileId,
969 allow_overwrite: bool,
970 ) -> Result<(), Error> {
971 if [src, dst]
972 .iter()
973 .any(|id| matches!(*id, FileId::FILE_TABLE | FileId::EXTENDED_FILE_TABLE))
974 {
975 return Err(Error::FileId);
976 }
977
978 if !allow_overwrite && self.state.fcbs.contains_key(&dst) {
979 return Err(Error::OverwriteMove);
980 }
981
982 let mut temp_state = self.temp_state();
983
984 let fcb = temp_state
986 .fcbs
987 .remove(&src)
988 .ok_or(Error::FileInfoNotAllocated(src))?;
989 temp_state.fcbs.insert(dst, fcb);
990
991 self.write_files_internal(BTreeMap::new(), Some(&mut temp_state))
993 .await?;
994
995 self.write_header_and_apply(temp_state).await?;
997
998 Ok(())
999 }
1000
1001 pub async fn delete_file(&mut self, file_id: FileId) -> Result<(), Error> {
1003 if matches!(file_id, FileId::FILE_TABLE | FileId::EXTENDED_FILE_TABLE) {
1004 return Err(Error::FileId);
1005 }
1006
1007 let mut temp_state = self.temp_state();
1008
1009 temp_state
1011 .fcbs
1012 .remove(&file_id)
1013 .ok_or(Error::FileInfoNotAllocated(file_id))?;
1014
1015 self.write_files_internal(BTreeMap::new(), Some(&mut temp_state))
1017 .await?;
1018
1019 self.write_header_and_apply(temp_state).await?;
1021
1022 Ok(())
1023 }
1024
1025 #[cfg(feature = "encryption")]
1028 pub async fn unlock_with_encryption_key(
1029 &mut self,
1030 encryption_key: &[u8; VMGS_ENCRYPTION_KEY_SIZE],
1031 ) -> Result<(), Error> {
1032 if self.state.version < VMGS_VERSION_3_0 {
1033 return Err(Error::EncryptionNotSupported);
1034 }
1035 if !self.encrypted() {
1036 return Err(Error::NotEncrypted);
1037 }
1038
1039 let mut temp_state = self.temp_state();
1040
1041 let mut valid_index_and_key = None;
1044 let mut errs = [None, None];
1045
1046 for (i, key) in temp_state.encrypted_metadata_keys.iter().enumerate() {
1047 let result = decrypt_metadata_key(
1048 encryption_key,
1049 &key.nonce,
1050 &key.encryption_key,
1051 &key.authentication_tag,
1052 );
1053
1054 match result {
1055 Ok(metadata_key) => {
1056 valid_index_and_key = Some((i, metadata_key));
1057 break;
1058 }
1059 Err(err) => {
1060 errs[i] = Some(err);
1061 }
1062 }
1063 }
1064
1065 match valid_index_and_key {
1066 Some((i, metadata_key)) => {
1067 let fcb = temp_state
1068 .fcbs
1069 .get_mut(&FileId::EXTENDED_FILE_TABLE)
1070 .ok_or(Error::FileInfoNotAllocated(FileId::EXTENDED_FILE_TABLE))?;
1071 fcb.attributes.set_encrypted(true);
1074 fcb.attributes.set_authenticated(true);
1075 fcb.encryption_key.copy_from_slice(&metadata_key);
1076 temp_state.datastore_keys[i].copy_from_slice(encryption_key);
1077 temp_state.active_datastore_key_index = Some(i);
1078 temp_state
1079 .unused_metadata_key
1080 .copy_from_slice(&metadata_key);
1081 }
1082 None => {
1083 tracing::error!(
1084 CVM_ALLOWED,
1085 error = &errs[0].take().unwrap() as &dyn std::error::Error,
1086 "first index failed to decrypt",
1087 );
1088 tracing::error!(
1089 CVM_ALLOWED,
1090 error = &errs[1].take().unwrap() as &dyn std::error::Error,
1091 "second index failed to decrypt",
1092 );
1093 return Err(Error::DecryptMetadataKey);
1094 }
1095 }
1096
1097 let extended_file_table_buffer = self
1099 .read_file_internal(FileId::EXTENDED_FILE_TABLE, true, Some(&temp_state))
1100 .await?;
1101
1102 let extended_file_table =
1104 VmgsExtendedFileTable::ref_from_bytes(extended_file_table_buffer.as_bytes())
1105 .map_err(|_| Error::InvalidFormat("incorrect extended file table size".into()))?;
1106
1107 for (file_id, fcb) in temp_state.fcbs.iter_mut() {
1108 if *file_id != FileId::EXTENDED_FILE_TABLE {
1109 fcb.update_extended_data(&extended_file_table.entries[*file_id]);
1110 }
1111 }
1112
1113 self.apply(temp_state);
1114
1115 Ok(())
1116 }
1117
1118 #[cfg(feature = "encryption")]
1122 pub async fn update_encryption_key(
1123 &mut self,
1124 encryption_key: &[u8],
1125 encryption_algorithm: EncryptionAlgorithm,
1126 ) -> Result<(), Error> {
1127 let old_index = self.state.active_datastore_key_index;
1128
1129 match self
1130 .add_new_encryption_key(encryption_key, encryption_algorithm)
1131 .await
1132 {
1133 Ok(_) => {}
1134 Err(Error::DatastoreKeysFull) => {
1135 if let Some(old_index) = old_index {
1136 let inactive_index = if old_index == 0 { 1 } else { 0 };
1137 tracing::warn!(CVM_ALLOWED, inactive_index, "removing inactive key");
1138 self.remove_encryption_key(inactive_index).await?;
1139 tracing::trace!(CVM_ALLOWED, "attempting to add the key again");
1140 self.add_new_encryption_key(encryption_key, encryption_algorithm)
1141 .await?;
1142 } else {
1143 return Err(Error::NoActiveDatastoreKey);
1144 }
1145 }
1146 Err(e) => return Err(e),
1147 };
1148
1149 if let Some(old_index) = old_index {
1150 self.remove_encryption_key(old_index).await?;
1151 }
1152
1153 Ok(())
1154 }
1155
1156 #[cfg(feature = "encryption")]
1158 async fn add_new_encryption_key(
1159 &mut self,
1160 encryption_key: &[u8],
1161 encryption_algorithm: EncryptionAlgorithm,
1162 ) -> Result<(), Error> {
1163 if self.state.version < VMGS_VERSION_3_0 {
1164 return Err(Error::EncryptionNotSupported);
1165 }
1166 if self.encrypted() && !self.state.unlocked() {
1167 return Err(Error::NeedsUnlock);
1168 }
1169 if self.state.datastore_key_count == self.state.datastore_keys.len() as u8 {
1170 return Err(Error::DatastoreKeysFull);
1171 }
1172 if is_empty_key(encryption_key) {
1173 return Err(Error::InvalidArgument("empty encryption key"));
1174 }
1175 if encryption_algorithm == EncryptionAlgorithm::NONE {
1176 return Err(Error::InvalidArgument(
1177 "encryption algorithm cannot be none",
1178 ));
1179 }
1180 if self.encrypted() && encryption_algorithm != self.state.encryption_algorithm {
1181 return Err(Error::InvalidArgument(
1182 "Encryption algorithm provided to add_new_encryption_key does not match VMGS's encryption algorithm.",
1183 ));
1184 }
1185
1186 let new_key_index = self
1187 .state
1188 .active_datastore_key_index
1189 .map_or(0, |i| if i == 0 { 1 } else { 0 });
1190
1191 let mut temp_state = self.temp_state();
1192 temp_state.encryption_algorithm = encryption_algorithm;
1193 temp_state.datastore_keys[new_key_index].copy_from_slice(encryption_key);
1194 temp_state.active_datastore_key_index = Some(new_key_index);
1195 temp_state.datastore_key_count += 1;
1196 temp_state.encrypted_metadata_keys[new_key_index] = VmgsEncryptionKey::new_zeroed();
1198
1199 if self.state.datastore_key_count == 0 {
1201 self.write_files_internal(BTreeMap::new(), Some(&mut temp_state))
1202 .await?;
1203 } else {
1204 temp_state.encrypt_metadata_key()?;
1207 }
1208
1209 self.write_header_and_apply(temp_state).await?;
1211
1212 Ok(())
1213 }
1214
1215 #[cfg(feature = "encryption")]
1217 async fn remove_encryption_key(&mut self, key_index: usize) -> Result<(), Error> {
1218 if self.state.version < VMGS_VERSION_3_0 {
1219 return Err(Error::EncryptionNotSupported);
1220 }
1221 if self.encrypted() && !self.state.unlocked() {
1222 return Err(Error::NeedsUnlock);
1223 }
1224 if self.state.datastore_key_count != self.state.datastore_keys.len() as u8
1225 && self.state.active_datastore_key_index != Some(key_index)
1226 {
1227 return Err(Error::InvalidArgument("key index"));
1228 }
1229
1230 let mut temp_state = self.temp_state();
1231
1232 temp_state.datastore_keys[key_index].fill(0);
1234
1235 temp_state.encrypted_metadata_keys[key_index] = VmgsEncryptionKey::new_zeroed();
1237
1238 if temp_state.datastore_key_count == 1 {
1240 temp_state.encryption_algorithm = EncryptionAlgorithm::NONE;
1241 temp_state.datastore_key_count = 0;
1242 temp_state.active_datastore_key_index = None;
1243 } else {
1244 temp_state.datastore_key_count = 1;
1245
1246 let new_active_datastore_key_index = if key_index == 0 { 1 } else { 0 };
1247 if is_empty_key(&temp_state.datastore_keys[new_active_datastore_key_index]) {
1248 temp_state.active_datastore_key_index = None;
1249 } else {
1250 temp_state.active_datastore_key_index = Some(new_active_datastore_key_index);
1251 }
1252 }
1253
1254 self.write_header_and_apply(temp_state).await?;
1255
1256 Ok(())
1257 }
1258
1259 pub fn get_encryption_algorithm(&self) -> EncryptionAlgorithm {
1261 self.state.encryption_algorithm
1262 }
1263
1264 pub fn encrypted(&self) -> bool {
1266 self.state.encrypted()
1267 }
1268
1269 pub fn was_provisioned_this_boot(&self) -> bool {
1271 self.state.provisioning_reason.is_some()
1272 }
1273
1274 pub fn provisioning_reason(&self) -> Option<VmgsProvisioningReason> {
1276 self.state.provisioning_reason
1277 }
1278
1279 pub async fn write_provisioning_marker(
1281 &mut self,
1282 marker: &VmgsProvisioningMarker,
1283 ) -> Result<(), Error> {
1284 self.write_file(
1285 FileId::PROVISIONING_MARKER,
1286 serde_json::to_string(marker)?.as_bytes(),
1287 )
1288 .await
1289 }
1290
1291 async fn set_reprovisioned(&mut self, value: bool) -> Result<(), Error> {
1292 if self.state.reprovisioned != value {
1293 tracing::info!(reprovisioned = value, "update vmgs marker");
1294 let mut temp_state = self.temp_state();
1295 temp_state.reprovisioned = value;
1296 self.write_header_and_apply(temp_state).await?;
1297 }
1298 Ok(())
1299 }
1300
1301 fn temp_state(&self) -> VmgsState {
1303 self.state.clone()
1304 }
1305
1306 fn apply(&mut self, temp_state: VmgsState) {
1308 self.state = temp_state;
1309 }
1310
1311 async fn write_header_and_apply(&mut self, mut temp_state: VmgsState) -> Result<(), Error> {
1313 self.storage.flush().await.map_err(Error::FlushDisk)?;
1315
1316 let (new_header, index) = temp_state.make_header();
1317 self.write_header_internal(&new_header, index).await?;
1318 self.apply(temp_state);
1319 Ok(())
1320 }
1321}
1322
1323impl VmgsState {
1324 fn new(version: u32, provisioning_reason: Option<VmgsProvisioningReason>) -> Self {
1325 Self {
1326 active_header_index: 1,
1327 active_header_sequence_number: 0,
1328 version,
1329 fcbs: HashMap::new(),
1330 encryption_algorithm: EncryptionAlgorithm::NONE,
1331 datastore_key_count: 0,
1332 active_datastore_key_index: None,
1333 datastore_keys: [VmgsDatastoreKey::new_zeroed(); 2],
1334 unused_metadata_key: VmgsDatastoreKey::new_zeroed(),
1335 encrypted_metadata_keys: std::array::from_fn(|_| VmgsEncryptionKey::new_zeroed()),
1336 reprovisioned: false,
1337 provisioning_reason,
1338 }
1339 }
1340
1341 fn from_header(header: VmgsHeader, header_index: usize) -> Self {
1342 let mut state = Self::new(header.version, None);
1343
1344 state.active_header_index = header_index;
1345 state.active_header_sequence_number = header.sequence;
1346
1347 if header.version >= VMGS_VERSION_3_0 {
1348 state.encryption_algorithm = header.encryption_algorithm;
1349 state.encrypted_metadata_keys = header.metadata_keys;
1350 for key in &state.encrypted_metadata_keys {
1351 if !is_empty_key(&key.encryption_key) {
1352 state.datastore_key_count += 1;
1353 }
1354 }
1355 state.reprovisioned = header.markers.reprovisioned();
1356 }
1357
1358 state.fcbs.insert(
1359 FileId::FILE_TABLE,
1360 ResolvedFileControlBlock::new(
1361 header.file_table_offset,
1362 header.file_table_size,
1363 size_of::<VmgsFileTable>(),
1364 false,
1365 ),
1366 );
1367
1368 state
1369 }
1370
1371 fn make_header(&mut self) -> (VmgsHeader, usize) {
1374 let file_table_fcb = self.fcbs.get(&FileId::FILE_TABLE).unwrap();
1375 let mut header = VmgsHeader {
1376 signature: VMGS_SIGNATURE,
1377 version: self.version,
1378 header_size: size_of::<VmgsHeader>() as u32,
1379 file_table_offset: file_table_fcb.block_offset,
1380 file_table_size: file_table_fcb.allocated_blocks.get(),
1381 encryption_algorithm: self.encryption_algorithm,
1382 markers: VmgsMarkers::new().with_reprovisioned(self.reprovisioned),
1383 ..VmgsHeader::new_zeroed()
1384 };
1385 header.metadata_keys = self.encrypted_metadata_keys.clone();
1386
1387 self.active_header_sequence_number = self.active_header_sequence_number.wrapping_add(1);
1388 self.active_header_index = if self.active_header_index == 0 { 1 } else { 0 };
1389
1390 header.sequence = self.active_header_sequence_number;
1391 header.checksum = 0;
1392 header.checksum = compute_crc32(header.as_bytes());
1393
1394 (header, self.active_header_index)
1395 }
1396
1397 fn encrypted(&self) -> bool {
1399 self.encryption_algorithm != EncryptionAlgorithm::NONE
1400 }
1401
1402 fn unlocked(&self) -> bool {
1404 self.active_datastore_key_index.is_some()
1405 }
1406
1407 fn encrypted_and_unlocked(&self) -> bool {
1409 self.encrypted() && self.unlocked()
1410 }
1411
1412 fn encrypt_metadata_key(&mut self) -> Result<(), Error> {
1414 let current_index = self.active_datastore_key_index.ok_or(Error::NeedsUnlock)?;
1415 let metadata_key = &self
1416 .fcbs
1417 .get(&FileId::EXTENDED_FILE_TABLE)
1418 .ok_or(Error::FileInfoNotAllocated(FileId::EXTENDED_FILE_TABLE))?
1419 .encryption_key;
1420
1421 self.unused_metadata_key.copy_from_slice(metadata_key);
1422
1423 if is_empty_key(&self.encrypted_metadata_keys[current_index].nonce) {
1424 self.encrypted_metadata_keys[current_index]
1425 .nonce
1426 .copy_from_slice(&generate_nonce());
1427 } else {
1428 increment_nonce(&mut self.encrypted_metadata_keys[current_index].nonce)?;
1429 }
1430
1431 let mut metadata_key_auth_tag = VmgsAuthTag::new_zeroed();
1432 let encrypted_metadata_key = encrypt_metadata_key(
1433 &self.datastore_keys[current_index],
1434 &self.encrypted_metadata_keys[current_index].nonce,
1435 metadata_key,
1436 &mut metadata_key_auth_tag,
1437 )?;
1438
1439 self.encrypted_metadata_keys[current_index]
1440 .authentication_tag
1441 .copy_from_slice(&metadata_key_auth_tag);
1442 self.encrypted_metadata_keys[current_index]
1443 .encryption_key
1444 .copy_from_slice(&encrypted_metadata_key);
1445
1446 Ok(())
1447 }
1448
1449 fn make_file_table(&self) -> Result<VmgsFileTable, Error> {
1451 let mut new_file_table = VmgsFileTable::new_zeroed();
1452 for (file_id, fcb) in self.fcbs.iter() {
1453 fcb.fill_file_entry(self.version, &mut new_file_table.entries[*file_id]);
1454 }
1455 Ok(new_file_table)
1456 }
1457
1458 fn make_extended_file_table(&self) -> Result<VmgsExtendedFileTable, Error> {
1460 let mut new_extended_file_table = VmgsExtendedFileTable::new_zeroed();
1461 for (file_id, fcb) in self.fcbs.iter() {
1462 fcb.fill_extended_file_entry(&mut new_extended_file_table.entries[*file_id]);
1463 }
1464 Ok(new_extended_file_table)
1465 }
1466
1467 fn allocate_space<'a>(
1470 &self,
1471 files_to_allocate: BTreeMap<FileId, AllocRequest<'a>>,
1472 block_capacity: u32,
1473 ) -> Result<BTreeMap<FileId, AllocResult<'a>>, Error> {
1474 let mut allocation_list = self
1476 .fcbs
1477 .values()
1478 .map(|fcb| AllocationBlock {
1479 block_offset: fcb.block_offset,
1480 allocated_blocks: fcb.allocated_blocks.get(),
1481 })
1482 .collect();
1483
1484 files_to_allocate
1486 .into_iter()
1487 .map(|(file_id, req)| {
1488 Ok((file_id, req.allocate(&mut allocation_list, block_capacity)?))
1489 })
1490 .collect()
1491 }
1492}
1493
1494#[cfg(feature = "test_helpers")]
1497mod test_helpers {
1498 use super::*;
1499
1500 impl Vmgs {
1501 pub fn test_get_active_datastore_key_index(&self) -> Option<usize> {
1503 self.state.active_datastore_key_index
1504 }
1505
1506 #[cfg(feature = "encryption")]
1508 pub async fn test_add_new_encryption_key(
1509 &mut self,
1510 encryption_key: &[u8],
1511 encryption_algorithm: EncryptionAlgorithm,
1512 ) -> Result<(), Error> {
1513 self.add_new_encryption_key(encryption_key, encryption_algorithm)
1514 .await
1515 }
1516 }
1517}
1518
1519pub async fn read_headers(
1521 disk: Disk,
1522) -> Result<(VmgsHeader, VmgsHeader), (Error, Option<(VmgsHeader, VmgsHeader)>)> {
1523 let mut storage = VmgsStorage::new(disk);
1524 match (storage.validate(), read_headers_inner(&mut storage).await) {
1525 (Ok(_), res) => res,
1526 (Err(e), res) => Err((Error::Initialization(e), res.ok())),
1527 }
1528}
1529
1530async fn read_headers_inner(
1531 storage: &mut VmgsStorage,
1532) -> Result<(VmgsHeader, VmgsHeader), (Error, Option<(VmgsHeader, VmgsHeader)>)> {
1533 let mut first_two_blocks = [0; (VMGS_BYTES_PER_BLOCK * 2) as usize];
1535
1536 storage
1537 .read_block(0, &mut first_two_blocks)
1538 .await
1539 .map_err(|e| (Error::ReadDisk(e), None))?;
1540
1541 let header_1 = VmgsHeader::read_from_prefix(&first_two_blocks).unwrap().0; let header_2 =
1543 VmgsHeader::read_from_prefix(&first_two_blocks[storage.aligned_header_size() as usize..])
1544 .unwrap()
1545 .0; let headers = (header_1, header_2);
1547
1548 if vmgs_is_v1(&first_two_blocks) {
1549 Err((Error::V1Format, Some(headers)))
1550 } else if vmgs_headers_empty(&headers.0, &headers.1) {
1551 Err((Error::EmptyFile, Some(headers)))
1552 } else {
1553 Ok(headers)
1554 }
1555}
1556
1557fn vmgs_is_v1(first_two_blocks: &[u8; 2 * VMGS_BYTES_PER_BLOCK as usize]) -> bool {
1558 const EFI_SIGNATURE: &[u8] = b"EFI PART";
1559 const EFI_SIGNATURE_OFFSET: usize = 512;
1560
1561 EFI_SIGNATURE
1562 == &first_two_blocks[EFI_SIGNATURE_OFFSET..EFI_SIGNATURE_OFFSET + EFI_SIGNATURE.len()]
1563}
1564
1565fn vmgs_headers_empty(header_1: &VmgsHeader, header_2: &VmgsHeader) -> bool {
1566 let empty_header = VmgsHeader::new_zeroed();
1567
1568 header_1.as_bytes() == empty_header.as_bytes() && header_2.as_bytes() == empty_header.as_bytes()
1569}
1570
1571pub fn get_active_header(
1574 header_1: Result<&VmgsHeader, Error>,
1575 header_2: Result<&VmgsHeader, Error>,
1576) -> Result<usize, Error> {
1577 let active_header_index =
1578 if let (Ok(header_1), Ok(header_2)) = (header_1.as_deref(), header_2.as_deref()) {
1579 if header_1.sequence == header_2.sequence.wrapping_add(1) {
1584 0
1585 } else if header_2.sequence == header_1.sequence.wrapping_add(1) {
1586 1
1587 } else {
1588 return Err(Error::CorruptFormat(format!(
1589 "Invalid header sequence numbers. Header 1: {}, Header 2: {}",
1590 header_1.sequence, header_2.sequence
1591 )));
1592 }
1593 } else if header_1.is_ok() {
1594 0
1595 } else if header_2.is_ok() {
1596 1
1597 } else {
1598 return Err(Error::InvalidFormat(format!(
1599 "No valid header: Header 1: {} Header 2: {}",
1600 header_1.err().unwrap(),
1601 header_2.err().unwrap()
1602 )));
1603 };
1604
1605 Ok(active_header_index)
1606}
1607
1608pub fn validate_header(header: &VmgsHeader) -> Result<&VmgsHeader, Error> {
1610 if header.signature != VMGS_SIGNATURE {
1611 return Err(Error::InvalidFormat(String::from(
1612 "Invalid header signature",
1613 )));
1614 }
1615 if header.version != VMGS_VERSION_3_0 {
1616 return Err(Error::InvalidFormat(String::from("Invalid header version")));
1617 }
1618 if header.header_size != size_of::<VmgsHeader>() as u32 {
1619 return Err(Error::InvalidFormat(String::from("Invalid header size")));
1620 }
1621 if header.file_table_offset < VMGS_MIN_FILE_BLOCK_OFFSET {
1622 return Err(Error::InvalidFormat(String::from(
1623 "Invalid file table offset",
1624 )));
1625 }
1626 if header.file_table_size != VMGS_FILE_TABLE_BLOCK_SIZE {
1627 return Err(Error::InvalidFormat(String::from(
1628 "Invalid file table size",
1629 )));
1630 }
1631 if header.encryption_algorithm > EncryptionAlgorithm::AES_GCM {
1632 return Err(Error::InvalidFormat(String::from(
1633 "Invalid encryption algorithm",
1634 )));
1635 }
1636
1637 let stored_checksum = header.checksum;
1638 let mut zero_checksum_header = header.clone();
1639 zero_checksum_header.checksum = 0;
1640 let computed_checksum = compute_crc32(zero_checksum_header.as_bytes());
1641 if stored_checksum != computed_checksum {
1642 return Err(Error::CorruptFormat(String::from(
1643 "Invalid header checksum",
1644 )));
1645 }
1646 Ok(header)
1647}
1648
1649fn initialize_file_metadata(
1651 file_table: &VmgsFileTable,
1652 version: u32,
1653 block_capacity: u32,
1654) -> Result<HashMap<FileId, ResolvedFileControlBlock>, Error> {
1655 let mut fcbs = HashMap::new();
1656
1657 for (file_id, file_entry) in file_table.entries.iter().enumerate() {
1658 let file_id = FileId(file_id as u32);
1659
1660 if file_entry.allocation_size == 0 {
1662 continue;
1663 };
1664
1665 if file_entry.offset < VMGS_MIN_FILE_BLOCK_OFFSET || file_entry.offset >= block_capacity {
1667 return Err(Error::CorruptFormat(format!(
1668 "Invalid file offset {} for file_id {:?} \n{:?}",
1669 file_entry.offset, file_id, file_entry
1670 )));
1671 }
1672
1673 let file_allocation_end_block = file_entry.offset + file_entry.allocation_size;
1675 if file_allocation_end_block > block_capacity {
1676 return Err(Error::CorruptFormat(String::from(
1677 "Invalid file allocation end block",
1678 )));
1679 }
1680
1681 let file_allocation_size_bytes = block_count_to_byte_count(file_entry.allocation_size);
1683 if file_entry.valid_data_size > file_allocation_size_bytes {
1684 return Err(Error::CorruptFormat(String::from("Invalid data size")));
1685 }
1686
1687 let fcb = ResolvedFileControlBlock::from_file_entry(version, file_entry);
1688
1689 fcbs.insert(file_id, fcb);
1691 }
1692
1693 Ok(fcbs)
1694}
1695
1696fn block_count_to_byte_count(block_count: u32) -> u64 {
1698 block_count as u64 * VMGS_BYTES_PER_BLOCK as u64
1699}
1700
1701fn round_up_count(count: usize, pow2: u32) -> u64 {
1702 (count as u64 + pow2 as u64 - 1) & !(pow2 as u64 - 1)
1703}
1704
1705fn generate_nonce() -> VmgsNonce {
1707 let mut nonce = VmgsNonce::new_zeroed();
1708 getrandom::fill(&mut nonce[..vmgs_format::VMGS_NONCE_RANDOM_SEED_SIZE]).expect("rng failure");
1710 nonce
1711}
1712
1713fn increment_nonce(nonce: &mut VmgsNonce) -> Result<(), Error> {
1715 getrandom::fill(&mut nonce[..vmgs_format::VMGS_NONCE_RANDOM_SEED_SIZE]).expect("rng failure");
1717
1718 for i in &mut nonce[vmgs_format::VMGS_NONCE_RANDOM_SEED_SIZE..] {
1720 *i = i.wrapping_add(1);
1721
1722 if *i != 0 {
1723 break;
1724 }
1725 }
1726
1727 Ok(())
1728}
1729
1730fn is_empty_key(encryption_key: &[u8]) -> bool {
1732 encryption_key.iter().all(|&x| x == 0)
1733}
1734
1735#[cfg_attr(not(feature = "encryption"), expect(unused_variables))]
1737fn encrypt_metadata_key(
1738 encryption_key: &[u8; VMGS_ENCRYPTION_KEY_SIZE],
1739 nonce: &[u8; VMGS_NONCE_SIZE],
1740 metadata_key: &[u8],
1741 authentication_tag: &mut [u8],
1742) -> Result<Vec<u8>, Error> {
1743 #[cfg(not(feature = "encryption"))]
1744 unreachable!("Encryption requires the encryption feature");
1745 #[cfg(feature = "encryption")]
1746 {
1747 let encrypted_metadata_key =
1748 crate::encrypt::vmgs_encrypt(encryption_key, nonce, metadata_key, authentication_tag)?;
1749
1750 if encrypted_metadata_key.len() != metadata_key.len() {
1751 return Err(Error::UnexpectedLength(
1752 "encrypted metadata key",
1753 encrypted_metadata_key.len(),
1754 metadata_key.len(),
1755 ));
1756 }
1757 Ok(encrypted_metadata_key)
1758 }
1759}
1760
1761#[cfg_attr(
1763 not(feature = "encryption"),
1764 expect(unused_variables),
1765 expect(dead_code)
1766)]
1767fn decrypt_metadata_key(
1768 datastore_key: &[u8; VMGS_ENCRYPTION_KEY_SIZE],
1769 nonce: &[u8; VMGS_NONCE_SIZE],
1770 metadata_key: &[u8],
1771 authentication_tag: &[u8],
1772) -> Result<Vec<u8>, Error> {
1773 #[cfg(not(feature = "encryption"))]
1774 unreachable!("Encryption requires the encryption feature");
1775 #[cfg(feature = "encryption")]
1776 {
1777 let decrypted_metadata_key =
1778 crate::encrypt::vmgs_decrypt(datastore_key, nonce, metadata_key, authentication_tag)?;
1779 if decrypted_metadata_key.len() != metadata_key.len() {
1780 return Err(Error::UnexpectedLength(
1781 "decrypted metadata key",
1782 metadata_key.len(),
1783 decrypted_metadata_key.len(),
1784 ));
1785 }
1786
1787 Ok(decrypted_metadata_key)
1788 }
1789}
1790
1791fn compute_crc32(buf: &[u8]) -> u32 {
1793 let mut hasher = crc32fast::Hasher::new();
1794 hasher.update(buf);
1795 hasher.finalize()
1796}
1797
1798struct AllocationBlock {
1799 block_offset: u32,
1800 allocated_blocks: u32,
1801}
1802
1803fn allocate_helper(
1806 allocation_list: &mut Vec<AllocationBlock>,
1807 block_count: u32,
1808 block_capacity: u32,
1809) -> Result<u32, Error> {
1810 allocation_list.sort_by_key(|a| a.block_offset);
1812
1813 let mut best_offset = 0;
1814 let mut best_free_count = 0;
1815 let mut last_allocation_end_offset = VMGS_MIN_FILE_BLOCK_OFFSET;
1816 let mut found = false;
1817
1818 for fcb in allocation_list.iter() {
1820 if fcb.block_offset < last_allocation_end_offset {
1821 return Err(Error::AllocateOffset);
1822 }
1823 let free_count = fcb.block_offset - last_allocation_end_offset;
1824 if free_count >= block_count && (best_free_count == 0 || free_count < best_free_count) {
1825 best_free_count = free_count;
1826 best_offset = last_allocation_end_offset;
1827 found = true;
1828 }
1829 last_allocation_end_offset = fcb.block_offset + fcb.allocated_blocks;
1830 }
1831 if last_allocation_end_offset < block_capacity {
1832 let free_count = block_capacity - last_allocation_end_offset;
1833 if free_count >= block_count && (best_free_count == 0 || free_count < best_free_count) {
1834 best_offset = last_allocation_end_offset;
1835 found = true;
1836 }
1837 }
1838 if !found {
1839 return Err(Error::InsufficientResources);
1840 }
1841
1842 allocation_list.push(AllocationBlock {
1843 block_offset: best_offset,
1844 allocated_blocks: block_count,
1845 });
1846 Ok(best_offset)
1847}
1848
1849#[cfg(feature = "save_restore")]
1850#[expect(missing_docs)]
1851pub mod save_restore {
1852 use super::*;
1853
1854 pub mod state {
1855 use mesh_protobuf::Protobuf;
1856 use std::num::NonZeroU32;
1857
1858 pub type SavedVmgsNonce = [u8; 12];
1859 pub type SavedVmgsAuthTag = [u8; 16];
1860 pub type SavedVmgsDatastoreKey = [u8; 32];
1861
1862 #[derive(Protobuf)]
1863 #[mesh(package = "vmgs")]
1864 pub struct SavedResolvedFileControlBlock {
1865 #[mesh(1)]
1866 pub block_offset: u32,
1867 #[mesh(2)]
1868 pub allocated_blocks: NonZeroU32,
1869 #[mesh(3)]
1870 pub valid_bytes: u64,
1871 #[mesh(4)]
1872 pub nonce: SavedVmgsNonce,
1873 #[mesh(5)]
1874 pub authentication_tag: SavedVmgsAuthTag,
1875 #[mesh(6)]
1876 pub attributes: u32,
1877 #[mesh(7)]
1878 pub encryption_key: SavedVmgsDatastoreKey,
1879 }
1880
1881 #[derive(Protobuf)]
1882 #[mesh(package = "vmgs")]
1883 pub struct SavedVmgsEncryptionKey {
1884 #[mesh(1)]
1885 pub nonce: SavedVmgsNonce,
1886 #[mesh(2)]
1887 pub authentication_tag: SavedVmgsAuthTag,
1888 #[mesh(3)]
1889 pub encryption_key: SavedVmgsDatastoreKey,
1890 }
1891
1892 #[derive(Protobuf)]
1893 #[mesh(package = "vmgs")]
1894 pub struct SavedVmgsState {
1895 #[mesh(1)]
1896 pub active_header_index: usize,
1897 #[mesh(2)]
1898 pub active_header_sequence_number: u32,
1899 #[mesh(3)]
1900 pub version: u32,
1901 #[mesh(4)]
1902 pub fcbs: Vec<(u32, SavedResolvedFileControlBlock)>,
1903 #[mesh(5)]
1904 pub encryption_algorithm: u16,
1905 #[mesh(6)]
1906 pub datastore_key_count: u8,
1907 #[mesh(7)]
1908 pub active_datastore_key_index: Option<usize>,
1909 #[mesh(8)]
1910 pub datastore_keys: [SavedVmgsDatastoreKey; 2],
1911 #[mesh(9)]
1912 pub metadata_key: SavedVmgsDatastoreKey,
1913 #[mesh(10)]
1914 pub encrypted_metadata_keys: [SavedVmgsEncryptionKey; 2],
1915 #[mesh(11)]
1916 pub reprovisioned: bool,
1917 }
1918 }
1919
1920 impl Vmgs {
1921 pub fn open_from_saved(
1939 disk: Disk,
1940 state: state::SavedVmgsState,
1941 logger: Option<Arc<dyn VmgsLogger>>,
1942 ) -> Self {
1943 let state::SavedVmgsState {
1944 active_header_index,
1945 active_header_sequence_number,
1946 version,
1947 fcbs,
1948 encryption_algorithm,
1949 datastore_key_count,
1950 active_datastore_key_index,
1951 datastore_keys,
1952 metadata_key,
1953 encrypted_metadata_keys,
1954 reprovisioned,
1955 } = state;
1956
1957 Self {
1958 storage: VmgsStorage::new(disk),
1959 #[cfg(feature = "inspect")]
1960 stats: Default::default(),
1961
1962 state: VmgsState {
1963 active_header_index,
1964 active_header_sequence_number,
1965 version,
1966 fcbs: fcbs
1967 .into_iter()
1968 .map(|(file_id, fcb)| {
1969 let state::SavedResolvedFileControlBlock {
1970 block_offset,
1971 allocated_blocks,
1972 valid_bytes,
1973 nonce,
1974 authentication_tag,
1975 attributes,
1976 encryption_key,
1977 } = fcb;
1978
1979 (
1980 FileId(file_id),
1981 ResolvedFileControlBlock {
1982 block_offset,
1983 allocated_blocks,
1984 valid_bytes,
1985 nonce,
1986 authentication_tag,
1987 attributes: FileAttribute::from(attributes),
1988 encryption_key,
1989 },
1990 )
1991 })
1992 .collect(),
1993 encryption_algorithm: EncryptionAlgorithm(encryption_algorithm),
1994 datastore_key_count,
1995 active_datastore_key_index,
1996 datastore_keys,
1997 unused_metadata_key: metadata_key,
1998 encrypted_metadata_keys: encrypted_metadata_keys.map(|k| {
1999 let state::SavedVmgsEncryptionKey {
2000 nonce,
2001 authentication_tag,
2002 encryption_key,
2003 } = k;
2004
2005 VmgsEncryptionKey {
2006 nonce,
2007 reserved: 0,
2008 authentication_tag,
2009 encryption_key,
2010 }
2011 }),
2012 reprovisioned,
2013 provisioning_reason: None,
2014 },
2015
2016 logger,
2017 }
2018 }
2019
2020 pub fn save(&self) -> state::SavedVmgsState {
2026 let Self {
2027 storage: _,
2028
2029 #[cfg(feature = "inspect")]
2030 stats: _,
2031
2032 state:
2033 VmgsState {
2034 active_header_index,
2035 active_header_sequence_number,
2036 version,
2037 fcbs,
2038 encryption_algorithm,
2039 datastore_key_count,
2040 active_datastore_key_index,
2041 datastore_keys,
2042 unused_metadata_key: metadata_key,
2043 encrypted_metadata_keys,
2044 reprovisioned,
2045 provisioning_reason: _,
2046 },
2047
2048 logger: _,
2049 } = self;
2050
2051 state::SavedVmgsState {
2052 active_header_index: *active_header_index,
2053 active_header_sequence_number: *active_header_sequence_number,
2054 version: *version,
2055 fcbs: fcbs
2056 .iter()
2057 .map(|(file_id, fcb)| {
2058 let ResolvedFileControlBlock {
2059 block_offset,
2060 allocated_blocks,
2061 valid_bytes,
2062 nonce,
2063 authentication_tag,
2064 attributes,
2065 encryption_key,
2066 } = fcb;
2067
2068 (
2069 file_id.0,
2070 state::SavedResolvedFileControlBlock {
2071 block_offset: *block_offset,
2072 allocated_blocks: *allocated_blocks,
2073 valid_bytes: *valid_bytes,
2074 nonce: *nonce,
2075 authentication_tag: *authentication_tag,
2076 attributes: (*attributes).into(),
2077 encryption_key: *encryption_key,
2078 },
2079 )
2080 })
2081 .collect(),
2082 encryption_algorithm: encryption_algorithm.0,
2083 datastore_key_count: *datastore_key_count,
2084 active_datastore_key_index: *active_datastore_key_index,
2085 datastore_keys: *datastore_keys,
2086 metadata_key: *metadata_key,
2087 encrypted_metadata_keys: std::array::from_fn(|i| {
2088 let VmgsEncryptionKey {
2089 nonce,
2090 reserved: _,
2091 authentication_tag,
2092 encryption_key,
2093 } = encrypted_metadata_keys[i];
2094
2095 state::SavedVmgsEncryptionKey {
2096 nonce,
2097 authentication_tag,
2098 encryption_key,
2099 }
2100 }),
2101 reprovisioned: *reprovisioned,
2102 }
2103 }
2104 }
2105}
2106
2107#[cfg(test)]
2108mod tests {
2109 use super::*;
2110 use pal_async::async_test;
2111 use parking_lot::Mutex;
2112 use std::sync::Arc;
2113 #[cfg(feature = "encryption")]
2114 use vmgs_format::VMGS_ENCRYPTION_KEY_SIZE;
2115 use vmgs_format::VmgsProvisioner;
2116
2117 const ONE_MEGA_BYTE: u64 = 1024 * 1024;
2118
2119 struct TestVmgsLogger {
2120 data: Arc<Mutex<String>>,
2121 }
2122
2123 #[async_trait::async_trait]
2124 impl VmgsLogger for TestVmgsLogger {
2125 async fn log_event_fatal(&self, _event: VmgsLogEvent) {
2126 let mut data = self.data.lock();
2127 *data = "test logger".to_string();
2128 }
2129 }
2130
2131 fn new_test_file() -> Disk {
2132 disklayer_ram::ram_disk(4 * ONE_MEGA_BYTE, false).unwrap()
2133 }
2134
2135 #[async_test]
2136 async fn empty_vmgs() {
2137 let disk = new_test_file();
2138
2139 let result = Vmgs::open(disk, None).await;
2140 assert!(matches!(result, Err(Error::EmptyFile)));
2141 }
2142
2143 #[async_test]
2144 async fn format_empty_vmgs() {
2145 let disk = new_test_file();
2146 let result = Vmgs::format_new(disk, None).await;
2147 assert!(result.is_ok());
2148 }
2149
2150 #[async_test]
2151 async fn basic_read_write() {
2152 let disk = new_test_file();
2153 let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2154 assert_eq!(vmgs.state.active_header_index, 0);
2155 assert_eq!(vmgs.state.active_header_sequence_number, 1);
2156 assert_eq!(vmgs.state.version, VMGS_VERSION_3_0);
2157
2158 let buf = b"hello world";
2160 vmgs.write_file(FileId::BIOS_NVRAM, buf).await.unwrap();
2161
2162 assert_eq!(vmgs.state.active_header_index, 1);
2163 assert_eq!(vmgs.state.active_header_sequence_number, 2);
2164
2165 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2167
2168 assert_eq!(buf, &*read_buf);
2169 assert_eq!(vmgs.state.active_header_index, 1);
2170 assert_eq!(vmgs.state.active_header_sequence_number, 2);
2171 }
2172
2173 #[async_test]
2174 async fn basic_read_write_large() {
2175 let disk = new_test_file();
2176 let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2177
2178 let buf: Vec<u8> = (0..).map(|x| x as u8).take(1024 * 4 + 1).collect();
2180
2181 vmgs.write_file(FileId::BIOS_NVRAM, &buf).await.unwrap();
2182
2183 assert_eq!(vmgs.state.active_header_index, 1);
2184 assert_eq!(vmgs.state.active_header_sequence_number, 2);
2185
2186 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2188
2189 assert_eq!(buf, read_buf);
2190 assert_eq!(vmgs.state.active_header_index, 1);
2191 assert_eq!(vmgs.state.active_header_sequence_number, 2);
2192
2193 let buf: Vec<u8> = (0..).map(|x| x as u8).take(1024 * 4 * 4 + 1).collect();
2195
2196 vmgs.write_file(FileId::TPM_PPI, &buf).await.unwrap();
2197
2198 assert_eq!(vmgs.state.active_header_index, 0);
2199 assert_eq!(vmgs.state.active_header_sequence_number, 3);
2200
2201 let read_buf = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2203
2204 assert_eq!(buf, read_buf);
2205 assert_eq!(vmgs.state.active_header_index, 0);
2206 assert_eq!(vmgs.state.active_header_sequence_number, 3);
2207
2208 let buf: Vec<u8> = (0..).map(|x| x as u8).take(1024 * 4 * 4 * 4 + 1).collect();
2210
2211 vmgs.write_file(FileId::GUEST_FIRMWARE, &buf).await.unwrap();
2212
2213 assert_eq!(vmgs.state.active_header_index, 1);
2214 assert_eq!(vmgs.state.active_header_sequence_number, 4);
2215
2216 let read_buf = vmgs.read_file(FileId::GUEST_FIRMWARE).await.unwrap();
2218
2219 assert_eq!(buf, read_buf);
2220 assert_eq!(vmgs.state.active_header_index, 1);
2221 assert_eq!(vmgs.state.active_header_sequence_number, 4);
2222 }
2223
2224 #[async_test]
2225 async fn move_delete() {
2226 let disk = new_test_file();
2227 let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2228
2229 let buf = b"hello world";
2231 vmgs.write_file(FileId::TPM_NVRAM, buf).await.unwrap();
2232
2233 let read_buf = vmgs.read_file(FileId::TPM_NVRAM).await.unwrap();
2235 assert_eq!(buf, &*read_buf);
2236
2237 vmgs.move_file(FileId::TPM_NVRAM, FileId::ATTEST, false)
2239 .await
2240 .unwrap();
2241 vmgs.read_file(FileId::TPM_NVRAM).await.unwrap_err();
2242 let read_buf = vmgs.read_file(FileId::ATTEST).await.unwrap();
2243 assert_eq!(buf, &*read_buf);
2244
2245 vmgs.delete_file(FileId::ATTEST).await.unwrap();
2247 vmgs.read_file(FileId::ATTEST).await.unwrap_err();
2248 }
2249
2250 #[async_test]
2251 async fn open_existing_file() {
2252 let buf_1 = b"hello world";
2253 let buf_2 = b"short sentence";
2254 let buf_3 = b"funny joke";
2255
2256 let disk = new_test_file();
2258 let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2259
2260 vmgs.write_file(FileId::BIOS_NVRAM, buf_1).await.unwrap();
2261
2262 assert_eq!(vmgs.state.active_header_index, 1);
2263 assert_eq!(vmgs.state.active_header_sequence_number, 2);
2264 assert_eq!(vmgs.state.fcbs[&FileId(0)].block_offset, 4);
2265 assert_eq!(vmgs.state.fcbs[&FileId(1)].block_offset, 5);
2266
2267 vmgs.write_file(FileId::TPM_PPI, buf_2).await.unwrap();
2268
2269 assert_eq!(vmgs.state.active_header_index, 0);
2270 assert_eq!(vmgs.state.active_header_sequence_number, 3);
2271 assert_eq!(vmgs.state.fcbs[&FileId(0)].block_offset, 2);
2272 assert_eq!(vmgs.state.fcbs[&FileId(1)].block_offset, 5);
2273 assert_eq!(vmgs.state.fcbs[&FileId(2)].block_offset, 6);
2274
2275 vmgs.write_file(FileId::BIOS_NVRAM, buf_3).await.unwrap();
2276
2277 assert_eq!(vmgs.state.active_header_index, 1);
2278 assert_eq!(vmgs.state.active_header_sequence_number, 4);
2279 assert_eq!(vmgs.state.fcbs[&FileId(0)].block_offset, 4);
2280 assert_eq!(vmgs.state.fcbs[&FileId(1)].block_offset, 7);
2281 assert_eq!(vmgs.state.fcbs[&FileId(2)].block_offset, 6);
2282
2283 drop(vmgs);
2285
2286 let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2287
2288 assert_eq!(vmgs.state.fcbs[&FileId(0)].block_offset, 4);
2289 assert_eq!(vmgs.state.fcbs[&FileId(1)].block_offset, 7);
2290 assert_eq!(vmgs.state.fcbs[&FileId(2)].block_offset, 6);
2291 let read_buf_1 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2292
2293 assert_eq!(buf_3, &*read_buf_1);
2294 assert_eq!(vmgs.state.active_header_index, 1);
2295 assert_eq!(vmgs.state.active_header_sequence_number, 4);
2296
2297 let read_buf_2 = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2298
2299 assert_eq!(buf_2, &*read_buf_2);
2300 assert_eq!(vmgs.state.fcbs[&FileId(0)].block_offset, 4);
2301 assert_eq!(vmgs.state.fcbs[&FileId(1)].block_offset, 7);
2302 assert_eq!(vmgs.state.fcbs[&FileId(2)].block_offset, 6);
2303 }
2304
2305 #[async_test]
2306 async fn multiple_read_write() {
2307 let disk = new_test_file();
2308 let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2309
2310 let buf_1 = b"Data data data";
2311 let buf_2 = b"password";
2312 let buf_3 = b"other data data";
2313
2314 vmgs.write_file(FileId::BIOS_NVRAM, buf_1).await.unwrap();
2315 let read_buf_1 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2316 assert_eq!(buf_1, &*read_buf_1);
2317 assert_eq!(vmgs.state.active_header_index, 1);
2318 assert_eq!(vmgs.state.active_header_sequence_number, 2);
2319
2320 vmgs.write_file(FileId::TPM_PPI, buf_2).await.unwrap();
2321 let info = vmgs.get_file_info(FileId::TPM_PPI).unwrap();
2322 assert_eq!(info.valid_bytes as usize, buf_2.len());
2323 let read_buf_2 = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2324 assert_eq!(buf_2, &*read_buf_2);
2325 assert_eq!(vmgs.state.active_header_index, 0);
2326 assert_eq!(vmgs.state.active_header_sequence_number, 3);
2327
2328 vmgs.write_file(FileId::BIOS_NVRAM, buf_3).await.unwrap();
2329 let read_buf_3 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2330 assert_eq!(buf_3, &*read_buf_3);
2331 assert_eq!(vmgs.state.active_header_index, 1);
2332 assert_eq!(vmgs.state.active_header_sequence_number, 4);
2333
2334 vmgs.write_file(FileId::BIOS_NVRAM, buf_1).await.unwrap();
2335 let read_buf_1 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2336 assert_eq!(buf_1, &*read_buf_1);
2337 assert_eq!(vmgs.state.active_header_index, 0);
2338 assert_eq!(vmgs.state.active_header_sequence_number, 5);
2339
2340 vmgs.write_file(FileId::TPM_PPI, buf_2).await.unwrap();
2341 let read_buf_2 = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2342 assert_eq!(buf_2, &*read_buf_2);
2343 assert_eq!(vmgs.state.active_header_index, 1);
2344 assert_eq!(vmgs.state.active_header_sequence_number, 6);
2345
2346 vmgs.write_file(FileId::BIOS_NVRAM, buf_3).await.unwrap();
2347 let read_buf_3 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2348 assert_eq!(buf_3, &*read_buf_3);
2349 assert_eq!(vmgs.state.active_header_index, 0);
2350 assert_eq!(vmgs.state.active_header_sequence_number, 7);
2351 }
2352
2353 #[async_test]
2354 async fn test_insufficient_resources() {
2355 let disk = new_test_file();
2356 let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2357
2358 let buf: Vec<u8> = vec![1; ONE_MEGA_BYTE as usize * 5];
2359 let result = vmgs.write_file(FileId::BIOS_NVRAM, &buf).await;
2360 assert!(result.is_err());
2361 if let Err(e) = result {
2362 match e {
2363 Error::InsufficientResources => (),
2364 _ => panic!("Wrong error returned"),
2365 }
2366 } else {
2367 panic!("Should have returned Insufficient resources error");
2368 }
2369 }
2370
2371 #[async_test]
2372 async fn test_empty_write() {
2373 let disk = new_test_file();
2374 let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2375
2376 let buf: Vec<u8> = Vec::new();
2377 vmgs.write_file(FileId::BIOS_NVRAM, &buf).await.unwrap();
2378
2379 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2381
2382 assert_eq!(buf, read_buf);
2383 assert_eq!(read_buf.len(), 0);
2384 assert_eq!(vmgs.state.active_header_index, 1);
2385 assert_eq!(vmgs.state.active_header_sequence_number, 2);
2386 }
2387
2388 #[test]
2390 fn test_block_count_to_byte_count() {
2391 let block_count = 10;
2392 let byte_count = block_count_to_byte_count(block_count);
2393 assert!(byte_count == block_count as u64 * VMGS_BYTES_PER_BLOCK as u64);
2394 }
2395
2396 #[test]
2397 fn test_validate_header() {
2398 let mut header = VmgsHeader::new_zeroed();
2399 header.signature = VMGS_SIGNATURE;
2400 header.version = VMGS_VERSION_3_0;
2401 header.header_size = size_of::<VmgsHeader>() as u32;
2402 header.file_table_offset = VMGS_MIN_FILE_BLOCK_OFFSET;
2403 header.file_table_size = VMGS_FILE_TABLE_BLOCK_SIZE;
2404 header.checksum = compute_crc32(header.as_bytes());
2405
2406 let result = validate_header(&header);
2407 assert!(result.is_ok());
2408
2409 let mut header_signature = header.clone();
2410 header_signature.signature = 0;
2411 header_signature.checksum = 0;
2412 header_signature.checksum = compute_crc32(header_signature.as_bytes());
2413 let result = validate_header(&header_signature);
2414 match result {
2415 Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid header signature"),
2416 _ => panic!(),
2417 };
2418
2419 let mut header_version = header.clone();
2420 header_version.version = 0;
2421 header_version.checksum = 0;
2422 header_version.checksum = compute_crc32(header_version.as_bytes());
2423 match validate_header(&header_version) {
2424 Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid header version"),
2425 _ => panic!(),
2426 };
2427
2428 let mut header_header_size = header.clone();
2429 header_header_size.header_size = 0;
2430 header_header_size.checksum = 0;
2431 header_header_size.checksum = compute_crc32(header_header_size.as_bytes());
2432 match validate_header(&header_header_size) {
2433 Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid header size"),
2434 _ => panic!(),
2435 };
2436
2437 let mut header_ft_offset = header.clone();
2438 header_ft_offset.file_table_offset = 0;
2439 header_ft_offset.checksum = 0;
2440 header_ft_offset.checksum = compute_crc32(header_ft_offset.as_bytes());
2441 match validate_header(&header_ft_offset) {
2442 Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid file table offset"),
2443 _ => panic!(),
2444 };
2445
2446 let mut header_ft_size = header.clone();
2447 header_ft_size.file_table_size = 0;
2448 header_ft_size.checksum = 0;
2449 header_ft_size.checksum = compute_crc32(header_ft_size.as_bytes());
2450 match validate_header(&header_ft_size) {
2451 Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid file table size"),
2452 _ => panic!(),
2453 };
2454 }
2455
2456 #[test]
2457 fn test_initialize_file_metadata() {
2458 let mut file_table = VmgsFileTable::new_zeroed();
2459
2460 file_table.entries[0].offset = 6;
2461 file_table.entries[0].allocation_size = 1;
2462 file_table.entries[1].offset = 2;
2463 file_table.entries[1].allocation_size = 1;
2464 file_table.entries[2].offset = 4;
2465 file_table.entries[2].allocation_size = 5;
2466 file_table.entries[3].offset = 3;
2467 file_table.entries[3].allocation_size = 3;
2468
2469 let block_capacity = 1000;
2470
2471 let fcbs = initialize_file_metadata(&file_table, VMGS_VERSION_3_0, block_capacity).unwrap();
2472 assert!(fcbs[&FileId(0)].block_offset == 6);
2474 assert!(fcbs[&FileId(0)].allocated_blocks.get() == 1);
2475 assert!(fcbs[&FileId(1)].block_offset == 2);
2476 assert!(fcbs[&FileId(1)].allocated_blocks.get() == 1);
2477 assert!(fcbs[&FileId(2)].block_offset == 4);
2478 assert!(fcbs[&FileId(2)].allocated_blocks.get() == 5);
2479 assert!(fcbs[&FileId(3)].block_offset == 3);
2480 assert!(fcbs[&FileId(3)].allocated_blocks.get() == 3);
2481 }
2482
2483 #[test]
2484 fn test_round_up_count() {
2485 assert!(round_up_count(0, 4096) == 0);
2486 assert!(round_up_count(1, 4096) == 4096);
2487 assert!(round_up_count(4095, 4096) == 4096);
2488 assert!(round_up_count(4096, 4096) == 4096);
2489 assert!(round_up_count(4097, 4096) == 8192);
2490 }
2491
2492 #[async_test]
2493 async fn test_header_sequence_overflow() {
2494 let disk = new_test_file();
2495 let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2496
2497 vmgs.state.active_header_sequence_number = u32::MAX;
2498
2499 let buf = b"hello world";
2501 vmgs.write_file(FileId::BIOS_NVRAM, buf).await.unwrap();
2502
2503 assert_eq!(vmgs.state.active_header_index, 1);
2504 assert_eq!(vmgs.state.active_header_sequence_number, 0);
2505
2506 vmgs.state.active_header_index = 0;
2507 vmgs.state.active_header_sequence_number = u32::MAX;
2508
2509 let mut temp_state = vmgs.temp_state();
2510
2511 let (new_header, index) = temp_state.make_header();
2512 vmgs.write_header_internal(&new_header, index)
2513 .await
2514 .unwrap();
2515 vmgs.apply(temp_state);
2516
2517 assert_eq!(vmgs.state.active_header_index, 1);
2518 assert_eq!(vmgs.state.active_header_sequence_number, 0);
2519 assert_eq!(new_header.sequence, 0);
2520 }
2521
2522 #[cfg(feature = "encryption")]
2523 #[async_test]
2524 async fn write_file_v3() {
2525 let disk = new_test_file();
2526 let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2527 let encryption_key = [12; VMGS_ENCRYPTION_KEY_SIZE];
2528
2529 let buf = b"hello world";
2531 let buf_1 = b"hello universe";
2532 vmgs.write_file(FileId::BIOS_NVRAM, buf).await.unwrap();
2533 vmgs.update_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2534 .await
2535 .unwrap();
2536 vmgs.write_file_encrypted(FileId::TPM_PPI, buf_1)
2537 .await
2538 .unwrap();
2539
2540 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2542 assert_eq!(buf, &*read_buf);
2543 let info = vmgs.get_file_info(FileId::TPM_PPI).unwrap();
2544 assert_eq!(info.valid_bytes as usize, buf_1.len());
2545 let read_buf = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2546 assert_eq!(buf_1, &*read_buf);
2547
2548 drop(vmgs);
2550 let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2551 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2552 assert_eq!(buf, read_buf.as_bytes());
2553 let info = vmgs.get_file_info(FileId::TPM_PPI).unwrap();
2554 assert_eq!(info.valid_bytes as usize, buf_1.len());
2555 let read_buf = vmgs.read_file_raw(FileId::TPM_PPI).await.unwrap();
2556 assert_ne!(buf_1, read_buf.as_bytes());
2557
2558 vmgs.unlock_with_encryption_key(&encryption_key)
2560 .await
2561 .unwrap();
2562 let info = vmgs.get_file_info(FileId::TPM_PPI).unwrap();
2563 assert_eq!(info.valid_bytes as usize, buf_1.len());
2564 let read_buf = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2565 assert_eq!(buf_1, &*read_buf);
2566 }
2567
2568 #[cfg(feature = "encryption")]
2569 #[async_test]
2570 async fn overwrite_file_v3() {
2571 let disk = new_test_file();
2572 let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2573 let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2574 let buf = vec![1; 8 * 1024];
2575 let buf_1 = vec![2; 8 * 1024];
2576
2577 vmgs.add_new_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2579 .await
2580 .unwrap();
2581 assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2582
2583 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2585 .await
2586 .unwrap();
2587
2588 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf_1)
2590 .await
2591 .unwrap();
2592
2593 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2595 assert_eq!(buf_1, read_buf);
2596 }
2597
2598 #[cfg(feature = "encryption")]
2599 #[async_test]
2600 async fn file_encryption() {
2601 let buf: Vec<u8> = (0..255).collect();
2602 let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2603
2604 let disk = new_test_file();
2605 let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2606
2607 vmgs.add_new_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2609 .await
2610 .unwrap();
2611 assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2612
2613 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2615 .await
2616 .unwrap();
2617
2618 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2620 assert_eq!(buf, read_buf);
2621
2622 drop(vmgs);
2623
2624 let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2626
2627 let info = vmgs.get_file_info(FileId::BIOS_NVRAM).unwrap();
2628 assert_eq!(info.valid_bytes as usize, buf.len());
2629
2630 vmgs.unlock_with_encryption_key(&encryption_key)
2633 .await
2634 .unwrap();
2635
2636 assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2637
2638 let new_encryption_key = [2; VMGS_ENCRYPTION_KEY_SIZE];
2640 vmgs.add_new_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2641 .await
2642 .unwrap();
2643 assert_eq!(vmgs.state.active_datastore_key_index, Some(1));
2644 vmgs.remove_encryption_key(0).await.unwrap();
2645
2646 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2647 assert_eq!(buf, read_buf);
2648 }
2649
2650 #[cfg(feature = "encryption")]
2651 #[async_test]
2652 async fn add_new_encryption_key() {
2653 let buf: Vec<u8> = (0..255).collect();
2654 let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2655 let new_encryption_key = [5; VMGS_ENCRYPTION_KEY_SIZE];
2656
2657 let disk = new_test_file();
2659 let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2660
2661 vmgs.add_new_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2663 .await
2664 .unwrap();
2665 assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2666
2667 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2669 .await
2670 .unwrap();
2671
2672 drop(vmgs);
2674 let mut vmgs = Vmgs::open(disk.clone(), None).await.unwrap();
2675 vmgs.unlock_with_encryption_key(&encryption_key)
2676 .await
2677 .unwrap();
2678 assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2679 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2680 assert_eq!(read_buf, buf);
2681
2682 vmgs.add_new_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2684 .await
2685 .unwrap();
2686 assert_eq!(vmgs.state.active_datastore_key_index, Some(1));
2687
2688 drop(vmgs);
2690 let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2691 vmgs.unlock_with_encryption_key(&encryption_key)
2692 .await
2693 .unwrap();
2694 assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2695 vmgs.unlock_with_encryption_key(&new_encryption_key)
2696 .await
2697 .unwrap();
2698 assert_eq!(vmgs.state.active_datastore_key_index, Some(1));
2699 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2700 assert_eq!(read_buf, buf);
2701
2702 vmgs.remove_encryption_key(1).await.unwrap();
2704 vmgs.add_new_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2705 .await
2706 .unwrap();
2707 assert_eq!(vmgs.state.active_datastore_key_index, Some(1));
2708
2709 vmgs.remove_encryption_key(0).await.unwrap();
2711 let result = vmgs.unlock_with_encryption_key(&encryption_key).await;
2712 assert!(matches!(result, Err(Error::DecryptMetadataKey)));
2713
2714 let result = vmgs.remove_encryption_key(0).await;
2716 assert!(matches!(result, Err(Error::InvalidArgument(_))));
2717
2718 vmgs.remove_encryption_key(1).await.unwrap();
2720 let read_buf = vmgs.read_file_raw(FileId::BIOS_NVRAM).await;
2721 assert_ne!(read_buf.unwrap(), buf);
2722 }
2723
2724 #[cfg(feature = "encryption")]
2725 #[async_test]
2726 async fn test_write_file_encrypted() {
2727 let disk = new_test_file();
2731 let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2732 let buf = b"This is plaintext";
2733
2734 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, buf)
2736 .await
2737 .unwrap();
2738
2739 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2741 assert_eq!(vmgs.state.encryption_algorithm, EncryptionAlgorithm::NONE);
2742 assert_eq!(buf, &*read_buf);
2743
2744 drop(vmgs);
2747 let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2748
2749 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2750 assert_eq!(vmgs.state.encryption_algorithm, EncryptionAlgorithm::NONE);
2751 assert_eq!(buf, &*read_buf);
2752 }
2753
2754 #[cfg(feature = "encryption")]
2755 #[async_test]
2756 async fn test_logger() {
2757 let disk = new_test_file();
2758 let data = Arc::new(Mutex::new(String::new()));
2759 let mut vmgs = Vmgs::format_new(
2760 disk.clone(),
2761 Some(Arc::new(TestVmgsLogger { data: data.clone() })),
2762 )
2763 .await
2764 .unwrap();
2765 let encryption_key = [12; VMGS_ENCRYPTION_KEY_SIZE];
2766
2767 let buf = b"hello world";
2769 vmgs.update_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2770 .await
2771 .unwrap();
2772 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, buf)
2773 .await
2774 .unwrap();
2775
2776 let fcb = vmgs.state.fcbs.get_mut(&FileId::BIOS_NVRAM).unwrap();
2777
2778 fcb.nonce[0] ^= 1;
2780
2781 let result = vmgs.read_file(FileId::BIOS_NVRAM).await;
2783 assert!(result.is_err());
2784
2785 let result = data.lock();
2787 assert_eq!(*result, "test logger");
2788 }
2789
2790 #[cfg(feature = "encryption")]
2791 #[async_test]
2792 async fn update_key() {
2793 let buf: Vec<u8> = (0..255).collect();
2794 let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2795
2796 let disk = new_test_file();
2797 let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2798
2799 vmgs.update_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2801 .await
2802 .unwrap();
2803 assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2804 assert_eq!(vmgs.state.datastore_key_count, 1);
2805
2806 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2808 .await
2809 .unwrap();
2810
2811 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2813 assert_eq!(buf, read_buf);
2814
2815 drop(vmgs);
2817 let mut vmgs = Vmgs::open(disk.clone(), None).await.unwrap();
2818
2819 vmgs.unlock_with_encryption_key(&encryption_key)
2821 .await
2822 .unwrap();
2823 assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2824
2825 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2827 assert_eq!(buf, read_buf);
2828
2829 let new_encryption_key = [2; VMGS_ENCRYPTION_KEY_SIZE];
2831 vmgs.update_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2832 .await
2833 .unwrap();
2834 assert_eq!(vmgs.state.active_datastore_key_index, Some(1));
2835 assert_eq!(vmgs.state.datastore_key_count, 1);
2836
2837 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2839 assert_eq!(buf, read_buf);
2840
2841 drop(vmgs);
2843 let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2844
2845 vmgs.unlock_with_encryption_key(&new_encryption_key)
2847 .await
2848 .unwrap();
2849 assert_eq!(vmgs.state.active_datastore_key_index, Some(1));
2850
2851 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2853 assert_eq!(buf, read_buf);
2854 }
2855
2856 #[cfg(feature = "encryption")]
2857 #[async_test]
2858 async fn update_key_no_space() {
2859 let buf: Vec<u8> = (0..255).collect();
2860 let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2861
2862 let disk = new_test_file();
2863 let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2864
2865 vmgs.update_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2867 .await
2868 .unwrap();
2869 assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2870 assert_eq!(vmgs.state.datastore_key_count, 1);
2871
2872 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2874 .await
2875 .unwrap();
2876
2877 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2879 assert_eq!(buf, read_buf);
2880
2881 let new_encryption_key = [2; VMGS_ENCRYPTION_KEY_SIZE];
2883 vmgs.add_new_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2884 .await
2885 .unwrap();
2886 assert_eq!(vmgs.state.active_datastore_key_index, Some(1));
2887 assert_eq!(vmgs.state.datastore_key_count, 2);
2888
2889 drop(vmgs);
2891 let mut vmgs = Vmgs::open(disk.clone(), None).await.unwrap();
2892
2893 vmgs.unlock_with_encryption_key(&new_encryption_key)
2895 .await
2896 .unwrap();
2897 assert_eq!(vmgs.state.active_datastore_key_index, Some(1));
2898
2899 let another_encryption_key = [2; VMGS_ENCRYPTION_KEY_SIZE];
2901 vmgs.update_encryption_key(&another_encryption_key, EncryptionAlgorithm::AES_GCM)
2902 .await
2903 .unwrap();
2904 assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2905 assert_eq!(vmgs.state.datastore_key_count, 1);
2906
2907 drop(vmgs);
2909 let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2910
2911 vmgs.unlock_with_encryption_key(&another_encryption_key)
2913 .await
2914 .unwrap();
2915 assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2916
2917 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2919 assert_eq!(buf, read_buf);
2920 }
2921
2922 #[test]
2923 fn test_allocate_helper() {
2924 let block_capacity =
2925 (vmgs_format::VMGS_DEFAULT_CAPACITY / (VMGS_BYTES_PER_BLOCK as u64)) as u32;
2926 assert_eq!(block_capacity, 1024);
2928
2929 let mut allocation_list = Vec::new();
2930
2931 assert_eq!(
2933 allocate_helper(&mut allocation_list, 3, block_capacity).unwrap(),
2934 2
2935 );
2936 assert_eq!(
2937 allocate_helper(&mut allocation_list, 95, block_capacity).unwrap(),
2938 5
2939 );
2940 assert_eq!(
2941 allocate_helper(&mut allocation_list, 2, block_capacity).unwrap(),
2942 100
2943 );
2944
2945 allocation_list.remove(0);
2947
2948 assert_eq!(
2949 allocate_helper(&mut allocation_list, 1, block_capacity).unwrap(),
2950 2
2951 );
2952 assert_eq!(
2953 allocate_helper(&mut allocation_list, 3, block_capacity).unwrap(),
2954 102
2955 );
2956 assert_eq!(
2957 allocate_helper(&mut allocation_list, 2, block_capacity).unwrap(),
2958 3
2959 );
2960
2961 let mut allocation_list = Vec::new();
2963
2964 allocate_helper(&mut allocation_list, 1025, block_capacity).unwrap_err();
2965 assert_eq!(
2966 allocate_helper(&mut allocation_list, 511, block_capacity).unwrap(),
2967 2
2968 );
2969 assert_eq!(
2970 allocate_helper(&mut allocation_list, 511, block_capacity).unwrap(),
2971 513
2972 );
2973 allocate_helper(&mut allocation_list, 1, block_capacity).unwrap_err();
2974 }
2975
2976 #[async_test]
2977 async fn test_provisioning_marker() {
2978 const EXPECTED_MARKER: &str = r#"{"provisioner":"openhcl","reason":"empty","tpm_version":"1.38","tpm_nvram_size":32768,"akcert_size":4096,"akcert_attrs":"0x42060004","provisioner_version":"unit test"}"#;
2979
2980 let disk = new_test_file();
2981 let data = Arc::new(Mutex::new(String::new()));
2982 let mut vmgs = Vmgs::format_new_with_reason(
2983 disk.clone(),
2984 VmgsProvisioningReason::Empty,
2985 Some(Arc::new(TestVmgsLogger { data: data.clone() })),
2986 )
2987 .await
2988 .unwrap();
2989
2990 let marker = VmgsProvisioningMarker {
2991 provisioner: VmgsProvisioner::OpenHcl,
2992 reason: vmgs.provisioning_reason().unwrap(),
2993 tpm_version: "1.38".to_string(),
2994 tpm_nvram_size: 32768,
2995 akcert_size: 4096,
2996 akcert_attrs: "0x42060004".to_string(),
2997 provisioner_version: "unit test".to_string(),
2998 };
2999
3000 vmgs.write_provisioning_marker(&marker).await.unwrap();
3001
3002 let read_buf = vmgs.read_file(FileId::PROVISIONING_MARKER).await.unwrap();
3003 assert_eq!(EXPECTED_MARKER.as_bytes(), read_buf);
3004 }
3005
3006 #[cfg(feature = "encryption")]
3007 #[async_test]
3008 async fn verify_no_zero_keys() {
3009 let buf: Vec<u8> = (0..255).collect();
3010 let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
3011
3012 let disk = new_test_file();
3013 let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
3014
3015 vmgs.add_new_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
3017 .await
3018 .unwrap();
3019 assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
3020
3021 assert_eq!(
3023 vmgs.state.unused_metadata_key,
3024 vmgs.state
3025 .fcbs
3026 .get(&FileId::EXTENDED_FILE_TABLE)
3027 .unwrap()
3028 .encryption_key
3029 );
3030 assert_ne!(
3032 vmgs.state.unused_metadata_key,
3033 VmgsDatastoreKey::new_zeroed()
3034 );
3035
3036 vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
3038 .await
3039 .unwrap();
3040
3041 let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
3043 assert_eq!(buf, read_buf);
3044 let bios_nvram_key = vmgs
3046 .state
3047 .fcbs
3048 .get(&FileId::BIOS_NVRAM)
3049 .unwrap()
3050 .encryption_key;
3051 assert_ne!(bios_nvram_key, VmgsDatastoreKey::new_zeroed());
3052
3053 let metadata_key = vmgs
3054 .state
3055 .fcbs
3056 .get(&FileId::EXTENDED_FILE_TABLE)
3057 .unwrap()
3058 .encryption_key;
3059 let extended_file_table_data = vmgs
3060 .read_file_internal(FileId::EXTENDED_FILE_TABLE, true, None)
3061 .await
3062 .unwrap();
3063
3064 drop(vmgs);
3065
3066 let mut vmgs = Vmgs::open(disk, None).await.unwrap();
3068
3069 let mut fcb = vmgs
3071 .state
3072 .fcbs
3073 .get(&FileId::EXTENDED_FILE_TABLE)
3074 .unwrap()
3075 .clone();
3076 assert_eq!(fcb.encryption_key, VmgsDatastoreKey::new_zeroed());
3077 let encrypted_data = vmgs
3078 .read_file_internal(FileId::EXTENDED_FILE_TABLE, false, None)
3079 .await
3080 .unwrap();
3081 let result = fcb.decrypt(&encrypted_data);
3082 assert!(result.is_err());
3083 fcb.encryption_key.copy_from_slice(&metadata_key);
3085 let read_buf = fcb.decrypt(&encrypted_data).unwrap();
3086 assert_eq!(extended_file_table_data, read_buf);
3087
3088 let mut fcb = vmgs.state.fcbs.get(&FileId::BIOS_NVRAM).unwrap().clone();
3090 assert_eq!(fcb.encryption_key, VmgsDatastoreKey::new_zeroed());
3091 let encrypted_data = vmgs
3092 .read_file_internal(FileId::BIOS_NVRAM, false, None)
3093 .await
3094 .unwrap();
3095 let result = fcb.decrypt(&encrypted_data);
3096 assert!(result.is_err());
3097 fcb.encryption_key.copy_from_slice(&bios_nvram_key);
3099 let read_buf = fcb.decrypt(&encrypted_data).unwrap();
3100 assert_eq!(buf, read_buf);
3101
3102 vmgs.unlock_with_encryption_key(&encryption_key)
3104 .await
3105 .unwrap();
3106
3107 assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
3108 assert_eq!(
3109 vmgs.state.unused_metadata_key,
3110 vmgs.state
3111 .fcbs
3112 .get(&FileId::EXTENDED_FILE_TABLE)
3113 .unwrap()
3114 .encryption_key
3115 );
3116 assert_ne!(
3117 vmgs.state.unused_metadata_key,
3118 VmgsDatastoreKey::new_zeroed()
3119 );
3120 }
3121}