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