Skip to main content

vmgs/
vmgs_impl.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use 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/// Operation types for provisioning telemetry.
45#[derive(Debug)]
46enum LogOpType {
47    VmgsProvision,
48}
49
50/// Info about a specific VMGS file.
51#[derive(Debug)]
52#[cfg_attr(feature = "mesh", derive(mesh_protobuf::Protobuf))]
53pub struct VmgsFileInfo {
54    /// Number of bytes allocated in the file.
55    pub allocated_bytes: u64,
56    /// Number of valid bytes in the file.
57    pub valid_bytes: u64,
58    /// Whether this file is encrypted.
59    pub encrypted: bool,
60}
61
62/// GSP types that can be used to encrypt a VMGS file.
63#[derive(Debug, Clone, Copy)]
64pub enum GspType {
65    /// No GSP
66    None,
67    /// GSP by ID
68    GspById,
69    /// GSP key
70    GspKey,
71}
72
73// Aggregates fully validated data from the FILE_TABLE and EXTENDED_FILE_TABLE
74// control blocks.
75#[derive(Clone, PartialEq, Eq, Debug)]
76#[cfg_attr(feature = "inspect", derive(Inspect))]
77struct ResolvedFileControlBlock {
78    // FILE_TABLE data
79    // ---------------
80    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    // EXTENDED_FILE_TABLE data
89    // ---------------
90    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            // sanity check: encrypted data should never be all zeros. if we
224            // find that it is all-zeroes, then that's indicative of some kind
225            // of logic error / data corruption
226            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        // Always allocate at least one block, to allow for zero sized data buffers
307        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/// Implementation of the VMGS file format, backed by a generic [`Disk`]
346/// device.
347#[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, retained for save-restore backwards compatibility
374    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    // explicit inspect implementation, since we want to massage the data's
392    // presentation a bit
393    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    /// Attempt to open the VMGS file, optionally formatting if it is
425    /// empty or corrupted.
426    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                    // The data store format is invalid or not supported.
445                    Error::InvalidFormat(_) => VmgsLogEvent::InvalidFormat,
446                    // The data store is corrupted.
447                    Error::CorruptFormat(_) => VmgsLogEvent::CorruptFormat,
448                    // All other errors
449                    _ => VmgsLogEvent::InitFailed,
450                };
451
452                logger.log_event_fatal(event_log_id).await;
453                Err(err)
454            }
455        }
456    }
457
458    /// Open the VMGS file.
459    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    /// Format and open a new VMGS file.
466    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    /// Format and open a new VMGS file.
474    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    /// Format and open a new VMGS file.
490    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                // set the reprovisioned marker to prevent the vmgs from
512                // repeatedly being reset
513                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        // clear the reprovisioned marker after successfully opening the vmgs
530        // without being requested to reprovision.
531        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    /// Formats the backing store with initial metadata, and sets active header.
600    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        // zero out the active header, the other one will be populated below
611        vmgs.write_header_internal(&VmgsHeader::new_zeroed(), vmgs.state.active_header_index)
612            .await?;
613
614        // write a blank, unencrypted file table for consistency with old impls
615        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        // write a blank file table
626        vmgs.write_files_internal(files, None).await?;
627
628        // write the active header
629        let (new_header, index) = vmgs.state.make_header();
630        vmgs.write_header_internal(&new_header, index).await?;
631
632        // Flush the device to persist changes
633        vmgs.storage.flush().await.map_err(Error::FlushDisk)?;
634
635        Ok(vmgs)
636    }
637
638    /// Get allocated and valid bytes from File Control Block for file_id.
639    ///
640    /// When reading data from a file, the buffer must be at least `valid_bytes` long.
641    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    /// Returns whether a file id is allocated
651    pub fn check_file_allocated(&self, file_id: FileId) -> bool {
652        self.state.fcbs.contains_key(&file_id)
653    }
654
655    /// Get info about all the files currently in the file table
656    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    /// Writes `buf` to a file_id, optionally encrypting or overwriting
668    /// encrypted data with plaintext. Updates file tables as appropriate.
669    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        // Update the header
724        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    /// Write a set of files and any necessary file tables
738    async fn write_files_internal<'a>(
739        &mut self,
740        // using a BTreeMap here so that the allocations are predictable
741        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        // ensure the necessary file tables are in the allocation list
747        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        // allocate space for the files
759        let mut files = state.allocate_space(files, self.storage.block_capacity())?;
760
761        // encrypt anything that needs to be encrypted except the extended file
762        // table, which hasn't been generated yet.
763        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        // generate and encrypt the extended file table
773        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            // add the blank table if specified, even if not encrypted
779            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        // generate the file table now that all of the nonces and auth tags
788        // are in the temporary fcbs
789        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        // write the files
797        for res in files.values() {
798            self.write_file_internal(&res.fcb, res.data.get()).await?;
799        }
800
801        Ok(())
802    }
803
804    /// Writes `buf` to the block offset specified in the file control block.
805    /// Encrypts the data and returns the auth tag if applicable.
806    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    /// write the new header to disk.
827    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    /// Reads the specified `file_id`, decrypting its contents.
844    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    /// Reads the specified `file_id`, but does not decrypt the contents.
849    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    /// User-facing file read
854    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    /// read a file_id, decrypting if requested and possible
881    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        // read the file
895        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        // decrypt if necessary
914        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    /// Writes `buf` to a file_id without encrypting it.
937    ///
938    /// If the file is already encrypted, this will return a failure. Use
939    /// [`Self::write_file_allow_overwrite_encrypted`] if you want to allow
940    /// this.
941    ///
942    /// To write encrypted data, use `write_file_encrypted` instead.
943    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    /// Writes `buf` to a file_id without encrypting it, allowing overwrites of
948    /// an already-encrypted file.
949    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    /// Encrypts `buf` and writes the encrypted payload to a file_id if the VMGS file has encryption configured.
958    /// If the VMGS doesn't have encryption configured, will do a plaintext write instead.
959    #[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    /// Move a file to a new file_id
965    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        // move the fcb to a different file id
985        let fcb = temp_state
986            .fcbs
987            .remove(&src)
988            .ok_or(Error::FileInfoNotAllocated(src))?;
989        temp_state.fcbs.insert(dst, fcb);
990
991        // write the new file table(s)
992        self.write_files_internal(BTreeMap::new(), Some(&mut temp_state))
993            .await?;
994
995        // Update the header
996        self.write_header_and_apply(temp_state).await?;
997
998        Ok(())
999    }
1000
1001    /// Delete a file
1002    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        // delete the fcb
1010        temp_state
1011            .fcbs
1012            .remove(&file_id)
1013            .ok_or(Error::FileInfoNotAllocated(file_id))?;
1014
1015        // write the new file table(s)
1016        self.write_files_internal(BTreeMap::new(), Some(&mut temp_state))
1017            .await?;
1018
1019        // Update the header
1020        self.write_header_and_apply(temp_state).await?;
1021
1022        Ok(())
1023    }
1024
1025    /// Decrypts the extended file table by the encryption_key and
1026    /// updates the related metadata in memory.
1027    #[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        // Iterate through two metadata keys and get the index of the valid key which can be successfully
1042        // decrypted by the encryption_key, as well as set the decrypted key as the VMGS's metadata key
1043        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                // older implementations didn't write unencrypted attributes
1072                // so configure them here so that the table is decrypted below
1073                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        // Read and decrypt the extended file table
1098        let extended_file_table_buffer = self
1099            .read_file_internal(FileId::EXTENDED_FILE_TABLE, true, Some(&temp_state))
1100            .await?;
1101
1102        // Update the cached extended file table
1103        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    /// Associates a new root key with the data store and removes the old
1119    /// encryption key, if it exists. If two keys already exist, the
1120    /// inactive key is removed first.
1121    #[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    /// Associates a new root key with the data store.
1157    #[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        // zero out the keys to ensure we get a new nonce
1197        temp_state.encrypted_metadata_keys[new_key_index] = VmgsEncryptionKey::new_zeroed();
1198
1199        // Allocate and write the new file tables
1200        if self.state.datastore_key_count == 0 {
1201            self.write_files_internal(BTreeMap::new(), Some(&mut temp_state))
1202                .await?;
1203        } else {
1204            // the extended file table should already exist, but we still need
1205            // to re-encrypt the metadata key.
1206            temp_state.encrypt_metadata_key()?;
1207        }
1208
1209        // Update the header on the storage device
1210        self.write_header_and_apply(temp_state).await?;
1211
1212        Ok(())
1213    }
1214
1215    /// Disassociates the root key at the specified index from the data store.
1216    #[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        // Remove the corresponding datastore_key
1233        temp_state.datastore_keys[key_index].fill(0);
1234
1235        // Remove the corresponding metadata_key
1236        temp_state.encrypted_metadata_keys[key_index] = VmgsEncryptionKey::new_zeroed();
1237
1238        // Update cached metadata
1239        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    /// Gets the encryption algorithm of the VMGS
1260    pub fn get_encryption_algorithm(&self) -> EncryptionAlgorithm {
1261        self.state.encryption_algorithm
1262    }
1263
1264    /// Whether the VMGS file is encrypted
1265    pub fn encrypted(&self) -> bool {
1266        self.state.encrypted()
1267    }
1268
1269    /// Whether the VMGS file was provisioned during the most recent boot
1270    pub fn was_provisioned_this_boot(&self) -> bool {
1271        self.state.provisioning_reason.is_some()
1272    }
1273
1274    /// Why this VMGS file was provisioned
1275    pub fn provisioning_reason(&self) -> Option<VmgsProvisioningReason> {
1276        self.state.provisioning_reason
1277    }
1278
1279    /// Write a provisioning marker to this VMGS file
1280    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    /// Get temporary Vmgs state
1302    fn temp_state(&self) -> VmgsState {
1303        self.state.clone()
1304    }
1305
1306    /// Apply the temporary Vmgs state
1307    fn apply(&mut self, temp_state: VmgsState) {
1308        self.state = temp_state;
1309    }
1310
1311    /// Apply the temporary Vmgs state
1312    async fn write_header_and_apply(&mut self, mut temp_state: VmgsState) -> Result<(), Error> {
1313        // Data must be hardened on persistent storage before the header is updated.
1314        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    /// Initializes a new VMGS header populated using the temporary state,
1372    /// which is updated to point to the new header.
1373    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    /// Whether the VMGS file is encrypted
1398    fn encrypted(&self) -> bool {
1399        self.encryption_algorithm != EncryptionAlgorithm::NONE
1400    }
1401
1402    /// Whether the VMGS file is unlocked
1403    fn unlocked(&self) -> bool {
1404        self.active_datastore_key_index.is_some()
1405    }
1406
1407    /// Whether the VMGS file is encrypted and unlocked
1408    fn encrypted_and_unlocked(&self) -> bool {
1409        self.encrypted() && self.unlocked()
1410    }
1411
1412    /// Update the metadata key
1413    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    /// Copies current file metadata to a file table structure.
1450    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    /// Copies current file metadata to an extended file table structure.
1459    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    /// maps out the used/unused space in the file and finds the smallest
1468    /// unused space to allocate new data.
1469    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        // populate the allocation list with any existing files
1475        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        // allocate space for the new files
1485        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/// Additional test-only functions for use in other crates that reveal
1495/// implmentation details of the vmgs datastore encryption keys.
1496#[cfg(feature = "test_helpers")]
1497mod test_helpers {
1498    use super::*;
1499
1500    impl Vmgs {
1501        /// Get the active datastore key index
1502        pub fn test_get_active_datastore_key_index(&self) -> Option<usize> {
1503            self.state.active_datastore_key_index
1504        }
1505
1506        /// Associates a new root key with the data store.
1507        #[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
1519/// Attempt to read both headers and separately return any validation errors
1520pub 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    // first_two_blocks will contain enough bytes to read the first two headers
1534    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; // TODO: zerocopy: use-rest-of-range (https://github.com/microsoft/openvmm/issues/759)
1542    let header_2 =
1543        VmgsHeader::read_from_prefix(&first_two_blocks[storage.aligned_header_size() as usize..])
1544            .unwrap()
1545            .0; // TODO: zerocopy: from-prefix (read_from_prefix): use-rest-of-range (https://github.com/microsoft/openvmm/issues/759)
1546    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
1571/// Determines which header to use given the results of checking the
1572/// validity of each of the headers.
1573pub 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 both headers are valid, find the header with the larger sequence number.
1580            // The header with the most recent sequence number is considered
1581            // the current copy. To handle integer overflow, a header with sequence number 0
1582            // is considered the current copy if and only if the other header contains 0xFFFFFFFF.
1583            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
1608/// Validate the contents of header match VMGS file type.
1609pub 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
1649/// Initializes cached file metadata from the specified header. (File control blocks)
1650fn 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        // Check if the file is allocated.
1661        if file_entry.allocation_size == 0 {
1662            continue;
1663        };
1664
1665        // Validate the file offset.
1666        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        // The file must entirely fit in the available space.
1674        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        // Validate the valid data size.
1682        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        // Initialize the file control block for this file ID
1690        fcbs.insert(file_id, fcb);
1691    }
1692
1693    Ok(fcbs)
1694}
1695
1696/// Convert block count to byte count.
1697fn 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
1705/// Generates a nonce for the encryption. First 4 bytes are a random seed, and last 8 bytes are zero's.
1706fn generate_nonce() -> VmgsNonce {
1707    let mut nonce = VmgsNonce::new_zeroed();
1708    // Generate a 4-byte random seed for nonce
1709    getrandom::fill(&mut nonce[..vmgs_format::VMGS_NONCE_RANDOM_SEED_SIZE]).expect("rng failure");
1710    nonce
1711}
1712
1713/// Increment Nonce by one.
1714fn increment_nonce(nonce: &mut VmgsNonce) -> Result<(), Error> {
1715    // Update the random seed of nonce
1716    getrandom::fill(&mut nonce[..vmgs_format::VMGS_NONCE_RANDOM_SEED_SIZE]).expect("rng failure");
1717
1718    // Increment the counter of nonce by 1.
1719    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
1730/// Checks whether an encryption key is all zero's.
1731fn is_empty_key(encryption_key: &[u8]) -> bool {
1732    encryption_key.iter().all(|&x| x == 0)
1733}
1734
1735/// Encrypts MetadataKey. Returns encrypted_metadata_key.
1736#[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/// Decrypts metadata_key. Returns decrypted_metadata_key.
1762#[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
1791/// Computes the cr32 checksum for a given byte stream.
1792fn 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
1803/// maps out the used/unused space in the file and finds the smallest
1804/// unused space to allocate new data.
1805fn allocate_helper(
1806    allocation_list: &mut Vec<AllocationBlock>,
1807    block_count: u32,
1808    block_capacity: u32,
1809) -> Result<u32, Error> {
1810    // sort by block offset
1811    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    // find smallest set of blocks that will fit the data we're allocating
1819    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        /// Construct a [`Vmgs`] instance, re-using existing saved-state from an
1922        /// earlier instance.
1923        ///
1924        /// # Safety
1925        ///
1926        /// `open_from_saved` does NOT perform ANY validation on the provided
1927        /// `state`, and will blindly assume that it matches the underlying
1928        /// `storage` instance!
1929        ///
1930        /// Callers MUST ensure that the provided `state` matches the provided
1931        /// `storage`, and that no external entities have modified `storage` between
1932        /// the call to `save` and `open_from_saved`.
1933        ///
1934        /// Failing to do so may result in data corruption/loss, read/write
1935        /// failures, encryption errors, etc... (though, notably: it will _not_
1936        /// result in any memory-unsafety, hence why the function isn't marked
1937        /// `unsafe`).
1938        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        /// Save the in-memory Vmgs file metadata.
2021        ///
2022        /// This saved state can be used alongside `open_from_saved` to obtain a
2023        /// new `Vmgs` instance _without_ needing to invoke any IOs on the
2024        /// underlying storage.
2025        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        // write
2159        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        // read
2166        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        // write
2179        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        // read
2187        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        // write
2194        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        // read
2202        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        // write
2209        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        // read
2217        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        // write
2230        let buf = b"hello world";
2231        vmgs.write_file(FileId::TPM_NVRAM, buf).await.unwrap();
2232
2233        // read
2234        let read_buf = vmgs.read_file(FileId::TPM_NVRAM).await.unwrap();
2235        assert_eq!(buf, &*read_buf);
2236
2237        // move
2238        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        // delete
2246        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        // Create VMGS file and write to different FileId's
2257        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        // Re-open VMGS file and read from the same FileId's
2284        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        // read
2380        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    // general functions
2389    #[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 VmgsFileEntry correctly converted to FileControlBlock
2473        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        // write
2500        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        // write
2530        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        // read
2541        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        // Read the file after re-opening the vmgs file
2549        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        // Unlock datastore
2559        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        // Add root key.
2578        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        // Write a file to the store.
2584        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2585            .await
2586            .unwrap();
2587
2588        // Encrypt and overwrite the original file.
2589        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf_1)
2590            .await
2591            .unwrap();
2592
2593        // Verify new file contents
2594        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        // Add datastore key.
2608        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        // Write a file to the store.
2614        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2615            .await
2616            .unwrap();
2617
2618        // Read the file, without closing the datastore
2619        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2620        assert_eq!(buf, read_buf);
2621
2622        drop(vmgs);
2623
2624        // Read the file, after closing and reopening the data store.
2625        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        // Unlock the store.
2631
2632        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        // Change to a new datastore key.
2639        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        // Initialize version 3 data store
2658        let disk = new_test_file();
2659        let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2660
2661        // Add datastore key.
2662        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        // Write a file to the store.
2668        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2669            .await
2670            .unwrap();
2671
2672        // Read the file, after closing and reopening the data store.
2673        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        // Add new datastore key.
2683        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        // Read the file by using two different datastore keys, after closing and reopening the data store.
2689        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        // Remove the newly added datastore key and add it again.
2703        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        // Remove the old datastore key
2710        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        // Try to remove the old datastore key again
2715        let result = vmgs.remove_encryption_key(0).await;
2716        assert!(matches!(result, Err(Error::InvalidArgument(_))));
2717
2718        // Remove the new datastore key and try to read file content, which should be in encrypted state
2719        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        // Call write_file_encrypted on an unencrypted VMGS and check that plaintext was written
2728
2729        // Initialize version 3 data store
2730        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        // call write file encrypted
2735        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, buf)
2736            .await
2737            .unwrap();
2738
2739        // Read
2740        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        // ensure that when we re-create the VMGS object, we can still read the
2745        // FileId as plaintext
2746        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        // write
2768        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        // Manipulate the nonce and expect the read to fail.
2779        fcb.nonce[0] ^= 1;
2780
2781        // read and expect to fail
2782        let result = vmgs.read_file(FileId::BIOS_NVRAM).await;
2783        assert!(result.is_err());
2784
2785        // verify that the string is logged
2786        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        // Add datastore key.
2800        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        // Write a file to the store.
2807        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2808            .await
2809            .unwrap();
2810
2811        // Read the file, without closing the datastore
2812        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2813        assert_eq!(buf, read_buf);
2814
2815        // Close and reopen the store
2816        drop(vmgs);
2817        let mut vmgs = Vmgs::open(disk.clone(), None).await.unwrap();
2818
2819        // Unlock the store.
2820        vmgs.unlock_with_encryption_key(&encryption_key)
2821            .await
2822            .unwrap();
2823        assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2824
2825        // Read the file again
2826        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2827        assert_eq!(buf, read_buf);
2828
2829        // Change to a new datastore key.
2830        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        // Read the file again
2838        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2839        assert_eq!(buf, read_buf);
2840
2841        // Close and reopen the store
2842        drop(vmgs);
2843        let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2844
2845        // Unlock the store.
2846        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        // Read the file again
2852        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        // Add datastore key.
2866        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        // Write a file to the store.
2873        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2874            .await
2875            .unwrap();
2876
2877        // Read the file, without closing the datastore
2878        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2879        assert_eq!(buf, read_buf);
2880
2881        // Add a new datastore key, but don't remove the old one.
2882        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        // Close and reopen the store
2890        drop(vmgs);
2891        let mut vmgs = Vmgs::open(disk.clone(), None).await.unwrap();
2892
2893        // Unlock the store.
2894        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        // Add yet another new datastore key. This should remove both previous keys
2900        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        // Close and reopen the store
2908        drop(vmgs);
2909        let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2910
2911        // Unlock the store.
2912        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        // Read the file again
2918        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        // this test assumes the block capacity is 1024
2927        assert_eq!(block_capacity, 1024);
2928
2929        let mut allocation_list = Vec::new();
2930
2931        // add some "files"
2932        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        // remove the first one and make sure subsequent "files" are placed there
2946        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        // Make sure we error correctly when dealing with large files
2962        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        // Add datastore key.
3016        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        // Verify that the metadata key is consistent
3022        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        // ... and that it is not zero.
3031        assert_ne!(
3032            vmgs.state.unused_metadata_key,
3033            VmgsDatastoreKey::new_zeroed()
3034        );
3035
3036        // Write a file to the store.
3037        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
3038            .await
3039            .unwrap();
3040
3041        // Verify that we can decrypt the file
3042        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
3043        assert_eq!(buf, read_buf);
3044        // Verify that the file key is not zero
3045        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        // Read the file, after closing and reopening the data store.
3067        let mut vmgs = Vmgs::open(disk, None).await.unwrap();
3068
3069        // Verify that we can't read the extended file table with zeroed keys
3070        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        // Make sure we can with the correct key
3084        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        // Do the same for another file
3089        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        // Make sure we can with the correct key
3098        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        // Unlock the vmgs and verify that the metadata key is consistent
3103        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}