diff --git a/dev_tests/src/ratchet.rs b/dev_tests/src/ratchet.rs index 276452d4d..7f610ca33 100644 --- a/dev_tests/src/ratchet.rs +++ b/dev_tests/src/ratchet.rs @@ -36,8 +36,8 @@ fn ratchet_globals() -> Result<()> { ("dev_bench/", 1), ("litebox/", 9), ("litebox_platform_linux_kernel/", 6), - ("litebox_platform_linux_userland/", 5), - ("litebox_platform_lvbs/", 23), + ("litebox_platform_linux_userland/", 6), + ("litebox_platform_lvbs/", 24), ("litebox_platform_multiplex/", 1), ("litebox_platform_windows_userland/", 8), ("litebox_runner_linux_userland/", 1), diff --git a/litebox/src/platform/mod.rs b/litebox/src/platform/mod.rs index 983f20c84..f6970ee35 100644 --- a/litebox/src/platform/mod.rs +++ b/litebox/src/platform/mod.rs @@ -754,3 +754,40 @@ pub trait CrngProvider { /// failures. fn fill_bytes_crng(&self, buf: &mut [u8]); } + +/// A provider of the Platform Root Key (PRK). +/// +/// The PRK is a platform-unique secret which can be used to derive secret keys +/// (e.g., TA unique keys, secure storage keys). It serves as the root of trust +/// for cryptographic operations within the trusted or sandboxed execution +/// environment. It resembles the root secret keys provided by trusted hardware +/// devices (e.g., OP-TEE's Hardware Unique Key, DICE's Unique Device Secret, +/// and TPM's Primary Seeds). +/// +/// Ideally, the platform should feature a persistent, hardware-backed key (e.g., +/// fused OTP, PUF-derived). The key should be unique per device, inaccessible +/// outside the trusted environment, and never directly exposed---only used to +/// derive other keys. If the platform lacks such hardware support (e.g., a +/// userland platform), it may generate an ephemeral key from a boot nonce and +/// CRNG, valid only for the current session. +/// +/// Note that we do not specify `set_platform_root_key` (yet). This is because +/// each platform has its own way to initialize the PRK (e.g., no initialization +/// if the PRK is baked into hardware, random numbers with different lengths, +/// ...). The platform should initialize the PRK by itself or via the runner. +pub trait PlatformRootKeyProvider { + /// Returns a reference to the Platform Root Key. + /// + /// # Errors + /// + /// Returns [`PlatformRootKeyError`] if the PRK is not supported or not + /// initialized. + fn platform_root_key(&self) -> Result<&[u8], PlatformRootKeyError> { + Err(PlatformRootKeyError) + } +} + +/// Error returned when the platform root key is not supported on the platform. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)] +#[error("platform root key is not supported on this platform")] +pub struct PlatformRootKeyError; diff --git a/litebox_platform_linux_userland/src/lib.rs b/litebox_platform_linux_userland/src/lib.rs index c3e60a83a..91dd9c8ca 100644 --- a/litebox_platform_linux_userland/src/lib.rs +++ b/litebox_platform_linux_userland/src/lib.rs @@ -2747,6 +2747,43 @@ impl litebox::platform::CrngProvider for LinuxUserland { } } +/// Length of the Platform Root Key in bytes. +#[cfg(all(target_arch = "x86_64", feature = "optee_syscall"))] +pub const PRK_LEN: usize = 32; + +#[cfg(all(target_arch = "x86_64", feature = "optee_syscall"))] +static PRK_ONCE: std::sync::OnceLock<[u8; PRK_LEN]> = std::sync::OnceLock::new(); + +/// Sets the Platform Root Key (PRK) for this platform. +/// +/// This should be called once during platform initialization with a key derived +/// from hardware or a nonce. +/// +/// # Panics +/// Panics if `key` length does not match `PRK_LEN`. +#[cfg(all(target_arch = "x86_64", feature = "optee_syscall"))] +pub fn set_platform_root_key(key: &[u8]) { + assert_eq!(key.len(), PRK_LEN, "Platform Root Key length mismatch"); + PRK_ONCE.get_or_init(|| { + let mut prk = [0u8; PRK_LEN]; + prk.copy_from_slice(key); + prk + }); +} + +#[cfg(all(target_arch = "x86_64", feature = "optee_syscall"))] +impl litebox::platform::PlatformRootKeyProvider for LinuxUserland { + fn platform_root_key(&self) -> Result<&[u8], litebox::platform::PlatformRootKeyError> { + PRK_ONCE + .get() + .map(<[u8; PRK_LEN]>::as_slice) + .ok_or(litebox::platform::PlatformRootKeyError) + } +} + +#[cfg(not(all(target_arch = "x86_64", feature = "optee_syscall")))] +impl litebox::platform::PlatformRootKeyProvider for LinuxUserland {} + /// Dummy `VmapManager`. /// /// In general, userland platforms do not support `vmap` and `vunmap` (which are kernel functions). diff --git a/litebox_platform_lvbs/src/host/lvbs_impl.rs b/litebox_platform_lvbs/src/host/lvbs_impl.rs index 8bb1f415b..d0be9b534 100644 --- a/litebox_platform_lvbs/src/host/lvbs_impl.rs +++ b/litebox_platform_lvbs/src/host/lvbs_impl.rs @@ -114,6 +114,43 @@ impl litebox::platform::CrngProvider for LvbsLinuxKernel { } } +/// Length of the Platform Root Key in bytes. +#[cfg(feature = "optee_syscall")] +pub const PRK_LEN: usize = 32; + +#[cfg(feature = "optee_syscall")] +static PRK_ONCE: spin::Once<[u8; PRK_LEN]> = spin::Once::new(); + +/// Sets the Platform Root Key (PRK) for this platform. +/// +/// This should be called once during platform initialization with a key derived +/// from hardware or a boot nonce. +/// +/// # Panics +/// Panics if `key` length does not match `PRK_LEN`. +#[cfg(feature = "optee_syscall")] +pub fn set_platform_root_key(key: &[u8]) { + assert_eq!(key.len(), PRK_LEN, "Platform Root Key length mismatch"); + PRK_ONCE.call_once(|| { + let mut prk = [0u8; PRK_LEN]; + prk.copy_from_slice(key); + prk + }); +} + +#[cfg(feature = "optee_syscall")] +impl litebox::platform::PlatformRootKeyProvider for LvbsLinuxKernel { + fn platform_root_key(&self) -> Result<&[u8], litebox::platform::PlatformRootKeyError> { + PRK_ONCE + .get() + .map(<[u8; PRK_LEN]>::as_slice) + .ok_or(litebox::platform::PlatformRootKeyError) + } +} + +#[cfg(not(feature = "optee_syscall"))] +impl litebox::platform::PlatformRootKeyProvider for LvbsLinuxKernel {} + pub struct HostLvbsInterface; impl HostLvbsInterface {} diff --git a/litebox_platform_lvbs/src/host/mod.rs b/litebox_platform_lvbs/src/host/mod.rs index 48fa248e8..ecf266380 100644 --- a/litebox_platform_lvbs/src/host/mod.rs +++ b/litebox_platform_lvbs/src/host/mod.rs @@ -8,6 +8,8 @@ pub mod lvbs_impl; pub mod per_cpu_variables; pub use lvbs_impl::LvbsLinuxKernel; +#[cfg(feature = "optee_syscall")] +pub use lvbs_impl::{PRK_LEN, set_platform_root_key}; #[cfg(test)] pub mod mock; diff --git a/litebox_platform_lvbs/src/mshv/error.rs b/litebox_platform_lvbs/src/mshv/error.rs index 5d6274be5..ebcd09368 100644 --- a/litebox_platform_lvbs/src/mshv/error.rs +++ b/litebox_platform_lvbs/src/mshv/error.rs @@ -106,7 +106,7 @@ pub enum VsmError { OperationNotSupported(&'static str), // VTL0 Memory Copy Errors - #[error("failed to copy data to VTL0")] + #[error("failed to copy data from/to VTL0")] Vtl0CopyFailed, // Hypercall Errors @@ -152,6 +152,9 @@ pub enum VsmError { #[error("symbol name contains invalid UTF-8")] SymbolNameInvalidUtf8, + + #[error("root key is invalid")] + PlatformRootKeyInvalid, } impl From for VsmError { @@ -217,7 +220,8 @@ impl From for Errno { | VsmError::SymbolNameInvalidUtf8 | VsmError::SymbolNameNoTerminator | VsmError::CertificateDerLengthInvalid { .. } - | VsmError::CertificateParseFailed => Errno::EINVAL, + | VsmError::CertificateParseFailed + | VsmError::PlatformRootKeyInvalid => Errno::EINVAL, // Signature verification failures delegate to VerificationError's Errno mapping VsmError::SignatureVerificationFailed(e) => Errno::from(e), diff --git a/litebox_platform_lvbs/src/mshv/mod.rs b/litebox_platform_lvbs/src/mshv/mod.rs index 0da18875d..c250fc748 100644 --- a/litebox_platform_lvbs/src/mshv/mod.rs +++ b/litebox_platform_lvbs/src/mshv/mod.rs @@ -130,6 +130,9 @@ pub const VSM_VTL_CALL_FUNC_ID_KEXEC_VALIDATE: u32 = 0x1_ffea; pub const VSM_VTL_CALL_FUNC_ID_PATCH_TEXT: u32 = 0x1_ffeb; pub const VSM_VTL_CALL_FUNC_ID_ALLOCATE_RINGBUFFER_MEMORY: u32 = 0x1_ffec; +// This VSM function ID for setting the platform root key is subject to change +pub const VSM_VTL_CALL_FUNC_ID_SET_PLATFORM_ROOT_KEY: u32 = 0x1_ffed; + // This VSM function ID for OP-TEE messages is subject to change pub const VSM_VTL_CALL_FUNC_ID_OPTEE_MESSAGE: u32 = 0x1_fff0; @@ -152,6 +155,7 @@ pub enum VsmFunction { PatchText = VSM_VTL_CALL_FUNC_ID_PATCH_TEXT, OpteeMessage = VSM_VTL_CALL_FUNC_ID_OPTEE_MESSAGE, AllocateRingbufferMemory = VSM_VTL_CALL_FUNC_ID_ALLOCATE_RINGBUFFER_MEMORY, + SetPlatformRootKey = VSM_VTL_CALL_FUNC_ID_SET_PLATFORM_ROOT_KEY, } pub const MSR_EFER: u32 = 0xc000_0080; diff --git a/litebox_platform_lvbs/src/mshv/vsm.rs b/litebox_platform_lvbs/src/mshv/vsm.rs index 458fb2f66..4edea881f 100644 --- a/litebox_platform_lvbs/src/mshv/vsm.rs +++ b/litebox_platform_lvbs/src/mshv/vsm.rs @@ -9,9 +9,11 @@ use crate::mshv::ringbuffer::set_ringbuffer; use crate::{ debug_serial_println, host::{ + PRK_LEN, bootparam::get_vtl1_memory_info, linux::{CpuMask, KEXEC_SEGMENT_MAX, Kimage}, per_cpu_variables::with_per_cpu_variables, + set_platform_root_key, }, mshv::{ HV_REGISTER_CR_INTERCEPT_CONTROL, HV_REGISTER_CR_INTERCEPT_CR0_MASK, @@ -916,6 +918,34 @@ fn mshv_vsm_allocate_ringbuffer_memory(phys_addr: u64, size: usize) -> Result Result { + if crate::platform_low().vtl0_kernel_info.check_end_of_boot() { + return Err(VsmError::OperationAfterEndOfBoot("set platform root key")); + } + + let key_pa = PhysAddr::try_new(key_pa).map_err(|_| VsmError::InvalidPhysicalAddress)?; + let key_size: usize = key_size.truncate(); + if key_size != PRK_LEN { + return Err(VsmError::PlatformRootKeyInvalid); + } + + let mut keybuf = [0u8; PRK_LEN]; + if unsafe { crate::platform_low().copy_slice_from_vtl0_phys(key_pa, &mut keybuf) } { + set_platform_root_key(&keybuf); + Ok(0) + } else { + Err(VsmError::Vtl0CopyFailed) + } +} + /// VSM function dispatcher pub fn vsm_dispatch(func_id: VsmFunction, params: &[u64]) -> i64 { let result: Result = match func_id { @@ -939,6 +969,7 @@ pub fn vsm_dispatch(func_id: VsmFunction, params: &[u64]) -> i64 { let size: usize = params[1].truncate(); mshv_vsm_allocate_ringbuffer_memory(params[0], size) } + VsmFunction::SetPlatformRootKey => mshv_vsm_set_platform_root_key(params[0], params[1]), VsmFunction::OpteeMessage => Err(VsmError::OperationNotSupported("OP-TEE communication")), }; match result { diff --git a/litebox_runner_optee_on_linux_userland/src/lib.rs b/litebox_runner_optee_on_linux_userland/src/lib.rs index 4cc021dea..99f1b463c 100644 --- a/litebox_runner_optee_on_linux_userland/src/lib.rs +++ b/litebox_runner_optee_on_linux_userland/src/lib.rs @@ -3,6 +3,7 @@ use anyhow::Result; use clap::Parser; +use litebox::platform::CrngProvider; use litebox_common_optee::{TeeUuid, UteeEntryFunc, UteeParamOwned}; use litebox_platform_multiplex::Platform; use litebox_shim_optee::session::allocate_session_id; @@ -98,6 +99,11 @@ pub fn run(cli_args: CliArgs) -> Result<()> { InterceptionBackend::Rewriter => {} } + // For now, we use a random PRK for this runner. We can get one via command line if needed. + let mut prk = [0u8; litebox_platform_linux_userland::PRK_LEN]; + platform.fill_bytes_crng(&mut prk); + litebox_platform_linux_userland::set_platform_root_key(&prk); + if cli_args.command_sequence.is_empty() { run_ta_with_default_commands(&shim, ldelf_data.as_slice(), prog_data.as_slice()); } else { diff --git a/litebox_shim_optee/src/syscalls/pta.rs b/litebox_shim_optee/src/syscalls/pta.rs index 976cb7754..f465aeb15 100644 --- a/litebox_shim_optee/src/syscalls/pta.rs +++ b/litebox_shim_optee/src/syscalls/pta.rs @@ -6,7 +6,7 @@ use crate::{Task, UserConstPtr, UserMutPtr}; use litebox::{ - platform::{RawConstPointer as _, RawMutPointer as _}, + platform::{PlatformRootKeyProvider, RawConstPointer as _, RawMutPointer as _}, utils::TruncateExt, }; use litebox_common_optee::{TeeParamType, TeeResult, TeeUuid, UteeParams}; @@ -115,8 +115,6 @@ impl Task { let output_addr: usize = output.0.truncate(); let output_ptr = UserMutPtr::::from_usize(output_addr); - // TODO: checks whether output is within the secure memory - // TODO: derive a TA unique key using the hardware unique key (HUK), TA's UUID, and `extra_data` litebox::log_println!( self.global.platform, @@ -124,9 +122,15 @@ impl Task { output_addr, output_len ); - // TODO: replace below with a secure key derivation function + let huk = self + .global + .platform + .platform_root_key() + .map_err(|_| TeeResult::NoData)?; + // TODO: the below is a place holder. Replace it with a secure key derivation function (next PR) let mut key_buf = alloc::vec![0u8; output_len]; - self.sys_cryp_random_number_generate(&mut key_buf)?; + let copy_len = core::cmp::min(key_buf.len(), huk.len()); + key_buf[..copy_len].copy_from_slice(&huk[..copy_len]); output_ptr .copy_from_slice(0, &key_buf) .ok_or(TeeResult::BadParameters)?;