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_FILE_TABLE_BLOCK_SIZE;
23use vmgs_format::VMGS_MIN_FILE_BLOCK_OFFSET;
24use vmgs_format::VMGS_SIGNATURE;
25use vmgs_format::VMGS_VERSION_3_0;
26use vmgs_format::VmgsAuthTag;
27use vmgs_format::VmgsDatastoreKey;
28use vmgs_format::VmgsEncryptionKey;
29use vmgs_format::VmgsExtendedFileEntry;
30use vmgs_format::VmgsExtendedFileTable;
31use vmgs_format::VmgsFileEntry;
32use vmgs_format::VmgsFileTable;
33use vmgs_format::VmgsHeader;
34use vmgs_format::VmgsMarkers;
35use vmgs_format::VmgsNonce;
36use vmgs_format::VmgsProvisioningMarker;
37use vmgs_format::VmgsProvisioningReason;
38use zerocopy::FromBytes;
39use zerocopy::FromZeros;
40use zerocopy::IntoBytes;
41
42/// Operation types for provisioning telemetry.
43#[derive(Debug)]
44enum LogOpType {
45    VmgsProvision,
46}
47
48/// Info about a specific VMGS file.
49#[derive(Debug)]
50#[cfg_attr(feature = "mesh", derive(mesh_protobuf::Protobuf))]
51pub struct VmgsFileInfo {
52    /// Number of bytes allocated in the file.
53    pub allocated_bytes: u64,
54    /// Number of valid bytes in the file.
55    pub valid_bytes: u64,
56    /// Whether this file is encrypted.
57    pub encrypted: bool,
58}
59
60/// GSP types that can be used to encrypt a VMGS file.
61#[derive(Debug, Clone, Copy)]
62pub enum GspType {
63    /// No GSP
64    None,
65    /// GSP by ID
66    GspById,
67    /// GSP key
68    GspKey,
69}
70
71// Aggregates fully validated data from the FILE_TABLE and EXTENDED_FILE_TABLE
72// control blocks.
73#[derive(Clone, PartialEq, Eq, Debug)]
74#[cfg_attr(feature = "inspect", derive(Inspect))]
75struct ResolvedFileControlBlock {
76    // FILE_TABLE data
77    // ---------------
78    block_offset: u32,
79    #[cfg_attr(feature = "inspect", inspect(with = "|x| x.get()"))]
80    allocated_blocks: NonZeroU32,
81    valid_bytes: u64,
82
83    nonce: VmgsNonce,
84    authentication_tag: VmgsAuthTag,
85
86    // EXTENDED_FILE_TABLE data
87    // ---------------
88    attributes: FileAttribute,
89    encryption_key: VmgsDatastoreKey,
90}
91
92impl ResolvedFileControlBlock {
93    fn new(block_offset: u32, block_count: u32, valid_bytes: usize, encrypt: bool) -> Self {
94        let (attributes, encryption_key, nonce) = if encrypt {
95            (
96                FileAttribute::new()
97                    .with_encrypted(true)
98                    .with_authenticated(true),
99                {
100                    let mut encryption_key = VmgsDatastoreKey::new_zeroed();
101                    getrandom::fill(&mut encryption_key).expect("rng failure");
102                    encryption_key
103                },
104                generate_nonce(),
105            )
106        } else {
107            (
108                FileAttribute::new(),
109                VmgsDatastoreKey::new_zeroed(),
110                VmgsNonce::new_zeroed(),
111            )
112        };
113
114        ResolvedFileControlBlock {
115            block_offset,
116            allocated_blocks: NonZeroU32::new(block_count).unwrap(),
117            valid_bytes: valid_bytes as u64,
118
119            nonce,
120            authentication_tag: VmgsAuthTag::new_zeroed(),
121
122            attributes,
123            encryption_key,
124        }
125    }
126
127    fn file_info(&self) -> VmgsFileInfo {
128        VmgsFileInfo {
129            allocated_bytes: block_count_to_byte_count(self.allocated_blocks.get()),
130            valid_bytes: self.valid_bytes,
131            encrypted: self.encrypted(),
132        }
133    }
134
135    fn encrypted(&self) -> bool {
136        self.attributes.encrypted() || self.attributes.authenticated()
137    }
138
139    fn fill_file_entry(&self, version: u32, file_entry: &mut VmgsFileEntry) {
140        file_entry.offset = self.block_offset;
141        file_entry.allocation_size = self.allocated_blocks.get();
142        file_entry.valid_data_size = self.valid_bytes;
143
144        if version >= VMGS_VERSION_3_0 {
145            file_entry.nonce.copy_from_slice(&self.nonce);
146            file_entry
147                .authentication_tag
148                .copy_from_slice(&self.authentication_tag);
149            file_entry.attributes = self.attributes;
150        }
151    }
152
153    fn fill_extended_file_entry(&self, extended_file_entry: &mut VmgsExtendedFileEntry) {
154        extended_file_entry.attributes = self.attributes;
155        extended_file_entry
156            .encryption_key
157            .copy_from_slice(&self.encryption_key);
158    }
159
160    fn from_file_entry(version: u32, file_entry: &VmgsFileEntry) -> Self {
161        let (nonce, authentication_tag, attributes) = if version >= VMGS_VERSION_3_0 {
162            (
163                file_entry.nonce,
164                file_entry.authentication_tag,
165                file_entry.attributes,
166            )
167        } else {
168            Default::default()
169        };
170
171        ResolvedFileControlBlock {
172            block_offset: file_entry.offset,
173            allocated_blocks: NonZeroU32::new(file_entry.allocation_size).unwrap(),
174            valid_bytes: file_entry.valid_data_size,
175
176            nonce,
177            authentication_tag,
178
179            attributes,
180            encryption_key: VmgsDatastoreKey::new_zeroed(),
181        }
182    }
183
184    #[cfg_attr(not(with_encryption), expect(dead_code))]
185    fn update_extended_data(&mut self, extended_file_entry: &VmgsExtendedFileEntry) {
186        self.attributes = extended_file_entry.attributes;
187        self.encryption_key = extended_file_entry.encryption_key;
188    }
189
190    #[cfg_attr(not(with_encryption), expect(unused_variables))]
191    fn encrypt(&mut self, data: &[u8]) -> Result<Vec<u8>, Error> {
192        #[cfg(not(with_encryption))]
193        unreachable!("Encryption requires the encryption feature");
194        #[cfg(with_encryption)]
195        {
196            let encrypted = crate::encrypt::vmgs_encrypt(
197                &self.encryption_key,
198                &self.nonce,
199                data,
200                &mut self.authentication_tag,
201            )?;
202
203            if encrypted.len() as u64 != self.valid_bytes {
204                return Err(Error::UnexpectedLength(
205                    "encrypted data",
206                    self.valid_bytes as usize,
207                    encrypted.len(),
208                ));
209            }
210
211            Ok(encrypted)
212        }
213    }
214
215    #[cfg_attr(not(with_encryption), expect(unused_variables))]
216    fn decrypt(&self, data: &[u8]) -> Result<Vec<u8>, Error> {
217        #[cfg(not(with_encryption))]
218        unreachable!("Encryption requires the encryption feature");
219        #[cfg(with_encryption)]
220        {
221            // sanity check: encrypted data should never be all zeros. if we
222            // find that it is all-zeroes, then that's indicative of some kind
223            // of logic error / data corruption
224            if data.iter().all(|x| *x == 0) {
225                return Err(Error::InvalidFormat("encrypted data is all-zeros".into()));
226            }
227
228            let decrypted = crate::encrypt::vmgs_decrypt(
229                &self.encryption_key,
230                &self.nonce,
231                data,
232                &self.authentication_tag,
233            )?;
234
235            if decrypted.len() as u64 != self.valid_bytes {
236                return Err(Error::UnexpectedLength(
237                    "decrypted data",
238                    self.valid_bytes as usize,
239                    decrypted.len(),
240                ));
241            }
242
243            Ok(decrypted)
244        }
245    }
246
247    fn clear_encryption(&mut self) {
248        self.nonce.zero();
249        self.authentication_tag.zero();
250        self.encryption_key.zero();
251    }
252}
253
254enum RefOrOwned<'a> {
255    Ref(&'a [u8]),
256    Owned(Vec<u8>),
257}
258
259impl<'a> RefOrOwned<'a> {
260    fn placeholder<T>() -> Self {
261        RefOrOwned::Owned(vec![0; size_of::<T>()])
262    }
263
264    fn len(&self) -> usize {
265        match self {
266            RefOrOwned::Ref(x) => x.len(),
267            RefOrOwned::Owned(x) => x.len(),
268        }
269    }
270
271    fn copy_from_slice(&mut self, src: &[u8]) {
272        match self {
273            RefOrOwned::Ref(_) => panic!("cannot modify ref"),
274            RefOrOwned::Owned(x) => x.copy_from_slice(src),
275        }
276    }
277
278    fn get(&self) -> &[u8] {
279        match self {
280            RefOrOwned::Ref(x) => x,
281            RefOrOwned::Owned(x) => x,
282        }
283    }
284
285    fn replace(&mut self, new_value: Self) {
286        assert_eq!(self.len(), new_value.len());
287        *self = new_value;
288    }
289}
290
291struct AllocRequest<'a> {
292    data: RefOrOwned<'a>,
293    encrypt: bool,
294}
295
296impl<'a> AllocRequest<'a> {
297    fn new(data: RefOrOwned<'a>, encrypt: bool) -> Self {
298        Self { data, encrypt }
299    }
300
301    fn allocate(
302        self,
303        allocation_list: &mut Vec<AllocationBlock>,
304        block_capacity: u32,
305    ) -> Result<AllocResult<'a>, Error> {
306        let valid_bytes = self.data.len();
307
308        let mut block_count = (round_up_count(valid_bytes, VMGS_BYTES_PER_BLOCK)
309            / VMGS_BYTES_PER_BLOCK as u64) as u32;
310        // Always allocate at least one block, to allow for zero sized data buffers
311        if block_count == 0 {
312            block_count = 1;
313        }
314        if block_count as u64 > vmgs_format::VMGS_MAX_FILE_SIZE_BLOCKS {
315            return Err(Error::WriteFileBlocks);
316        }
317
318        let block_offset = allocate_helper(allocation_list, block_count, block_capacity)?;
319
320        let fcb =
321            ResolvedFileControlBlock::new(block_offset, block_count, valid_bytes, self.encrypt);
322
323        Ok(AllocResult {
324            fcb,
325            data: self.data,
326        })
327    }
328}
329
330struct AllocResult<'a> {
331    fcb: ResolvedFileControlBlock,
332    data: RefOrOwned<'a>,
333}
334
335impl<'a> AllocResult<'a> {
336    fn encrypt(&mut self) -> Result<(), Error> {
337        self.data
338            .replace(RefOrOwned::Owned(self.fcb.encrypt(self.data.get())?));
339        Ok(())
340    }
341
342    fn encrypt_from(&mut self, data: &[u8]) -> Result<(), Error> {
343        self.data
344            .replace(RefOrOwned::Owned(self.fcb.encrypt(data)?));
345        Ok(())
346    }
347}
348
349/// Implementation of the VMGS file format, backed by a generic [`Disk`]
350/// device.
351#[cfg_attr(feature = "inspect", derive(Inspect))]
352pub struct Vmgs {
353    storage: VmgsStorage,
354
355    #[cfg(feature = "inspect")]
356    stats: vmgs_inspect::VmgsStats,
357
358    state: VmgsState,
359
360    #[cfg_attr(feature = "inspect", inspect(skip))]
361    logger: Option<Arc<dyn VmgsLogger>>,
362}
363
364#[cfg_attr(feature = "inspect", derive(Inspect))]
365#[derive(Clone)]
366struct VmgsState {
367    active_header_index: usize,
368    active_header_sequence_number: u32,
369    version: u32,
370    #[cfg_attr(feature = "inspect", inspect(with = "vmgs_inspect::fcbs"))]
371    fcbs: HashMap<FileId, ResolvedFileControlBlock>,
372    encryption_algorithm: EncryptionAlgorithm,
373    datastore_key_count: u8,
374    active_datastore_key_index: Option<usize>,
375    #[cfg_attr(feature = "inspect", inspect(iter_by_index))]
376    datastore_keys: [VmgsDatastoreKey; 2],
377    /// unused, retained for save-restore backwards compatibility
378    unused_metadata_key: VmgsDatastoreKey,
379    #[cfg_attr(feature = "inspect", inspect(iter_by_index))]
380    encrypted_metadata_keys: [VmgsEncryptionKey; 2],
381    reprovisioned: bool,
382    provisioning_reason: Option<VmgsProvisioningReason>,
383}
384
385#[cfg(feature = "inspect")]
386mod vmgs_inspect {
387    use super::*;
388
389    #[derive(Default)]
390    pub struct IoStat {
391        pub attempt: Counter,
392        pub resolved: Counter,
393    }
394
395    // explicit inspect implementation, since we want to massage the data's
396    // presentation a bit
397    impl Inspect for IoStat {
398        fn inspect(&self, req: inspect::Request<'_>) {
399            let mut resp = req.respond();
400            resp.counter("ok", self.resolved.get())
401                .counter("err", self.attempt.get() - self.resolved.get());
402        }
403    }
404
405    #[derive(Inspect, Default)]
406    pub struct VmgsStats {
407        #[inspect(with = "stat_map")]
408        pub read: HashMap<FileId, IoStat>,
409        #[inspect(with = "stat_map")]
410        pub write: HashMap<FileId, IoStat>,
411    }
412
413    pub(super) fn fcbs(fcbs: &HashMap<FileId, ResolvedFileControlBlock>) -> impl Inspect + '_ {
414        inspect::adhoc(|req| {
415            let mut res = req.respond();
416            for (id, fcb) in fcbs.iter() {
417                res.field(&format!("{}-{:?}", id.0, id), fcb);
418            }
419        })
420    }
421
422    pub fn stat_map(map: &HashMap<FileId, IoStat>) -> impl Inspect + '_ {
423        inspect::iter_by_key(map).map_key(|x| format!("{:?}", x))
424    }
425}
426
427impl Vmgs {
428    /// Attempt to open the VMGS file, optionally formatting if it is
429    /// empty or corrupted.
430    pub async fn try_open(
431        disk: Disk,
432        logger: Option<Arc<dyn VmgsLogger>>,
433        format_on_empty: bool,
434        format_on_failure: bool,
435    ) -> Result<Self, Error> {
436        match Self::open(disk.clone(), logger.clone()).await {
437            Ok(vmgs) => Ok(vmgs),
438            Err(Error::EmptyFile) if format_on_empty => {
439                tracing::info!(CVM_ALLOWED, "empty vmgs file, formatting");
440                Self::format_new_with_reason(disk, VmgsProvisioningReason::Empty, logger).await
441            }
442            Err(err) if format_on_failure => {
443                tracing::warn!(CVM_ALLOWED, ?err, "vmgs initialization error, reformatting");
444                Self::format_new_with_reason(disk, VmgsProvisioningReason::Failure, logger).await
445            }
446            Err(err) => {
447                let event_log_id = match err {
448                    // The data store format is invalid or not supported.
449                    Error::InvalidFormat(_) => VmgsLogEvent::InvalidFormat,
450                    // The data store is corrupted.
451                    Error::CorruptFormat(_) => VmgsLogEvent::CorruptFormat,
452                    // All other errors
453                    _ => VmgsLogEvent::InitFailed,
454                };
455
456                logger.log_event_fatal(event_log_id).await;
457                Err(err)
458            }
459        }
460    }
461
462    /// Open the VMGS file.
463    pub async fn open(disk: Disk, logger: Option<Arc<dyn VmgsLogger>>) -> Result<Self, Error> {
464        tracing::debug!(CVM_ALLOWED, "opening VMGS datastore");
465        let storage = VmgsStorage::new_validated(disk).map_err(Error::Initialization)?;
466        Self::open_inner(storage, logger).await
467    }
468
469    /// Format and open a new VMGS file.
470    pub async fn format_new(
471        disk: Disk,
472        logger: Option<Arc<dyn VmgsLogger>>,
473    ) -> Result<Self, Error> {
474        Self::format_new_with_reason(disk, VmgsProvisioningReason::Request, logger).await
475    }
476
477    /// Format and open a new VMGS file.
478    pub async fn format_new_with_reason(
479        disk: Disk,
480        reason: VmgsProvisioningReason,
481        logger: Option<Arc<dyn VmgsLogger>>,
482    ) -> Result<Self, Error> {
483        tracing::info!(
484            CVM_ALLOWED,
485            op_type = ?LogOpType::VmgsProvision,
486            ?reason,
487            "formatting and initializing VMGS datastore"
488        );
489        let storage = VmgsStorage::new_validated(disk).map_err(Error::Initialization)?;
490        Self::format_new_inner(storage, VMGS_VERSION_3_0, reason, logger).await
491    }
492
493    /// Format and open a new VMGS file.
494    pub async fn request_format(
495        disk: Disk,
496        logger: Option<Arc<dyn VmgsLogger>>,
497    ) -> Result<Self, Error> {
498        let mut storage = VmgsStorage::new_validated(disk).map_err(Error::Initialization)?;
499
500        match Self::open_header(&mut storage).await {
501            Ok((active_header, active_header_index)) if active_header.markers.reprovisioned() => {
502                tracing::info!(CVM_ALLOWED, "reprovisioned marker found, skipping format");
503                Self::finish_open(storage, active_header, active_header_index, logger).await
504            }
505            _ => {
506                tracing::info!(CVM_ALLOWED, "formatting vmgs file on request");
507                let mut vmgs = Vmgs::format_new_inner(
508                    storage,
509                    VMGS_VERSION_3_0,
510                    VmgsProvisioningReason::Request,
511                    logger,
512                )
513                .await?;
514
515                // set the reprovisioned marker to prevent the vmgs from
516                // repeatedly being reset
517                vmgs.set_reprovisioned(true).await?;
518
519                Ok(vmgs)
520            }
521        }
522    }
523
524    async fn open_inner(
525        mut storage: VmgsStorage,
526        logger: Option<Arc<dyn VmgsLogger>>,
527    ) -> Result<Self, Error> {
528        let (active_header, active_header_index) = Self::open_header(&mut storage).await?;
529
530        let mut vmgs =
531            Self::finish_open(storage, active_header, active_header_index, logger).await?;
532
533        // clear the reprovisioned marker after successfully opening the vmgs
534        // without being requested to reprovision.
535        vmgs.set_reprovisioned(false).await?;
536
537        Ok(vmgs)
538    }
539
540    async fn open_header(storage: &mut VmgsStorage) -> Result<(VmgsHeader, usize), Error> {
541        let (header_1, header_2) = read_headers_inner(storage).await.map_err(|(e, _)| e)?;
542
543        let active_header_index =
544            get_active_header(validate_header(&header_1), validate_header(&header_2))?;
545
546        let active_header = if active_header_index == 0 {
547            header_1
548        } else {
549            header_2
550        };
551
552        Ok((active_header, active_header_index))
553    }
554
555    async fn finish_open(
556        storage: VmgsStorage,
557        active_header: VmgsHeader,
558        active_header_index: usize,
559        logger: Option<Arc<dyn VmgsLogger>>,
560    ) -> Result<Vmgs, Error> {
561        let mut vmgs = Self {
562            storage,
563
564            state: VmgsState::from_header(active_header, active_header_index),
565
566            #[cfg(feature = "inspect")]
567            stats: Default::default(),
568
569            logger,
570        };
571
572        let file_table_buffer = vmgs
573            .read_file_internal(FileId::FILE_TABLE, false, None)
574            .await?;
575        vmgs.state.fcbs = initialize_file_metadata(
576            VmgsFileTable::ref_from_bytes(&file_table_buffer)
577                .map_err(|_| Error::InvalidFormat("incorrect file table size".into()))?,
578            vmgs.state.version,
579            vmgs.storage.block_capacity(),
580        )?;
581
582        Ok(vmgs)
583    }
584
585    fn new(
586        storage: VmgsStorage,
587        version: u32,
588        reason: VmgsProvisioningReason,
589        logger: Option<Arc<dyn VmgsLogger>>,
590    ) -> Vmgs {
591        Self {
592            storage,
593
594            state: VmgsState::new(version, Some(reason)),
595
596            #[cfg(feature = "inspect")]
597            stats: Default::default(),
598
599            logger,
600        }
601    }
602
603    /// Formats the backing store with initial metadata, and sets active header.
604    async fn format_new_inner(
605        storage: VmgsStorage,
606        version: u32,
607        reason: VmgsProvisioningReason,
608        logger: Option<Arc<dyn VmgsLogger>>,
609    ) -> Result<Vmgs, Error> {
610        tracing::info!(CVM_ALLOWED, "Formatting new VMGS file.");
611
612        let mut vmgs = Self::new(storage, version, reason, logger);
613
614        // zero out the active header, the other one will be populated below
615        vmgs.write_header_internal(&VmgsHeader::new_zeroed(), vmgs.state.active_header_index)
616            .await?;
617
618        // write a blank, unencrypted file table for consistency with old impls
619        let files = if version >= VMGS_VERSION_3_0 {
620            [(
621                FileId::EXTENDED_FILE_TABLE,
622                AllocRequest::new(RefOrOwned::placeholder::<VmgsExtendedFileTable>(), false),
623            )]
624            .into()
625        } else {
626            BTreeMap::new()
627        };
628
629        // write a blank file table
630        vmgs.write_files_internal(files, None).await?;
631
632        // write the active header
633        let (new_header, index) = vmgs.state.make_header();
634        vmgs.write_header_internal(&new_header, index).await?;
635
636        // Flush the device to persist changes
637        vmgs.storage.flush().await.map_err(Error::FlushDisk)?;
638
639        Ok(vmgs)
640    }
641
642    /// Get allocated and valid bytes from File Control Block for file_id.
643    ///
644    /// When reading data from a file, the buffer must be at least `valid_bytes` long.
645    pub fn get_file_info(&self, file_id: FileId) -> Result<VmgsFileInfo, Error> {
646        Ok(self
647            .state
648            .fcbs
649            .get(&file_id)
650            .ok_or(Error::FileInfoNotAllocated(file_id))?
651            .file_info())
652    }
653
654    /// Returns whether a file id is allocated
655    pub fn check_file_allocated(&self, file_id: FileId) -> bool {
656        self.state.fcbs.contains_key(&file_id)
657    }
658
659    /// Get info about all the files currently in the file table
660    pub fn dump_file_table(&self) -> Vec<(FileId, VmgsFileInfo)> {
661        let mut file_table = self
662            .state
663            .fcbs
664            .iter()
665            .map(|(file_id, fcb)| (*file_id, fcb.file_info()))
666            .collect::<Vec<_>>();
667        file_table.sort_by_key(|(file_id, _)| *file_id);
668        file_table
669    }
670
671    /// Writes `buf` to a file_id, optionally encrypting or overwriting
672    /// encrypted data with plaintext. Updates file tables as appropriate.
673    async fn write_file_inner(
674        &mut self,
675        file_id: FileId,
676        buf: &[u8],
677        encrypt: bool,
678        overwrite_encrypted: bool,
679    ) -> Result<(), Error> {
680        #[cfg(feature = "inspect")]
681        self.stats
682            .write
683            .entry(file_id)
684            .or_default()
685            .attempt
686            .increment();
687
688        if matches!(file_id, FileId::FILE_TABLE | FileId::EXTENDED_FILE_TABLE) {
689            return Err(Error::FileId);
690        }
691        if buf.len() > vmgs_format::VMGS_MAX_FILE_SIZE_BYTES as usize {
692            return Err(Error::WriteFileLength);
693        }
694
695        let mut temp_state = self.temp_state();
696
697        if encrypt && !temp_state.encrypted_and_unlocked() {
698            tracing::trace!(
699                CVM_ALLOWED,
700                "VMGS file not encrypted and unlocked, performing plaintext write"
701            );
702        }
703
704        let encrypt = encrypt && temp_state.encrypted_and_unlocked();
705        let existing_encrypted = temp_state
706            .fcbs
707            .get(&file_id)
708            .is_some_and(|fcb| fcb.encrypted());
709
710        if !encrypt && existing_encrypted {
711            if overwrite_encrypted {
712                tracing::warn!(
713                    CVM_ALLOWED,
714                    "overwriting encrypted file with plaintext data!"
715                )
716            } else {
717                return Err(Error::OverwriteEncrypted);
718            }
719        }
720
721        self.write_files_internal(
722            [(file_id, AllocRequest::new(RefOrOwned::Ref(buf), encrypt))].into(),
723            Some(&mut temp_state),
724        )
725        .await?;
726
727        // Update the header
728        self.write_header_and_apply(temp_state).await?;
729
730        #[cfg(feature = "inspect")]
731        self.stats
732            .write
733            .entry(file_id)
734            .or_default()
735            .resolved
736            .increment();
737
738        Ok(())
739    }
740
741    /// Write a set of files and any necessary file tables
742    async fn write_files_internal<'a>(
743        &mut self,
744        // using a BTreeMap here so that the allocations are predictable
745        mut files: BTreeMap<FileId, AllocRequest<'a>>,
746        temp_state: Option<&mut VmgsState>,
747    ) -> Result<(), Error> {
748        let state = temp_state.unwrap_or(&mut self.state);
749
750        // ensure the necessary file tables are in the allocation list
751        files.insert(
752            FileId::FILE_TABLE,
753            AllocRequest::new(RefOrOwned::placeholder::<VmgsFileTable>(), false),
754        );
755        if state.encrypted_and_unlocked() {
756            files.insert(
757                FileId::EXTENDED_FILE_TABLE,
758                AllocRequest::new(RefOrOwned::placeholder::<VmgsExtendedFileTable>(), true),
759            );
760        }
761
762        // allocate space for the files
763        let mut files = state.allocate_space(files, self.storage.block_capacity())?;
764
765        // encrypt anything that needs to be encrypted except the extended file
766        // table, which hasn't been generated yet.
767        for (file_id, res) in files.iter_mut() {
768            if *file_id != FileId::EXTENDED_FILE_TABLE {
769                if res.fcb.encrypted() {
770                    res.encrypt()?;
771                }
772                state.fcbs.insert(*file_id, res.fcb.clone());
773            }
774        }
775
776        // generate and encrypt the extended file table
777        if let Some(res) = files.get_mut(&FileId::EXTENDED_FILE_TABLE) {
778            if state.encrypted_and_unlocked() {
779                // clear encryption key so we don't try to decrypt with the old key
780                res.fcb.clear_encryption();
781                let new_extended_file_table = state.make_extended_file_table()?;
782                res.encrypt_from(new_extended_file_table.as_bytes())?;
783            }
784            // add the blank table if specified, even if not encrypted
785            state
786                .fcbs
787                .insert(FileId::EXTENDED_FILE_TABLE, res.fcb.clone());
788            if state.encrypted_and_unlocked() {
789                state.encrypt_metadata_key()?;
790            }
791        }
792
793        // generate the file table now that all of the nonces and auth tags
794        // are in the temporary fcbs
795        let new_file_table = state.make_file_table()?;
796        files
797            .get_mut(&FileId::FILE_TABLE)
798            .unwrap()
799            .data
800            .copy_from_slice(new_file_table.as_bytes());
801
802        // write the files
803        for (_, res) in files.iter() {
804            self.write_file_internal(&res.fcb, res.data.get()).await?;
805        }
806
807        Ok(())
808    }
809
810    /// Writes `buf` to the block offset specified in the file control block.
811    /// Encrypts the data and returns the auth tag if applicable.
812    async fn write_file_internal(
813        &mut self,
814        fcb: &ResolvedFileControlBlock,
815        buf: &[u8],
816    ) -> Result<(), Error> {
817        if let Err(e) = self
818            .storage
819            .write_block(block_count_to_byte_count(fcb.block_offset), buf)
820            .await
821        {
822            self.logger
823                .log_event_fatal(VmgsLogEvent::AccessFailed)
824                .await;
825
826            return Err(Error::WriteDisk(e));
827        }
828
829        Ok(())
830    }
831
832    /// write the new header to disk.
833    async fn write_header_internal(
834        &mut self,
835        header: &VmgsHeader,
836        index: usize,
837    ) -> Result<(), Error> {
838        assert!(index < 2);
839        self.storage
840            .write_block(
841                index as u64 * self.storage.aligned_header_size(),
842                header.as_bytes(),
843            )
844            .await
845            .map_err(Error::WriteDisk)?;
846        Ok(())
847    }
848
849    /// Reads the specified `file_id`, decrypting its contents.
850    pub async fn read_file(&mut self, file_id: FileId) -> Result<Vec<u8>, Error> {
851        self.read_file_inner(file_id, true).await
852    }
853
854    /// Reads the specified `file_id`, but does not decrypt the contents.
855    pub async fn read_file_raw(&mut self, file_id: FileId) -> Result<Vec<u8>, Error> {
856        self.read_file_inner(file_id, false).await
857    }
858
859    /// User-facing file read
860    async fn read_file_inner(&mut self, file_id: FileId, decrypt: bool) -> Result<Vec<u8>, Error> {
861        #[cfg(feature = "inspect")]
862        self.stats
863            .read
864            .entry(file_id)
865            .or_default()
866            .attempt
867            .increment();
868
869        if matches!(file_id, FileId::FILE_TABLE | FileId::EXTENDED_FILE_TABLE) {
870            return Err(Error::FileId);
871        }
872
873        let buf = self.read_file_internal(file_id, decrypt, None).await?;
874
875        #[cfg(feature = "inspect")]
876        self.stats
877            .read
878            .entry(file_id)
879            .or_default()
880            .resolved
881            .increment();
882
883        Ok(buf)
884    }
885
886    /// read a file_id, decrypting if requested and possible
887    async fn read_file_internal(
888        &mut self,
889        file_id: FileId,
890        decrypt: bool,
891        temp_state: Option<&VmgsState>,
892    ) -> Result<Vec<u8>, Error> {
893        let state = temp_state.unwrap_or(&self.state);
894
895        let fcb = state
896            .fcbs
897            .get(&file_id)
898            .ok_or(Error::FileInfoNotAllocated(file_id))?;
899
900        // read the file
901        let buf = {
902            let mut buf = vec![0; fcb.valid_bytes as usize];
903
904            if let Err(e) = self
905                .storage
906                .read_block(block_count_to_byte_count(fcb.block_offset), &mut buf)
907                .await
908            {
909                self.logger
910                    .log_event_fatal(VmgsLogEvent::AccessFailed)
911                    .await;
912
913                return Err(Error::ReadDisk(e));
914            }
915
916            buf
917        };
918
919        // decrypt if necessary
920        if decrypt
921            && state.version >= VMGS_VERSION_3_0
922            && state.encrypted_and_unlocked()
923            && fcb.encrypted()
924        {
925            match fcb.decrypt(&buf) {
926                Err(e) => {
927                    self.logger
928                        .log_event_fatal(VmgsLogEvent::AccessFailed)
929                        .await;
930
931                    Err(e)
932                }
933                Ok(b) => Ok(b),
934            }
935        } else if fcb.encrypted() && decrypt {
936            Err(Error::NeedsUnlock)
937        } else {
938            Ok(buf)
939        }
940    }
941
942    /// Writes `buf` to a file_id without encrypting it.
943    ///
944    /// If the file is already encrypted, this will return a failure. Use
945    /// [`Self::write_file_allow_overwrite_encrypted`] if you want to allow
946    /// this.
947    ///
948    /// To write encrypted data, use `write_file_encrypted` instead.
949    pub async fn write_file(&mut self, file_id: FileId, buf: &[u8]) -> Result<(), Error> {
950        self.write_file_inner(file_id, buf, false, false).await
951    }
952
953    /// Writes `buf` to a file_id without encrypting it, allowing overwrites of
954    /// an already-encrypted file.
955    pub async fn write_file_allow_overwrite_encrypted(
956        &mut self,
957        file_id: FileId,
958        buf: &[u8],
959    ) -> Result<(), Error> {
960        self.write_file_inner(file_id, buf, false, true).await
961    }
962
963    /// Encrypts `buf` and writes the encrypted payload to a file_id if the VMGS file has encryption configured.
964    /// If the VMGS doesn't have encryption configured, will do a plaintext write instead.
965    #[cfg(with_encryption)]
966    pub async fn write_file_encrypted(&mut self, file_id: FileId, buf: &[u8]) -> Result<(), Error> {
967        self.write_file_inner(file_id, buf, true, true).await
968    }
969
970    /// Move a file to a new file_id
971    pub async fn move_file(
972        &mut self,
973        src: FileId,
974        dst: FileId,
975        allow_overwrite: bool,
976    ) -> Result<(), Error> {
977        if [src, dst]
978            .iter()
979            .any(|id| matches!(*id, FileId::FILE_TABLE | FileId::EXTENDED_FILE_TABLE))
980        {
981            return Err(Error::FileId);
982        }
983
984        if !allow_overwrite && self.state.fcbs.contains_key(&dst) {
985            return Err(Error::OverwriteMove);
986        }
987
988        let mut temp_state = self.temp_state();
989
990        // move the fcb to a different file id
991        let fcb = temp_state
992            .fcbs
993            .remove(&src)
994            .ok_or(Error::FileInfoNotAllocated(src))?;
995        temp_state.fcbs.insert(dst, fcb);
996
997        // write the new file table(s)
998        self.write_files_internal(BTreeMap::new(), Some(&mut temp_state))
999            .await?;
1000
1001        // Update the header
1002        self.write_header_and_apply(temp_state).await?;
1003
1004        Ok(())
1005    }
1006
1007    /// Delete a file
1008    pub async fn delete_file(&mut self, file_id: FileId) -> Result<(), Error> {
1009        if matches!(file_id, FileId::FILE_TABLE | FileId::EXTENDED_FILE_TABLE) {
1010            return Err(Error::FileId);
1011        }
1012
1013        let mut temp_state = self.temp_state();
1014
1015        // delete the fcb
1016        temp_state
1017            .fcbs
1018            .remove(&file_id)
1019            .ok_or(Error::FileInfoNotAllocated(file_id))?;
1020
1021        // write the new file table(s)
1022        self.write_files_internal(BTreeMap::new(), Some(&mut temp_state))
1023            .await?;
1024
1025        // Update the header
1026        self.write_header_and_apply(temp_state).await?;
1027
1028        Ok(())
1029    }
1030
1031    /// Decrypts the extended file table by the encryption_key and
1032    /// updates the related metadata in memory.
1033    #[cfg(with_encryption)]
1034    pub async fn unlock_with_encryption_key(&mut self, encryption_key: &[u8]) -> Result<(), Error> {
1035        if self.state.version < VMGS_VERSION_3_0 {
1036            return Err(Error::EncryptionNotSupported);
1037        }
1038        if !self.encrypted() {
1039            return Err(Error::NotEncrypted);
1040        }
1041
1042        let mut temp_state = self.temp_state();
1043
1044        // Iterate through two metadata keys and get the index of the valid key which can be successfully
1045        // decrypted by the encryption_key, as well as set the decrypted key as the VMGS's metadata key
1046        let mut valid_index_and_key = None;
1047        let mut errs = [None, None];
1048
1049        for (i, key) in temp_state.encrypted_metadata_keys.iter().enumerate() {
1050            let result = decrypt_metadata_key(
1051                encryption_key,
1052                &key.nonce,
1053                &key.encryption_key,
1054                &key.authentication_tag,
1055            );
1056
1057            match result {
1058                Ok(metadata_key) => {
1059                    valid_index_and_key = Some((i, metadata_key));
1060                    break;
1061                }
1062                Err(err) => {
1063                    errs[i] = Some(err);
1064                }
1065            }
1066        }
1067
1068        match valid_index_and_key {
1069            Some((i, metadata_key)) => {
1070                let fcb = temp_state
1071                    .fcbs
1072                    .get_mut(&FileId::EXTENDED_FILE_TABLE)
1073                    .ok_or(Error::FileInfoNotAllocated(FileId::EXTENDED_FILE_TABLE))?;
1074                // older implementations didn't write unencrypted attributes
1075                // so configure them here so that the table is decrypted below
1076                fcb.attributes.set_encrypted(true);
1077                fcb.attributes.set_authenticated(true);
1078                fcb.encryption_key.copy_from_slice(&metadata_key);
1079                temp_state.datastore_keys[i].copy_from_slice(encryption_key);
1080                temp_state.active_datastore_key_index = Some(i);
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(with_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(with_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(with_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(with_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(with_encryption), expect(unused_variables))]
1737fn encrypt_metadata_key(
1738    encryption_key: &[u8],
1739    nonce: &[u8],
1740    metadata_key: &[u8],
1741    authentication_tag: &mut [u8],
1742) -> Result<Vec<u8>, Error> {
1743    #[cfg(not(with_encryption))]
1744    unreachable!("Encryption requires the encryption feature");
1745    #[cfg(with_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(not(with_encryption), expect(unused_variables), expect(dead_code))]
1763fn decrypt_metadata_key(
1764    datastore_key: &[u8],
1765    nonce: &[u8],
1766    metadata_key: &[u8],
1767    authentication_tag: &[u8],
1768) -> Result<Vec<u8>, Error> {
1769    #[cfg(not(with_encryption))]
1770    unreachable!("Encryption requires the encryption feature");
1771    #[cfg(with_encryption)]
1772    {
1773        let decrypted_metadata_key =
1774            crate::encrypt::vmgs_decrypt(datastore_key, nonce, metadata_key, authentication_tag)?;
1775        if decrypted_metadata_key.len() != metadata_key.len() {
1776            return Err(Error::UnexpectedLength(
1777                "decrypted metadata key",
1778                metadata_key.len(),
1779                decrypted_metadata_key.len(),
1780            ));
1781        }
1782
1783        Ok(decrypted_metadata_key)
1784    }
1785}
1786
1787/// Computes the cr32 checksum for a given byte stream.
1788fn compute_crc32(buf: &[u8]) -> u32 {
1789    let mut hasher = crc32fast::Hasher::new();
1790    hasher.update(buf);
1791    hasher.finalize()
1792}
1793
1794struct AllocationBlock {
1795    block_offset: u32,
1796    allocated_blocks: u32,
1797}
1798
1799/// maps out the used/unused space in the file and finds the smallest
1800/// unused space to allocate new data.
1801fn allocate_helper(
1802    allocation_list: &mut Vec<AllocationBlock>,
1803    block_count: u32,
1804    block_capacity: u32,
1805) -> Result<u32, Error> {
1806    // sort by block offset
1807    allocation_list.sort_by_key(|a| a.block_offset);
1808
1809    let mut best_offset = 0;
1810    let mut best_free_count = 0;
1811    let mut last_allocation_end_offset = VMGS_MIN_FILE_BLOCK_OFFSET;
1812    let mut found = false;
1813
1814    // find smallest set of blocks that will fit the data we're allocating
1815    for fcb in allocation_list.iter() {
1816        if fcb.block_offset < last_allocation_end_offset {
1817            return Err(Error::AllocateOffset);
1818        }
1819        let free_count = fcb.block_offset - last_allocation_end_offset;
1820        if free_count >= block_count && (best_free_count == 0 || free_count < best_free_count) {
1821            best_free_count = free_count;
1822            best_offset = last_allocation_end_offset;
1823            found = true;
1824        }
1825        last_allocation_end_offset = fcb.block_offset + fcb.allocated_blocks;
1826    }
1827    if last_allocation_end_offset < block_capacity {
1828        let free_count = block_capacity - last_allocation_end_offset;
1829        if free_count >= block_count && (best_free_count == 0 || free_count < best_free_count) {
1830            best_offset = last_allocation_end_offset;
1831            found = true;
1832        }
1833    }
1834    if !found {
1835        return Err(Error::InsufficientResources);
1836    }
1837
1838    allocation_list.push(AllocationBlock {
1839        block_offset: best_offset,
1840        allocated_blocks: block_count,
1841    });
1842    Ok(best_offset)
1843}
1844
1845#[cfg(feature = "save_restore")]
1846#[expect(missing_docs)]
1847pub mod save_restore {
1848    use super::*;
1849
1850    pub mod state {
1851        use mesh_protobuf::Protobuf;
1852        use std::num::NonZeroU32;
1853
1854        pub type SavedVmgsNonce = [u8; 12];
1855        pub type SavedVmgsAuthTag = [u8; 16];
1856        pub type SavedVmgsDatastoreKey = [u8; 32];
1857
1858        #[derive(Protobuf)]
1859        #[mesh(package = "vmgs")]
1860        pub struct SavedResolvedFileControlBlock {
1861            #[mesh(1)]
1862            pub block_offset: u32,
1863            #[mesh(2)]
1864            pub allocated_blocks: NonZeroU32,
1865            #[mesh(3)]
1866            pub valid_bytes: u64,
1867            #[mesh(4)]
1868            pub nonce: SavedVmgsNonce,
1869            #[mesh(5)]
1870            pub authentication_tag: SavedVmgsAuthTag,
1871            #[mesh(6)]
1872            pub attributes: u32,
1873            #[mesh(7)]
1874            pub encryption_key: SavedVmgsDatastoreKey,
1875        }
1876
1877        #[derive(Protobuf)]
1878        #[mesh(package = "vmgs")]
1879        pub struct SavedVmgsEncryptionKey {
1880            #[mesh(1)]
1881            pub nonce: SavedVmgsNonce,
1882            #[mesh(2)]
1883            pub authentication_tag: SavedVmgsAuthTag,
1884            #[mesh(3)]
1885            pub encryption_key: SavedVmgsDatastoreKey,
1886        }
1887
1888        #[derive(Protobuf)]
1889        #[mesh(package = "vmgs")]
1890        pub struct SavedVmgsState {
1891            #[mesh(1)]
1892            pub active_header_index: usize,
1893            #[mesh(2)]
1894            pub active_header_sequence_number: u32,
1895            #[mesh(3)]
1896            pub version: u32,
1897            #[mesh(4)]
1898            pub fcbs: Vec<(u32, SavedResolvedFileControlBlock)>,
1899            #[mesh(5)]
1900            pub encryption_algorithm: u16,
1901            #[mesh(6)]
1902            pub datastore_key_count: u8,
1903            #[mesh(7)]
1904            pub active_datastore_key_index: Option<usize>,
1905            #[mesh(8)]
1906            pub datastore_keys: [SavedVmgsDatastoreKey; 2],
1907            #[mesh(9)]
1908            pub metadata_key: SavedVmgsDatastoreKey,
1909            #[mesh(10)]
1910            pub encrypted_metadata_keys: [SavedVmgsEncryptionKey; 2],
1911            #[mesh(11)]
1912            pub reprovisioned: bool,
1913        }
1914    }
1915
1916    impl Vmgs {
1917        /// Construct a [`Vmgs`] instance, re-using existing saved-state from an
1918        /// earlier instance.
1919        ///
1920        /// # Safety
1921        ///
1922        /// `open_from_saved` does NOT perform ANY validation on the provided
1923        /// `state`, and will blindly assume that it matches the underlying
1924        /// `storage` instance!
1925        ///
1926        /// Callers MUST ensure that the provided `state` matches the provided
1927        /// `storage`, and that no external entities have modified `storage` between
1928        /// the call to `save` and `open_from_saved`.
1929        ///
1930        /// Failing to do so may result in data corruption/loss, read/write
1931        /// failures, encryption errors, etc... (though, notably: it will _not_
1932        /// result in any memory-unsafety, hence why the function isn't marked
1933        /// `unsafe`).
1934        pub fn open_from_saved(
1935            disk: Disk,
1936            state: state::SavedVmgsState,
1937            logger: Option<Arc<dyn VmgsLogger>>,
1938        ) -> Self {
1939            let state::SavedVmgsState {
1940                active_header_index,
1941                active_header_sequence_number,
1942                version,
1943                fcbs,
1944                encryption_algorithm,
1945                datastore_key_count,
1946                active_datastore_key_index,
1947                datastore_keys,
1948                metadata_key,
1949                encrypted_metadata_keys,
1950                reprovisioned,
1951            } = state;
1952
1953            Self {
1954                storage: VmgsStorage::new(disk),
1955                #[cfg(feature = "inspect")]
1956                stats: Default::default(),
1957
1958                state: VmgsState {
1959                    active_header_index,
1960                    active_header_sequence_number,
1961                    version,
1962                    fcbs: fcbs
1963                        .into_iter()
1964                        .map(|(file_id, fcb)| {
1965                            let state::SavedResolvedFileControlBlock {
1966                                block_offset,
1967                                allocated_blocks,
1968                                valid_bytes,
1969                                nonce,
1970                                authentication_tag,
1971                                attributes,
1972                                encryption_key,
1973                            } = fcb;
1974
1975                            (
1976                                FileId(file_id),
1977                                ResolvedFileControlBlock {
1978                                    block_offset,
1979                                    allocated_blocks,
1980                                    valid_bytes,
1981                                    nonce,
1982                                    authentication_tag,
1983                                    attributes: FileAttribute::from(attributes),
1984                                    encryption_key,
1985                                },
1986                            )
1987                        })
1988                        .collect(),
1989                    encryption_algorithm: EncryptionAlgorithm(encryption_algorithm),
1990                    datastore_key_count,
1991                    active_datastore_key_index,
1992                    datastore_keys,
1993                    unused_metadata_key: metadata_key,
1994                    encrypted_metadata_keys: encrypted_metadata_keys.map(|k| {
1995                        let state::SavedVmgsEncryptionKey {
1996                            nonce,
1997                            authentication_tag,
1998                            encryption_key,
1999                        } = k;
2000
2001                        VmgsEncryptionKey {
2002                            nonce,
2003                            reserved: 0,
2004                            authentication_tag,
2005                            encryption_key,
2006                        }
2007                    }),
2008                    reprovisioned,
2009                    provisioning_reason: None,
2010                },
2011
2012                logger,
2013            }
2014        }
2015
2016        /// Save the in-memory Vmgs file metadata.
2017        ///
2018        /// This saved state can be used alongside `open_from_saved` to obtain a
2019        /// new `Vmgs` instance _without_ needing to invoke any IOs on the
2020        /// underlying storage.
2021        pub fn save(&self) -> state::SavedVmgsState {
2022            let Self {
2023                storage: _,
2024
2025                #[cfg(feature = "inspect")]
2026                    stats: _,
2027
2028                state:
2029                    VmgsState {
2030                        active_header_index,
2031                        active_header_sequence_number,
2032                        version,
2033                        fcbs,
2034                        encryption_algorithm,
2035                        datastore_key_count,
2036                        active_datastore_key_index,
2037                        datastore_keys,
2038                        unused_metadata_key: metadata_key,
2039                        encrypted_metadata_keys,
2040                        reprovisioned,
2041                        provisioning_reason: _,
2042                    },
2043
2044                logger: _,
2045            } = self;
2046
2047            state::SavedVmgsState {
2048                active_header_index: *active_header_index,
2049                active_header_sequence_number: *active_header_sequence_number,
2050                version: *version,
2051                fcbs: fcbs
2052                    .iter()
2053                    .map(|(file_id, fcb)| {
2054                        let ResolvedFileControlBlock {
2055                            block_offset,
2056                            allocated_blocks,
2057                            valid_bytes,
2058                            nonce,
2059                            authentication_tag,
2060                            attributes,
2061                            encryption_key,
2062                        } = fcb;
2063
2064                        (
2065                            file_id.0,
2066                            state::SavedResolvedFileControlBlock {
2067                                block_offset: *block_offset,
2068                                allocated_blocks: *allocated_blocks,
2069                                valid_bytes: *valid_bytes,
2070                                nonce: *nonce,
2071                                authentication_tag: *authentication_tag,
2072                                attributes: (*attributes).into(),
2073                                encryption_key: *encryption_key,
2074                            },
2075                        )
2076                    })
2077                    .collect(),
2078                encryption_algorithm: encryption_algorithm.0,
2079                datastore_key_count: *datastore_key_count,
2080                active_datastore_key_index: *active_datastore_key_index,
2081                datastore_keys: *datastore_keys,
2082                metadata_key: *metadata_key,
2083                encrypted_metadata_keys: std::array::from_fn(|i| {
2084                    let VmgsEncryptionKey {
2085                        nonce,
2086                        reserved: _,
2087                        authentication_tag,
2088                        encryption_key,
2089                    } = encrypted_metadata_keys[i];
2090
2091                    state::SavedVmgsEncryptionKey {
2092                        nonce,
2093                        authentication_tag,
2094                        encryption_key,
2095                    }
2096                }),
2097                reprovisioned: *reprovisioned,
2098            }
2099        }
2100    }
2101}
2102
2103#[cfg(test)]
2104mod tests {
2105    use super::*;
2106    use pal_async::async_test;
2107    use parking_lot::Mutex;
2108    use std::sync::Arc;
2109    #[cfg(with_encryption)]
2110    use vmgs_format::VMGS_ENCRYPTION_KEY_SIZE;
2111    use vmgs_format::VmgsProvisioner;
2112
2113    const ONE_MEGA_BYTE: u64 = 1024 * 1024;
2114
2115    struct TestVmgsLogger {
2116        data: Arc<Mutex<String>>,
2117    }
2118
2119    #[async_trait::async_trait]
2120    impl VmgsLogger for TestVmgsLogger {
2121        async fn log_event_fatal(&self, _event: VmgsLogEvent) {
2122            let mut data = self.data.lock();
2123            *data = "test logger".to_string();
2124        }
2125    }
2126
2127    fn new_test_file() -> Disk {
2128        disklayer_ram::ram_disk(4 * ONE_MEGA_BYTE, false).unwrap()
2129    }
2130
2131    #[async_test]
2132    async fn empty_vmgs() {
2133        let disk = new_test_file();
2134
2135        let result = Vmgs::open(disk, None).await;
2136        assert!(matches!(result, Err(Error::EmptyFile)));
2137    }
2138
2139    #[async_test]
2140    async fn format_empty_vmgs() {
2141        let disk = new_test_file();
2142        let result = Vmgs::format_new(disk, None).await;
2143        assert!(result.is_ok());
2144    }
2145
2146    #[async_test]
2147    async fn basic_read_write() {
2148        let disk = new_test_file();
2149        let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2150        assert_eq!(vmgs.state.active_header_index, 0);
2151        assert_eq!(vmgs.state.active_header_sequence_number, 1);
2152        assert_eq!(vmgs.state.version, VMGS_VERSION_3_0);
2153
2154        // write
2155        let buf = b"hello world";
2156        vmgs.write_file(FileId::BIOS_NVRAM, buf).await.unwrap();
2157
2158        assert_eq!(vmgs.state.active_header_index, 1);
2159        assert_eq!(vmgs.state.active_header_sequence_number, 2);
2160
2161        // read
2162        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2163
2164        assert_eq!(buf, &*read_buf);
2165        assert_eq!(vmgs.state.active_header_index, 1);
2166        assert_eq!(vmgs.state.active_header_sequence_number, 2);
2167    }
2168
2169    #[async_test]
2170    async fn basic_read_write_large() {
2171        let disk = new_test_file();
2172        let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2173
2174        // write
2175        let buf: Vec<u8> = (0..).map(|x| x as u8).take(1024 * 4 + 1).collect();
2176
2177        vmgs.write_file(FileId::BIOS_NVRAM, &buf).await.unwrap();
2178
2179        assert_eq!(vmgs.state.active_header_index, 1);
2180        assert_eq!(vmgs.state.active_header_sequence_number, 2);
2181
2182        // read
2183        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2184
2185        assert_eq!(buf, read_buf);
2186        assert_eq!(vmgs.state.active_header_index, 1);
2187        assert_eq!(vmgs.state.active_header_sequence_number, 2);
2188
2189        // write
2190        let buf: Vec<u8> = (0..).map(|x| x as u8).take(1024 * 4 * 4 + 1).collect();
2191
2192        vmgs.write_file(FileId::TPM_PPI, &buf).await.unwrap();
2193
2194        assert_eq!(vmgs.state.active_header_index, 0);
2195        assert_eq!(vmgs.state.active_header_sequence_number, 3);
2196
2197        // read
2198        let read_buf = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2199
2200        assert_eq!(buf, read_buf);
2201        assert_eq!(vmgs.state.active_header_index, 0);
2202        assert_eq!(vmgs.state.active_header_sequence_number, 3);
2203
2204        // write
2205        let buf: Vec<u8> = (0..).map(|x| x as u8).take(1024 * 4 * 4 * 4 + 1).collect();
2206
2207        vmgs.write_file(FileId::GUEST_FIRMWARE, &buf).await.unwrap();
2208
2209        assert_eq!(vmgs.state.active_header_index, 1);
2210        assert_eq!(vmgs.state.active_header_sequence_number, 4);
2211
2212        // read
2213        let read_buf = vmgs.read_file(FileId::GUEST_FIRMWARE).await.unwrap();
2214
2215        assert_eq!(buf, read_buf);
2216        assert_eq!(vmgs.state.active_header_index, 1);
2217        assert_eq!(vmgs.state.active_header_sequence_number, 4);
2218    }
2219
2220    #[async_test]
2221    async fn move_delete() {
2222        let disk = new_test_file();
2223        let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2224
2225        // write
2226        let buf = b"hello world";
2227        vmgs.write_file(FileId::TPM_NVRAM, buf).await.unwrap();
2228
2229        // read
2230        let read_buf = vmgs.read_file(FileId::TPM_NVRAM).await.unwrap();
2231        assert_eq!(buf, &*read_buf);
2232
2233        // move
2234        vmgs.move_file(FileId::TPM_NVRAM, FileId::ATTEST, false)
2235            .await
2236            .unwrap();
2237        vmgs.read_file(FileId::TPM_NVRAM).await.unwrap_err();
2238        let read_buf = vmgs.read_file(FileId::ATTEST).await.unwrap();
2239        assert_eq!(buf, &*read_buf);
2240
2241        // delete
2242        vmgs.delete_file(FileId::ATTEST).await.unwrap();
2243        vmgs.read_file(FileId::ATTEST).await.unwrap_err();
2244    }
2245
2246    #[async_test]
2247    async fn open_existing_file() {
2248        let buf_1 = b"hello world";
2249        let buf_2 = b"short sentence";
2250        let buf_3 = b"funny joke";
2251
2252        // Create VMGS file and write to different FileId's
2253        let disk = new_test_file();
2254        let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2255
2256        vmgs.write_file(FileId::BIOS_NVRAM, buf_1).await.unwrap();
2257
2258        assert_eq!(vmgs.state.active_header_index, 1);
2259        assert_eq!(vmgs.state.active_header_sequence_number, 2);
2260        assert_eq!(vmgs.state.fcbs[&FileId(0)].block_offset, 4);
2261        assert_eq!(vmgs.state.fcbs[&FileId(1)].block_offset, 5);
2262
2263        vmgs.write_file(FileId::TPM_PPI, buf_2).await.unwrap();
2264
2265        assert_eq!(vmgs.state.active_header_index, 0);
2266        assert_eq!(vmgs.state.active_header_sequence_number, 3);
2267        assert_eq!(vmgs.state.fcbs[&FileId(0)].block_offset, 2);
2268        assert_eq!(vmgs.state.fcbs[&FileId(1)].block_offset, 5);
2269        assert_eq!(vmgs.state.fcbs[&FileId(2)].block_offset, 6);
2270
2271        vmgs.write_file(FileId::BIOS_NVRAM, buf_3).await.unwrap();
2272
2273        assert_eq!(vmgs.state.active_header_index, 1);
2274        assert_eq!(vmgs.state.active_header_sequence_number, 4);
2275        assert_eq!(vmgs.state.fcbs[&FileId(0)].block_offset, 4);
2276        assert_eq!(vmgs.state.fcbs[&FileId(1)].block_offset, 7);
2277        assert_eq!(vmgs.state.fcbs[&FileId(2)].block_offset, 6);
2278
2279        // Re-open VMGS file and read from the same FileId's
2280        drop(vmgs);
2281
2282        let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2283
2284        assert_eq!(vmgs.state.fcbs[&FileId(0)].block_offset, 4);
2285        assert_eq!(vmgs.state.fcbs[&FileId(1)].block_offset, 7);
2286        assert_eq!(vmgs.state.fcbs[&FileId(2)].block_offset, 6);
2287        let read_buf_1 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2288
2289        assert_eq!(buf_3, &*read_buf_1);
2290        assert_eq!(vmgs.state.active_header_index, 1);
2291        assert_eq!(vmgs.state.active_header_sequence_number, 4);
2292
2293        let read_buf_2 = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2294
2295        assert_eq!(buf_2, &*read_buf_2);
2296        assert_eq!(vmgs.state.fcbs[&FileId(0)].block_offset, 4);
2297        assert_eq!(vmgs.state.fcbs[&FileId(1)].block_offset, 7);
2298        assert_eq!(vmgs.state.fcbs[&FileId(2)].block_offset, 6);
2299    }
2300
2301    #[async_test]
2302    async fn multiple_read_write() {
2303        let disk = new_test_file();
2304        let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2305
2306        let buf_1 = b"Data data data";
2307        let buf_2 = b"password";
2308        let buf_3 = b"other data data";
2309
2310        vmgs.write_file(FileId::BIOS_NVRAM, buf_1).await.unwrap();
2311        let read_buf_1 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2312        assert_eq!(buf_1, &*read_buf_1);
2313        assert_eq!(vmgs.state.active_header_index, 1);
2314        assert_eq!(vmgs.state.active_header_sequence_number, 2);
2315
2316        vmgs.write_file(FileId::TPM_PPI, buf_2).await.unwrap();
2317        let info = vmgs.get_file_info(FileId::TPM_PPI).unwrap();
2318        assert_eq!(info.valid_bytes as usize, buf_2.len());
2319        let read_buf_2 = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2320        assert_eq!(buf_2, &*read_buf_2);
2321        assert_eq!(vmgs.state.active_header_index, 0);
2322        assert_eq!(vmgs.state.active_header_sequence_number, 3);
2323
2324        vmgs.write_file(FileId::BIOS_NVRAM, buf_3).await.unwrap();
2325        let read_buf_3 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2326        assert_eq!(buf_3, &*read_buf_3);
2327        assert_eq!(vmgs.state.active_header_index, 1);
2328        assert_eq!(vmgs.state.active_header_sequence_number, 4);
2329
2330        vmgs.write_file(FileId::BIOS_NVRAM, buf_1).await.unwrap();
2331        let read_buf_1 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2332        assert_eq!(buf_1, &*read_buf_1);
2333        assert_eq!(vmgs.state.active_header_index, 0);
2334        assert_eq!(vmgs.state.active_header_sequence_number, 5);
2335
2336        vmgs.write_file(FileId::TPM_PPI, buf_2).await.unwrap();
2337        let read_buf_2 = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2338        assert_eq!(buf_2, &*read_buf_2);
2339        assert_eq!(vmgs.state.active_header_index, 1);
2340        assert_eq!(vmgs.state.active_header_sequence_number, 6);
2341
2342        vmgs.write_file(FileId::BIOS_NVRAM, buf_3).await.unwrap();
2343        let read_buf_3 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2344        assert_eq!(buf_3, &*read_buf_3);
2345        assert_eq!(vmgs.state.active_header_index, 0);
2346        assert_eq!(vmgs.state.active_header_sequence_number, 7);
2347    }
2348
2349    #[async_test]
2350    async fn test_insufficient_resources() {
2351        let disk = new_test_file();
2352        let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2353
2354        let buf: Vec<u8> = vec![1; ONE_MEGA_BYTE as usize * 5];
2355        let result = vmgs.write_file(FileId::BIOS_NVRAM, &buf).await;
2356        assert!(result.is_err());
2357        if let Err(e) = result {
2358            match e {
2359                Error::InsufficientResources => (),
2360                _ => panic!("Wrong error returned"),
2361            }
2362        } else {
2363            panic!("Should have returned Insufficient resources error");
2364        }
2365    }
2366
2367    #[async_test]
2368    async fn test_empty_write() {
2369        let disk = new_test_file();
2370        let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2371
2372        let buf: Vec<u8> = Vec::new();
2373        vmgs.write_file(FileId::BIOS_NVRAM, &buf).await.unwrap();
2374
2375        // read
2376        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2377
2378        assert_eq!(buf, read_buf);
2379        assert_eq!(read_buf.len(), 0);
2380        assert_eq!(vmgs.state.active_header_index, 1);
2381        assert_eq!(vmgs.state.active_header_sequence_number, 2);
2382    }
2383
2384    // general functions
2385    #[test]
2386    fn test_block_count_to_byte_count() {
2387        let block_count = 10;
2388        let byte_count = block_count_to_byte_count(block_count);
2389        assert!(byte_count == block_count as u64 * VMGS_BYTES_PER_BLOCK as u64);
2390    }
2391
2392    #[test]
2393    fn test_validate_header() {
2394        let mut header = VmgsHeader::new_zeroed();
2395        header.signature = VMGS_SIGNATURE;
2396        header.version = VMGS_VERSION_3_0;
2397        header.header_size = size_of::<VmgsHeader>() as u32;
2398        header.file_table_offset = VMGS_MIN_FILE_BLOCK_OFFSET;
2399        header.file_table_size = VMGS_FILE_TABLE_BLOCK_SIZE;
2400        header.checksum = compute_crc32(header.as_bytes());
2401
2402        let result = validate_header(&header);
2403        assert!(result.is_ok());
2404
2405        let mut header_signature = header.clone();
2406        header_signature.signature = 0;
2407        header_signature.checksum = 0;
2408        header_signature.checksum = compute_crc32(header_signature.as_bytes());
2409        let result = validate_header(&header_signature);
2410        match result {
2411            Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid header signature"),
2412            _ => panic!(),
2413        };
2414
2415        let mut header_version = header.clone();
2416        header_version.version = 0;
2417        header_version.checksum = 0;
2418        header_version.checksum = compute_crc32(header_version.as_bytes());
2419        match validate_header(&header_version) {
2420            Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid header version"),
2421            _ => panic!(),
2422        };
2423
2424        let mut header_header_size = header.clone();
2425        header_header_size.header_size = 0;
2426        header_header_size.checksum = 0;
2427        header_header_size.checksum = compute_crc32(header_header_size.as_bytes());
2428        match validate_header(&header_header_size) {
2429            Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid header size"),
2430            _ => panic!(),
2431        };
2432
2433        let mut header_ft_offset = header.clone();
2434        header_ft_offset.file_table_offset = 0;
2435        header_ft_offset.checksum = 0;
2436        header_ft_offset.checksum = compute_crc32(header_ft_offset.as_bytes());
2437        match validate_header(&header_ft_offset) {
2438            Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid file table offset"),
2439            _ => panic!(),
2440        };
2441
2442        let mut header_ft_size = header.clone();
2443        header_ft_size.file_table_size = 0;
2444        header_ft_size.checksum = 0;
2445        header_ft_size.checksum = compute_crc32(header_ft_size.as_bytes());
2446        match validate_header(&header_ft_size) {
2447            Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid file table size"),
2448            _ => panic!(),
2449        };
2450    }
2451
2452    #[test]
2453    fn test_initialize_file_metadata() {
2454        let mut file_table = VmgsFileTable::new_zeroed();
2455
2456        file_table.entries[0].offset = 6;
2457        file_table.entries[0].allocation_size = 1;
2458        file_table.entries[1].offset = 2;
2459        file_table.entries[1].allocation_size = 1;
2460        file_table.entries[2].offset = 4;
2461        file_table.entries[2].allocation_size = 5;
2462        file_table.entries[3].offset = 3;
2463        file_table.entries[3].allocation_size = 3;
2464
2465        let block_capacity = 1000;
2466
2467        let fcbs = initialize_file_metadata(&file_table, VMGS_VERSION_3_0, block_capacity).unwrap();
2468        // assert VmgsFileEntry correctly converted to FileControlBlock
2469        assert!(fcbs[&FileId(0)].block_offset == 6);
2470        assert!(fcbs[&FileId(0)].allocated_blocks.get() == 1);
2471        assert!(fcbs[&FileId(1)].block_offset == 2);
2472        assert!(fcbs[&FileId(1)].allocated_blocks.get() == 1);
2473        assert!(fcbs[&FileId(2)].block_offset == 4);
2474        assert!(fcbs[&FileId(2)].allocated_blocks.get() == 5);
2475        assert!(fcbs[&FileId(3)].block_offset == 3);
2476        assert!(fcbs[&FileId(3)].allocated_blocks.get() == 3);
2477    }
2478
2479    #[test]
2480    fn test_round_up_count() {
2481        assert!(round_up_count(0, 4096) == 0);
2482        assert!(round_up_count(1, 4096) == 4096);
2483        assert!(round_up_count(4095, 4096) == 4096);
2484        assert!(round_up_count(4096, 4096) == 4096);
2485        assert!(round_up_count(4097, 4096) == 8192);
2486    }
2487
2488    #[async_test]
2489    async fn test_header_sequence_overflow() {
2490        let disk = new_test_file();
2491        let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2492
2493        vmgs.state.active_header_sequence_number = u32::MAX;
2494
2495        // write
2496        let buf = b"hello world";
2497        vmgs.write_file(FileId::BIOS_NVRAM, buf).await.unwrap();
2498
2499        assert_eq!(vmgs.state.active_header_index, 1);
2500        assert_eq!(vmgs.state.active_header_sequence_number, 0);
2501
2502        vmgs.state.active_header_index = 0;
2503        vmgs.state.active_header_sequence_number = u32::MAX;
2504
2505        let mut temp_state = vmgs.temp_state();
2506
2507        let (new_header, index) = temp_state.make_header();
2508        vmgs.write_header_internal(&new_header, index)
2509            .await
2510            .unwrap();
2511        vmgs.apply(temp_state);
2512
2513        assert_eq!(vmgs.state.active_header_index, 1);
2514        assert_eq!(vmgs.state.active_header_sequence_number, 0);
2515        assert_eq!(new_header.sequence, 0);
2516    }
2517
2518    #[cfg(with_encryption)]
2519    #[async_test]
2520    async fn write_file_v3() {
2521        let disk = new_test_file();
2522        let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2523        let encryption_key = [12; VMGS_ENCRYPTION_KEY_SIZE];
2524
2525        // write
2526        let buf = b"hello world";
2527        let buf_1 = b"hello universe";
2528        vmgs.write_file(FileId::BIOS_NVRAM, buf).await.unwrap();
2529        vmgs.update_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2530            .await
2531            .unwrap();
2532        vmgs.write_file_encrypted(FileId::TPM_PPI, buf_1)
2533            .await
2534            .unwrap();
2535
2536        // read
2537        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2538        assert_eq!(buf, &*read_buf);
2539        let info = vmgs.get_file_info(FileId::TPM_PPI).unwrap();
2540        assert_eq!(info.valid_bytes as usize, buf_1.len());
2541        let read_buf = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2542        assert_eq!(buf_1, &*read_buf);
2543
2544        // Read the file after re-opening the vmgs file
2545        drop(vmgs);
2546        let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2547        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2548        assert_eq!(buf, read_buf.as_bytes());
2549        let info = vmgs.get_file_info(FileId::TPM_PPI).unwrap();
2550        assert_eq!(info.valid_bytes as usize, buf_1.len());
2551        let read_buf = vmgs.read_file_raw(FileId::TPM_PPI).await.unwrap();
2552        assert_ne!(buf_1, read_buf.as_bytes());
2553
2554        // Unlock datastore
2555        vmgs.unlock_with_encryption_key(&encryption_key)
2556            .await
2557            .unwrap();
2558        let info = vmgs.get_file_info(FileId::TPM_PPI).unwrap();
2559        assert_eq!(info.valid_bytes as usize, buf_1.len());
2560        let read_buf = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2561        assert_eq!(buf_1, &*read_buf);
2562    }
2563
2564    #[cfg(with_encryption)]
2565    #[async_test]
2566    async fn overwrite_file_v3() {
2567        let disk = new_test_file();
2568        let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2569        let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2570        let buf = vec![1; 8 * 1024];
2571        let buf_1 = vec![2; 8 * 1024];
2572
2573        // Add root key.
2574        vmgs.add_new_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2575            .await
2576            .unwrap();
2577        assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2578
2579        // Write a file to the store.
2580        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2581            .await
2582            .unwrap();
2583
2584        // Encrypt and overwrite the original file.
2585        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf_1)
2586            .await
2587            .unwrap();
2588
2589        // Verify new file contents
2590        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2591        assert_eq!(buf_1, read_buf);
2592    }
2593
2594    #[cfg(with_encryption)]
2595    #[async_test]
2596    async fn file_encryption() {
2597        let buf: Vec<u8> = (0..255).collect();
2598        let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2599
2600        let disk = new_test_file();
2601        let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2602
2603        // Add datastore key.
2604        vmgs.add_new_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2605            .await
2606            .unwrap();
2607        assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2608
2609        // Write a file to the store.
2610        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2611            .await
2612            .unwrap();
2613
2614        // Read the file, without closing the datastore
2615        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2616        assert_eq!(buf, read_buf);
2617
2618        drop(vmgs);
2619
2620        // Read the file, after closing and reopening the data store.
2621        let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2622
2623        let info = vmgs.get_file_info(FileId::BIOS_NVRAM).unwrap();
2624        assert_eq!(info.valid_bytes as usize, buf.len());
2625
2626        // Unlock the store.
2627
2628        vmgs.unlock_with_encryption_key(&encryption_key)
2629            .await
2630            .unwrap();
2631
2632        assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2633
2634        // Change to a new datastore key.
2635        let new_encryption_key = [2; VMGS_ENCRYPTION_KEY_SIZE];
2636        vmgs.add_new_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2637            .await
2638            .unwrap();
2639        assert_eq!(vmgs.state.active_datastore_key_index, Some(1));
2640        vmgs.remove_encryption_key(0).await.unwrap();
2641
2642        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2643        assert_eq!(buf, read_buf);
2644    }
2645
2646    #[cfg(with_encryption)]
2647    #[async_test]
2648    async fn add_new_encryption_key() {
2649        let buf: Vec<u8> = (0..255).collect();
2650        let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2651        let new_encryption_key = [5; VMGS_ENCRYPTION_KEY_SIZE];
2652
2653        // Initialize version 3 data store
2654        let disk = new_test_file();
2655        let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2656
2657        // Add datastore key.
2658        vmgs.add_new_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2659            .await
2660            .unwrap();
2661        assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2662
2663        // Write a file to the store.
2664        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2665            .await
2666            .unwrap();
2667
2668        // Read the file, after closing and reopening the data store.
2669        drop(vmgs);
2670        let mut vmgs = Vmgs::open(disk.clone(), None).await.unwrap();
2671        vmgs.unlock_with_encryption_key(&encryption_key)
2672            .await
2673            .unwrap();
2674        assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2675        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2676        assert_eq!(read_buf, buf);
2677
2678        // Add new datastore key.
2679        vmgs.add_new_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2680            .await
2681            .unwrap();
2682        assert_eq!(vmgs.state.active_datastore_key_index, Some(1));
2683
2684        // Read the file by using two different datastore keys, after closing and reopening the data store.
2685        drop(vmgs);
2686        let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2687        vmgs.unlock_with_encryption_key(&encryption_key)
2688            .await
2689            .unwrap();
2690        assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2691        vmgs.unlock_with_encryption_key(&new_encryption_key)
2692            .await
2693            .unwrap();
2694        assert_eq!(vmgs.state.active_datastore_key_index, Some(1));
2695        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2696        assert_eq!(read_buf, buf);
2697
2698        // Remove the newly added datastore key and add it again.
2699        vmgs.remove_encryption_key(1).await.unwrap();
2700        vmgs.add_new_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2701            .await
2702            .unwrap();
2703        assert_eq!(vmgs.state.active_datastore_key_index, Some(1));
2704
2705        // Remove the old datastore key
2706        vmgs.remove_encryption_key(0).await.unwrap();
2707        let result = vmgs.unlock_with_encryption_key(&encryption_key).await;
2708        assert!(matches!(result, Err(Error::DecryptMetadataKey)));
2709
2710        // Try to remove the old datastore key again
2711        let result = vmgs.remove_encryption_key(0).await;
2712        assert!(matches!(result, Err(Error::InvalidArgument(_))));
2713
2714        // Remove the new datastore key and try to read file content, which should be in encrypted state
2715        vmgs.remove_encryption_key(1).await.unwrap();
2716        let read_buf = vmgs.read_file_raw(FileId::BIOS_NVRAM).await;
2717        assert_ne!(read_buf.unwrap(), buf);
2718    }
2719
2720    #[cfg(with_encryption)]
2721    #[async_test]
2722    async fn test_write_file_encrypted() {
2723        // Call write_file_encrypted on an unencrypted VMGS and check that plaintext was written
2724
2725        // Initialize version 3 data store
2726        let disk = new_test_file();
2727        let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2728        let buf = b"This is plaintext";
2729
2730        // call write file encrypted
2731        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, buf)
2732            .await
2733            .unwrap();
2734
2735        // Read
2736        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2737        assert_eq!(vmgs.state.encryption_algorithm, EncryptionAlgorithm::NONE);
2738        assert_eq!(buf, &*read_buf);
2739
2740        // ensure that when we re-create the VMGS object, we can still read the
2741        // FileId as plaintext
2742        drop(vmgs);
2743        let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2744
2745        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2746        assert_eq!(vmgs.state.encryption_algorithm, EncryptionAlgorithm::NONE);
2747        assert_eq!(buf, &*read_buf);
2748    }
2749
2750    #[cfg(with_encryption)]
2751    #[async_test]
2752    async fn test_logger() {
2753        let disk = new_test_file();
2754        let data = Arc::new(Mutex::new(String::new()));
2755        let mut vmgs = Vmgs::format_new(
2756            disk.clone(),
2757            Some(Arc::new(TestVmgsLogger { data: data.clone() })),
2758        )
2759        .await
2760        .unwrap();
2761        let encryption_key = [12; VMGS_ENCRYPTION_KEY_SIZE];
2762
2763        // write
2764        let buf = b"hello world";
2765        vmgs.update_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2766            .await
2767            .unwrap();
2768        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, buf)
2769            .await
2770            .unwrap();
2771
2772        let fcb = vmgs.state.fcbs.get_mut(&FileId::BIOS_NVRAM).unwrap();
2773
2774        // Manipulate the nonce and expect the read to fail.
2775        fcb.nonce[0] ^= 1;
2776
2777        // read and expect to fail
2778        let result = vmgs.read_file(FileId::BIOS_NVRAM).await;
2779        assert!(result.is_err());
2780
2781        // verify that the string is logged
2782        let result = data.lock();
2783        assert_eq!(*result, "test logger");
2784    }
2785
2786    #[cfg(with_encryption)]
2787    #[async_test]
2788    async fn update_key() {
2789        let buf: Vec<u8> = (0..255).collect();
2790        let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2791
2792        let disk = new_test_file();
2793        let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2794
2795        // Add datastore key.
2796        vmgs.update_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2797            .await
2798            .unwrap();
2799        assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2800        assert_eq!(vmgs.state.datastore_key_count, 1);
2801
2802        // Write a file to the store.
2803        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2804            .await
2805            .unwrap();
2806
2807        // Read the file, without closing the datastore
2808        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2809        assert_eq!(buf, read_buf);
2810
2811        // Close and reopen the store
2812        drop(vmgs);
2813        let mut vmgs = Vmgs::open(disk.clone(), None).await.unwrap();
2814
2815        // Unlock the store.
2816        vmgs.unlock_with_encryption_key(&encryption_key)
2817            .await
2818            .unwrap();
2819        assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2820
2821        // Read the file again
2822        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2823        assert_eq!(buf, read_buf);
2824
2825        // Change to a new datastore key.
2826        let new_encryption_key = [2; VMGS_ENCRYPTION_KEY_SIZE];
2827        vmgs.update_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2828            .await
2829            .unwrap();
2830        assert_eq!(vmgs.state.active_datastore_key_index, Some(1));
2831        assert_eq!(vmgs.state.datastore_key_count, 1);
2832
2833        // Read the file again
2834        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2835        assert_eq!(buf, read_buf);
2836
2837        // Close and reopen the store
2838        drop(vmgs);
2839        let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2840
2841        // Unlock the store.
2842        vmgs.unlock_with_encryption_key(&new_encryption_key)
2843            .await
2844            .unwrap();
2845        assert_eq!(vmgs.state.active_datastore_key_index, Some(1));
2846
2847        // Read the file again
2848        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2849        assert_eq!(buf, read_buf);
2850    }
2851
2852    #[cfg(with_encryption)]
2853    #[async_test]
2854    async fn update_key_no_space() {
2855        let buf: Vec<u8> = (0..255).collect();
2856        let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2857
2858        let disk = new_test_file();
2859        let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2860
2861        // Add datastore key.
2862        vmgs.update_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2863            .await
2864            .unwrap();
2865        assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2866        assert_eq!(vmgs.state.datastore_key_count, 1);
2867
2868        // Write a file to the store.
2869        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2870            .await
2871            .unwrap();
2872
2873        // Read the file, without closing the datastore
2874        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2875        assert_eq!(buf, read_buf);
2876
2877        // Add a new datastore key, but don't remove the old one.
2878        let new_encryption_key = [2; VMGS_ENCRYPTION_KEY_SIZE];
2879        vmgs.add_new_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2880            .await
2881            .unwrap();
2882        assert_eq!(vmgs.state.active_datastore_key_index, Some(1));
2883        assert_eq!(vmgs.state.datastore_key_count, 2);
2884
2885        // Close and reopen the store
2886        drop(vmgs);
2887        let mut vmgs = Vmgs::open(disk.clone(), None).await.unwrap();
2888
2889        // Unlock the store.
2890        vmgs.unlock_with_encryption_key(&new_encryption_key)
2891            .await
2892            .unwrap();
2893        assert_eq!(vmgs.state.active_datastore_key_index, Some(1));
2894
2895        // Add yet another new datastore key. This should remove both previous keys
2896        let another_encryption_key = [2; VMGS_ENCRYPTION_KEY_SIZE];
2897        vmgs.update_encryption_key(&another_encryption_key, EncryptionAlgorithm::AES_GCM)
2898            .await
2899            .unwrap();
2900        assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2901        assert_eq!(vmgs.state.datastore_key_count, 1);
2902
2903        // Close and reopen the store
2904        drop(vmgs);
2905        let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2906
2907        // Unlock the store.
2908        vmgs.unlock_with_encryption_key(&another_encryption_key)
2909            .await
2910            .unwrap();
2911        assert_eq!(vmgs.state.active_datastore_key_index, Some(0));
2912
2913        // Read the file again
2914        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2915        assert_eq!(buf, read_buf);
2916    }
2917
2918    #[test]
2919    fn test_allocate_helper() {
2920        let block_capacity =
2921            (vmgs_format::VMGS_DEFAULT_CAPACITY / (VMGS_BYTES_PER_BLOCK as u64)) as u32;
2922        // this test assumes the block capacity is 1024
2923        assert_eq!(block_capacity, 1024);
2924
2925        let mut allocation_list = Vec::new();
2926
2927        // add some "files"
2928        assert_eq!(
2929            allocate_helper(&mut allocation_list, 3, block_capacity).unwrap(),
2930            2
2931        );
2932        assert_eq!(
2933            allocate_helper(&mut allocation_list, 95, block_capacity).unwrap(),
2934            5
2935        );
2936        assert_eq!(
2937            allocate_helper(&mut allocation_list, 2, block_capacity).unwrap(),
2938            100
2939        );
2940
2941        // remove the first one and make sure subsequent "files" are placed there
2942        allocation_list.remove(0);
2943
2944        assert_eq!(
2945            allocate_helper(&mut allocation_list, 1, block_capacity).unwrap(),
2946            2
2947        );
2948        assert_eq!(
2949            allocate_helper(&mut allocation_list, 3, block_capacity).unwrap(),
2950            102
2951        );
2952        assert_eq!(
2953            allocate_helper(&mut allocation_list, 2, block_capacity).unwrap(),
2954            3
2955        );
2956
2957        // Make sure we error correctly when dealing with large files
2958        let mut allocation_list = Vec::new();
2959
2960        allocate_helper(&mut allocation_list, 1025, block_capacity).unwrap_err();
2961        assert_eq!(
2962            allocate_helper(&mut allocation_list, 511, block_capacity).unwrap(),
2963            2
2964        );
2965        assert_eq!(
2966            allocate_helper(&mut allocation_list, 511, block_capacity).unwrap(),
2967            513
2968        );
2969        allocate_helper(&mut allocation_list, 1, block_capacity).unwrap_err();
2970    }
2971
2972    #[async_test]
2973    async fn test_provisioning_marker() {
2974        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"}"#;
2975
2976        let disk = new_test_file();
2977        let data = Arc::new(Mutex::new(String::new()));
2978        let mut vmgs = Vmgs::format_new_with_reason(
2979            disk.clone(),
2980            VmgsProvisioningReason::Empty,
2981            Some(Arc::new(TestVmgsLogger { data: data.clone() })),
2982        )
2983        .await
2984        .unwrap();
2985
2986        let marker = VmgsProvisioningMarker {
2987            provisioner: VmgsProvisioner::OpenHcl,
2988            reason: vmgs.provisioning_reason().unwrap(),
2989            tpm_version: "1.38".to_string(),
2990            tpm_nvram_size: 32768,
2991            akcert_size: 4096,
2992            akcert_attrs: "0x42060004".to_string(),
2993            provisioner_version: "unit test".to_string(),
2994        };
2995
2996        vmgs.write_provisioning_marker(&marker).await.unwrap();
2997
2998        let read_buf = vmgs.read_file(FileId::PROVISIONING_MARKER).await.unwrap();
2999        assert_eq!(EXPECTED_MARKER.as_bytes(), read_buf);
3000    }
3001}