Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
348 changes: 218 additions & 130 deletions security-monitor/Cargo.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions security-monitor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "0.2"

# Fixed capacity, heapless collections
heapless = { version = "0.8", default-features = false }

# The `spin` crate provides synchronization primitives (Mutexes etc) using spinlocks
spin = {version="0.10", default-features = false, features = ["once", "rwlock", "spin_mutex"]}

Expand Down
3 changes: 2 additions & 1 deletion security-monitor/rust-crates/riscv_cove_tap/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ description = "Library to parse the RISC-V CoVE's TEE attestation payload"
edition = "2024"

[dependencies]
heapless = { version = "0.8", default-features = false }
rand = {version = "0.8.5", optional=true}

hybrid-array = "0.2.3"
ml-kem = {version = "0.3.0-pre", default-features=false, features = [] }
ml-kem = {version = "0.3.2", default-features=false, features = [] }

# for symmetric encryption of payload
aes-gcm = {version="0.11.0-rc.1", default-features = false, features=["aes", "alloc", "rand_core"]}
Expand Down
8 changes: 8 additions & 0 deletions security-monitor/rust-crates/riscv_cove_tap/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
124 changes: 74 additions & 50 deletions security-monitor/rust-crates/riscv_cove_tap/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@
// SPDX-FileContributor: Wojciech Ozga <woz@zurich.ibm.com>, 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,
Expand All @@ -19,110 +17,136 @@ impl AttestationPayloadParser {

pub fn parse_and_verify(&mut self, decapsulation_key: &[u8]) -> Result<AttestationPayload, TapError> {
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<u8, MAX_TSK_SIZE> = 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::<MAX_NONCE_SIZE>(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::<MAX_TAG_SIZE>(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::<MAX_TSK_SIZE>(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<Digest, MAX_NUMBER_OF_DIGESTS> = 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::<MAX_DIGEST_VALUE_SIZE>(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<Secret, MAX_NUMBER_OF_SECRETS> = 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::<MAX_SECRET_VALUE_SIZE>(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::<MAX_NONCE_SIZE>(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::<MAX_TAG_SIZE>(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::<Aes256Gcm>::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<u16, TapError> {
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<u32, TapError> {
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<u64, TapError> {
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<Vec<u8>, TapError> {
let mut result = Vec::with_capacity(size);
fn read_exact_n<const N: usize>(&mut self, size: usize) -> Result<heapless::Vec<u8, N>, TapError> {
ensure!(size <= N, TapError::ValueTooLarge())?;
let mut result: heapless::Vec<u8, N> = 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)
}
}
}
Loading
Loading