diff --git a/security-monitor/Cargo.lock b/security-monitor/Cargo.lock index 56a530d6..c0449e25 100644 --- a/security-monitor/Cargo.lock +++ b/security-monitor/Cargo.lock @@ -84,6 +84,12 @@ dependencies = [ "hybrid-array", ] +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "cexpr" version = "0.6.0" @@ -162,9 +168,9 @@ dependencies = [ [[package]] name = "ctutils" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" +checksum = "03bb0e1cc970d482d121d9a1744999169b69a07470b3d644a7894e53fcaf4574" dependencies = [ "cmov", ] @@ -230,6 +236,25 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + [[package]] name = "hybrid-array" version = "0.4.15" @@ -271,9 +296,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.189" +version = "0.2.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" +checksum = "ce5d3ddc6d3fa000eb1536d85e147bfe31aacaba692ed6a876f95cb7c855be78" [[package]] name = "memchr" @@ -448,6 +473,7 @@ name = "riscv_cove_tap" version = "0.2.0" dependencies = [ "aes-gcm", + "heapless", "hybrid-array", "ml-kem", "thiserror-no-std", @@ -473,6 +499,7 @@ name = "security-monitor" version = "0.4.0" dependencies = [ "flattened_device_tree", + "heapless", "hybrid-array", "memoffset 0.9.1", "opensbi-sys", @@ -521,6 +548,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0134f9043ed38b087ac4f7d4af44c79e2c9e5094421fe3164f435ce585953b10" +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + [[package]] name = "static_assertions" version = "1.1.0" diff --git a/security-monitor/Cargo.toml b/security-monitor/Cargo.toml index a410ce41..453815ad 100644 --- a/security-monitor/Cargo.toml +++ b/security-monitor/Cargo.toml @@ -24,6 +24,9 @@ pointers_utility = {path = "rust-crates/pointers_utility"} # This crate decodes RISC-V instructions unfortunately it does not support the C-instructions yet riscv-decode = {version = "0.2", default-features=false } +# Fixed capacity, heapless collections +heapless = { version = "0.8", default-features = false } + # The `spin` crate provides synchronization primitives (Mutexes etc) using spinlocks spin = {version="0.12", default-features = false, features = ["once", "rwlock", "spin_mutex"]} diff --git a/security-monitor/rust-crates/riscv_cove_tap/Cargo.toml b/security-monitor/rust-crates/riscv_cove_tap/Cargo.toml index 291eecc7..772be320 100644 --- a/security-monitor/rust-crates/riscv_cove_tap/Cargo.toml +++ b/security-monitor/rust-crates/riscv_cove_tap/Cargo.toml @@ -6,6 +6,7 @@ description = "Library to parse the RISC-V CoVE's TEE attestation payload (TAP)" edition = "2024" [dependencies] +heapless = { version = "0.8", default-features = false } rand = {version = "0.10", default-features=false, optional=true, features=["thread_rng"] } hybrid-array = {version = "0.4", default-features=false } diff --git a/security-monitor/rust-crates/riscv_cove_tap/src/error.rs b/security-monitor/rust-crates/riscv_cove_tap/src/error.rs index a74e233b..fec6e2f5 100644 --- a/security-monitor/rust-crates/riscv_cove_tap/src/error.rs +++ b/security-monitor/rust-crates/riscv_cove_tap/src/error.rs @@ -17,6 +17,14 @@ pub enum TapError { InvalidMagicStart(), #[error("Invalid size of the TAP")] InvalidSize(), + #[error("TAP contains more lockboxes than MAX_NUMBER_OF_LOCKBOXES")] + TooManyLockboxes(), + #[error("TAP contains more digests than MAX_NUMBER_OF_DIGESTS")] + TooManyDigests(), + #[error("TAP contains more secrets than MAX_NUMBER_OF_SECRETS")] + TooManySecrets(), + #[error("A value in the TAP exceeds the maximum allowed size")] + ValueTooLarge(), #[error("Aes error {0}")] AesError(#[from] aes_gcm::Error), #[error("Key from slice error")] diff --git a/security-monitor/rust-crates/riscv_cove_tap/src/parser.rs b/security-monitor/rust-crates/riscv_cove_tap/src/parser.rs index 9a1ad32d..bc1b6666 100644 --- a/security-monitor/rust-crates/riscv_cove_tap/src/parser.rs +++ b/security-monitor/rust-crates/riscv_cove_tap/src/parser.rs @@ -2,10 +2,8 @@ // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 pub use crate::error::TapError; -use alloc::vec::Vec; -use crate::spec::*; -use alloc::vec; use crate::ensure; +use crate::spec::*; pub struct AttestationPayloadParser { pub pointer: *const u8, @@ -19,110 +17,136 @@ impl AttestationPayloadParser { pub fn parse_and_verify(&mut self, decapsulation_key: &[u8]) -> Result { ensure!(self.read_u32()? == ACE_MAGIC_TAP_START, TapError::InvalidMagicStart())?; - self.read_u16()?; - // if self.read_u16()? as usize != self.size { - // return Err(TapError::InvalidSize()); - // } + self.read_u16()?; // reserved / total-size field (ignored for now) let number_of_lockboxes = self.read_u16()?; - ensure!(usize::from(number_of_lockboxes) <= MAX_NUMBER_OF_LOCKBOXES, TapError::InvalidSize())?; + ensure!(usize::from(number_of_lockboxes) <= MAX_NUMBER_OF_LOCKBOXES, TapError::TooManyLockboxes())?; - let mut symmetric_key = vec![]; + let mut symmetric_key: heapless::Vec = heapless::Vec::new(); for _ in 0..number_of_lockboxes { let _size = self.read_u16()? as usize; - // TODO: decide based on the lockbox name if this lockbox is intended for this device or not let _name = self.read_u64()?; let algorithm = LockboxAlgorithm::from_u16(self.read_u16()?)?; + let esk_size = self.read_u16()? as usize; - ensure!(esk_size < 100_000, TapError::InvalidSize())?; - let esk = self.read_exact(esk_size)?; + ensure!(esk_size <= MAX_ESK_SIZE, TapError::ValueTooLarge())?; + let esk_ptr = self.pointer; + self.pointer = self.pointer.wrapping_add(esk_size); + let nonce_size = self.read_u16()? as usize; - ensure!(nonce_size < 100_000, TapError::InvalidSize())?; - let nonce = self.read_exact(nonce_size)?; + ensure!(nonce_size <= MAX_NONCE_SIZE, TapError::ValueTooLarge())?; + let nonce_buf = self.read_exact_n::(nonce_size)?; + let tag_size = self.read_u16()? as usize; - ensure!(tag_size < 100_000, TapError::InvalidSize())?; - let tag = self.read_exact(tag_size)?; + ensure!(tag_size <= MAX_TAG_SIZE, TapError::ValueTooLarge())?; + let tag_buf = self.read_exact_n::(tag_size)?; + let tsk_size = self.read_u16()? as usize; - ensure!(tsk_size < 100_000, TapError::InvalidSize())?; - symmetric_key = self.read_exact(tsk_size)?; - algorithm.decode(decapsulation_key, &esk, &nonce, &tag, &mut symmetric_key)?; + ensure!(tsk_size <= MAX_TSK_SIZE, TapError::ValueTooLarge())?; + symmetric_key.clear(); + let tsk_buf = self.read_exact_n::(tsk_size)?; + symmetric_key.extend_from_slice(&tsk_buf).map_err(|_| TapError::ValueTooLarge())?; + + let esk: &[u8] = unsafe { core::slice::from_raw_parts(esk_ptr, esk_size) }; + ensure!(decapsulation_key.len() <= MAX_DK_SIZE, TapError::ValueTooLarge())?; + algorithm.decode(decapsulation_key, esk, &nonce_buf, &tag_buf, &mut symmetric_key)?; } ensure!(!symmetric_key.is_empty(), TapError::NoLockboxFound())?; let payload_encryption_algorithm = PayloadEncryptionAlgorithm::from_u16(self.read_u16()?)?; match payload_encryption_algorithm { - PayloadEncryptionAlgorithm::Debug => {}, + PayloadEncryptionAlgorithm::Debug => {} PayloadEncryptionAlgorithm::AesGcm256 => self.decrypt_aes_gcm_256(&symmetric_key)?, } let number_of_digests = self.read_u16()?; - let mut digests = Vec::with_capacity(number_of_digests.into()); + ensure!(usize::from(number_of_digests) <= MAX_NUMBER_OF_DIGESTS, TapError::TooManyDigests())?; + let mut digests: heapless::Vec = heapless::Vec::new(); for _ in 0..number_of_digests { let size = self.read_u16()? as usize; - ensure!(4 <= size && size < 100_000, TapError::InvalidSize())?; + ensure!(4 <= size, TapError::InvalidSize())?; let pcr_id = self.read_u16()?; let algorithm = DigestAlgorithm::from_u16(self.read_u16()?)?; - let value = self.read_exact(size-4)?; - digests.push(Digest { - pcr_id, - algorithm, - value - }); + let value_len = size - 4; + ensure!(value_len <= MAX_DIGEST_VALUE_SIZE, TapError::ValueTooLarge())?; + let value = self.read_exact_n::(value_len)?; + digests.push(Digest { pcr_id, algorithm, value }).map_err(|_| TapError::TooManyDigests())?; } let number_of_secrets = self.read_u16()?; - let mut secrets = vec![]; + ensure!(usize::from(number_of_secrets) <= MAX_NUMBER_OF_SECRETS, TapError::TooManySecrets())?; + let mut secrets: heapless::Vec = heapless::Vec::new(); for _ in 0..number_of_secrets { let size = self.read_u16()? as usize; - ensure!(10 <= size && size < 100_000, TapError::InvalidSize())?; - let name = self.read_u64()? as u64; - let value = self.read_exact(size-10)?; - secrets.push(Secret { name, value }); + ensure!(10 <= size, TapError::InvalidSize())?; + let name = self.read_u64()?; + let value_len = size - 10; + ensure!(value_len <= MAX_SECRET_VALUE_SIZE, TapError::ValueTooLarge())?; + let value = self.read_exact_n::(value_len)?; + secrets.push(Secret { name, value }).map_err(|_| TapError::TooManySecrets())?; } Ok(AttestationPayload { digests, secrets }) } fn decrypt_aes_gcm_256(&mut self, symmetric_key: &[u8]) -> Result<(), TapError> { - use aes_gcm::{Aes256Gcm, AeadInOut, Key, KeyInit, Tag, Nonce}; + use aes_gcm::{AeadInOut, Aes256Gcm, Key, KeyInit, Nonce, Tag}; use aes_gcm::aead::inout::InOutBuf; let nonce_size = self.read_u16()? as usize; - ensure!(nonce_size < 100_000, TapError::InvalidSize())?; - let nonce = self.read_exact(nonce_size)?; + ensure!(nonce_size <= MAX_NONCE_SIZE, TapError::ValueTooLarge())?; + let nonce_buf = self.read_exact_n::(nonce_size)?; + let tag_size = self.read_u16()? as usize; - ensure!(tag_size < 100_000, TapError::InvalidSize())?; - let tag = self.read_exact(tag_size)?; + ensure!(tag_size <= MAX_TAG_SIZE, TapError::ValueTooLarge())?; + let tag_buf = self.read_exact_n::(tag_size)?; + let payload_size = self.read_u16()? as usize; - ensure!(payload_size < 100_000, TapError::InvalidSize())?; + ensure!(payload_size <= ACE_MAX_TAP_SIZE, TapError::InvalidSize())?; ensure!(symmetric_key.len() == 32, TapError::InvalidTskSize())?; let cipher = Aes256Gcm::new(&Key::::try_from(symmetric_key)?); - let nonce = Nonce::try_from(nonce.as_slice())?; - let tag = Tag::try_from(tag.as_slice())?; - let data_slice = unsafe{ core::slice::from_raw_parts_mut(self.pointer as *mut u8, payload_size) }; + let nonce = Nonce::try_from(nonce_buf.as_slice())?; + let tag = Tag::try_from(tag_buf.as_slice())?; + let data_slice = unsafe { core::slice::from_raw_parts_mut(self.pointer as *mut u8, payload_size) }; cipher.decrypt_inout_detached(&nonce, b"", InOutBuf::from(data_slice), &tag)?; Ok(()) } fn read_u16(&mut self) -> Result { - Ok(u16::from_le_bytes(self.read_exact(2)?.try_into().map_err(|_| TapError::InvalidSize())?)) + let mut buf = [0u8; 2]; + for b in buf.iter_mut() { + *b = unsafe { self.pointer.read_volatile() }; + self.pointer = self.pointer.wrapping_add(1); + } + Ok(u16::from_le_bytes(buf)) } fn read_u32(&mut self) -> Result { - Ok(u32::from_le_bytes(self.read_exact(4)?.try_into().map_err(|_| TapError::InvalidSize())?)) + let mut buf = [0u8; 4]; + for b in buf.iter_mut() { + *b = unsafe { self.pointer.read_volatile() }; + self.pointer = self.pointer.wrapping_add(1); + } + Ok(u32::from_le_bytes(buf)) } fn read_u64(&mut self) -> Result { - Ok(u64::from_le_bytes(self.read_exact(8)?.try_into().map_err(|_| TapError::InvalidSize())?)) + let mut buf = [0u8; 8]; + for b in buf.iter_mut() { + *b = unsafe { self.pointer.read_volatile() }; + self.pointer = self.pointer.wrapping_add(1); + } + Ok(u64::from_le_bytes(buf)) } - fn read_exact(&mut self, size: usize) -> Result, TapError> { - let mut result = Vec::with_capacity(size); + fn read_exact_n(&mut self, size: usize) -> Result, TapError> { + ensure!(size <= N, TapError::ValueTooLarge())?; + let mut result: heapless::Vec = heapless::Vec::new(); for _ in 0..size { - let value = unsafe { self.pointer.read_volatile() }; + let byte = unsafe { self.pointer.read_volatile() }; self.pointer = self.pointer.wrapping_add(1); - result.push(value); + result.push(byte).map_err(|_| TapError::ValueTooLarge())?; } Ok(result) } -} \ No newline at end of file +} diff --git a/security-monitor/rust-crates/riscv_cove_tap/src/serializer.rs b/security-monitor/rust-crates/riscv_cove_tap/src/serializer.rs index e79a1733..d71f511e 100644 --- a/security-monitor/rust-crates/riscv_cove_tap/src/serializer.rs +++ b/security-monitor/rust-crates/riscv_cove_tap/src/serializer.rs @@ -2,92 +2,114 @@ // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 use crate::error::TapError; -use alloc::vec; use crate::spec::*; -use alloc::vec::Vec; +use heapless::Vec; -pub struct AttestationPayloadSerializer { +/// Type alias for a bounded wire output buffer sized to the maximum TAP blob. +type WireBuffer = Vec; +#[inline] +fn push_u16(buf: &mut WireBuffer, value: u16) -> Result<(), TapError> { + buf.extend_from_slice(&value.to_le_bytes()).map_err(|_| TapError::InvalidSize()) } +#[inline] +fn push_u32(buf: &mut WireBuffer, value: u32) -> Result<(), TapError> { + buf.extend_from_slice(&value.to_le_bytes()).map_err(|_| TapError::InvalidSize()) +} + +#[inline] +fn push_u64(buf: &mut WireBuffer, value: u64) -> Result<(), TapError> { + buf.extend_from_slice(&value.to_le_bytes()).map_err(|_| TapError::InvalidSize()) +} + +#[inline] +fn push_bytes(buf: &mut WireBuffer, bytes: &[u8]) -> Result<(), TapError> { + buf.extend_from_slice(bytes).map_err(|_| TapError::InvalidSize()) +} + +pub struct AttestationPayloadSerializer {} + impl AttestationPayloadSerializer { pub fn new() -> Self { Self {} } - pub fn serialize(&self, lockboxes: Vec, mut payload: AttestationPayload, tsk: &[u8]) -> Result, TapError> { + pub fn serialize(&self, lockboxes: Vec, mut payload: AttestationPayload, tsk: &[u8]) -> Result { let digests = self.serialize_digests(&mut payload)?; let secrets = self.serialize_secrets(&mut payload)?; - let mut encrypted_part = self.encrypt_aes_gcm_256(digests, secrets, tsk)?; - let mut lockboxes = self.serialize_lockboxes(lockboxes)?; + let encrypted_part = self.encrypt_aes_gcm_256(digests, secrets, tsk)?; + let lockboxes = self.serialize_lockboxes(lockboxes)?; let total_size = lockboxes.len() + encrypted_part.len(); - let mut result = vec![]; - result.append(&mut ACE_MAGIC_TAP_START.to_le_bytes().to_vec()); - result.append(&mut (total_size as u16).to_le_bytes().to_vec()); - result.append(&mut lockboxes); - result.append(&mut encrypted_part); + let mut result = WireBuffer::new(); + push_u32(&mut result, ACE_MAGIC_TAP_START)?; + push_u16(&mut result, total_size as u16)?; + result.extend_from_slice(&lockboxes).map_err(|_| TapError::InvalidSize())?; + result.extend_from_slice(&encrypted_part).map_err(|_| TapError::InvalidSize())?; Ok(result) } - fn serialize_lockboxes(&self, mut lockboxes: Vec) -> Result, TapError> { - // TODO: sanity check: lockboxes < 1024 - let mut result = vec![]; - result.append(&mut (lockboxes.len() as u16).to_le_bytes().to_vec()); - for mut lockbox in lockboxes.drain(..) { + fn serialize_lockboxes(&self, lockboxes: Vec) -> Result { + let mut result = WireBuffer::new(); + push_u16(&mut result, lockboxes.len() as u16)?; + for lockbox in lockboxes.into_iter() { let entry_size = lockbox.esk.len() + lockbox.nonce.len() + lockbox.tag.len() + lockbox.tsk.len() + 18; - result.append(&mut (entry_size as u16).to_le_bytes().to_vec()); - result.append(&mut (lockbox.name as u64).to_le_bytes().to_vec()); - result.append(&mut (lockbox.algorithm as u16).to_le_bytes().to_vec()); - result.append(&mut (lockbox.esk.len() as u16).to_le_bytes().to_vec()); - result.append(&mut lockbox.esk); - result.append(&mut (lockbox.nonce.len() as u16).to_le_bytes().to_vec()); - result.append(&mut lockbox.nonce); - result.append(&mut (lockbox.tag.len() as u16).to_le_bytes().to_vec()); - result.append(&mut lockbox.tag); - result.append(&mut (lockbox.tsk.len() as u16).to_le_bytes().to_vec()); - result.append(&mut lockbox.tsk); + push_u16(&mut result, entry_size as u16)?; + push_u64(&mut result, lockbox.name)?; + push_u16(&mut result, lockbox.algorithm as u16)?; + push_u16(&mut result, lockbox.esk.len() as u16)?; + push_bytes(&mut result, &lockbox.esk)?; + push_u16(&mut result, lockbox.nonce.len() as u16)?; + push_bytes(&mut result, &lockbox.nonce)?; + push_u16(&mut result, lockbox.tag.len() as u16)?; + push_bytes(&mut result, &lockbox.tag)?; + push_u16(&mut result, lockbox.tsk.len() as u16)?; + push_bytes(&mut result, &lockbox.tsk)?; } Ok(result) } - fn serialize_digests(&self, payload: &mut AttestationPayload) -> Result, TapError> { - // TODO: sanity check: digests < 1024 - let mut result = vec![]; - result.append(&mut (payload.digests.len() as u16).to_le_bytes().to_vec()); - for mut digest in payload.digests.drain(..) { + fn serialize_digests(&self, payload: &AttestationPayload) -> Result { + let mut result = WireBuffer::new(); + push_u16(&mut result, payload.digests.len() as u16)?; + for digest in payload.digests.iter() { let entry_size = digest.value.len() + 2 + 2; - result.append(&mut (entry_size as u16).to_le_bytes().to_vec()); - result.append(&mut (digest.pcr_id).to_le_bytes().to_vec()); - result.append(&mut (digest.algorithm as u16).to_le_bytes().to_vec()); - result.append(&mut digest.value); + push_u16(&mut result, entry_size as u16)?; + push_u16(&mut result, digest.pcr_id)?; + push_u16(&mut result, digest.algorithm as u16)?; + push_bytes(&mut result, &digest.value)?; } Ok(result) } - fn serialize_secrets(&self, payload: &mut AttestationPayload) -> Result, TapError> { - // TODO: sanity check: secrets < 1024 - let mut result = vec![]; - result.append(&mut (payload.secrets.len() as u16).to_le_bytes().to_vec()); - for mut secret in payload.secrets.drain(..) { + fn serialize_secrets(&self, payload: &AttestationPayload) -> Result { + let mut result = WireBuffer::new(); + push_u16(&mut result, payload.secrets.len() as u16)?; + for secret in payload.secrets.iter() { let entry_size = secret.value.len() + 10; - result.append(&mut (entry_size as u16).to_le_bytes().to_vec()); - result.append(&mut (secret.name).to_le_bytes().to_vec()); - result.append(&mut secret.value); + push_u16(&mut result, entry_size as u16)?; + push_u64(&mut result, secret.name)?; + push_bytes(&mut result, &secret.value)?; } Ok(result) } - fn encrypt_aes_gcm_256(&self, mut digests: Vec, mut secrets: Vec, tsk: &[u8]) -> Result, TapError> { + fn encrypt_aes_gcm_256( + &self, + digests: WireBuffer, + secrets: WireBuffer, + tsk: &[u8] + ) -> Result { use aes_gcm::{AeadInOut, Aes256Gcm, Key, KeyInit}; use aes_gcm::aead::inout::InOutBuf; use rand::RngExt; - let mut encrypted_part = vec![]; - encrypted_part.append(&mut digests); - encrypted_part.append(&mut secrets); + let mut plaintext = WireBuffer::new(); + push_bytes(&mut plaintext, &digests)?; + push_bytes(&mut plaintext, &secrets)?; let key = Key::::try_from(tsk)?; let cipher = Aes256Gcm::new(&key); @@ -96,28 +118,18 @@ impl AttestationPayloadSerializer { rng.fill(&mut nonce_bytes); let nonce = aes_gcm::Nonce::try_from(nonce_bytes.as_slice())?; let tag = cipher - .encrypt_inout_detached(&nonce, b"", InOutBuf::from(encrypted_part.as_mut_slice())) + .encrypt_inout_detached(&nonce, b"", InOutBuf::from(plaintext.as_mut_slice())) .map_err(|_| TapError::KemError())?; - let mut result = vec![]; - result.append(&mut (PayloadEncryptionAlgorithm::AesGcm256 as u16).to_le_bytes().to_vec()); - result.append(&mut (nonce.as_slice().len() as u16).to_le_bytes().to_vec()); - result.append(&mut nonce.as_slice().to_vec()); - result.append(&mut (tag.as_slice().len() as u16).to_le_bytes().to_vec()); - result.append(&mut tag.as_slice().to_vec()); - result.append(&mut (encrypted_part.len() as u16).to_le_bytes().to_vec()); - result.append(&mut encrypted_part); + let mut result = WireBuffer::new(); + push_u16(&mut result, PayloadEncryptionAlgorithm::AesGcm256 as u16)?; + push_u16(&mut result, nonce_bytes.len() as u16)?; + push_bytes(&mut result, &nonce_bytes)?; + push_u16(&mut result, tag.as_slice().len() as u16)?; + push_bytes(&mut result, tag.as_slice())?; + push_u16(&mut result, plaintext.len() as u16)?; + push_bytes(&mut result, &plaintext)?; Ok(result) } - - // fn encrypt_rsa_2048_sha256_oasp(value: &[u8], public_key_file: String) -> Result, Error> { - // use rsa::pkcs1::DecodeRsaPublicKey; - // let public_key_pem: Vec = std::fs::read(public_key_file.clone()) - // .map_err(|_| Error::CannotOpenFile(public_key_file))?; - // let public_key = rsa::RsaPublicKey::from_pkcs1_pem(&String::from_utf8_lossy(&public_key_pem))?; - // let padding = rsa::Oaep::new::(); - // let encrypted_data = public_key.encrypt(&mut rand::thread_rng(), padding, value)?; - // Ok(encrypted_data) - // } -} \ No newline at end of file +} diff --git a/security-monitor/rust-crates/riscv_cove_tap/src/spec.rs b/security-monitor/rust-crates/riscv_cove_tap/src/spec.rs index 8f348c1c..576e6976 100644 --- a/security-monitor/rust-crates/riscv_cove_tap/src/spec.rs +++ b/security-monitor/rust-crates/riscv_cove_tap/src/spec.rs @@ -2,46 +2,87 @@ // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 use crate::error::TapError; -use alloc::vec::Vec; +use heapless::Vec; pub const ACE_HEADER_SIZE: usize = 8; pub const ACE_FOOTER_SIZE: usize = 8; pub const ACE_MAGIC_TAP_START: u32 = 0xACE0ACE0; pub const ACE_MAGIC_TAP_END: u32 = 0xACE1ACE1; -pub const ACE_MAX_TAP_SIZE: usize = 4096; // size of the 4KiB page -pub const MAX_NUMBER_OF_LOCKBOXES: usize = 1024; +/// Maximum total wire size of a TAP blob (one 4 KiB page). +pub const ACE_MAX_TAP_SIZE: usize = 4096; + +/// Maximum number of lockboxes (key-encapsulations) in one TAP. +/// Each lockbox targets one device identity; 16 is more than enough for any +/// realistic deployment while keeping the parser's stack budget bounded. +pub const MAX_NUMBER_OF_LOCKBOXES: usize = 16; + +/// Maximum number of measurement digests (PCR values) in one TAP. +/// CoVE defines at most 24 PCR slots; we round up to 32 for headroom. +pub const MAX_NUMBER_OF_DIGESTS: usize = 32; + +/// Maximum number of secrets carried in one TAP. +/// A VM rarely needs more than a handful of symmetric keys at provisioning. +pub const MAX_NUMBER_OF_SECRETS: usize = 8; + +/// Maximum byte length of a single secret value. +/// 256 bytes covers all standard symmetric key material: +/// AES-256 (32 B), HMAC-SHA-512 key (64 B), two AES keys + nonce (76 B), etc. +pub const MAX_SECRET_VALUE_SIZE: usize = 256; + +/// Maximum byte length of a single digest value. +/// SHA-512 produces 64 bytes; 128 bytes gives comfortable headroom. +pub const MAX_DIGEST_VALUE_SIZE: usize = 128; + +/// Maximum size of the encapsulated symmetric key (esk). +/// ML-KEM-1024 ciphertext is exactly 1568 bytes (FIPS 203, K=4, Du=11, Dv=5: +/// 32*(Du*K + Dv) = 32*49 = 1568). +pub const MAX_ESK_SIZE: usize = 1568; + +/// Maximum size of an AES-GCM nonce. +/// AES-256-GCM uses a 96-bit (12-byte) nonce. +pub const MAX_NONCE_SIZE: usize = 12; + +/// Maximum size of an AES-GCM authentication tag. +/// AES-256-GCM produces a 128-bit (16-byte) tag. +pub const MAX_TAG_SIZE: usize = 16; + +/// Maximum size of the transport symmetric key (tsk) stored in a Lockbox. +/// Reuses MAX_SECRET_VALUE_SIZE: the tsk is used to decrypt the payload that +/// carries the secrets, so it must fit within the same bound. +pub const MAX_TSK_SIZE: usize = MAX_SECRET_VALUE_SIZE; + +/// Maximum size of the ML-KEM-1024 decapsulation key accepted by the parser. +/// FIPS 203: dk size = 768*K + 96 = 768*4 + 96 = 3168 bytes. +pub const MAX_DK_SIZE: usize = 3168; pub struct AttestationPayload { - pub digests: Vec, - pub secrets: Vec, + pub digests: Vec, + pub secrets: Vec, } pub struct Lockbox { pub name: u64, pub algorithm: LockboxAlgorithm, - pub esk: Vec, - pub nonce: Vec, - pub tag: Vec, - pub tsk: Vec + pub esk: Vec, + pub nonce: Vec, + pub tag: Vec, + pub tsk: Vec, } impl Lockbox { #[cfg(feature = "serializer")] - pub fn new(lockbox_algorithm: LockboxAlgorithm, encapsulation_key: &Vec, tsk: &mut Vec) -> Result { + pub fn new( + lockbox_algorithm: LockboxAlgorithm, + encapsulation_key: &[u8], + tsk: &mut Vec, + ) -> Result { let (esk, nonce, tag, tsk) = lockbox_algorithm.encode(encapsulation_key, tsk)?; - Ok(Self { - name: 0, - algorithm: lockbox_algorithm, - esk, - nonce, - tag, - tsk - }) + Ok(Self { name: 0, algorithm: lockbox_algorithm, esk, nonce, tag, tsk }) } } #[repr(u16)] -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub enum LockboxAlgorithm { Debug = 0, MlKem1024Aes256 = 1, @@ -57,18 +98,27 @@ impl LockboxAlgorithm { } #[cfg(feature = "serializer")] - pub fn encode(&self, encapsulation_key: &Vec, tsk: &mut Vec) -> Result<(Vec, Vec, Vec, Vec), TapError> { - use alloc::vec; + pub fn encode( + &self, + encapsulation_key: &[u8], + tsk: &mut Vec, + ) -> Result<( + Vec, + Vec, + Vec, + Vec, + ), TapError> { match self { LockboxAlgorithm::Debug => { - Ok((vec![], vec![], vec![], tsk.to_vec())) + // Debug mode: all fields empty, tsk is passed through unchanged. + Ok((Vec::new(), Vec::new(), Vec::new(), Vec::new())) } LockboxAlgorithm::MlKem1024Aes256 => { use rand::RngExt; let mut rng = rand::rng(); use ml_kem::{B32, ml_kem_1024::EncapsulationKey, kem::Key as KemKey}; - let ek_key_arr = KemKey::::try_from(encapsulation_key.as_slice()) + let ek_key_arr = KemKey::::try_from(encapsulation_key) .map_err(|_| TapError::KemError())?; let ek = EncapsulationKey::new(&ek_key_arr).map_err(|_| TapError::KemError())?; let mut m_bytes = [0u8; 32]; @@ -78,26 +128,36 @@ impl LockboxAlgorithm { use aes_gcm::{AeadInOut, Aes256Gcm, Key, KeyInit}; use aes_gcm::aead::inout::InOutBuf; - let mut nonce = [0u8; 12]; - rng.fill(&mut nonce); + let mut nonce_bytes = [0u8; MAX_NONCE_SIZE]; + rng.fill(&mut nonce_bytes); let key: &Key = &Key::::try_from(aes_key.as_slice())?; let cipher = Aes256Gcm::new(key); - let nonce = aes_gcm::Nonce::try_from(nonce.as_slice())?; - let tag = cipher.encrypt_inout_detached(&nonce, b"", InOutBuf::from(tsk.as_mut_slice()))?; - - Ok((esk_arr.to_vec(), nonce.as_slice().to_vec(), tag.as_slice().to_vec(), tsk.to_vec())) + let nonce = aes_gcm::Nonce::try_from(nonce_bytes.as_slice())?; + let tag_arr = cipher.encrypt_inout_detached(&nonce, b"", InOutBuf::from(tsk.as_mut_slice()))?; + + let mut esk: Vec = Vec::new(); + esk.extend_from_slice(esk_arr.as_slice()).map_err(|_| TapError::ValueTooLarge())?; + let mut nonce_out: Vec = Vec::new(); + nonce_out.extend_from_slice(nonce.as_slice()).map_err(|_| TapError::ValueTooLarge())?; + let mut tag_out: Vec = Vec::new(); + tag_out.extend_from_slice(tag_arr.as_slice()).map_err(|_| TapError::ValueTooLarge())?; + let mut tsk_out: Vec = Vec::new(); + tsk_out.extend_from_slice(tsk.as_slice()).map_err(|_| TapError::ValueTooLarge())?; + + Ok((esk, nonce_out, tag_out, tsk_out)) } } } #[cfg(feature = "parser")] - pub fn decode(&self, decapsulation_key: &[u8], esk: &[u8], nonce: &[u8], tag: &[u8], tsk: &mut Vec) -> Result<(), TapError> { + pub fn decode( + &self, decapsulation_key: &[u8], esk: &[u8], nonce: &[u8], tag: &[u8], + tsk: &mut Vec, + ) -> Result<(), TapError> { match self { - LockboxAlgorithm::Debug => { - Ok(()) - }, + LockboxAlgorithm::Debug => Ok(()), LockboxAlgorithm::MlKem1024Aes256 => { - use aes_gcm::{AeadInOut, Aes256Gcm, Key, KeyInit, Tag, Nonce}; + use aes_gcm::{AeadInOut, Aes256Gcm, Key, KeyInit, Nonce, Tag}; use aes_gcm::aead::inout::InOutBuf; use ml_kem::{ ml_kem_1024::{Ciphertext, DecapsulationKey}, @@ -113,7 +173,14 @@ impl LockboxAlgorithm { let sk = dk.decapsulate(&ct_arr); let cipher = Aes256Gcm::new(&Key::::try_from(sk.as_slice())?); - cipher.decrypt_inout_detached(&Nonce::try_from(nonce)?, b"", InOutBuf::from(tsk.as_mut_slice()), &Tag::try_from(tag)?).unwrap(); + cipher + .decrypt_inout_detached( + &Nonce::try_from(nonce)?, + b"", + InOutBuf::from(tsk.as_mut_slice()), + &Tag::try_from(tag)?, + ) + .unwrap(); Ok(()) } } @@ -123,13 +190,17 @@ impl LockboxAlgorithm { pub struct Digest { pub pcr_id: u16, pub algorithm: DigestAlgorithm, - pub value: Vec, + pub value: Vec, } impl Digest { pub fn value_in_hex(&self) -> alloc::string::String { use crate::alloc::string::ToString; - self.value.iter().map(|b| alloc::format!("{:02x}", b).to_string()).collect::>().join("") + self.value + .iter() + .map(|b| alloc::format!("{:02x}", b).to_string()) + .collect::>() + .join("") } pub fn pcr_id(&self) -> u16 { @@ -138,36 +209,36 @@ impl Digest { } #[repr(u16)] -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum DigestAlgorithm { Debug = 0, - Sha512 = 1, + Sha3_256 = 1, } impl DigestAlgorithm { pub fn from_u16(value: u16) -> Result { match value { 0 => Ok(Self::Debug), - 1 => Ok(Self::Sha512), + 1 => Ok(Self::Sha3_256), v => Err(TapError::UnsupportedDigestAlgorithm(v)), } } pub fn digest_size(&self) -> u16 { match self { - &Self::Debug => 0, - &Self::Sha512 => 512 / 8, + Self::Debug => 0, + Self::Sha3_256 => 256 / 8, } } } pub struct Secret { pub name: u64, - pub value: Vec, + pub value: Vec, } #[repr(u16)] -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub enum PayloadEncryptionAlgorithm { Debug = 0, AesGcm256 = 1, @@ -181,4 +252,4 @@ impl PayloadEncryptionAlgorithm { v => Err(TapError::UnsupportedPayloadEncryptionAlgorithm(v)), } } -} \ No newline at end of file +} diff --git a/security-monitor/src/confidential_flow/finite_state_machine.rs b/security-monitor/src/confidential_flow/finite_state_machine.rs index 8928d0a5..71acaaa9 100644 --- a/security-monitor/src/confidential_flow/finite_state_machine.rs +++ b/security-monitor/src/confidential_flow/finite_state_machine.rs @@ -264,12 +264,12 @@ impl<'a> ConfidentialFlow<'a> { confidential_vm.try_confidential_hart_remote_commands( self.confidential_hart_id(), |ref mut confidential_hart_remote_commands| { - confidential_hart_remote_commands.drain(..).for_each(|confidential_hart_remote_command| { + while let Some(confidential_hart_remote_command) = confidential_hart_remote_commands.pop() { // The confidential flow has an ownership of the confidential hart because the confidential hart // is assigned to the hardware hart. self.confidential_hart_mut().execute(&confidential_hart_remote_command); requests_processed = true; - }); + } Ok(()) }, ) diff --git a/security-monitor/src/core/architecture/riscv/extensions/vector_unit.rs b/security-monitor/src/core/architecture/riscv/extensions/vector_unit.rs index 8403b853..484e4e51 100644 --- a/security-monitor/src/core/architecture/riscv/extensions/vector_unit.rs +++ b/security-monitor/src/core/architecture/riscv/extensions/vector_unit.rs @@ -2,7 +2,6 @@ // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 #![allow(unused)] -use alloc::vec::Vec; use core::ops::Range; pub const MAX_NUMBER_OF_REGISTER_LENGTH: usize = 32; diff --git a/security-monitor/src/core/architecture/riscv/general_purpose_registers.rs b/security-monitor/src/core/architecture/riscv/general_purpose_registers.rs index fd48024b..fcfa072f 100644 --- a/security-monitor/src/core/architecture/riscv/general_purpose_registers.rs +++ b/security-monitor/src/core/architecture/riscv/general_purpose_registers.rs @@ -2,7 +2,6 @@ // SPDX-FileContributor: Wojciech Ozga , IBM Research - Zurich // SPDX-License-Identifier: Apache-2.0 use crate::core::control_data::{DigestType, MeasurementDigest}; -use alloc::vec::Vec; use core::ops::Range; #[repr(C)] diff --git a/security-monitor/src/core/architecture/riscv/mmu/page_table.rs b/security-monitor/src/core/architecture/riscv/mmu/page_table.rs index b41d1c80..988dfafb 100644 --- a/security-monitor/src/core/architecture/riscv/mmu/page_table.rs +++ b/security-monitor/src/core/architecture/riscv/mmu/page_table.rs @@ -127,7 +127,7 @@ impl PageTable { let address = NonConfidentialMemoryAddress::new(pointer)?; let page_size = paging_system.data_page_size(level); let page = PageAllocator::acquire_page(page_size)?.copy_from_non_confidential_memory(address)?; - LogicalPageTableEntry::PageWithConfidentialVmData(Box::new(page)) + LogicalPageTableEntry::PageWithConfidentialVmData(page) } }; serialized_representation.write(index, logical_page_table_entry.serialize()).unwrap(); @@ -161,7 +161,7 @@ impl PageTable { &mut self, confidential_vm_address: &ConfidentialVmPhysicalAddress, page_size: &PageSize, ) -> Result { let page = PageAllocator::acquire_page(*page_size)?.zeroize(); - let entry = LogicalPageTableEntry::PageWithConfidentialVmData(Box::new(page)); + let entry = LogicalPageTableEntry::PageWithConfidentialVmData(page); self.map_page(confidential_vm_address, page_size, entry)?; Ok(*page_size) } @@ -308,7 +308,7 @@ impl PageTable { self.serialized_representation.write(self.paging_system.entry_size() * virtual_page_number, entry.serialize()).unwrap(); let entry_to_remove = core::mem::replace(&mut self.logical_representation[virtual_page_number], entry); if let LogicalPageTableEntry::PageWithConfidentialVmData(page) = entry_to_remove { - PageAllocator::release_pages(alloc::vec![page.deallocate()]); + PageAllocator::release_page(page.deallocate()); } } diff --git a/security-monitor/src/core/architecture/riscv/mmu/page_table_entry.rs b/security-monitor/src/core/architecture/riscv/mmu/page_table_entry.rs index 4442ab96..770bb11f 100644 --- a/security-monitor/src/core/architecture/riscv/mmu/page_table_entry.rs +++ b/security-monitor/src/core/architecture/riscv/mmu/page_table_entry.rs @@ -19,7 +19,7 @@ pub(super) enum LogicalPageTableEntry { PointerToNextPageTable(Box), #[rr::pattern("PageWithConfidentialVmData" $ "p", "conf", "perm")] #[rr::refinement("-[ #(#p); #conf; #perm]")] - PageWithConfidentialVmData(Box>), + PageWithConfidentialVmData(Page), #[rr::pattern("PageSharedWithHypervisor" $ "sp", "conf", "perm")] #[rr::refinement("-[ #sp; #conf; #perm]")] PageSharedWithHypervisor(SharedPage), diff --git a/security-monitor/src/core/control_data/confidential_vm.rs b/security-monitor/src/core/control_data/confidential_vm.rs index a22fec71..fd676800 100644 --- a/security-monitor/src/core/control_data/confidential_vm.rs +++ b/security-monitor/src/core/control_data/confidential_vm.rs @@ -7,30 +7,33 @@ use crate::core::control_data::{ }; use crate::core::memory_protector::ConfidentialVmMemoryProtector; use crate::error::Error; -use alloc::collections::BTreeMap; -use alloc::vec::Vec; +use heapless::{FnvIndexMap, Vec}; use riscv_cove_tap::Secret; use spin::{Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard}; pub struct ConfidentialVm { id: ConfidentialVmId, - confidential_harts: Vec>, + confidential_harts: Vec, { Self::MAX_NUMBER_OF_HARTS_PER_VM }>, _measurements: StaticMeasurements, - secrets: Vec, - remote_commands: BTreeMap>>, + secrets: Vec, + remote_commands: FnvIndexMap< + usize, + Mutex>, + { Self::MAX_NUMBER_OF_HARTS_PER_VM }, + >, memory_protector: RwLock, allowed_external_interrupts: usize, - mmio_regions: Vec, + mmio_regions: Vec, } impl ConfidentialVm { - pub const MAX_NUMBER_OF_HARTS_PER_VM: usize = 1024; - /// An average number of inter hart requests that can be buffered before being processed. - const AVG_NUMBER_OF_COMMANDS: usize = 3; + pub const MAX_NUMBER_OF_HARTS_PER_VM: usize = 8; + /// A maximum number of secrets per VM + pub const MAX_NUMBER_OF_SECRETS: usize = 8; /// A maximum number of inter hart requests that can be buffered. - const MAX_NUMBER_OF_COMMANDS: usize = 64; + pub const MAX_NUMBER_OF_COMMANDS: usize = 32; /// A maximum number of MMIO regions that a confidential VM can register - const MAX_NUMBER_OF_MMIO_REGIONS: usize = 1024; + const MAX_NUMBER_OF_MMIO_REGIONS: usize = 64; /// Constructs a new confidential VM. /// @@ -38,27 +41,29 @@ impl ConfidentialVm { /// /// The id of the confidential VM must be unique. pub fn new( - id: ConfidentialVmId, mut confidential_harts: Vec, _measurements: StaticMeasurements, secrets: Vec, + id: ConfidentialVmId, confidential_harts: Vec, { Self::MAX_NUMBER_OF_HARTS_PER_VM }>, + remote_commands: FnvIndexMap< + usize, + Mutex>, + { Self::MAX_NUMBER_OF_HARTS_PER_VM }, + >, + _measurements: StaticMeasurements, secrets: Vec, mut memory_protector: ConfidentialVmMemoryProtector, - ) -> Self { + ) -> Result { memory_protector.set_confidential_vm_id(id); - let remote_commands = confidential_harts - .iter_mut() - .map(|confidential_hart| { - confidential_hart.set_confidential_vm_id(id); - (confidential_hart.confidential_hart_id(), Mutex::new(Vec::with_capacity(Self::AVG_NUMBER_OF_COMMANDS))) - }) - .collect(); - Self { + for hart_lock in confidential_harts.iter() { + hart_lock.write().set_confidential_vm_id(id); + } + Ok(Self { id, - confidential_harts: confidential_harts.into_iter().map(|h| RwLock::new(h)).collect(), + confidential_harts, _measurements, secrets, remote_commands, memory_protector: RwLock::new(memory_protector), allowed_external_interrupts: 0, - mmio_regions: Vec::with_capacity(8), - } + mmio_regions: Vec::new(), + }) } pub fn confidential_vm_id(&self) -> ConfidentialVmId { @@ -73,12 +78,8 @@ impl ConfidentialVm { self.memory_protector.write() } - pub fn secret(&self, secret_id: usize) -> Result, Error> { - self.secrets - .iter() - .find(|ref s| s.name == secret_id as u64) - .and_then(|s| Some(s.value.to_vec())) - .ok_or_else(|| Error::InvalidParameter()) + pub fn secret(&self, secret_id: usize) -> Result, Error> { + self.secrets.iter().find(|s| s.name == secret_id as u64).map(|s| s.value.clone()).ok_or_else(|| Error::InvalidParameter()) } pub(super) fn deallocate(self) { @@ -138,7 +139,8 @@ impl ConfidentialVm { pub fn add_mmio_region(&mut self, region: ConfidentialVmMmioRegion) -> Result<(), Error> { ensure!(self.mmio_regions.len() < Self::MAX_NUMBER_OF_MMIO_REGIONS, Error::ReachedMaxNumberOfMmioRegions())?; ensure!(!self.mmio_regions.iter().any(|x| x.overlaps(®ion)), Error::OverlappingMmioRegion())?; - Ok(self.mmio_regions.push(region)) + self.mmio_regions.push(region).map_err(|_| Error::ReachedMaxNumberOfMmioRegions())?; + Ok(()) } pub fn remove_mmio_region(&mut self, region: &ConfidentialVmMmioRegion) { @@ -195,7 +197,7 @@ impl ConfidentialVm { self.try_confidential_hart_remote_commands(confidential_hart_id, |ref mut remote_commands| { ensure!(remote_commands.len() < Self::MAX_NUMBER_OF_COMMANDS, Error::ReachedMaxNumberOfRemoteCommands())?; if remote_commands.iter().find(|c| **c == remote_command).is_none() { - remote_commands.push(remote_command.clone()); + remote_commands.push(remote_command.clone()).map_err(|_| Error::ReachedMaxNumberOfRemoteCommands())?; } Ok(()) })?; @@ -206,7 +208,7 @@ impl ConfidentialVm { } pub fn try_confidential_hart_remote_commands(&self, confidential_hart_id: usize, op: O) -> Result - where O: FnOnce(MutexGuard<'_, Vec>) -> Result { + where O: FnOnce(MutexGuard<'_, Vec>) -> Result { op(self.remote_commands.get(&confidential_hart_id).ok_or(Error::InvalidHartId())?.lock()) } } diff --git a/security-monitor/src/core/hardware_setup/mod.rs b/security-monitor/src/core/hardware_setup/mod.rs index cd082036..65ccef9b 100644 --- a/security-monitor/src/core/hardware_setup/mod.rs +++ b/security-monitor/src/core/hardware_setup/mod.rs @@ -4,7 +4,7 @@ use crate::core::architecture::HardwareExtension; use crate::core::architecture::riscv::specification::*; use crate::error::Error; -use alloc::vec::Vec; +use heapless::Vec; use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard}; /// Global static description of hardware setup. It is created during the system initiization and used in runtime to perform operations that @@ -12,7 +12,7 @@ use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard}; static HARDWARE_SETUP: Once> = Once::new(); pub struct HardwareSetup { - isa_extensions: Vec, + isa_extensions: Vec, } impl HardwareSetup { @@ -31,16 +31,17 @@ impl HardwareSetup { // example prop value rv64imafdch_zicsr_zifencei_zba_zbb_zbc_zbs debug!("{}", prop); ensure!(prop.starts_with(RISCV_ARCH), Error::InvalidCpuArch())?; - let extensions = &prop.split('_').collect::>(); - Self::REQUIRED_BASE_EXTENSIONS + let mut extensions = prop.split('_'); + let base = extensions.next().unwrap_or(""); + Self::REQUIRED_BASE_EXTENSIONS.into_iter().try_for_each(|ext| ensure!(base.contains(*ext), Error::MissingCpuExtension()))?; + Self::REQUIRED_EXTENSIONS .into_iter() - .try_for_each(|ext| ensure!(extensions[0].contains(*ext), Error::MissingCpuExtension()))?; - Self::REQUIRED_EXTENSIONS.into_iter().try_for_each(|ext| ensure!(extensions.contains(ext), Error::MissingCpuExtension()))?; + .try_for_each(|ext| ensure!(prop.split('_').any(|item| item == *ext), Error::MissingCpuExtension()))?; Ok(()) } pub fn add_extension(extension: HardwareExtension) -> Result<(), Error> { - Self::try_write(|hardware_setup| Ok(hardware_setup.isa_extensions.push(extension))) + Self::try_write(|hardware_setup| hardware_setup.isa_extensions.push(extension).map_err(|_| Error::Failed())) } pub fn is_extension_supported(extension: HardwareExtension) -> bool { diff --git a/security-monitor/src/core/initialization/mod.rs b/security-monitor/src/core/initialization/mod.rs index 7b8ccf8c..2ace3e0f 100644 --- a/security-monitor/src/core/initialization/mod.rs +++ b/security-monitor/src/core/initialization/mod.rs @@ -11,13 +11,15 @@ use crate::core::memory_layout::{ConfidentialMemoryAddress, MemoryLayout}; use crate::core::memory_protector::HypervisorMemoryProtector; use crate::core::page_allocator::{Page, PageAllocator, UnAllocated}; use crate::error::Error; -use alloc::vec::Vec; use core::mem::size_of; +use core::sync::atomic::{AtomicBool, Ordering}; use flattened_device_tree::FlattenedDeviceTree; +use heapless::Vec; use pointers_utility::ptr_byte_add_mut; -use spin::{Mutex, Once}; +use spin::Mutex; const NUMBER_OF_HEAP_PAGES: usize = 80 * 1024; +pub const MAX_SUPPORTED_PHYSICAL_HARTS: usize = 16; unsafe extern "C" { // Assembly function that is an entry point to the security monitor from the hypervisor or a virtual machine. @@ -32,7 +34,10 @@ unsafe extern "C" { /// /// Initialization procedure must guarantee that the mscratch register contains the address of the memory region that /// stores the state of the executing hart. -static HARTS_STATES: Once>> = Once::new(); +static HARTS_STATES: Mutex> = Mutex::new(Vec::new()); + +/// Set to `true` by the cold-boot hart once `prepare_harts()` completes. All other harts spin-wait on this flag +static HARTS_READY: AtomicBool = AtomicBool::new(false); /// The entry point to the security monitor initialization procedure. It should be called by the booting firmware (e.g., /// OpenSBI) during the boot process to initialize ACE. After the return, the control flow returns to the booting @@ -95,8 +100,8 @@ fn verify_harts(fdt: &FlattenedDeviceTree) -> Result { HardwareExtension::all().into_iter().for_each(|ext| { let is_extension_supported_by_all_harts = fdt.harts().all(|hart| { let prop = hart.property_str(FDT_RISCV_ISA).ok_or(Error::FdtParsing()).unwrap_or(""); - let extensions = &prop.split('_').collect::>(); - extensions[0].contains(&ext.code()) || extensions.contains(&ext.code()) + let base = prop.split('_').next().unwrap_or(""); + base.contains(&ext.code()) || prop.split('_').any(|item| item == ext.code()) }); if is_extension_supported_by_all_harts { debug!("Enabling support for extension: {:?}", ext); @@ -193,16 +198,17 @@ fn initalize_security_monitor_state( } fn prepare_harts(number_of_harts: usize) -> Result<(), Error> { + ensure!(number_of_harts <= MAX_SUPPORTED_PHYSICAL_HARTS, Error::InvalidNumberOfHartsInFdt())?; // We need to allocate stack for the dumped state of each physical hart. - let mut harts_states = Vec::with_capacity(number_of_harts); for hart_id in 0..number_of_harts { let stack = PageAllocator::acquire_page(PageSize::Size2MiB)?; let hypervisor_memory_protector = HypervisorMemoryProtector::create(); debug!("Hart[{}] stack \t 0x{:x}-0x{:x}", hart_id, stack.start_address(), stack.end_address()); - harts_states.insert(hart_id, HardwareHart::init(hart_id, stack, hypervisor_memory_protector)); + HARTS_STATES.lock().push(HardwareHart::init(hart_id, stack, hypervisor_memory_protector)).map_err(|_| Error::Failed())?; } - HARTS_STATES.call_once(|| Mutex::new(harts_states)); fence_wo(); + // Signal other harts that HARTS_STATES is fully populated. + HARTS_READY.store(true, Ordering::Release); Ok(()) } @@ -211,7 +217,7 @@ fn prepare_harts(number_of_harts: usize) -> Result<(), Error> { #[unsafe(no_mangle)] extern "C" fn ace_setup_this_hart() { // wait until the boot hart initializes the security monitor's data structures - while !HARTS_STATES.is_completed() { + while !HARTS_READY.load(Ordering::Acquire) { fence_wo(); } @@ -220,8 +226,8 @@ extern "C" fn ace_setup_this_hart() { // OpenSBI requires that mscratch points to an internal OpenSBI's structure. We have to store this pointer during // init and restore it every time we delegate exception/interrupt to the Sbi firmware (e.g., OpenSbi). - let mut harts = HARTS_STATES.get().expect("Bug. Could not set mscratch before initializing memory region for harts states").lock(); - let hart = harts.get_mut(hart_id).expect("Bug. Incorrectly setup memory region for harts states"); + let mut harts = HARTS_STATES.lock(); + let hart = harts.get_mut(hart_id).expect("Bug. Incorrectly set up memory region for harts states"); // The mscratch must point to the memory region when the security monitor stores the dumped states of // confidential harts. This is crucial for context switches because assembly code will use the mscratch diff --git a/security-monitor/src/core/page_allocator/allocator.rs b/security-monitor/src/core/page_allocator/allocator.rs index b3faafc7..fceaa53d 100644 --- a/security-monitor/src/core/page_allocator/allocator.rs +++ b/security-monitor/src/core/page_allocator/allocator.rs @@ -275,6 +275,18 @@ impl PageAllocator { } /// Consumes the page tokens given by the caller, allowing for their further acquisition. This is equivalent to deallocation of the + /// Releases a single page token back to the PageAllocator without dynamic allocation. + pub fn release_page(released_page: Page) { + let _ = Self::try_write(|page_allocator| { + let base_address = page_allocator.base_address; + let page_size = page_allocator.page_size; + let root_node = &mut page_allocator.root; + root_node.store_page_token(base_address, page_size, released_page); + Ok(()) + }) + .unwrap(); + } + /// physical memory region owned by the returned page tokens. Given vector of pages might contains pages of arbitrary sizes. #[rr::params("MEMORY_CONFIG" : "memory_layout")] /// Precondition: We require the page allocator to be initialized. @@ -631,6 +643,7 @@ impl PageStorageTreeNode { assert!(merged_token.size() == this_node_page_size); self.page_token = Some(merged_token); self.max_allocable_page_size = Some(this_node_page_size); + self.children = vec![]; } } diff --git a/security-monitor/src/non_confidential_flow/handlers/cove_host_extension/promote_to_confidential_vm.rs b/security-monitor/src/non_confidential_flow/handlers/cove_host_extension/promote_to_confidential_vm.rs index 4564e963..cfacc32d 100644 --- a/security-monitor/src/non_confidential_flow/handlers/cove_host_extension/promote_to_confidential_vm.rs +++ b/security-monitor/src/non_confidential_flow/handlers/cove_host_extension/promote_to_confidential_vm.rs @@ -14,8 +14,8 @@ use crate::core::time_controller::TimeController; use crate::error::Error; use crate::non_confidential_flow::handlers::supervisor_binary_interface::SbiResponse; use crate::non_confidential_flow::{ApplyToHypervisorHart, NonConfidentialFlow}; -use alloc::vec::Vec; use flattened_device_tree::FlattenedDeviceTree; +use heapless::Vec; use riscv_cove_tap::{AttestationPayload, AttestationPayloadParser, Secret}; /// Creates a confidential VM in a single-step. This handler implements the Promote to TVM call defined by the COVH ABI in the CoVE @@ -72,7 +72,7 @@ impl PromoteToConfidentialVm { SbiResponse::error(error) } }; - PageAllocator::release_pages(alloc::vec![large_page.deallocate()]); + PageAllocator::release_page(large_page.deallocate()); non_confidential_flow.apply_and_exit_to_hypervisor(ApplyToHypervisorHart::PromoteResponse((self, sbi_response))) } @@ -89,42 +89,54 @@ impl PromoteToConfidentialVm { let (vm_memory_layout, number_of_confidential_harts) = self.process_device_tree(large_page, &memory_protector, &fdt_address)?; debug!("Number of confidential harts: {}", number_of_confidential_harts); - // We create a fixed number of harts (all but the boot hart are in the reset state). - let confidential_harts: Vec<_> = (0..number_of_confidential_harts) - .map(|id| match id { + ensure!(number_of_confidential_harts <= ConfidentialVm::MAX_NUMBER_OF_HARTS_PER_VM, Error::InvalidNumberOfHartsInFdt())?; + let mut harts_locks = Vec::new(); + let mut remote_commands = heapless::FnvIndexMap::new(); + for id in 0..number_of_confidential_harts { + let hart = match id { Self::BOOT_HART_ID => { ConfidentialHart::from_vm_hart(id, self.program_counter, &fdt_address, self.htimedelta, shared_memory) } _ => ConfidentialHart::from_vm_hart_reset(id, self.htimedelta, shared_memory), - }) - .collect(); + }; + let hart_id = hart.confidential_hart_id(); + harts_locks.push(spin::RwLock::new(hart)).map_err(|_| Error::InvalidNumberOfHartsInFdt())?; + remote_commands.insert(hart_id, spin::Mutex::new(Vec::new())).map_err(|_| Error::InvalidNumberOfHartsInFdt())?; + } let payload = self .read_attestation_payload(large_page, &memory_protector) .inspect_err(|e| debug!("TAP reading failed: {:?}", e)) .unwrap_or(None); - let measurements = self.measure(&mut memory_protector, &vm_memory_layout, &confidential_harts)?; + let measurements = self.measure(&mut memory_protector, &vm_memory_layout, &harts_locks)?; let secrets = self .authenticate_and_authorize_vm(payload, &measurements) .inspect_err(|e| debug!("Local attestation failed: {:?}", e)) - .unwrap_or(alloc::vec![]); + .unwrap_or_default(); ControlDataStorage::try_write(|control_data| { // We have a write lock on the entire control data! Spend here as little time as possible because we are // blocking all other harts from accessing the control data. This influences all confidential VMs in the system! let id = control_data.unique_id()?; - control_data.insert_confidential_vm(ConfidentialVm::new(id, confidential_harts, measurements, secrets, memory_protector)) + control_data.insert_confidential_vm(ConfidentialVm::new( + id, + harts_locks, + remote_commands, + measurements, + secrets, + memory_protector, + )?) }) } - fn measure( + fn measure( &self, memory_protector: &mut ConfidentialVmMemoryProtector, vm_memory_layout: &ConfidentialVmMemoryLayout, - confidential_harts: &Vec, + confidential_harts: &Vec, N>, ) -> Result { let mut measurements = StaticMeasurements::default(); memory_protector.finalize(&mut measurements, vm_memory_layout)?; - confidential_harts[Self::BOOT_HART_ID].measure(measurements.pcr_boot_hart_mut()); + confidential_harts[Self::BOOT_HART_ID].read().measure(measurements.pcr_boot_hart_mut()); // codeql[rust/cleartext-logging] - digest values intentionally logged in debug builds debug!("VM measurements: {:?}", measurements); Ok(measurements) @@ -199,7 +211,7 @@ impl PromoteToConfidentialVm { /// for this confidential VM. fn authenticate_and_authorize_vm( &self, attestation_payload: Option, measurements: &StaticMeasurements, - ) -> Result, Error> { + ) -> Result, Error> { use crate::core::control_data::MeasurementDigest; match attestation_payload { Some(attestation_payload) => { @@ -207,14 +219,18 @@ impl PromoteToConfidentialVm { for digest in attestation_payload.digests.iter() { // codeql[rust/cleartext-logging] - digest values intentionally logged in debug builds debug!("Reference PCR{:?}={:?}=0x{}", digest.pcr_id, digest.algorithm, digest.value_in_hex()); - ensure!(digest.algorithm == riscv_cove_tap::DigestAlgorithm::Sha512, Error::LocalAttestationNotSupportedDigest())?; + ensure!(digest.algorithm == riscv_cove_tap::DigestAlgorithm::Sha3_256, Error::LocalAttestationNotSupportedDigest())?; let pcr_value = MeasurementDigest::try_from(digest.value.as_slice()).map_err(|_| Error::LocalAttestationFailed())?; ensure!(measurements.compare(digest.pcr_id() as usize, pcr_value)?, Error::LocalAttestationFailed())?; } debug!("Attestation succeeded, fetched {} secrets", attestation_payload.secrets.len()); - Ok(attestation_payload.secrets) + let mut secrets = Vec::new(); + for secret in attestation_payload.secrets { + secrets.push(secret).map_err(|_| Error::LocalAttestationFailed())?; + } + Ok(secrets) } - None => Ok(alloc::vec![]), + None => Ok(Vec::new()), } } diff --git a/tools/cove_tap_tool/Cargo.lock b/tools/cove_tap_tool/Cargo.lock index f5a40362..b836bad3 100644 --- a/tools/cove_tap_tool/Cargo.lock +++ b/tools/cove_tap_tool/Cargo.lock @@ -205,6 +205,7 @@ dependencies = [ "byteorder", "clap", "clap-num", + "heapless", "hybrid-array", "ml-kem", "rand", @@ -250,9 +251,9 @@ dependencies = [ [[package]] name = "ctutils" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" +checksum = "03bb0e1cc970d482d121d9a1744999169b69a07470b3d644a7894e53fcaf4574" dependencies = [ "cmov", ] @@ -288,6 +289,25 @@ dependencies = [ "polyval", ] +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.5.0" @@ -341,9 +361,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.189" +version = "0.2.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" +checksum = "ce5d3ddc6d3fa000eb1536d85e147bfe31aacaba692ed6a876f95cb7c855be78" [[package]] name = "ml-kem" @@ -441,6 +461,7 @@ name = "riscv_cove_tap" version = "0.2.0" dependencies = [ "aes-gcm", + "heapless", "hybrid-array", "ml-kem", "rand", @@ -474,6 +495,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a0219bd7d979d58245a4f41f695e1ac9f8befdffadd7f61f1bae9e39abc6620" +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + [[package]] name = "strsim" version = "0.11.1" diff --git a/tools/cove_tap_tool/Cargo.toml b/tools/cove_tap_tool/Cargo.toml index 31f60b47..09c5e91a 100644 --- a/tools/cove_tap_tool/Cargo.toml +++ b/tools/cove_tap_tool/Cargo.toml @@ -6,6 +6,7 @@ description = "Tool to create TVM attestation payload for CoVE's confidential VM edition = "2021" [dependencies] +heapless = { version = "0.8", default-features = false } clap = { version="4.6", features = ["derive"] } # for command line argument parsing clap-num = {version = "1.2", default-features=false } byteorder = {version = "1.5", default-features=false, features = ["std"] } # to serialize numbers using little/big endianness diff --git a/tools/cove_tap_tool/src/generate.rs b/tools/cove_tap_tool/src/generate.rs index adeb5836..1992bb73 100644 --- a/tools/cove_tap_tool/src/generate.rs +++ b/tools/cove_tap_tool/src/generate.rs @@ -3,13 +3,11 @@ // SPDX-License-Identifier: Apache-2.0 use crate::ensure; use crate::error::Error; -use riscv_cove_tap::AttestationPayload; -use riscv_cove_tap::AttestationPayloadSerializer; -use riscv_cove_tap::Digest; -use riscv_cove_tap::DigestAlgorithm; -use riscv_cove_tap::Lockbox; -use riscv_cove_tap::LockboxAlgorithm; -use riscv_cove_tap::Secret; +use riscv_cove_tap::{ + AttestationPayload, AttestationPayloadSerializer, Digest, DigestAlgorithm, Lockbox, + LockboxAlgorithm, Secret, MAX_DIGEST_VALUE_SIZE, MAX_NUMBER_OF_DIGESTS, + MAX_NUMBER_OF_LOCKBOXES, MAX_NUMBER_OF_SECRETS, MAX_SECRET_VALUE_SIZE, MAX_TSK_SIZE, +}; use std::fs::OpenOptions; use std::io::Write; @@ -20,20 +18,36 @@ pub fn generate_tap( output_file: String, ) -> Result<(), Error> { ensure!( - confidential_vm_secrets.len() < 256, - Error::InvalidParameter(format!("Confidential VM can receive maximum 256 secrets")) + confidential_vm_secrets.len() <= MAX_NUMBER_OF_SECRETS, + Error::InvalidParameter(format!( + "Too many secrets: max {} allowed", + MAX_NUMBER_OF_SECRETS + )) )?; ensure!( - tee_public_keys_files.len() < 1024, - Error::InvalidParameter(format!("Confidential VM TAP supports max 1024 lockboxes")) + tee_public_keys_files.len() <= MAX_NUMBER_OF_LOCKBOXES, + Error::InvalidParameter(format!( + "Too many lockboxes: max {} allowed", + MAX_NUMBER_OF_LOCKBOXES + )) + )?; + ensure!( + pcrs.len() <= MAX_NUMBER_OF_DIGESTS, + Error::InvalidParameter(format!( + "Too many PCR digests: max {} allowed", + MAX_NUMBER_OF_DIGESTS + )) )?; - let mut symmetric_key = vec![0u8; 32]; + let mut symmetric_key: heapless::Vec = heapless::Vec::new(); + symmetric_key + .extend_from_slice(&[0u8; 32]) + .map_err(|_| Error::InvalidParameter("symmetric_key too large".into()))?; use rand::RngExt; rand::rng().fill(symmetric_key.as_mut_slice()); let lockbox_algorithm = LockboxAlgorithm::MlKem1024Aes256; let test_encapsulation_key = "307A4CEA4148219B958EA0B7886659235A4D1980B192610847D86EF32739F94C3B446C4D81D89B8B422A9D079C88B11ACAF321B014294E18B296E52F3F744CF9634A4FB01DB0D99EF20A633A552E76A0585C6109F018768B763AF3678B4780089C1342B96907A29A1C11521C744C2797D0BF2B9CCDCA614672B45076773F458A31EF869BE1EB2EFEB50D0E37495DC5CA55E07528934F6293C4168027D0E53D07FACC6630CB08197E53FB193A171135DC8AD9979402A71B6926BCDCDC47B93401910A5FCC1A813B682B09BA7A72D2486D6C799516465C14729B26949B0B7CBC7C640F267FED80B162C51FD8E09227C101D505A8FAE8A2D7054E28A78BA8750DECF9057C83979F7ABB084945648006C5B28804F34E73B238111A65A1F500B1CC606A848F2859070BEBA7573179F36149CF5801BF89A1C38CC278415528D03BDB943F96280C8CC52042D9B91FAA9D6EA7BCBB7AB1897A3266966F78393426C76D8A49578B98B159EBB46EE0A883A270D8057CD0231C86906A91DBBADE6B2469581E2BCA2FEA8389F7C74BCD70961EA5B934FBCF9A6590BF86B8DB548854D9A3FB30110433BD7A1B659CA8568085639237B3BDC37B7FA716D482A25B54106B3A8F54D3AA99B5123DA96066904592F3A54EE23A7981AB608A2F4413CC658946C6D7780EA765644B3CC06C70034AB4EB351912E7715B56755D09021571BF340AB92598A24E811893195B96A1629F8041F58658431561FC0AB15292B913EC473F04479BC145CD4C563A286235646CD305A9BE1014E2C7B130C33EB77CC4A0D9786BD6BC2A954BF3005778F8917CE13789BBB962807858B67731572B6D3C9B4B5206FAC9A7C8961698D88324A915186899B29923F08442A3D386BD416BCC9A100164C930EC35EAFB6AB35851B6C8CE6377366A175F3D75298C518D44898933F53DEE617145093379C4659F68583B2B28122666BEC57838991FF16C368DD22C36E780C91A3582E25E19794C6BF2AB42458A8DD7705DE2C2AA20C054E84B3EF35032798626C248263253A71A11943571340A978CD0A602E47DEE540A8814BA06F31414797CDF6049582361BBABA387A83D89913FE4C0C112B95621A4BDA8123A14D1A842FB57B83A4FBAF33A8E552238A596AAE7A150D75DA648BC44644977BA1F87A4C68A8C4BD245B7D00721F7D64E822B085B901312EC37A8169802160CCE1160F010BE8CBCACE8E7B005D7839234A707868309D03784B4273B1C8A160133ED298184704625F29CFA086D13263EE5899123C596BA788E5C54A8E9BA829B8A9D904BC4BC0BBEA76BC53FF811214598472C9C202B73EFF035DC09703AF7BF1BABAAC73193CB46117A7C9492A43FC95789A924C5912787B2E2090EBBCFD3796221F06DEBF9CF70E056B8B9161D6347F47335F3E1776DA4BB87C15CC826146FF0249A413B45AA93A805196EA453114B524E310AEDAA46E3B99642368782566D049A726D6CCA910993AED621D0149EA588A9ABD909DBB69AA22829D9B83ADA2209A6C2659F2169D668B9314842C6E22A74958B4C25BBDCD293D99CB609D866749A485DFB56024883CF5465DBA0363206587F45597F89002FB8607232138E03B2A894525F265370054B48863614472B95D0A2303442E378B0DD1C75ACBAB971A9A8D1281C79613ACEC6933C377B3C578C2A61A1EC181B101297A37CC5197B2942F6A0E4704C0EC63540481B9F159DC255B59BB55DF496AE54217B7689BD51DBA0383A3D72D852FFCA76DF05B66EECCBD47BC53040817628C71E361D6AF889084916B408A466C96E7086C4A60A10FCF7537BB94AFBCC7D437590919C28650C4F2368259226A9BFDA3A3A0BA1B5087D9D76442FD786C6F81C68C0360D7194D7072C4533AEA86C2D1F8C0A27696066F6CFD11003F797270B32389713CFFA093D991B63844C385E72277F166F5A3934D6BB89A4788DE28321DEFC7457AB484BD30986DC1DAB3008CD7B22F69702FABB9A1045407DA4791C3590FF599D81D688CFA7CC12A68C50F51A1009411B44850F9015DC84A93B17C7A207552C661EA9838E31B95EAD546248E56BE7A5130505268771199880A141771A9E47ACFED590CB3AA7CB7C5F74911D8912C29D6233F4D53BC64139E2F55BE75507DD77868E384AEC581F3F411DB1A742972D3EBFD3315C84A5AD63A0E75C8BCA3E3041E05D9067AFF3B1244F763E7983"; - let encapsulation_key = (0..test_encapsulation_key.len()) + let encapsulation_key: Vec = (0..test_encapsulation_key.len()) .step_by(2) .map(|i| { test_encapsulation_key @@ -43,34 +57,84 @@ pub fn generate_tap( }) .collect(); - let mut lockboxes = vec![]; - lockboxes.push(Lockbox::new( - lockbox_algorithm, - &encapsulation_key, - &mut symmetric_key.clone(), - )?); + let mut lockboxes: heapless::Vec = heapless::Vec::new(); + lockboxes + .push(Lockbox::new( + lockbox_algorithm, + &encapsulation_key, + &mut symmetric_key.clone(), + )?) + .map_err(|_| { + Error::InvalidParameter(format!( + "Too many lockboxes: max {} allowed", + MAX_NUMBER_OF_LOCKBOXES + )) + })?; - let mut digests = vec![]; + let mut digests: heapless::Vec = heapless::Vec::new(); for (pcr_id, pcr_value) in pcrs.into_iter() { + ensure!( + pcr_value.len() <= MAX_DIGEST_VALUE_SIZE, + Error::InvalidParameter(format!( + "PCR{} value is {} bytes, max is {}", + pcr_id, + pcr_value.len(), + MAX_DIGEST_VALUE_SIZE + )) + )?; + let mut value: heapless::Vec = heapless::Vec::new(); + value.extend_from_slice(&pcr_value).map_err(|_| { + Error::InvalidParameter(format!( + "PCR{} value exceeds {} bytes", + pcr_id, MAX_DIGEST_VALUE_SIZE + )) + })?; let tap_digest = Digest { pcr_id, - algorithm: DigestAlgorithm::Sha512, - value: pcr_value, + algorithm: DigestAlgorithm::Sha3_256, + value, }; // codeql[rust/cleartext-logging] - intentional operator output for CLI tool println!("Writing PCR{}={}", pcr_id, tap_digest.value_in_hex()); - digests.push(tap_digest); + digests.push(tap_digest).map_err(|_| { + Error::InvalidParameter(format!( + "Too many digests: max {} allowed", + MAX_NUMBER_OF_DIGESTS + )) + })?; } - let mut secrets = vec![]; + let mut secrets: heapless::Vec = heapless::Vec::new(); for (secret_name, secret_value) in confidential_vm_secrets.into_iter() { - let secret = Secret { - name: secret_name, - value: secret_value, - }; + ensure!( + secret_value.len() <= MAX_SECRET_VALUE_SIZE, + Error::InvalidParameter(format!( + "Secret {} value is {} bytes, max is {}", + secret_name, + secret_value.len(), + MAX_SECRET_VALUE_SIZE + )) + )?; + let mut value: heapless::Vec = heapless::Vec::new(); + value.extend_from_slice(&secret_value).map_err(|_| { + Error::InvalidParameter(format!( + "Secret {} value exceeds {} bytes", + secret_name, MAX_SECRET_VALUE_SIZE + )) + })?; // codeql[rust/cleartext-logging] - intentional operator output for CLI tool println!("Writing secret {}", secret_name); - secrets.push(secret); + secrets + .push(Secret { + name: secret_name, + value, + }) + .map_err(|_| { + Error::InvalidParameter(format!( + "Too many secrets: max {} allowed", + MAX_NUMBER_OF_SECRETS + )) + })?; } let tap = AttestationPayload { digests, secrets }; @@ -78,7 +142,6 @@ pub fn generate_tap( let serializer = AttestationPayloadSerializer::new(); let serialized = serializer.serialize(lockboxes, tap, &symmetric_key)?; - // write the entire TAP to the output file let mut output = OpenOptions::new() .create_new(true) .read(true)