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;
8#[cfg(with_encryption)]
9use anyhow::Context;
10#[cfg(with_encryption)]
11use anyhow::anyhow;
12use cvm_tracing::CVM_ALLOWED;
13use disk_backend::Disk;
14#[cfg(feature = "inspect")]
15use inspect::Inspect;
16#[cfg(feature = "inspect")]
17use inspect_counters::Counter;
18use std::collections::HashMap;
19use std::num::NonZeroU32;
20use std::sync::Arc;
21use vmgs_format::EncryptionAlgorithm;
22use vmgs_format::FileAttribute;
23use vmgs_format::FileId;
24use vmgs_format::VMGS_BYTES_PER_BLOCK;
25use vmgs_format::VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE;
26use vmgs_format::VMGS_FILE_TABLE_BLOCK_SIZE;
27use vmgs_format::VMGS_MIN_FILE_BLOCK_OFFSET;
28use vmgs_format::VMGS_SIGNATURE;
29use vmgs_format::VMGS_VERSION_3_0;
30use vmgs_format::VmgsAuthTag;
31use vmgs_format::VmgsDatastoreKey;
32use vmgs_format::VmgsEncryptionKey;
33use vmgs_format::VmgsExtendedFileTable;
34use vmgs_format::VmgsFileTable;
35use vmgs_format::VmgsHeader;
36use vmgs_format::VmgsMarkers;
37use vmgs_format::VmgsNonce;
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, Copy, 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
92/// Implementation of the VMGS file format, backed by a generic [`Disk`]
93/// device.
94#[cfg_attr(feature = "inspect", derive(Inspect))]
95pub struct Vmgs {
96    storage: VmgsStorage,
97
98    #[cfg(feature = "inspect")]
99    stats: vmgs_inspect::VmgsStats,
100
101    active_header_index: usize,
102    active_header_sequence_number: u32,
103    version: u32,
104    #[cfg_attr(feature = "inspect", inspect(with = "vmgs_inspect::fcbs"))]
105    fcbs: HashMap<FileId, ResolvedFileControlBlock>,
106    encryption_algorithm: EncryptionAlgorithm,
107    #[allow(dead_code)]
108    datastore_key_count: u8,
109    active_datastore_key_index: Option<usize>,
110    #[cfg_attr(feature = "inspect", inspect(iter_by_index))]
111    datastore_keys: [VmgsDatastoreKey; 2],
112    metadata_key: VmgsDatastoreKey,
113    #[cfg_attr(feature = "inspect", inspect(iter_by_index))]
114    encrypted_metadata_keys: [VmgsEncryptionKey; 2],
115    reprovisioned: bool,
116    provisioned_this_boot: bool,
117
118    #[cfg_attr(feature = "inspect", inspect(skip))]
119    logger: Option<Arc<dyn VmgsLogger>>,
120}
121
122#[cfg(feature = "inspect")]
123mod vmgs_inspect {
124    use super::*;
125
126    #[derive(Default)]
127    pub struct IoStat {
128        pub attempt: Counter,
129        pub resolved: Counter,
130    }
131
132    // explicit inspect implementation, since we want to massage the data's
133    // presentation a bit
134    impl Inspect for IoStat {
135        fn inspect(&self, req: inspect::Request<'_>) {
136            let mut resp = req.respond();
137            resp.counter("ok", self.resolved.get())
138                .counter("err", self.attempt.get() - self.resolved.get());
139        }
140    }
141
142    #[derive(Inspect, Default)]
143    pub struct VmgsStats {
144        #[inspect(with = "stat_map")]
145        pub read: HashMap<FileId, IoStat>,
146        #[inspect(with = "stat_map")]
147        pub write: HashMap<FileId, IoStat>,
148    }
149
150    pub(super) fn fcbs(fcbs: &HashMap<FileId, ResolvedFileControlBlock>) -> impl Inspect + '_ {
151        inspect::adhoc(|req| {
152            let mut res = req.respond();
153            for (id, fcb) in fcbs.iter() {
154                res.field(&format!("{}-{:?}", id.0, id), fcb);
155            }
156        })
157    }
158
159    pub fn stat_map(map: &HashMap<FileId, IoStat>) -> impl Inspect + '_ {
160        inspect::iter_by_key(map).map_key(|x| format!("{:?}", x))
161    }
162}
163
164impl Vmgs {
165    /// Attempt to open the VMGS file, optionally formatting if it is
166    /// empty or corrupted.
167    pub async fn try_open(
168        disk: Disk,
169        logger: Option<Arc<dyn VmgsLogger>>,
170        format_on_empty: bool,
171        format_on_failure: bool,
172    ) -> Result<Self, Error> {
173        match Self::open(disk.clone(), logger.clone()).await {
174            Ok(vmgs) => Ok(vmgs),
175            Err(Error::EmptyFile) if format_on_empty => {
176                tracing::info!(CVM_ALLOWED, "empty vmgs file, formatting");
177                Self::format_new(disk, logger).await
178            }
179            Err(err) if format_on_failure => {
180                tracing::warn!(CVM_ALLOWED, ?err, "vmgs initialization error, reformatting");
181                Self::format_new(disk, logger).await
182            }
183            Err(err) => {
184                let event_log_id = match err {
185                    // The data store format is invalid or not supported.
186                    Error::InvalidFormat(_) => VmgsLogEvent::InvalidFormat,
187                    // The data store is corrupted.
188                    Error::CorruptFormat(_) => VmgsLogEvent::CorruptFormat,
189                    // All other errors
190                    _ => VmgsLogEvent::InitFailed,
191                };
192
193                logger.log_event_fatal(event_log_id).await;
194                Err(err)
195            }
196        }
197    }
198
199    /// Open the VMGS file.
200    pub async fn open(disk: Disk, logger: Option<Arc<dyn VmgsLogger>>) -> Result<Self, Error> {
201        tracing::debug!(CVM_ALLOWED, "opening VMGS datastore");
202        let storage = VmgsStorage::new_validated(disk).map_err(Error::Initialization)?;
203        Self::open_inner(storage, logger).await
204    }
205
206    /// Format and open a new VMGS file.
207    pub async fn format_new(
208        disk: Disk,
209        logger: Option<Arc<dyn VmgsLogger>>,
210    ) -> Result<Self, Error> {
211        tracing::info!(
212            CVM_ALLOWED,
213            op_type = ?LogOpType::VmgsProvision,
214            "formatting and initializing VMGS datastore"
215        );
216        let storage = VmgsStorage::new_validated(disk).map_err(Error::Initialization)?;
217        Self::format_new_inner(storage, logger).await
218    }
219
220    /// Format and open a new VMGS file.
221    pub async fn request_format(
222        disk: Disk,
223        logger: Option<Arc<dyn VmgsLogger>>,
224    ) -> Result<Self, Error> {
225        let mut storage = VmgsStorage::new_validated(disk).map_err(Error::Initialization)?;
226
227        match Self::open_header(&mut storage).await {
228            Ok((active_header, active_header_index)) if active_header.markers.reprovisioned() => {
229                tracing::info!(CVM_ALLOWED, "reprovisioned marker found, skipping format");
230                Self::finish_open(storage, active_header, active_header_index, logger).await
231            }
232            _ => {
233                tracing::info!(CVM_ALLOWED, "formatting vmgs file on request");
234                let mut vmgs = Vmgs::format_new_inner(storage, logger).await?;
235
236                // set the reprovisioned marker to prevent the vmgs from
237                // repeatedly being reset
238                vmgs.set_reprovisioned(true).await?;
239
240                Ok(vmgs)
241            }
242        }
243    }
244
245    async fn format_new_inner(
246        mut storage: VmgsStorage,
247        logger: Option<Arc<dyn VmgsLogger>>,
248    ) -> Result<Self, Error> {
249        let active_header = Self::format(&mut storage, VMGS_VERSION_3_0).await?;
250        let mut vmgs = Self::finish_open(storage, active_header, 0, logger).await?;
251        vmgs.provisioned_this_boot = true;
252        Ok(vmgs)
253    }
254
255    async fn open_inner(
256        mut storage: VmgsStorage,
257        logger: Option<Arc<dyn VmgsLogger>>,
258    ) -> Result<Self, Error> {
259        let (active_header, active_header_index) = Self::open_header(&mut storage).await?;
260
261        let mut vmgs =
262            Self::finish_open(storage, active_header, active_header_index, logger).await?;
263
264        // clear the reprovisioned marker after successfully opening the vmgs
265        // without being requested to reprovision.
266        vmgs.set_reprovisioned(false).await?;
267
268        Ok(vmgs)
269    }
270
271    async fn open_header(storage: &mut VmgsStorage) -> Result<(VmgsHeader, usize), Error> {
272        let (header_1, header_2) = read_headers_inner(storage).await.map_err(|(e, _)| e)?;
273
274        let active_header_index =
275            get_active_header(validate_header(&header_1), validate_header(&header_2))?;
276
277        let active_header = if active_header_index == 0 {
278            header_1
279        } else {
280            header_2
281        };
282
283        Ok((active_header, active_header_index))
284    }
285
286    async fn finish_open(
287        mut storage: VmgsStorage,
288        active_header: VmgsHeader,
289        active_header_index: usize,
290        logger: Option<Arc<dyn VmgsLogger>>,
291    ) -> Result<Vmgs, Error> {
292        let version = active_header.version;
293        let (encryption_algorithm, encrypted_metadata_keys, datastore_key_count, reprovisioned) =
294            if version >= VMGS_VERSION_3_0 {
295                let encryption_algorithm =
296                    if active_header.encryption_algorithm == EncryptionAlgorithm::AES_GCM {
297                        EncryptionAlgorithm::AES_GCM
298                    } else {
299                        EncryptionAlgorithm::NONE
300                    };
301                let encrypted_metadata_keys = active_header.metadata_keys;
302
303                let is_key_zero_empty = is_empty_key(&encrypted_metadata_keys[0].encryption_key);
304                let is_key_one_empty = is_empty_key(&encrypted_metadata_keys[1].encryption_key);
305                let datastore_key_count = {
306                    if is_key_zero_empty && is_key_one_empty {
307                        0
308                    } else if !is_key_zero_empty && !is_key_one_empty {
309                        encrypted_metadata_keys.len() as u8
310                    } else {
311                        1
312                    }
313                };
314                (
315                    encryption_algorithm,
316                    active_header.metadata_keys,
317                    datastore_key_count,
318                    active_header.markers.reprovisioned(),
319                )
320            } else {
321                (
322                    EncryptionAlgorithm::NONE,
323                    [VmgsEncryptionKey::new_zeroed(); 2],
324                    0,
325                    false,
326                )
327            };
328
329        // Read the file table and initialize the internal file metadata.
330        let file_table_size_bytes = block_count_to_byte_count(active_header.file_table_size);
331        let file_table_offset_bytes = block_count_to_byte_count(active_header.file_table_offset);
332
333        let mut file_table_buffer = vec![0; file_table_size_bytes as usize];
334
335        if let Err(e) = storage
336            .read_block(file_table_offset_bytes, file_table_buffer.as_mut_slice())
337            .await
338        {
339            return Err(Error::CorruptFormat(format!(
340                "Error reading file table: {:?}",
341                e
342            )));
343        }
344
345        let file_table = VmgsFileTable::ref_from_prefix(&file_table_buffer)
346            .unwrap()
347            .0; // TODO: zerocopy: ref-from-prefix: use-rest-of-range (https://github.com/microsoft/openvmm/issues/759)
348
349        let file_control_blocks =
350            initialize_file_metadata(file_table, version, storage.block_capacity())?;
351
352        Ok(Self {
353            storage,
354
355            active_header_index,
356            active_header_sequence_number: active_header.sequence,
357            version,
358            fcbs: file_control_blocks,
359            encryption_algorithm,
360            datastore_key_count,
361            active_datastore_key_index: None,
362            datastore_keys: [VmgsDatastoreKey::new_zeroed(); 2],
363            metadata_key: VmgsDatastoreKey::new_zeroed(),
364            encrypted_metadata_keys,
365            reprovisioned,
366            provisioned_this_boot: false,
367
368            #[cfg(feature = "inspect")]
369            stats: Default::default(),
370
371            logger,
372        })
373    }
374
375    /// Formats the backing store with initial metadata, and sets active header.
376    async fn format(storage: &mut VmgsStorage, version: u32) -> Result<VmgsHeader, Error> {
377        tracing::info!(CVM_ALLOWED, "Formatting new VMGS file.");
378        let aligned_header_size = round_up_count(size_of::<VmgsHeader>(), storage.sector_size());
379
380        // The second header is initialized as invalid (all zeros).
381        let mut header = VmgsHeader::new_zeroed();
382
383        storage
384            .write_block(aligned_header_size, header.as_bytes())
385            .await
386            .map_err(Error::WriteDisk)?;
387
388        // Write an empty file table at min offset. All entries are zeroed except
389        // for the first one, which is the file table itself
390        let mut file_table = VmgsFileTable::new_zeroed();
391        file_table.entries[FileId::FILE_TABLE].offset = VMGS_MIN_FILE_BLOCK_OFFSET;
392        file_table.entries[FileId::FILE_TABLE].allocation_size = VMGS_FILE_TABLE_BLOCK_SIZE;
393        file_table.entries[FileId::FILE_TABLE].valid_data_size =
394            block_count_to_byte_count(VMGS_FILE_TABLE_BLOCK_SIZE);
395        if version >= VMGS_VERSION_3_0 {
396            file_table.entries[FileId::EXTENDED_FILE_TABLE].offset =
397                VMGS_MIN_FILE_BLOCK_OFFSET + VMGS_FILE_TABLE_BLOCK_SIZE;
398            file_table.entries[FileId::EXTENDED_FILE_TABLE].allocation_size =
399                VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE;
400            file_table.entries[FileId::EXTENDED_FILE_TABLE].valid_data_size =
401                block_count_to_byte_count(VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE);
402        }
403
404        storage
405            .write_block(
406                block_count_to_byte_count(VMGS_MIN_FILE_BLOCK_OFFSET),
407                file_table.as_bytes(),
408            )
409            .await
410            .map_err(Error::WriteDisk)?;
411
412        initialize_file_metadata(&file_table, VMGS_VERSION_3_0, storage.block_capacity())?;
413
414        // Write an empty extended file table if the datastore supports V3.
415        if version >= VMGS_VERSION_3_0 {
416            let extended_file_table = VmgsExtendedFileTable::new_zeroed();
417            storage
418                .write_block(
419                    block_count_to_byte_count(
420                        VMGS_MIN_FILE_BLOCK_OFFSET + VMGS_FILE_TABLE_BLOCK_SIZE,
421                    ),
422                    extended_file_table.as_bytes(),
423                )
424                .await
425                .map_err(Error::WriteDisk)?;
426        }
427
428        // Write the first header as the valid header
429        header.signature = VMGS_SIGNATURE;
430        header.version = VMGS_VERSION_3_0;
431        header.sequence = 1;
432        header.header_size = size_of::<VmgsHeader>() as u32;
433        header.file_table_offset = VMGS_MIN_FILE_BLOCK_OFFSET;
434        header.file_table_size = VMGS_FILE_TABLE_BLOCK_SIZE;
435        header.checksum = compute_crc32(header.as_bytes());
436        header.encryption_algorithm = EncryptionAlgorithm::NONE;
437
438        storage
439            .write_block(0, header.as_bytes())
440            .await
441            .map_err(Error::WriteDisk)?;
442
443        // Flush the device to persist changes
444        storage.flush().await.map_err(Error::FlushDisk)?;
445
446        Ok(header)
447    }
448
449    /// Get allocated and valid bytes from File Control Block for file_id.
450    ///
451    /// When reading data from a file, the buffer must be at least `valid_bytes` long.
452    pub fn get_file_info(&self, file_id: FileId) -> Result<VmgsFileInfo, Error> {
453        let fcb = self.fcbs.get(&file_id).ok_or(Error::FileInfoNotAllocated)?;
454
455        Ok(VmgsFileInfo {
456            allocated_bytes: block_count_to_byte_count(fcb.allocated_blocks.get()),
457            valid_bytes: fcb.valid_bytes,
458            encrypted: fcb.attributes.encrypted() || fcb.attributes.authenticated(),
459        })
460    }
461
462    /// maps out the used/unused space in the file and finds the smallest unused space to allocate new data.
463    /// Appends the newly allocated FileControlBlock to the end of temp_fcbs.
464    ///
465    /// # Arguments
466    ///
467    /// * 'block_count' - allocated_blocks to allocate for new FCB
468    fn allocate_space(
469        &mut self,
470        block_count: u32,
471        temp_fcbs: &mut Vec<ResolvedFileControlBlock>,
472        valid_bytes: u64,
473    ) -> Result<(), Error> {
474        struct AllocationBlock {
475            block_offset: u32,
476            allocated_blocks: u32,
477        }
478
479        if block_count == 0 {
480            return Err(Error::AllocateZero);
481        }
482        // map out file offets/sizes to see what space is unused
483        let mut allocation_list = Vec::new();
484        for (_, fcb) in self.fcbs.iter() {
485            allocation_list.push(AllocationBlock {
486                block_offset: fcb.block_offset,
487                allocated_blocks: fcb.allocated_blocks.get(),
488            });
489        }
490
491        for temp_fcb in temp_fcbs.iter() {
492            allocation_list.push(AllocationBlock {
493                block_offset: temp_fcb.block_offset,
494                allocated_blocks: temp_fcb.allocated_blocks.get(),
495            });
496        }
497        // TODO: this will get removed when allocation_list is re-written
498        // sort by block offset
499        allocation_list.sort_by_key(|a| a.block_offset);
500
501        let mut best_offset = 0;
502        let mut best_free_count = 0;
503        let mut last_allocation_end_offset = VMGS_MIN_FILE_BLOCK_OFFSET;
504        let mut found = false;
505
506        // find smallest set of blocks that will fit the data we're allocating
507        for fcb in allocation_list.iter() {
508            if fcb.block_offset < last_allocation_end_offset {
509                return Err(Error::AllocateOffset);
510            }
511            let free_count = fcb.block_offset - last_allocation_end_offset;
512            if free_count >= block_count && (best_free_count == 0 || free_count < best_free_count) {
513                best_free_count = free_count;
514                best_offset = last_allocation_end_offset;
515                found = true;
516            }
517            last_allocation_end_offset = fcb.block_offset + fcb.allocated_blocks;
518        }
519        if last_allocation_end_offset < self.storage.block_capacity() {
520            let free_count = self.storage.block_capacity() - last_allocation_end_offset;
521            if free_count >= block_count && (best_free_count == 0 || free_count < best_free_count) {
522                best_offset = last_allocation_end_offset;
523                found = true;
524            }
525        }
526        if !found {
527            return Err(Error::InsufficientResources);
528        }
529        let new_fcb = ResolvedFileControlBlock {
530            block_offset: best_offset,
531            allocated_blocks: NonZeroU32::new(block_count).unwrap(),
532            valid_bytes,
533            attributes: FileAttribute::new(),
534            nonce: VmgsNonce::new_zeroed(),
535            authentication_tag: VmgsAuthTag::new_zeroed(),
536            encryption_key: VmgsDatastoreKey::new_zeroed(),
537        };
538        temp_fcbs.push(new_fcb);
539
540        Ok(())
541    }
542
543    /// Writes `buf` to file, then updates file table to point to updated data.
544    async fn write_file_internal(
545        &mut self,
546        file_id: FileId,
547        buf: &[u8],
548        file_table_fcb: &mut ResolvedFileControlBlock,
549        data_fcb: &mut ResolvedFileControlBlock,
550        should_encrypt: bool,
551        should_write_file_table: bool,
552    ) -> Result<(), Error> {
553        let data_nonce_auth_tag = if should_encrypt {
554            let data_encryption_key = {
555                let mut encryption_key = VmgsDatastoreKey::new_zeroed();
556                getrandom::fill(&mut encryption_key).expect("rng failure");
557                encryption_key
558            };
559            let data_nonce = generate_nonce();
560            let mut data_auth_tag = VmgsAuthTag::new_zeroed();
561
562            if let Err(e) = self
563                .write_encrypted_data(
564                    data_fcb.block_offset,
565                    &data_encryption_key,
566                    &data_nonce,
567                    buf,
568                    &mut data_auth_tag,
569                )
570                .await
571            {
572                self.logger
573                    .log_event_fatal(VmgsLogEvent::AccessFailed)
574                    .await;
575
576                return Err(e);
577            }
578
579            // Update the data file control block.
580            data_fcb.nonce.copy_from_slice(&data_nonce);
581            data_fcb
582                .encryption_key
583                .copy_from_slice(&data_encryption_key);
584            data_fcb.authentication_tag.copy_from_slice(&data_auth_tag);
585            Some((data_nonce, data_auth_tag))
586        } else {
587            // Write the file contents to the newly allocated space
588            if let Err(e) = self
589                .storage
590                .write_block(block_count_to_byte_count(data_fcb.block_offset), buf)
591                .await
592            {
593                self.logger
594                    .log_event_fatal(VmgsLogEvent::AccessFailed)
595                    .await;
596
597                return Err(Error::WriteDisk(e));
598            }
599            None
600        };
601
602        // Initialize the new file table with current metadata for all files.
603        let mut new_file_table = VmgsFileTable::new_zeroed();
604        for (file_id, fcb) in self.fcbs.iter() {
605            let new_file_entry = &mut new_file_table.entries[*file_id];
606
607            new_file_entry.offset = fcb.block_offset;
608            new_file_entry.allocation_size = fcb.allocated_blocks.get();
609            new_file_entry.valid_data_size = fcb.valid_bytes;
610
611            if self.version >= VMGS_VERSION_3_0 {
612                new_file_entry.nonce.copy_from_slice(&fcb.nonce);
613                new_file_entry
614                    .authentication_tag
615                    .copy_from_slice(&fcb.authentication_tag);
616            }
617        }
618
619        // Fill in the metadata for the file being written.
620        let file_entry = &mut new_file_table.entries[file_id];
621        *file_entry = vmgs_format::VmgsFileEntry {
622            offset: data_fcb.block_offset,
623            allocation_size: data_fcb.allocated_blocks.get(),
624            valid_data_size: buf.len() as u64,
625            ..vmgs_format::VmgsFileEntry::new_zeroed()
626        };
627
628        if let Some((data_nonce, data_auth_tag)) = data_nonce_auth_tag {
629            // Fill in the V3 fields of the file table entry.
630            file_entry.nonce.copy_from_slice(&data_nonce);
631            file_entry
632                .authentication_tag
633                .copy_from_slice(&data_auth_tag);
634        }
635
636        // Fill in the metadata for the new file table itself (file ID 0)
637        let file_table_entry = &mut new_file_table.entries[FileId::FILE_TABLE];
638        *file_table_entry = vmgs_format::VmgsFileEntry {
639            offset: file_table_fcb.block_offset,
640            allocation_size: file_table_fcb.allocated_blocks.get(),
641            valid_data_size: file_table_fcb.valid_bytes,
642            ..vmgs_format::VmgsFileEntry::new_zeroed()
643        };
644
645        if should_write_file_table {
646            // Write out the new file table.
647            if let Err(e) = self
648                .storage
649                .write_block(
650                    block_count_to_byte_count(file_table_fcb.block_offset),
651                    new_file_table.as_bytes(),
652                )
653                .await
654            {
655                self.logger
656                    .log_event_fatal(VmgsLogEvent::AccessFailed)
657                    .await;
658
659                return Err(Error::WriteDisk(e));
660            }
661        }
662
663        // Update the in-memory file control blocks. Updating file_control_block last ensures
664        // operation to be atomic, in case the program crashes on the write above or any intermediate operation
665
666        self.fcbs.insert(FileId::FILE_TABLE, *file_table_fcb);
667        self.fcbs.insert(file_id, *data_fcb);
668
669        Ok(())
670    }
671
672    /// Copies current file metadata to an extended file table structure.
673    fn fill_extended_file_table(
674        &mut self,
675        new_extended_file_table: &mut VmgsExtendedFileTable,
676    ) -> Result<(), Error> {
677        *new_extended_file_table = VmgsExtendedFileTable::new_zeroed();
678        for (file_id, fcb) in self.fcbs.iter_mut() {
679            let extended_file_entry = &mut new_extended_file_table.entries[*file_id];
680            extended_file_entry.attributes = fcb.attributes;
681            extended_file_entry
682                .encryption_key
683                .copy_from_slice(&fcb.encryption_key);
684        }
685
686        Ok(())
687    }
688
689    /// Update and write new header to storage device.
690    async fn update_header(&mut self, new_header: &mut VmgsHeader) -> Result<(), Error> {
691        // Wrapping prevents integer overflow checks
692        new_header.sequence = self.active_header_sequence_number.wrapping_add(1);
693        new_header.checksum = 0;
694        new_header.checksum = compute_crc32(new_header.as_bytes());
695
696        let new_header_index = if self.active_header_index == 0 { 1 } else { 0 };
697
698        self.storage
699            .write_block(
700                new_header_index as u64 * self.storage.aligned_header_size(),
701                new_header.as_bytes(),
702            )
703            .await
704            .map_err(Error::WriteDisk)?;
705        self.set_active_header(new_header_index, new_header.sequence);
706        Ok(())
707    }
708
709    /// Sets the active header index and sequence number.
710    fn set_active_header(
711        &mut self,
712        active_header_index: usize,
713        active_header_sequence_number: u32,
714    ) {
715        assert!(active_header_index < 2);
716        self.active_header_index = active_header_index;
717        self.active_header_sequence_number = active_header_sequence_number;
718    }
719
720    /// Reads the specified `file_id`, decrypting its contents.
721    pub async fn read_file(&mut self, file_id: FileId) -> Result<Vec<u8>, Error> {
722        self.read_file_inner(file_id, true).await
723    }
724
725    /// Reads the specified `file_id`, but does not decrypt the contents.
726    pub async fn read_file_raw(&mut self, file_id: FileId) -> Result<Vec<u8>, Error> {
727        self.read_file_inner(file_id, false).await
728    }
729
730    async fn read_file_inner(&mut self, file_id: FileId, decrypt: bool) -> Result<Vec<u8>, Error> {
731        #[cfg(feature = "inspect")]
732        self.stats
733            .read
734            .entry(file_id)
735            .or_default()
736            .attempt
737            .increment();
738
739        let file_info = self.get_file_info(file_id)?;
740        if file_id == FileId::FILE_TABLE {
741            return Err(Error::FileId);
742        }
743
744        let fcb = self.fcbs[&file_id];
745
746        let mut buf = vec![0; file_info.valid_bytes as usize];
747
748        let file_is_encrypted = fcb.attributes.encrypted() || fcb.attributes.authenticated();
749
750        if decrypt
751            && self.version >= VMGS_VERSION_3_0
752            && self.encryption_algorithm != EncryptionAlgorithm::NONE
753            && file_is_encrypted
754            && self.active_datastore_key_index.is_some()
755        {
756            if let Err(e) = self
757                .read_decrypted_data(
758                    fcb.block_offset,
759                    &fcb.encryption_key,
760                    &fcb.nonce,
761                    &fcb.authentication_tag,
762                    &mut buf,
763                )
764                .await
765            {
766                self.logger
767                    .log_event_fatal(VmgsLogEvent::AccessFailed)
768                    .await;
769
770                return Err(e);
771            }
772        } else if file_is_encrypted && decrypt {
773            return Err(Error::ReadEncrypted);
774        } else {
775            let byte_offset = block_count_to_byte_count(fcb.block_offset);
776            if let Err(e) = self.storage.read_block(byte_offset, &mut buf).await {
777                self.logger
778                    .log_event_fatal(VmgsLogEvent::AccessFailed)
779                    .await;
780
781                return Err(Error::ReadDisk(e));
782            }
783        }
784
785        #[cfg(feature = "inspect")]
786        self.stats
787            .read
788            .entry(file_id)
789            .or_default()
790            .resolved
791            .increment();
792
793        Ok(buf)
794    }
795
796    /// Writes `buf` to a file_id without encrypting it.
797    ///
798    /// If the file is already encrypted, this will return a failure. Use
799    /// [`Self::write_file_allow_overwrite_encrypted`] if you want to allow
800    /// this.
801    ///
802    /// To write encrypted data, use `write_file_encrypted` instead.
803    pub async fn write_file(&mut self, file_id: FileId, buf: &[u8]) -> Result<(), Error> {
804        self.write_file_inner(file_id, buf, false).await
805    }
806
807    /// Writes `buf` to a file_id without encrypting it, allowing overrites of
808    /// an already-encrypted file.
809    pub async fn write_file_allow_overwrite_encrypted(
810        &mut self,
811        file_id: FileId,
812        buf: &[u8],
813    ) -> Result<(), Error> {
814        self.write_file_inner(file_id, buf, true).await
815    }
816
817    async fn write_file_inner(
818        &mut self,
819        file_id: FileId,
820        buf: &[u8],
821        overwrite_encrypted: bool,
822    ) -> Result<(), Error> {
823        #[cfg(feature = "inspect")]
824        self.stats
825            .write
826            .entry(file_id)
827            .or_default()
828            .attempt
829            .increment();
830
831        if file_id == FileId::FILE_TABLE {
832            return Err(Error::FileId);
833        }
834        if buf.len() > vmgs_format::VMGS_MAX_FILE_SIZE_BYTES as usize {
835            return Err(Error::WriteFileLength);
836        }
837        let mut blocks_to_allocate =
838            (round_up_count(buf.len(), VMGS_BYTES_PER_BLOCK) / VMGS_BYTES_PER_BLOCK as u64) as u32;
839        // Always allocate at least one block, to allow for zero sized data buffers
840        if blocks_to_allocate == 0 {
841            blocks_to_allocate = 1;
842        }
843        if blocks_to_allocate as u64 > vmgs_format::VMGS_MAX_FILE_SIZE_BLOCKS {
844            return Err(Error::WriteFileBlocks);
845        }
846        if self
847            .fcbs
848            .get(&file_id)
849            .map(|fcb| fcb.attributes.encrypted())
850            .unwrap_or(false)
851        {
852            if overwrite_encrypted {
853                tracing::warn!(
854                    CVM_ALLOWED,
855                    "overwriting encrypted file with plaintext data!"
856                )
857            } else {
858                return Err(Error::OverwriteEncrypted);
859            }
860        }
861
862        // Allocate space for the new file contents and the new file table.
863        // On success, the contents of the temporary FCBs are copied to the existing FCBs.
864        let mut temp_fcbs: Vec<ResolvedFileControlBlock> = Vec::new();
865        // file_table_fcb
866        self.allocate_space(
867            VMGS_FILE_TABLE_BLOCK_SIZE,
868            &mut temp_fcbs,
869            block_count_to_byte_count(VMGS_FILE_TABLE_BLOCK_SIZE),
870        )?;
871        // data_fcb
872        self.allocate_space(blocks_to_allocate, &mut temp_fcbs, buf.len() as u64)?;
873
874        // extended_file_table_fcb is Some() if we should write to extended file table.
875        let extended_file_table_fcb = if self.encryption_algorithm == EncryptionAlgorithm::NONE
876            || self
877                .fcbs
878                .get(&file_id)
879                .map(|f| f.attributes == FileAttribute::new())
880                .unwrap_or(true)
881        {
882            None
883        } else {
884            self.allocate_space(
885                VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE,
886                &mut temp_fcbs,
887                block_count_to_byte_count(VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE),
888            )?;
889            temp_fcbs.last_mut().unwrap().attributes = FileAttribute::new()
890                .with_encrypted(true)
891                .with_authenticated(true);
892
893            Some(temp_fcbs.pop().unwrap())
894        };
895
896        // the C++ code originally implemented gsl::finally to deallocate these from the allocation list
897        // on exception. However, currently with this being a single threaded Rust crate, that shouldn't be
898        // needed. When switching to multithreading, that may change.
899        let mut data_fcb = temp_fcbs.pop().unwrap();
900        let mut file_table_fcb = temp_fcbs.pop().unwrap();
901
902        data_fcb.attributes = FileAttribute::new();
903
904        // Write the file contents to the newly allocated space.
905        self.write_file_internal(
906            file_id,
907            buf,
908            &mut file_table_fcb,
909            &mut data_fcb,
910            false,
911            // Write the file table to the storage since there is no need to manipulate the extended file table.
912            extended_file_table_fcb.is_none(),
913        )
914        .await?;
915
916        if let Some(mut extended_table_fcb) = extended_file_table_fcb {
917            // Initialize the new extended file table with current metadata for all files.
918            let mut new_extended_file_table = VmgsExtendedFileTable::new_zeroed();
919            self.fill_extended_file_table(&mut new_extended_file_table)?;
920
921            // Fill in the metadata for the new extended table.
922            let extended_file_entry = &mut new_extended_file_table.entries[file_id];
923            extended_file_entry.attributes = data_fcb.attributes;
924            extended_file_entry
925                .encryption_key
926                .copy_from_slice(&data_fcb.encryption_key);
927
928            // Write the extended file table to the newly allocated space.
929            self.write_file_internal(
930                FileId::EXTENDED_FILE_TABLE,
931                new_extended_file_table.as_bytes(),
932                &mut file_table_fcb,
933                &mut extended_table_fcb,
934                true,
935                true,
936            )
937            .await?;
938        }
939
940        // Data must be hardened on persistent storage before the header is updated.
941        self.storage.flush().await.map_err(Error::FlushDisk)?;
942
943        // Prepare a new header.
944        let mut new_header = self.prepare_new_header(&file_table_fcb);
945
946        if self.encryption_algorithm != EncryptionAlgorithm::NONE {
947            if let Some(extended_table_fcb) = extended_file_table_fcb {
948                let mut metadata_key_auth_tag = VmgsAuthTag::new_zeroed();
949                self.metadata_key
950                    .copy_from_slice(&extended_table_fcb.encryption_key);
951
952                let current_index = self.active_datastore_key_index.unwrap();
953
954                increment_nonce(&mut self.encrypted_metadata_keys[current_index].nonce)?;
955
956                let encrypted_metadata_key = encrypt_metadata_key(
957                    &self.datastore_keys[current_index],
958                    &self.encrypted_metadata_keys[current_index].nonce,
959                    &self.metadata_key,
960                    &mut metadata_key_auth_tag,
961                )?;
962
963                self.encrypted_metadata_keys[current_index]
964                    .authentication_tag
965                    .copy_from_slice(&metadata_key_auth_tag);
966                self.encrypted_metadata_keys[current_index]
967                    .encryption_key
968                    .copy_from_slice(&encrypted_metadata_key);
969            }
970
971            new_header.encryption_algorithm = self.encryption_algorithm;
972            new_header
973                .metadata_keys
974                .copy_from_slice(&self.encrypted_metadata_keys);
975        }
976
977        self.update_header(&mut new_header).await?;
978
979        #[cfg(feature = "inspect")]
980        self.stats
981            .write
982            .entry(file_id)
983            .or_default()
984            .resolved
985            .increment();
986
987        Ok(())
988    }
989
990    /// Encrypts `buf` and writes the encrypted payload to a file_id if the VMGS file has encryption configured.
991    /// If the VMGS doesn't have encryption configured, will do a plaintext write instead.
992    #[cfg(with_encryption)]
993    pub async fn write_file_encrypted(&mut self, file_id: FileId, buf: &[u8]) -> Result<(), Error> {
994        if file_id == FileId::FILE_TABLE {
995            return Err(Error::FileId);
996        }
997        if buf.len() > vmgs_format::VMGS_MAX_FILE_SIZE_BYTES as usize {
998            return Err(Error::WriteFileLength);
999        }
1000        let mut blocks_to_allocate =
1001            (round_up_count(buf.len(), VMGS_BYTES_PER_BLOCK) / VMGS_BYTES_PER_BLOCK as u64) as u32;
1002        // Always allocate at least one block, to allow for zero sized data buffers
1003        if blocks_to_allocate == 0 {
1004            blocks_to_allocate = 1;
1005        }
1006        if blocks_to_allocate as u64 > vmgs_format::VMGS_MAX_FILE_SIZE_BLOCKS {
1007            return Err(Error::WriteFileBlocks);
1008        }
1009        if self.encryption_algorithm == EncryptionAlgorithm::NONE {
1010            tracing::trace!(
1011                CVM_ALLOWED,
1012                "VMGS file not encrypted, performing plaintext write"
1013            );
1014            return self.write_file(file_id, buf).await;
1015        }
1016
1017        // Allocate space for the new file contents and the new file table.
1018        // On success, the contents of the temporary FCBs are copied to the existing FCBs.
1019        let mut temp_fcbs: Vec<ResolvedFileControlBlock> = Vec::new();
1020        // file_table_fcb
1021        self.allocate_space(
1022            VMGS_FILE_TABLE_BLOCK_SIZE,
1023            &mut temp_fcbs,
1024            block_count_to_byte_count(VMGS_FILE_TABLE_BLOCK_SIZE),
1025        )?;
1026        // data_fcb
1027        self.allocate_space(blocks_to_allocate, &mut temp_fcbs, buf.len() as u64)?;
1028
1029        let mut extended_file_table_fcb = {
1030            self.allocate_space(
1031                VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE,
1032                &mut temp_fcbs,
1033                block_count_to_byte_count(VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE),
1034            )?;
1035            temp_fcbs.last_mut().unwrap().attributes = FileAttribute::new()
1036                .with_encrypted(true)
1037                .with_authenticated(true);
1038            temp_fcbs.pop().unwrap()
1039        };
1040
1041        // the C++ code originally implemented gsl::finally to deallocate these from the allocation list
1042        // on exception. However, currently with this being a single threaded Rust crate, that shouldn't be
1043        // needed. When switching to multithreading, that may change.
1044        let mut data_fcb = temp_fcbs.pop().unwrap();
1045        let mut file_table_fcb = temp_fcbs.pop().unwrap();
1046
1047        data_fcb.attributes = FileAttribute::new()
1048            .with_encrypted(true)
1049            .with_authenticated(true);
1050
1051        // Write the file contents to the newly allocated space.
1052        self.write_file_internal(
1053            file_id,
1054            buf,
1055            &mut file_table_fcb,
1056            &mut data_fcb,
1057            true,
1058            false,
1059        )
1060        .await?;
1061
1062        // Initialize the new extended file table with current metadata for all files.
1063        let mut new_extended_file_table = VmgsExtendedFileTable::new_zeroed();
1064        self.fill_extended_file_table(&mut new_extended_file_table)?;
1065
1066        // Fill in the metadata for the new extended table.
1067        let extended_file_entry = &mut new_extended_file_table.entries[file_id];
1068        extended_file_entry.attributes = data_fcb.attributes;
1069        extended_file_entry
1070            .encryption_key
1071            .copy_from_slice(&data_fcb.encryption_key);
1072
1073        // Write the extended file table to the newly allocated space.
1074        self.write_file_internal(
1075            FileId::EXTENDED_FILE_TABLE,
1076            new_extended_file_table.as_bytes(),
1077            &mut file_table_fcb,
1078            &mut extended_file_table_fcb,
1079            true,
1080            true,
1081        )
1082        .await?;
1083
1084        // Data must be hardened on persistent storage before the header is updated.
1085        self.storage.flush().await.map_err(Error::FlushDisk)?;
1086
1087        // Prepare a new header.
1088        let mut new_header = self.prepare_new_header(&file_table_fcb);
1089
1090        if self.encryption_algorithm != EncryptionAlgorithm::NONE {
1091            let mut metadata_key_auth_tag = VmgsAuthTag::new_zeroed();
1092            self.metadata_key
1093                .copy_from_slice(&extended_file_table_fcb.encryption_key);
1094
1095            let active_key = self.active_datastore_key_index.unwrap();
1096            increment_nonce(&mut self.encrypted_metadata_keys[active_key].nonce)?;
1097
1098            let encrypted_metadata_key = encrypt_metadata_key(
1099                &self.datastore_keys[active_key],
1100                &self.encrypted_metadata_keys[active_key].nonce,
1101                &self.metadata_key,
1102                &mut metadata_key_auth_tag,
1103            )?;
1104
1105            self.encrypted_metadata_keys[active_key]
1106                .authentication_tag
1107                .copy_from_slice(&metadata_key_auth_tag);
1108            self.encrypted_metadata_keys[active_key]
1109                .encryption_key
1110                .copy_from_slice(&encrypted_metadata_key);
1111
1112            new_header.encryption_algorithm = self.encryption_algorithm;
1113            new_header
1114                .metadata_keys
1115                .copy_from_slice(&self.encrypted_metadata_keys);
1116        }
1117
1118        self.update_header(&mut new_header).await
1119    }
1120
1121    /// Decrypts the extended file table by the encryption_key and
1122    /// updates the related metadata in memory.
1123    #[cfg(with_encryption)]
1124    pub async fn unlock_with_encryption_key(&mut self, encryption_key: &[u8]) -> Result<(), Error> {
1125        if self.version < VMGS_VERSION_3_0 {
1126            return Err(Error::Other(anyhow!(
1127                "unlock_with_encryption_key() not supported with VMGS version"
1128            )));
1129        }
1130        if self.encryption_algorithm == EncryptionAlgorithm::NONE {
1131            return Err(Error::Other(anyhow!(
1132                "unlock_with_encryption_key() not supported with None EncryptionAlgorithm"
1133            )));
1134        }
1135
1136        // Iterate through two metadata keys and get the index of the valid key which can be successfully
1137        // decrypted by the encryption_key, as well as set the decrypted key as the VMGS's metadata key
1138        let mut valid_index = None;
1139        let mut errs = [None, None];
1140
1141        for (i, key) in self.encrypted_metadata_keys.iter().enumerate() {
1142            let result = decrypt_metadata_key(
1143                encryption_key,
1144                &key.nonce,
1145                &key.encryption_key,
1146                &key.authentication_tag,
1147            );
1148
1149            match result {
1150                Ok(metadata_key) => {
1151                    self.metadata_key.copy_from_slice(&metadata_key);
1152                    valid_index = Some(i);
1153                    break;
1154                }
1155                Err(err) => {
1156                    errs[i] = Some(err);
1157                }
1158            }
1159        }
1160
1161        let valid_index = match valid_index {
1162            Some(idx) => idx,
1163            None => {
1164                tracing::error!(
1165                    CVM_ALLOWED,
1166                    error = &errs[0].take().unwrap() as &dyn std::error::Error,
1167                    "first index failed to decrypt",
1168                );
1169                tracing::error!(
1170                    CVM_ALLOWED,
1171                    error = &errs[1].take().unwrap() as &dyn std::error::Error,
1172                    "second index failed to decrypt",
1173                );
1174                return Err(Error::Other(anyhow::anyhow!(
1175                    "failed to use the root key provided to decrypt VMGS metadata key"
1176                )));
1177            }
1178        };
1179        let extended_file_header = self.fcbs[&FileId::EXTENDED_FILE_TABLE];
1180        let extended_file_table_size_bytes =
1181            block_count_to_byte_count(extended_file_header.allocated_blocks.get());
1182        let mut extended_file_table_buffer = vec![0; extended_file_table_size_bytes as usize];
1183        let self_metadata_key = self.metadata_key;
1184
1185        // Read and decrypt the extended file table
1186        self.read_decrypted_data(
1187            extended_file_header.block_offset,
1188            &self_metadata_key,
1189            &extended_file_header.nonce,
1190            &extended_file_header.authentication_tag,
1191            &mut extended_file_table_buffer,
1192        )
1193        .await
1194        .context("failed to decrypt extended file table")?;
1195
1196        // Update the cached extended file table
1197        let extended_file_table =
1198            VmgsExtendedFileTable::read_from_prefix(extended_file_table_buffer.as_bytes())
1199                .map_err(|_| anyhow!("Invalid decrypted extended file table"))? // TODO: zerocopy: use result (https://github.com/microsoft/openvmm/issues/759)
1200                .0;
1201        for (file_id, fcb) in self.fcbs.iter_mut() {
1202            fcb.attributes = extended_file_table.entries[*file_id].attributes;
1203            fcb.encryption_key = extended_file_table.entries[*file_id].encryption_key;
1204        }
1205
1206        self.datastore_keys[valid_index].copy_from_slice(encryption_key);
1207        self.active_datastore_key_index = Some(valid_index);
1208
1209        Ok(())
1210    }
1211
1212    /// Encrypts the plaintext data and writes the encrypted data to the storage.
1213    #[cfg_attr(not(with_encryption), expect(unused_variables))]
1214    async fn write_encrypted_data(
1215        &mut self,
1216        block_offset: u32,
1217        encryption_key: &[u8],
1218        nonce: &[u8],
1219        plaintext_data: &[u8],
1220        authentication_tag: &mut [u8],
1221    ) -> Result<(), Error> {
1222        #[cfg(not(with_encryption))]
1223        unreachable!("Encryption requires the encryption feature");
1224        #[cfg(with_encryption)]
1225        {
1226            let encrypted_text = crate::encrypt::vmgs_encrypt(
1227                encryption_key,
1228                nonce,
1229                plaintext_data,
1230                authentication_tag,
1231            )?;
1232
1233            // Write the encrypted file contents to the newly allocated space.
1234            self.storage
1235                .write_block(block_count_to_byte_count(block_offset), &encrypted_text)
1236                .await
1237                .map_err(Error::WriteDisk)?;
1238
1239            Ok(())
1240        }
1241    }
1242
1243    /// Decrypts the encrypted data and reads it to the buffer.
1244    #[cfg_attr(not(with_encryption), expect(unused_variables))]
1245    async fn read_decrypted_data(
1246        &mut self,
1247        block_offset: u32,
1248        decryption_key: &[u8],
1249        nonce: &[u8],
1250        authentication_tag: &[u8],
1251        plaintext_data: &mut [u8],
1252    ) -> Result<(), Error> {
1253        #[cfg(not(with_encryption))]
1254        unreachable!("Encryption requires the encryption feature");
1255        #[cfg(with_encryption)]
1256        {
1257            // Read and decrypt the encrypted file contents.
1258            let mut buf = vec![0; plaintext_data.len()];
1259
1260            self.storage
1261                .read_block(block_count_to_byte_count(block_offset), &mut buf)
1262                .await
1263                .map_err(Error::ReadDisk)?;
1264
1265            // sanity check: encrypted data should never be all zeros. if we
1266            // find that it is all-zeroes, then that's indicative of some kind
1267            // of logic error / data corruption
1268            if buf.iter().all(|x| *x == 0) {
1269                return Err(Error::InvalidFormat("encrypted data is all-zeros".into()));
1270            }
1271
1272            let decrypted_text =
1273                crate::encrypt::vmgs_decrypt(decryption_key, nonce, &buf, authentication_tag)?;
1274            if decrypted_text.len() != plaintext_data.len() {
1275                return Err(Error::Other(anyhow!(
1276                    "Decrypt error, slice sizes should match."
1277                )));
1278            }
1279            plaintext_data.copy_from_slice(&decrypted_text);
1280
1281            Ok(())
1282        }
1283    }
1284
1285    /// Associates a new root key with the data store and removes the old
1286    /// encryption key, if it exists. If two keys already exist, the
1287    /// inactive key is removed first.
1288    #[cfg(with_encryption)]
1289    pub async fn update_encryption_key(
1290        &mut self,
1291        encryption_key: &[u8],
1292        encryption_algorithm: EncryptionAlgorithm,
1293    ) -> Result<(), Error> {
1294        let old_index = self.active_datastore_key_index;
1295
1296        match self
1297            .add_new_encryption_key(encryption_key, encryption_algorithm)
1298            .await
1299        {
1300            Ok(_) => {}
1301            Err(Error::DatastoreKeysFull) => {
1302                if let Some(old_index) = old_index {
1303                    let inactive_index = if old_index == 0 { 1 } else { 0 };
1304                    tracing::warn!(CVM_ALLOWED, inactive_index, "removing inactive key");
1305                    self.remove_encryption_key(inactive_index).await?;
1306                    tracing::trace!(CVM_ALLOWED, "attempting to add the key again");
1307                    self.add_new_encryption_key(encryption_key, encryption_algorithm)
1308                        .await?;
1309                } else {
1310                    return Err(Error::NoActiveDatastoreKey);
1311                }
1312            }
1313            Err(e) => return Err(e),
1314        };
1315
1316        if let Some(old_index) = old_index {
1317            self.remove_encryption_key(old_index).await?;
1318        }
1319
1320        Ok(())
1321    }
1322
1323    /// Associates a new root key with the data store.
1324    #[cfg(with_encryption)]
1325    async fn add_new_encryption_key(
1326        &mut self,
1327        encryption_key: &[u8],
1328        encryption_algorithm: EncryptionAlgorithm,
1329    ) -> Result<(), Error> {
1330        if self.version < VMGS_VERSION_3_0 {
1331            return Err(Error::Other(anyhow!(
1332                "add_new_encryption_key() not supported with VMGS version"
1333            )));
1334        }
1335        if self.encryption_algorithm != EncryptionAlgorithm::NONE
1336            && self.active_datastore_key_index.is_none()
1337        {
1338            return Err(Error::Other(anyhow!(
1339                "add_new_encryption_key() invalid datastore key index"
1340            )));
1341        }
1342        if self.datastore_key_count == self.datastore_keys.len() as u8 {
1343            return Err(Error::DatastoreKeysFull);
1344        }
1345        if is_empty_key(encryption_key) {
1346            return Err(Error::Other(anyhow!("Trying to add empty encryption key")));
1347        }
1348        if encryption_algorithm == EncryptionAlgorithm::NONE {
1349            return Err(Error::Other(anyhow!(
1350                "Encryption not supported for VMGS file"
1351            )));
1352        }
1353        if self.encryption_algorithm != EncryptionAlgorithm::NONE
1354            && encryption_algorithm != self.encryption_algorithm
1355        {
1356            return Err(Error::Other(anyhow!(
1357                "Encryption algorithm provided to add_new_encryption_key does not match VMGS's encryption algorithm."
1358            )));
1359        }
1360
1361        let mut new_key_index = 0;
1362        let mut new_metadata_key = self.metadata_key;
1363        if self.datastore_key_count == 0 {
1364            // Allocate space for the new file table and the new extended file table.
1365            // Two temporary FCBs will be added to the allocation list, and will be unlinked
1366            // from the allocation list no matter whether the function succeeds or fails.
1367            // On success, the contents of the temporary FCBs are copied to the existing FCBs.
1368            let mut temp_fcbs: Vec<ResolvedFileControlBlock> = Vec::new();
1369            self.allocate_space(
1370                VMGS_FILE_TABLE_BLOCK_SIZE,
1371                &mut temp_fcbs,
1372                block_count_to_byte_count(VMGS_FILE_TABLE_BLOCK_SIZE),
1373            )?;
1374
1375            self.allocate_space(
1376                VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE,
1377                &mut temp_fcbs,
1378                block_count_to_byte_count(VMGS_EXTENDED_FILE_TABLE_BLOCK_SIZE),
1379            )?;
1380
1381            let mut extended_file_table_fcb = temp_fcbs.pop().unwrap();
1382            let mut file_table_fcb = temp_fcbs.pop().unwrap();
1383
1384            extended_file_table_fcb.attributes = FileAttribute::new()
1385                .with_encrypted(true)
1386                .with_authenticated(true);
1387
1388            // Initialize a new extended file table.
1389            let new_extended_file_table = VmgsExtendedFileTable::new_zeroed();
1390            // Write the extended file table to the newly allocated space
1391            self.write_file_internal(
1392                FileId::EXTENDED_FILE_TABLE,
1393                new_extended_file_table.as_bytes(),
1394                &mut file_table_fcb,
1395                &mut extended_file_table_fcb,
1396                true,
1397                true,
1398            )
1399            .await?;
1400
1401            new_metadata_key
1402                .copy_from_slice(&self.fcbs[&FileId::EXTENDED_FILE_TABLE].encryption_key);
1403        } else if self.active_datastore_key_index == Some(0) {
1404            new_key_index = 1;
1405        }
1406
1407        // Prepare a new header.
1408        let mut new_header = self.prepare_new_header(&self.fcbs[&FileId::FILE_TABLE]);
1409        new_header.encryption_algorithm = EncryptionAlgorithm::AES_GCM;
1410
1411        // Use the new datastore key to encrypt the metadata key.
1412        let metadata_key_nonce = generate_nonce();
1413        let mut metadata_key_auth_tag = VmgsAuthTag::new_zeroed();
1414        let encrypted_metadata_key = encrypt_metadata_key(
1415            encryption_key,
1416            &metadata_key_nonce,
1417            &new_metadata_key,
1418            &mut metadata_key_auth_tag,
1419        )?;
1420
1421        self.encrypted_metadata_keys[new_key_index]
1422            .nonce
1423            .copy_from_slice(&metadata_key_nonce);
1424        self.encrypted_metadata_keys[new_key_index]
1425            .authentication_tag
1426            .copy_from_slice(&metadata_key_auth_tag);
1427        self.encrypted_metadata_keys[new_key_index]
1428            .encryption_key
1429            .copy_from_slice(&encrypted_metadata_key);
1430
1431        new_header
1432            .metadata_keys
1433            .copy_from_slice(&self.encrypted_metadata_keys);
1434
1435        // Update the header on the storage device
1436        self.update_header(&mut new_header).await?;
1437
1438        // Update the cached DataStore key.
1439        self.datastore_keys[new_key_index].copy_from_slice(encryption_key);
1440        self.metadata_key.copy_from_slice(&new_metadata_key);
1441        self.datastore_key_count += 1;
1442        self.encryption_algorithm = encryption_algorithm;
1443        self.active_datastore_key_index = Some(new_key_index);
1444
1445        Ok(())
1446    }
1447
1448    /// Disassociates the root key at the specified index from the data store.
1449    #[cfg(with_encryption)]
1450    async fn remove_encryption_key(&mut self, key_index: usize) -> Result<(), Error> {
1451        if self.version < VMGS_VERSION_3_0 {
1452            return Err(Error::Other(anyhow!(
1453                "remove_encryption_key() not supported with VMGS version."
1454            )));
1455        }
1456        if self.encryption_algorithm != EncryptionAlgorithm::NONE
1457            && self.active_datastore_key_index.is_none()
1458        {
1459            return Err(Error::Other(anyhow!(
1460                "remove_encryption_key() invalid datastore key index or encryption algorithm."
1461            )));
1462        }
1463        if self.datastore_key_count != self.datastore_keys.len() as u8
1464            && self.active_datastore_key_index != Some(key_index)
1465        {
1466            return Err(Error::Other(anyhow!(
1467                "remove_encryption_key() invalid key_index"
1468            )));
1469        }
1470
1471        // Remove the corresponding datastore_key
1472        self.datastore_keys[key_index].fill(0);
1473
1474        // Remove the corresponding metadata_key
1475        self.encrypted_metadata_keys[key_index] = VmgsEncryptionKey::new_zeroed();
1476
1477        // Prepare a new header
1478        let mut new_header = self.prepare_new_header(&self.fcbs[&FileId::FILE_TABLE]);
1479        new_header
1480            .metadata_keys
1481            .copy_from_slice(&self.encrypted_metadata_keys);
1482
1483        // Set the encryption algorithm to none, when there is only one valid metadata key before removal
1484        if self.datastore_key_count == 1 {
1485            new_header.encryption_algorithm = EncryptionAlgorithm::NONE;
1486        } else {
1487            new_header.encryption_algorithm = self.encryption_algorithm;
1488        }
1489
1490        // Update the header on the storage device
1491        self.update_header(&mut new_header).await?;
1492
1493        // Update cached metadata
1494        if self.datastore_key_count == 1 {
1495            self.encryption_algorithm = EncryptionAlgorithm::NONE;
1496            self.datastore_key_count = 0;
1497            self.active_datastore_key_index = None;
1498        } else {
1499            self.datastore_key_count = 1;
1500
1501            let new_active_datastore_key_index = if key_index == 0 { 1 } else { 0 };
1502            if is_empty_key(&self.datastore_keys[new_active_datastore_key_index]) {
1503                self.active_datastore_key_index = None;
1504            } else {
1505                self.active_datastore_key_index = Some(new_active_datastore_key_index);
1506            }
1507        }
1508
1509        Ok(())
1510    }
1511
1512    /// Gets the encryption algorithm of the VMGS
1513    pub fn get_encryption_algorithm(&self) -> EncryptionAlgorithm {
1514        self.encryption_algorithm
1515    }
1516
1517    /// Whether the VMGS file is encrypted
1518    pub fn is_encrypted(&self) -> bool {
1519        self.encryption_algorithm != EncryptionAlgorithm::NONE
1520    }
1521
1522    /// Whether the VMGS file was provisioned during the most recent boot
1523    pub fn was_provisioned_this_boot(&self) -> bool {
1524        self.provisioned_this_boot
1525    }
1526
1527    fn prepare_new_header(&self, file_table_fcb: &ResolvedFileControlBlock) -> VmgsHeader {
1528        VmgsHeader {
1529            signature: VMGS_SIGNATURE,
1530            version: self.version,
1531            header_size: size_of::<VmgsHeader>() as u32,
1532            file_table_offset: file_table_fcb.block_offset,
1533            file_table_size: file_table_fcb.allocated_blocks.get(),
1534            markers: VmgsMarkers::new().with_reprovisioned(self.reprovisioned),
1535            ..VmgsHeader::new_zeroed()
1536        }
1537    }
1538
1539    async fn set_reprovisioned(&mut self, value: bool) -> Result<(), Error> {
1540        if self.reprovisioned != value {
1541            tracing::info!(reprovisioned = value, "update vmgs marker");
1542            self.reprovisioned = value;
1543            let mut new_header = self.prepare_new_header(&self.fcbs[&FileId::FILE_TABLE]);
1544            self.update_header(&mut new_header).await?;
1545        }
1546        Ok(())
1547    }
1548}
1549
1550/// Additional test-only functions for use in other crates that reveal
1551/// implmentation details of the vmgs datastore encryption keys.
1552#[cfg(feature = "test_helpers")]
1553mod test_helpers {
1554    use super::*;
1555
1556    impl Vmgs {
1557        /// Get the active datastore key index
1558        pub fn test_get_active_datastore_key_index(&self) -> Option<usize> {
1559            self.active_datastore_key_index
1560        }
1561
1562        /// Associates a new root key with the data store.
1563        #[cfg(with_encryption)]
1564        pub async fn test_add_new_encryption_key(
1565            &mut self,
1566            encryption_key: &[u8],
1567            encryption_algorithm: EncryptionAlgorithm,
1568        ) -> Result<(), Error> {
1569            self.add_new_encryption_key(encryption_key, encryption_algorithm)
1570                .await
1571        }
1572    }
1573}
1574
1575/// Attempt to read both headers and separately return any validation errors
1576pub async fn read_headers(
1577    disk: Disk,
1578) -> Result<(VmgsHeader, VmgsHeader), (Error, Option<(VmgsHeader, VmgsHeader)>)> {
1579    let mut storage = VmgsStorage::new(disk);
1580    match (storage.validate(), read_headers_inner(&mut storage).await) {
1581        (Ok(_), res) => res,
1582        (Err(e), res) => Err((Error::Initialization(e), res.ok())),
1583    }
1584}
1585
1586async fn read_headers_inner(
1587    storage: &mut VmgsStorage,
1588) -> Result<(VmgsHeader, VmgsHeader), (Error, Option<(VmgsHeader, VmgsHeader)>)> {
1589    // first_two_blocks will contain enough bytes to read the first two headers
1590    let mut first_two_blocks = [0; (VMGS_BYTES_PER_BLOCK * 2) as usize];
1591
1592    storage
1593        .read_block(0, &mut first_two_blocks)
1594        .await
1595        .map_err(|e| (Error::ReadDisk(e), None))?;
1596
1597    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)
1598    let header_2 =
1599        VmgsHeader::read_from_prefix(&first_two_blocks[storage.aligned_header_size() as usize..])
1600            .unwrap()
1601            .0; // TODO: zerocopy: from-prefix (read_from_prefix): use-rest-of-range (https://github.com/microsoft/openvmm/issues/759)
1602    let headers = (header_1, header_2);
1603
1604    if vmgs_is_v1(&first_two_blocks) {
1605        Err((Error::V1Format, Some(headers)))
1606    } else if vmgs_headers_empty(&headers.0, &headers.1) {
1607        Err((Error::EmptyFile, Some(headers)))
1608    } else {
1609        Ok(headers)
1610    }
1611}
1612
1613fn vmgs_is_v1(first_two_blocks: &[u8; 2 * VMGS_BYTES_PER_BLOCK as usize]) -> bool {
1614    const EFI_SIGNATURE: &[u8] = b"EFI PART";
1615    const EFI_SIGNATURE_OFFSET: usize = 512;
1616
1617    EFI_SIGNATURE
1618        == &first_two_blocks[EFI_SIGNATURE_OFFSET..EFI_SIGNATURE_OFFSET + EFI_SIGNATURE.len()]
1619}
1620
1621fn vmgs_headers_empty(header_1: &VmgsHeader, header_2: &VmgsHeader) -> bool {
1622    let empty_header = VmgsHeader::new_zeroed();
1623
1624    header_1.as_bytes() == empty_header.as_bytes() && header_2.as_bytes() == empty_header.as_bytes()
1625}
1626
1627/// Determines which header to use given the results of checking the
1628/// validity of each of the headers.
1629pub fn get_active_header(
1630    header_1: Result<&VmgsHeader, Error>,
1631    header_2: Result<&VmgsHeader, Error>,
1632) -> Result<usize, Error> {
1633    let active_header_index =
1634        if let (Ok(header_1), Ok(header_2)) = (header_1.as_deref(), header_2.as_deref()) {
1635            // If both headers are valid, find the header with the larger sequence number.
1636            // The header with the most recent sequence number is considered
1637            // the current copy. To handle integer overflow, a header with sequence number 0
1638            // is considered the current copy if and only if the other header contains 0xFFFFFFFF.
1639            if header_1.sequence == header_2.sequence.wrapping_add(1) {
1640                0
1641            } else if header_2.sequence == header_1.sequence.wrapping_add(1) {
1642                1
1643            } else {
1644                return Err(Error::CorruptFormat(format!(
1645                    "Invalid header sequence numbers. Header 1: {}, Header 2: {}",
1646                    header_1.sequence, header_2.sequence
1647                )));
1648            }
1649        } else if header_1.is_ok() {
1650            0
1651        } else if header_2.is_ok() {
1652            1
1653        } else {
1654            return Err(Error::InvalidFormat(format!(
1655                "No valid header: Header 1: {} Header 2: {}",
1656                header_1.err().unwrap(),
1657                header_2.err().unwrap()
1658            )));
1659        };
1660
1661    Ok(active_header_index)
1662}
1663
1664/// Validate the contents of header match VMGS file type.
1665pub fn validate_header(header: &VmgsHeader) -> Result<&VmgsHeader, Error> {
1666    if header.signature != VMGS_SIGNATURE {
1667        return Err(Error::InvalidFormat(String::from(
1668            "Invalid header signature",
1669        )));
1670    }
1671    if header.version != VMGS_VERSION_3_0 {
1672        return Err(Error::InvalidFormat(String::from("Invalid header version")));
1673    }
1674    if header.header_size != size_of::<VmgsHeader>() as u32 {
1675        return Err(Error::InvalidFormat(String::from("Invalid header size")));
1676    }
1677    if header.file_table_offset < VMGS_MIN_FILE_BLOCK_OFFSET {
1678        return Err(Error::InvalidFormat(String::from(
1679            "Invalid file table offset",
1680        )));
1681    }
1682    if header.file_table_size != VMGS_FILE_TABLE_BLOCK_SIZE {
1683        return Err(Error::InvalidFormat(String::from(
1684            "Invalid file table size",
1685        )));
1686    }
1687
1688    let stored_checksum = header.checksum;
1689    let mut zero_checksum_header = *header;
1690    zero_checksum_header.checksum = 0;
1691    let computed_checksum = compute_crc32(zero_checksum_header.as_bytes());
1692    if stored_checksum != computed_checksum {
1693        return Err(Error::CorruptFormat(String::from(
1694            "Invalid header checksum",
1695        )));
1696    }
1697    Ok(header)
1698}
1699
1700/// Initializes cached file metadata from the specified header. (File control blocks)
1701fn initialize_file_metadata(
1702    file_table: &VmgsFileTable,
1703    version: u32,
1704    block_capacity: u32,
1705) -> Result<HashMap<FileId, ResolvedFileControlBlock>, Error> {
1706    let file_entries = file_table.entries;
1707    let mut file_control_blocks = HashMap::new();
1708
1709    for (file_id, file_entry) in file_entries.iter().enumerate() {
1710        let file_id = FileId(file_id as u32);
1711
1712        // Check if the file is allocated.
1713        let Some(allocated_blocks) = NonZeroU32::new(file_entry.allocation_size) else {
1714            continue;
1715        };
1716
1717        // Validate the file offset.
1718        if file_entry.offset < VMGS_MIN_FILE_BLOCK_OFFSET || file_entry.offset >= block_capacity {
1719            return Err(Error::CorruptFormat(format!(
1720                "Invalid file offset {} for file_id {:?} \n{:?}",
1721                file_entry.offset, file_id, file_entry
1722            )));
1723        }
1724
1725        // The file must entirely fit in the available space.
1726        let file_allocation_end_block = file_entry.offset + file_entry.allocation_size;
1727        if file_allocation_end_block > block_capacity {
1728            return Err(Error::CorruptFormat(String::from(
1729                "Invalid file allocation end block",
1730            )));
1731        }
1732
1733        // Validate the valid data size.
1734        let file_allocation_size_bytes = block_count_to_byte_count(file_entry.allocation_size);
1735        if file_entry.valid_data_size > file_allocation_size_bytes {
1736            return Err(Error::CorruptFormat(String::from("Invalid data size")));
1737        }
1738
1739        // Initialize the file control block for this file ID
1740        file_control_blocks.insert(file_id, {
1741            let (nonce, authentication_tag) = if version >= VMGS_VERSION_3_0 {
1742                (file_entry.nonce, file_entry.authentication_tag)
1743            } else {
1744                Default::default()
1745            };
1746
1747            ResolvedFileControlBlock {
1748                block_offset: file_entry.offset,
1749                allocated_blocks,
1750                valid_bytes: file_entry.valid_data_size,
1751
1752                nonce,
1753                authentication_tag,
1754
1755                attributes: FileAttribute::new(),
1756                encryption_key: VmgsDatastoreKey::new_zeroed(),
1757            }
1758        });
1759    }
1760
1761    Ok(file_control_blocks)
1762}
1763
1764/// Convert block count to byte count.
1765fn block_count_to_byte_count(block_count: u32) -> u64 {
1766    block_count as u64 * VMGS_BYTES_PER_BLOCK as u64
1767}
1768
1769fn round_up_count(count: usize, pow2: u32) -> u64 {
1770    (count as u64 + pow2 as u64 - 1) & !(pow2 as u64 - 1)
1771}
1772
1773/// Generates a nonce for the encryption. First 4 bytes are a random seed, and last 8 bytes are zero's.
1774fn generate_nonce() -> VmgsNonce {
1775    let mut nonce = VmgsNonce::new_zeroed();
1776    // Generate a 4-byte random seed for nonce
1777    getrandom::fill(&mut nonce[..4]).expect("rng failure");
1778    nonce
1779}
1780
1781/// Increment Nonce by one.
1782fn increment_nonce(nonce: &mut VmgsNonce) -> Result<(), Error> {
1783    // Update the random seed of nonce
1784    getrandom::fill(&mut nonce[..vmgs_format::VMGS_NONCE_RANDOM_SEED_SIZE]).expect("rng failure");
1785
1786    // Increment the counter of nonce by 1.
1787    for i in &mut nonce[vmgs_format::VMGS_NONCE_RANDOM_SEED_SIZE..] {
1788        *i = i.wrapping_add(1);
1789
1790        if *i != 0 {
1791            break;
1792        }
1793    }
1794
1795    Ok(())
1796}
1797
1798/// Checks whether an encryption key is all zero's.
1799fn is_empty_key(encryption_key: &[u8]) -> bool {
1800    encryption_key.iter().all(|&x| x == 0)
1801}
1802
1803/// Encrypts MetadataKey. Returns encrypted_metadata_key.
1804#[cfg_attr(not(with_encryption), expect(unused_variables))]
1805fn encrypt_metadata_key(
1806    encryption_key: &[u8],
1807    nonce: &[u8],
1808    metadata_key: &[u8],
1809    authentication_tag: &mut [u8],
1810) -> Result<Vec<u8>, Error> {
1811    #[cfg(not(with_encryption))]
1812    unreachable!("Encryption requires the encryption feature");
1813    #[cfg(with_encryption)]
1814    {
1815        let encrypted_metadata_key =
1816            crate::encrypt::vmgs_encrypt(encryption_key, nonce, metadata_key, authentication_tag)?;
1817
1818        if encrypted_metadata_key.len() != metadata_key.len() {
1819            return Err(Error::Other(anyhow!(format!(
1820                "encrypted metadata key length ({}) doesn't match metadata key length ({})",
1821                encrypted_metadata_key.len(),
1822                metadata_key.len()
1823            ))));
1824        }
1825        Ok(encrypted_metadata_key)
1826    }
1827}
1828
1829/// Decrypts metadata_key. Returns decrypted_metadata_key.
1830#[cfg_attr(not(with_encryption), expect(unused_variables), expect(dead_code))]
1831fn decrypt_metadata_key(
1832    datastore_key: &[u8],
1833    nonce: &[u8],
1834    metadata_key: &[u8],
1835    authentication_tag: &[u8],
1836) -> Result<Vec<u8>, Error> {
1837    #[cfg(not(with_encryption))]
1838    unreachable!("Encryption requires the encryption feature");
1839    #[cfg(with_encryption)]
1840    {
1841        let decrypted_metadata_key =
1842            crate::encrypt::vmgs_decrypt(datastore_key, nonce, metadata_key, authentication_tag)?;
1843        if decrypted_metadata_key.len() != metadata_key.len() {
1844            return Err(Error::Other(anyhow!(format!(
1845                "decrypted metadata key length ({}) doesn't match metadata key length ({})",
1846                decrypted_metadata_key.len(),
1847                metadata_key.len()
1848            ))));
1849        }
1850
1851        Ok(decrypted_metadata_key)
1852    }
1853}
1854
1855/// Computes the cr32 checksum for a given byte stream.
1856fn compute_crc32(buf: &[u8]) -> u32 {
1857    let mut hasher = crc32fast::Hasher::new();
1858    hasher.update(buf);
1859    hasher.finalize()
1860}
1861
1862#[cfg(feature = "save_restore")]
1863#[expect(missing_docs)]
1864pub mod save_restore {
1865    use super::*;
1866
1867    pub mod state {
1868        use mesh_protobuf::Protobuf;
1869        use std::num::NonZeroU32;
1870
1871        pub type SavedVmgsNonce = [u8; 12];
1872        pub type SavedVmgsAuthTag = [u8; 16];
1873        pub type SavedVmgsDatastoreKey = [u8; 32];
1874
1875        #[derive(Protobuf)]
1876        #[mesh(package = "vmgs")]
1877        pub struct SavedResolvedFileControlBlock {
1878            #[mesh(1)]
1879            pub block_offset: u32,
1880            #[mesh(2)]
1881            pub allocated_blocks: NonZeroU32,
1882            #[mesh(3)]
1883            pub valid_bytes: u64,
1884            #[mesh(4)]
1885            pub nonce: SavedVmgsNonce,
1886            #[mesh(5)]
1887            pub authentication_tag: SavedVmgsAuthTag,
1888            #[mesh(6)]
1889            pub attributes: u32,
1890            #[mesh(7)]
1891            pub encryption_key: SavedVmgsDatastoreKey,
1892        }
1893
1894        #[derive(Protobuf)]
1895        #[mesh(package = "vmgs")]
1896        pub struct SavedVmgsEncryptionKey {
1897            #[mesh(1)]
1898            pub nonce: SavedVmgsNonce,
1899            #[mesh(2)]
1900            pub authentication_tag: SavedVmgsAuthTag,
1901            #[mesh(3)]
1902            pub encryption_key: SavedVmgsDatastoreKey,
1903        }
1904
1905        #[derive(Protobuf)]
1906        #[mesh(package = "vmgs")]
1907        pub struct SavedVmgsState {
1908            #[mesh(1)]
1909            pub active_header_index: usize,
1910            #[mesh(2)]
1911            pub active_header_sequence_number: u32,
1912            #[mesh(3)]
1913            pub version: u32,
1914            #[mesh(4)]
1915            pub fcbs: Vec<(u32, SavedResolvedFileControlBlock)>,
1916            #[mesh(5)]
1917            pub encryption_algorithm: u16,
1918            #[mesh(6)]
1919            pub datastore_key_count: u8,
1920            #[mesh(7)]
1921            pub active_datastore_key_index: Option<usize>,
1922            #[mesh(8)]
1923            pub datastore_keys: [SavedVmgsDatastoreKey; 2],
1924            #[mesh(9)]
1925            pub metadata_key: SavedVmgsDatastoreKey,
1926            #[mesh(10)]
1927            pub encrypted_metadata_keys: [SavedVmgsEncryptionKey; 2],
1928            #[mesh(11)]
1929            pub reprovisioned: bool,
1930        }
1931    }
1932
1933    impl Vmgs {
1934        /// Construct a [`Vmgs`] instance, re-using existing saved-state from an
1935        /// earlier instance.
1936        ///
1937        /// # Safety
1938        ///
1939        /// `open_from_saved` does NOT perform ANY validation on the provided
1940        /// `state`, and will blindly assume that it matches the underlying
1941        /// `storage` instance!
1942        ///
1943        /// Callers MUST ensure that the provided `state` matches the provided
1944        /// `storage`, and that no external entities have modified `storage` between
1945        /// the call to `save` and `open_from_saved`.
1946        ///
1947        /// Failing to do so may result in data corruption/loss, read/write
1948        /// failures, encryption errors, etc... (though, notably: it will _not_
1949        /// result in any memory-unsafety, hence why the function isn't marked
1950        /// `unsafe`).
1951        pub fn open_from_saved(
1952            disk: Disk,
1953            state: state::SavedVmgsState,
1954            logger: Option<Arc<dyn VmgsLogger>>,
1955        ) -> Self {
1956            let state::SavedVmgsState {
1957                active_header_index,
1958                active_header_sequence_number,
1959                version,
1960                fcbs,
1961                encryption_algorithm,
1962                datastore_key_count,
1963                active_datastore_key_index,
1964                datastore_keys,
1965                metadata_key,
1966                encrypted_metadata_keys,
1967                reprovisioned,
1968            } = state;
1969
1970            Self {
1971                storage: VmgsStorage::new(disk),
1972                #[cfg(feature = "inspect")]
1973                stats: Default::default(),
1974
1975                active_header_index,
1976                active_header_sequence_number,
1977                version,
1978                fcbs: fcbs
1979                    .into_iter()
1980                    .map(|(file_id, fcb)| {
1981                        let state::SavedResolvedFileControlBlock {
1982                            block_offset,
1983                            allocated_blocks,
1984                            valid_bytes,
1985                            nonce,
1986                            authentication_tag,
1987                            attributes,
1988                            encryption_key,
1989                        } = fcb;
1990
1991                        (
1992                            FileId(file_id),
1993                            ResolvedFileControlBlock {
1994                                block_offset,
1995                                allocated_blocks,
1996                                valid_bytes,
1997                                nonce,
1998                                authentication_tag,
1999                                attributes: FileAttribute::from(attributes),
2000                                encryption_key,
2001                            },
2002                        )
2003                    })
2004                    .collect(),
2005                encryption_algorithm: EncryptionAlgorithm(encryption_algorithm),
2006                datastore_key_count,
2007                active_datastore_key_index,
2008                datastore_keys,
2009                metadata_key,
2010                encrypted_metadata_keys: encrypted_metadata_keys.map(|k| {
2011                    let state::SavedVmgsEncryptionKey {
2012                        nonce,
2013                        authentication_tag,
2014                        encryption_key,
2015                    } = k;
2016
2017                    VmgsEncryptionKey {
2018                        nonce,
2019                        reserved: 0,
2020                        authentication_tag,
2021                        encryption_key,
2022                    }
2023                }),
2024                reprovisioned,
2025                provisioned_this_boot: false,
2026                logger,
2027            }
2028        }
2029
2030        /// Save the in-memory Vmgs file metadata.
2031        ///
2032        /// This saved state can be used alongside `open_from_saved` to obtain a
2033        /// new `Vmgs` instance _without_ needing to invoke any IOs on the
2034        /// underlying storage.
2035        pub fn save(&self) -> state::SavedVmgsState {
2036            let Self {
2037                storage: _,
2038
2039                #[cfg(feature = "inspect")]
2040                    stats: _,
2041
2042                active_header_index,
2043                active_header_sequence_number,
2044                version,
2045                fcbs,
2046                encryption_algorithm,
2047                datastore_key_count,
2048                active_datastore_key_index,
2049                datastore_keys,
2050                metadata_key,
2051                encrypted_metadata_keys,
2052                logger: _,
2053                reprovisioned,
2054                provisioned_this_boot: _,
2055            } = self;
2056
2057            state::SavedVmgsState {
2058                active_header_index: *active_header_index,
2059                active_header_sequence_number: *active_header_sequence_number,
2060                version: *version,
2061                fcbs: fcbs
2062                    .iter()
2063                    .map(|(file_id, fcb)| {
2064                        let ResolvedFileControlBlock {
2065                            block_offset,
2066                            allocated_blocks,
2067                            valid_bytes,
2068                            nonce,
2069                            authentication_tag,
2070                            attributes,
2071                            encryption_key,
2072                        } = fcb;
2073
2074                        (
2075                            file_id.0,
2076                            state::SavedResolvedFileControlBlock {
2077                                block_offset: *block_offset,
2078                                allocated_blocks: *allocated_blocks,
2079                                valid_bytes: *valid_bytes,
2080                                nonce: *nonce,
2081                                authentication_tag: *authentication_tag,
2082                                attributes: (*attributes).into(),
2083                                encryption_key: *encryption_key,
2084                            },
2085                        )
2086                    })
2087                    .collect(),
2088                encryption_algorithm: encryption_algorithm.0,
2089                datastore_key_count: *datastore_key_count,
2090                active_datastore_key_index: *active_datastore_key_index,
2091                datastore_keys: *datastore_keys,
2092                metadata_key: *metadata_key,
2093                encrypted_metadata_keys: encrypted_metadata_keys.map(|k| {
2094                    let VmgsEncryptionKey {
2095                        nonce,
2096                        reserved: _,
2097                        authentication_tag,
2098                        encryption_key,
2099                    } = k;
2100
2101                    state::SavedVmgsEncryptionKey {
2102                        nonce,
2103                        authentication_tag,
2104                        encryption_key,
2105                    }
2106                }),
2107                reprovisioned: *reprovisioned,
2108            }
2109        }
2110    }
2111}
2112
2113#[cfg(test)]
2114mod tests {
2115    use super::*;
2116    use pal_async::async_test;
2117    #[cfg(with_encryption)]
2118    use parking_lot::Mutex;
2119    #[cfg(with_encryption)]
2120    use std::sync::Arc;
2121    #[cfg(with_encryption)]
2122    use vmgs_format::VMGS_ENCRYPTION_KEY_SIZE;
2123
2124    const ONE_MEGA_BYTE: u64 = 1024 * 1024;
2125
2126    #[cfg(with_encryption)]
2127    struct TestVmgsLogger {
2128        data: Arc<Mutex<String>>,
2129    }
2130
2131    #[cfg(with_encryption)]
2132    #[async_trait::async_trait]
2133    impl VmgsLogger for TestVmgsLogger {
2134        async fn log_event_fatal(&self, _event: VmgsLogEvent) {
2135            let mut data = self.data.lock();
2136            *data = "test logger".to_string();
2137        }
2138    }
2139
2140    fn new_test_file() -> Disk {
2141        disklayer_ram::ram_disk(4 * ONE_MEGA_BYTE, false).unwrap()
2142    }
2143
2144    #[async_test]
2145    async fn empty_vmgs() {
2146        let disk = new_test_file();
2147
2148        let result = Vmgs::open(disk, None).await;
2149        assert!(matches!(result, Err(Error::EmptyFile)));
2150    }
2151
2152    #[async_test]
2153    async fn format_empty_vmgs() {
2154        let disk = new_test_file();
2155        let result = Vmgs::format_new(disk, None).await;
2156        assert!(result.is_ok());
2157    }
2158
2159    #[async_test]
2160    async fn basic_read_write() {
2161        let disk = new_test_file();
2162        let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2163        assert_eq!(vmgs.active_header_index, 0);
2164        assert_eq!(vmgs.active_header_sequence_number, 1);
2165        assert_eq!(vmgs.version, VMGS_VERSION_3_0);
2166
2167        // write
2168        let buf = b"hello world";
2169        vmgs.write_file(FileId::BIOS_NVRAM, buf).await.unwrap();
2170
2171        assert_eq!(vmgs.active_header_index, 1);
2172        assert_eq!(vmgs.active_header_sequence_number, 2);
2173
2174        // read
2175        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2176
2177        assert_eq!(buf, &*read_buf);
2178        assert_eq!(vmgs.active_header_index, 1);
2179        assert_eq!(vmgs.active_header_sequence_number, 2);
2180    }
2181
2182    #[async_test]
2183    async fn basic_read_write_large() {
2184        let disk = new_test_file();
2185        let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2186
2187        // write
2188        let buf: Vec<u8> = (0..).map(|x| x as u8).take(1024 * 4 + 1).collect();
2189
2190        vmgs.write_file(FileId::BIOS_NVRAM, &buf).await.unwrap();
2191
2192        assert_eq!(vmgs.active_header_index, 1);
2193        assert_eq!(vmgs.active_header_sequence_number, 2);
2194
2195        // read
2196        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2197
2198        assert_eq!(buf, read_buf);
2199        assert_eq!(vmgs.active_header_index, 1);
2200        assert_eq!(vmgs.active_header_sequence_number, 2);
2201
2202        // write
2203        let buf: Vec<u8> = (0..).map(|x| x as u8).take(1024 * 4 * 4 + 1).collect();
2204
2205        vmgs.write_file(FileId::TPM_PPI, &buf).await.unwrap();
2206
2207        assert_eq!(vmgs.active_header_index, 0);
2208        assert_eq!(vmgs.active_header_sequence_number, 3);
2209
2210        // read
2211        let read_buf = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2212
2213        assert_eq!(buf, read_buf);
2214        assert_eq!(vmgs.active_header_index, 0);
2215        assert_eq!(vmgs.active_header_sequence_number, 3);
2216
2217        // write
2218        let buf: Vec<u8> = (0..).map(|x| x as u8).take(1024 * 4 * 4 * 4 + 1).collect();
2219
2220        vmgs.write_file(FileId::GUEST_FIRMWARE, &buf).await.unwrap();
2221
2222        assert_eq!(vmgs.active_header_index, 1);
2223        assert_eq!(vmgs.active_header_sequence_number, 4);
2224
2225        // read
2226        let read_buf = vmgs.read_file(FileId::GUEST_FIRMWARE).await.unwrap();
2227
2228        assert_eq!(buf, read_buf);
2229        assert_eq!(vmgs.active_header_index, 1);
2230        assert_eq!(vmgs.active_header_sequence_number, 4);
2231    }
2232
2233    #[async_test]
2234    async fn open_existing_file() {
2235        let buf_1 = b"hello world";
2236        let buf_2 = b"short sentence";
2237        let buf_3 = b"funny joke";
2238
2239        // Create VMGS file and write to different FileId's
2240        let disk = new_test_file();
2241        let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2242
2243        vmgs.write_file(FileId::BIOS_NVRAM, buf_1).await.unwrap();
2244
2245        assert_eq!(vmgs.active_header_index, 1);
2246        assert_eq!(vmgs.active_header_sequence_number, 2);
2247        assert_eq!(vmgs.fcbs[&FileId(0)].block_offset, 4);
2248        assert_eq!(vmgs.fcbs[&FileId(1)].block_offset, 5);
2249
2250        vmgs.write_file(FileId::TPM_PPI, buf_2).await.unwrap();
2251
2252        assert_eq!(vmgs.active_header_index, 0);
2253        assert_eq!(vmgs.active_header_sequence_number, 3);
2254        assert_eq!(vmgs.fcbs[&FileId(0)].block_offset, 2);
2255        assert_eq!(vmgs.fcbs[&FileId(1)].block_offset, 5);
2256        assert_eq!(vmgs.fcbs[&FileId(2)].block_offset, 6);
2257
2258        vmgs.write_file(FileId::BIOS_NVRAM, buf_3).await.unwrap();
2259
2260        assert_eq!(vmgs.active_header_index, 1);
2261        assert_eq!(vmgs.active_header_sequence_number, 4);
2262        assert_eq!(vmgs.fcbs[&FileId(0)].block_offset, 4);
2263        assert_eq!(vmgs.fcbs[&FileId(1)].block_offset, 7);
2264        assert_eq!(vmgs.fcbs[&FileId(2)].block_offset, 6);
2265
2266        // Re-open VMGS file and read from the same FileId's
2267        drop(vmgs);
2268
2269        let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2270
2271        assert_eq!(vmgs.fcbs[&FileId(0)].block_offset, 4);
2272        assert_eq!(vmgs.fcbs[&FileId(1)].block_offset, 7);
2273        assert_eq!(vmgs.fcbs[&FileId(2)].block_offset, 6);
2274        let read_buf_1 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2275
2276        assert_eq!(buf_3, &*read_buf_1);
2277        assert_eq!(vmgs.active_header_index, 1);
2278        assert_eq!(vmgs.active_header_sequence_number, 4);
2279
2280        let read_buf_2 = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2281
2282        assert_eq!(buf_2, &*read_buf_2);
2283        assert_eq!(vmgs.fcbs[&FileId(0)].block_offset, 4);
2284        assert_eq!(vmgs.fcbs[&FileId(1)].block_offset, 7);
2285        assert_eq!(vmgs.fcbs[&FileId(2)].block_offset, 6);
2286    }
2287
2288    #[async_test]
2289    async fn multiple_read_write() {
2290        let disk = new_test_file();
2291        let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2292
2293        let buf_1 = b"Data data data";
2294        let buf_2 = b"password";
2295        let buf_3 = b"other data data";
2296
2297        vmgs.write_file(FileId::BIOS_NVRAM, buf_1).await.unwrap();
2298        let read_buf_1 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2299        assert_eq!(buf_1, &*read_buf_1);
2300        assert_eq!(vmgs.active_header_index, 1);
2301        assert_eq!(vmgs.active_header_sequence_number, 2);
2302
2303        vmgs.write_file(FileId::TPM_PPI, buf_2).await.unwrap();
2304        let info = vmgs.get_file_info(FileId::TPM_PPI).unwrap();
2305        assert_eq!(info.valid_bytes as usize, buf_2.len());
2306        let read_buf_2 = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2307        assert_eq!(buf_2, &*read_buf_2);
2308        assert_eq!(vmgs.active_header_index, 0);
2309        assert_eq!(vmgs.active_header_sequence_number, 3);
2310
2311        vmgs.write_file(FileId::BIOS_NVRAM, buf_3).await.unwrap();
2312        let read_buf_3 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2313        assert_eq!(buf_3, &*read_buf_3);
2314        assert_eq!(vmgs.active_header_index, 1);
2315        assert_eq!(vmgs.active_header_sequence_number, 4);
2316
2317        vmgs.write_file(FileId::BIOS_NVRAM, buf_1).await.unwrap();
2318        let read_buf_1 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2319        assert_eq!(buf_1, &*read_buf_1);
2320        assert_eq!(vmgs.active_header_index, 0);
2321        assert_eq!(vmgs.active_header_sequence_number, 5);
2322
2323        vmgs.write_file(FileId::TPM_PPI, buf_2).await.unwrap();
2324        let read_buf_2 = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2325        assert_eq!(buf_2, &*read_buf_2);
2326        assert_eq!(vmgs.active_header_index, 1);
2327        assert_eq!(vmgs.active_header_sequence_number, 6);
2328
2329        vmgs.write_file(FileId::BIOS_NVRAM, buf_3).await.unwrap();
2330        let read_buf_3 = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2331        assert_eq!(buf_3, &*read_buf_3);
2332        assert_eq!(vmgs.active_header_index, 0);
2333        assert_eq!(vmgs.active_header_sequence_number, 7);
2334    }
2335
2336    #[async_test]
2337    async fn test_insufficient_resources() {
2338        let disk = new_test_file();
2339        let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2340
2341        let buf: Vec<u8> = vec![1; ONE_MEGA_BYTE as usize * 5];
2342        let result = vmgs.write_file(FileId::BIOS_NVRAM, &buf).await;
2343        assert!(result.is_err());
2344        if let Err(e) = result {
2345            match e {
2346                Error::InsufficientResources => (),
2347                _ => panic!("Wrong error returned"),
2348            }
2349        } else {
2350            panic!("Should have returned Insufficient resources error");
2351        }
2352    }
2353
2354    #[async_test]
2355    async fn test_empty_write() {
2356        let disk = new_test_file();
2357        let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2358
2359        let buf: Vec<u8> = Vec::new();
2360        vmgs.write_file(FileId::BIOS_NVRAM, &buf).await.unwrap();
2361
2362        // read
2363        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2364
2365        assert_eq!(buf, read_buf);
2366        assert_eq!(read_buf.len(), 0);
2367        assert_eq!(vmgs.active_header_index, 1);
2368        assert_eq!(vmgs.active_header_sequence_number, 2);
2369    }
2370
2371    // general functions
2372    #[test]
2373    fn test_block_count_to_byte_count() {
2374        let block_count = 10;
2375        let byte_count = block_count_to_byte_count(block_count);
2376        assert!(byte_count == block_count as u64 * VMGS_BYTES_PER_BLOCK as u64);
2377    }
2378
2379    #[test]
2380    fn test_validate_header() {
2381        let mut header = VmgsHeader::new_zeroed();
2382        header.signature = VMGS_SIGNATURE;
2383        header.version = VMGS_VERSION_3_0;
2384        header.header_size = size_of::<VmgsHeader>() as u32;
2385        header.file_table_offset = VMGS_MIN_FILE_BLOCK_OFFSET;
2386        header.file_table_size = VMGS_FILE_TABLE_BLOCK_SIZE;
2387        header.checksum = compute_crc32(header.as_bytes());
2388
2389        let result = validate_header(&header);
2390        assert!(result.is_ok());
2391
2392        let mut header_signature = header;
2393        header_signature.signature = 0;
2394        header_signature.checksum = 0;
2395        header_signature.checksum = compute_crc32(header_signature.as_bytes());
2396        let result = validate_header(&header_signature);
2397        match result {
2398            Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid header signature"),
2399            _ => panic!(),
2400        };
2401
2402        let mut header_version = header;
2403        header_version.version = 0;
2404        header_version.checksum = 0;
2405        header_version.checksum = compute_crc32(header_version.as_bytes());
2406        match validate_header(&header_version) {
2407            Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid header version"),
2408            _ => panic!(),
2409        };
2410
2411        let mut header_header_size = header;
2412        header_header_size.header_size = 0;
2413        header_header_size.checksum = 0;
2414        header_header_size.checksum = compute_crc32(header_header_size.as_bytes());
2415        match validate_header(&header_header_size) {
2416            Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid header size"),
2417            _ => panic!(),
2418        };
2419
2420        let mut header_ft_offset = header;
2421        header_ft_offset.file_table_offset = 0;
2422        header_ft_offset.checksum = 0;
2423        header_ft_offset.checksum = compute_crc32(header_ft_offset.as_bytes());
2424        match validate_header(&header_ft_offset) {
2425            Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid file table offset"),
2426            _ => panic!(),
2427        };
2428
2429        let mut header_ft_size = header;
2430        header_ft_size.file_table_size = 0;
2431        header_ft_size.checksum = 0;
2432        header_ft_size.checksum = compute_crc32(header_ft_size.as_bytes());
2433        match validate_header(&header_ft_size) {
2434            Err(Error::InvalidFormat(err)) => assert_eq!(err, "Invalid file table size"),
2435            _ => panic!(),
2436        };
2437    }
2438
2439    #[test]
2440    fn test_initialize_file_metadata() {
2441        let mut file_table = VmgsFileTable::new_zeroed();
2442
2443        file_table.entries[0].offset = 6;
2444        file_table.entries[0].allocation_size = 1;
2445        file_table.entries[1].offset = 2;
2446        file_table.entries[1].allocation_size = 1;
2447        file_table.entries[2].offset = 4;
2448        file_table.entries[2].allocation_size = 5;
2449        file_table.entries[3].offset = 3;
2450        file_table.entries[3].allocation_size = 3;
2451
2452        let block_capacity = 1000;
2453
2454        let fcbs = initialize_file_metadata(&file_table, VMGS_VERSION_3_0, block_capacity).unwrap();
2455        // assert VmgsFileEntry correctly converted to FileControlBlock
2456        assert!(fcbs[&FileId(0)].block_offset == 6);
2457        assert!(fcbs[&FileId(0)].allocated_blocks.get() == 1);
2458        assert!(fcbs[&FileId(1)].block_offset == 2);
2459        assert!(fcbs[&FileId(1)].allocated_blocks.get() == 1);
2460        assert!(fcbs[&FileId(2)].block_offset == 4);
2461        assert!(fcbs[&FileId(2)].allocated_blocks.get() == 5);
2462        assert!(fcbs[&FileId(3)].block_offset == 3);
2463        assert!(fcbs[&FileId(3)].allocated_blocks.get() == 3);
2464    }
2465
2466    #[test]
2467    fn test_round_up_count() {
2468        assert!(round_up_count(0, 4096) == 0);
2469        assert!(round_up_count(1, 4096) == 4096);
2470        assert!(round_up_count(4095, 4096) == 4096);
2471        assert!(round_up_count(4096, 4096) == 4096);
2472        assert!(round_up_count(4097, 4096) == 8192);
2473    }
2474
2475    #[async_test]
2476    async fn test_header_sequence_overflow() {
2477        let disk = new_test_file();
2478        let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2479
2480        vmgs.active_header_sequence_number = u32::MAX;
2481
2482        // write
2483        let buf = b"hello world";
2484        vmgs.write_file(FileId::BIOS_NVRAM, buf).await.unwrap();
2485
2486        assert_eq!(vmgs.active_header_index, 1);
2487        assert_eq!(vmgs.active_header_sequence_number, 0);
2488
2489        vmgs.set_active_header(0, u32::MAX);
2490
2491        let mut new_header = VmgsHeader::new_zeroed();
2492        vmgs.update_header(&mut new_header).await.unwrap();
2493
2494        assert_eq!(vmgs.active_header_index, 1);
2495        assert_eq!(vmgs.active_header_sequence_number, 0);
2496        assert_eq!(new_header.sequence, 0);
2497    }
2498
2499    #[cfg(with_encryption)]
2500    #[async_test]
2501    async fn write_file_v3() {
2502        let disk = new_test_file();
2503        let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2504        let encryption_key = [12; VMGS_ENCRYPTION_KEY_SIZE];
2505
2506        // write
2507        let buf = b"hello world";
2508        let buf_1 = b"hello universe";
2509        vmgs.write_file(FileId::BIOS_NVRAM, buf).await.unwrap();
2510        vmgs.update_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2511            .await
2512            .unwrap();
2513        vmgs.write_file_encrypted(FileId::TPM_PPI, buf_1)
2514            .await
2515            .unwrap();
2516
2517        // read
2518        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2519        assert_eq!(buf, &*read_buf);
2520        let info = vmgs.get_file_info(FileId::TPM_PPI).unwrap();
2521        assert_eq!(info.valid_bytes as usize, buf_1.len());
2522        let read_buf = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2523        assert_eq!(buf_1, &*read_buf);
2524
2525        // Read the file after re-opening the vmgs file
2526        drop(vmgs);
2527        let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2528        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2529        assert_eq!(buf, read_buf.as_bytes());
2530        let info = vmgs.get_file_info(FileId::TPM_PPI).unwrap();
2531        assert_eq!(info.valid_bytes as usize, buf_1.len());
2532        let read_buf = vmgs.read_file(FileId::TPM_PPI).await.unwrap();
2533        assert_ne!(buf_1, read_buf.as_bytes());
2534
2535        // Unlock datastore
2536        vmgs.unlock_with_encryption_key(&encryption_key)
2537            .await
2538            .unwrap();
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
2545    #[cfg(with_encryption)]
2546    #[async_test]
2547    async fn overwrite_file_v3() {
2548        let disk = new_test_file();
2549        let mut vmgs = Vmgs::format_new(disk, None).await.unwrap();
2550        let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2551        let buf = vec![1; 8 * 1024];
2552        let buf_1 = vec![2; 8 * 1024];
2553
2554        // Add root key.
2555        vmgs.add_new_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2556            .await
2557            .unwrap();
2558        assert_eq!(vmgs.active_datastore_key_index, Some(0));
2559
2560        // Write a file to the store.
2561        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2562            .await
2563            .unwrap();
2564
2565        // Encrypt and overwrite the original file.
2566        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf_1)
2567            .await
2568            .unwrap();
2569
2570        // Verify new file contents
2571        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2572        assert_eq!(buf_1, read_buf);
2573    }
2574
2575    #[cfg(with_encryption)]
2576    #[async_test]
2577    async fn file_encryption() {
2578        let buf: Vec<u8> = (0..255).collect();
2579        let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2580
2581        let disk = new_test_file();
2582        let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2583
2584        // Add datastore key.
2585        vmgs.add_new_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2586            .await
2587            .unwrap();
2588        assert_eq!(vmgs.active_datastore_key_index, Some(0));
2589
2590        // Write a file to the store.
2591        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2592            .await
2593            .unwrap();
2594
2595        // Read the file, without closing the datastore
2596        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2597        assert_eq!(buf, read_buf);
2598
2599        drop(vmgs);
2600
2601        // Read the file, after closing and reopening the data store.
2602        let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2603
2604        let info = vmgs.get_file_info(FileId::BIOS_NVRAM).unwrap();
2605        assert_eq!(info.valid_bytes as usize, buf.len());
2606
2607        // Unlock the store.
2608
2609        vmgs.unlock_with_encryption_key(&encryption_key)
2610            .await
2611            .unwrap();
2612
2613        assert_eq!(vmgs.active_datastore_key_index, Some(0));
2614
2615        // Change to a new datastore key.
2616        let new_encryption_key = [2; VMGS_ENCRYPTION_KEY_SIZE];
2617        vmgs.add_new_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2618            .await
2619            .unwrap();
2620        assert_eq!(vmgs.active_datastore_key_index, Some(1));
2621        vmgs.remove_encryption_key(0).await.unwrap();
2622
2623        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2624        assert_eq!(buf, read_buf);
2625    }
2626
2627    #[cfg(with_encryption)]
2628    #[async_test]
2629    async fn add_new_encryption_key() {
2630        let buf: Vec<u8> = (0..255).collect();
2631        let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2632        let new_encryption_key = [5; VMGS_ENCRYPTION_KEY_SIZE];
2633
2634        // Initialize version 3 data store
2635        let disk = new_test_file();
2636        let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2637
2638        // Add datastore key.
2639        vmgs.add_new_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2640            .await
2641            .unwrap();
2642        assert_eq!(vmgs.active_datastore_key_index, Some(0));
2643
2644        // Write a file to the store.
2645        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2646            .await
2647            .unwrap();
2648
2649        // Read the file, after closing and reopening the data store.
2650        drop(vmgs);
2651        let mut vmgs = Vmgs::open(disk.clone(), None).await.unwrap();
2652        vmgs.unlock_with_encryption_key(&encryption_key)
2653            .await
2654            .unwrap();
2655        assert_eq!(vmgs.active_datastore_key_index, Some(0));
2656        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2657        assert_eq!(read_buf, buf);
2658
2659        // Add new datastore key.
2660        vmgs.add_new_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2661            .await
2662            .unwrap();
2663        assert_eq!(vmgs.active_datastore_key_index, Some(1));
2664
2665        // Read the file by using two different datastore keys, after closing and reopening the data store.
2666        drop(vmgs);
2667        let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2668        vmgs.unlock_with_encryption_key(&encryption_key)
2669            .await
2670            .unwrap();
2671        assert_eq!(vmgs.active_datastore_key_index, Some(0));
2672        vmgs.unlock_with_encryption_key(&new_encryption_key)
2673            .await
2674            .unwrap();
2675        assert_eq!(vmgs.active_datastore_key_index, Some(1));
2676        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2677        assert_eq!(read_buf, buf);
2678
2679        // Remove the newly added datastore key and add it again.
2680        vmgs.remove_encryption_key(1).await.unwrap();
2681        vmgs.add_new_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2682            .await
2683            .unwrap();
2684        assert_eq!(vmgs.active_datastore_key_index, Some(1));
2685
2686        // Remove the old datastore key
2687        vmgs.remove_encryption_key(0).await.unwrap();
2688        let result = vmgs.unlock_with_encryption_key(&encryption_key).await;
2689        assert!(matches!(result, Err(Error::Other(_))));
2690
2691        // Try to remove the old datastore key again
2692        let result = vmgs.remove_encryption_key(0).await;
2693        assert!(matches!(result, Err(Error::Other(_))));
2694
2695        // Remove the new datastore key and try to read file content, which should be in encrypted state
2696        vmgs.remove_encryption_key(1).await.unwrap();
2697        let read_buf = vmgs.read_file_raw(FileId::BIOS_NVRAM).await;
2698        assert_ne!(read_buf.unwrap(), buf);
2699    }
2700
2701    #[cfg(with_encryption)]
2702    #[async_test]
2703    async fn test_write_file_encrypted() {
2704        // Call write_file_encrypted on an unencrypted VMGS and check that plaintext was written
2705
2706        // Initialize version 3 data store
2707        let disk = new_test_file();
2708        let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2709        let buf = b"This is plaintext";
2710
2711        // call write file encrypted
2712        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, buf)
2713            .await
2714            .unwrap();
2715
2716        // Read
2717        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2718        assert_eq!(vmgs.encryption_algorithm, EncryptionAlgorithm::NONE);
2719        assert_eq!(buf, &*read_buf);
2720
2721        // ensure that when we re-create the VMGS object, we can still read the
2722        // FileId as plaintext
2723        drop(vmgs);
2724        let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2725
2726        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2727        assert_eq!(vmgs.encryption_algorithm, EncryptionAlgorithm::NONE);
2728        assert_eq!(buf, &*read_buf);
2729    }
2730
2731    #[cfg(with_encryption)]
2732    #[async_test]
2733    async fn test_logger() {
2734        let disk = new_test_file();
2735        let data = Arc::new(Mutex::new(String::new()));
2736        let mut vmgs = Vmgs::format_new(
2737            disk.clone(),
2738            Some(Arc::new(TestVmgsLogger { data: data.clone() })),
2739        )
2740        .await
2741        .unwrap();
2742        let encryption_key = [12; VMGS_ENCRYPTION_KEY_SIZE];
2743
2744        // write
2745        let buf = b"hello world";
2746        vmgs.update_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2747            .await
2748            .unwrap();
2749        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, buf)
2750            .await
2751            .unwrap();
2752
2753        let fcb = vmgs.fcbs.get_mut(&FileId::BIOS_NVRAM).unwrap();
2754
2755        // Manipulate the nonce and expect the read to fail.
2756        fcb.nonce[0] ^= 1;
2757
2758        // read and expect to fail
2759        let result = vmgs.read_file(FileId::BIOS_NVRAM).await;
2760        assert!(result.is_err());
2761
2762        // verify that the string is logged
2763        let result = data.lock();
2764        assert_eq!(*result, "test logger");
2765    }
2766
2767    #[cfg(with_encryption)]
2768    #[async_test]
2769    async fn update_key() {
2770        let buf: Vec<u8> = (0..255).collect();
2771        let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2772
2773        let disk = new_test_file();
2774        let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2775
2776        // Add datastore key.
2777        vmgs.update_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2778            .await
2779            .unwrap();
2780        assert_eq!(vmgs.active_datastore_key_index, Some(0));
2781        assert_eq!(vmgs.datastore_key_count, 1);
2782
2783        // Write a file to the store.
2784        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2785            .await
2786            .unwrap();
2787
2788        // Read the file, without closing the datastore
2789        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2790        assert_eq!(buf, read_buf);
2791
2792        // Close and reopen the store
2793        drop(vmgs);
2794        let mut vmgs = Vmgs::open(disk.clone(), None).await.unwrap();
2795
2796        // Unlock the store.
2797        vmgs.unlock_with_encryption_key(&encryption_key)
2798            .await
2799            .unwrap();
2800        assert_eq!(vmgs.active_datastore_key_index, Some(0));
2801
2802        // Read the file again
2803        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2804        assert_eq!(buf, read_buf);
2805
2806        // Change to a new datastore key.
2807        let new_encryption_key = [2; VMGS_ENCRYPTION_KEY_SIZE];
2808        vmgs.update_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2809            .await
2810            .unwrap();
2811        assert_eq!(vmgs.active_datastore_key_index, Some(1));
2812        assert_eq!(vmgs.datastore_key_count, 1);
2813
2814        // Read the file again
2815        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2816        assert_eq!(buf, read_buf);
2817
2818        // Close and reopen the store
2819        drop(vmgs);
2820        let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2821
2822        // Unlock the store.
2823        vmgs.unlock_with_encryption_key(&new_encryption_key)
2824            .await
2825            .unwrap();
2826        assert_eq!(vmgs.active_datastore_key_index, Some(1));
2827
2828        // Read the file again
2829        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2830        assert_eq!(buf, read_buf);
2831    }
2832
2833    #[cfg(with_encryption)]
2834    #[async_test]
2835    async fn update_key_no_space() {
2836        let buf: Vec<u8> = (0..255).collect();
2837        let encryption_key = [1; VMGS_ENCRYPTION_KEY_SIZE];
2838
2839        let disk = new_test_file();
2840        let mut vmgs = Vmgs::format_new(disk.clone(), None).await.unwrap();
2841
2842        // Add datastore key.
2843        vmgs.update_encryption_key(&encryption_key, EncryptionAlgorithm::AES_GCM)
2844            .await
2845            .unwrap();
2846        assert_eq!(vmgs.active_datastore_key_index, Some(0));
2847        assert_eq!(vmgs.datastore_key_count, 1);
2848
2849        // Write a file to the store.
2850        vmgs.write_file_encrypted(FileId::BIOS_NVRAM, &buf)
2851            .await
2852            .unwrap();
2853
2854        // Read the file, without closing the datastore
2855        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2856        assert_eq!(buf, read_buf);
2857
2858        // Add a new datastore key, but don't remove the old one.
2859        let new_encryption_key = [2; VMGS_ENCRYPTION_KEY_SIZE];
2860        vmgs.add_new_encryption_key(&new_encryption_key, EncryptionAlgorithm::AES_GCM)
2861            .await
2862            .unwrap();
2863        assert_eq!(vmgs.active_datastore_key_index, Some(1));
2864        assert_eq!(vmgs.datastore_key_count, 2);
2865
2866        // Close and reopen the store
2867        drop(vmgs);
2868        let mut vmgs = Vmgs::open(disk.clone(), None).await.unwrap();
2869
2870        // Unlock the store.
2871        vmgs.unlock_with_encryption_key(&new_encryption_key)
2872            .await
2873            .unwrap();
2874        assert_eq!(vmgs.active_datastore_key_index, Some(1));
2875
2876        // Add yet another new datastore key. This should remove both previous keys
2877        let another_encryption_key = [2; VMGS_ENCRYPTION_KEY_SIZE];
2878        vmgs.update_encryption_key(&another_encryption_key, EncryptionAlgorithm::AES_GCM)
2879            .await
2880            .unwrap();
2881        assert_eq!(vmgs.active_datastore_key_index, Some(0));
2882        assert_eq!(vmgs.datastore_key_count, 1);
2883
2884        // Close and reopen the store
2885        drop(vmgs);
2886        let mut vmgs = Vmgs::open(disk, None).await.unwrap();
2887
2888        // Unlock the store.
2889        vmgs.unlock_with_encryption_key(&another_encryption_key)
2890            .await
2891            .unwrap();
2892        assert_eq!(vmgs.active_datastore_key_index, Some(0));
2893
2894        // Read the file again
2895        let read_buf = vmgs.read_file(FileId::BIOS_NVRAM).await.unwrap();
2896        assert_eq!(buf, read_buf);
2897    }
2898}