From 9f230c323d0755b6655e6a27b855334187356f87 Mon Sep 17 00:00:00 2001 From: Angelina Vu Date: Mon, 24 Aug 2026 14:24:13 +0000 Subject: [PATCH 1/5] Verify OP-TEE TA. Signed-off-by: Angelina Vu --- Cargo.lock | 2 + litebox_common_optee/Cargo.toml | 2 + litebox_common_optee/src/lib.rs | 132 +++++++++++++++++++++++++++++++- litebox_runner_lvbs/src/lib.rs | 21 ++++- 4 files changed, 151 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 647812e8d8..86f3d9eb6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1560,6 +1560,8 @@ dependencies = [ "litebox", "litebox_common_linux", "num_enum", + "rsa", + "sha2", "zerocopy", ] diff --git a/litebox_common_optee/Cargo.toml b/litebox_common_optee/Cargo.toml index 4e8b4d70dc..ee2a700273 100644 --- a/litebox_common_optee/Cargo.toml +++ b/litebox_common_optee/Cargo.toml @@ -9,6 +9,8 @@ elf = { version = "0.8.0", default-features = false } litebox = { path = "../litebox/", version = "0.1.0" } litebox_common_linux = { path = "../litebox_common_linux/", version = "0.1.0" } num_enum = { version = "0.7.3", default-features = false } +rsa = { version = "0.9.10", default-features = false, features = ["pem"] } +sha2 = { version = "0.10.9", default-features = false, features = ["oid"] } zerocopy = { version = "0.8", features = ["derive"] } [lints] diff --git a/litebox_common_optee/src/lib.rs b/litebox_common_optee/src/lib.rs index 976e5ab26c..01b876c9f7 100644 --- a/litebox_common_optee/src/lib.rs +++ b/litebox_common_optee/src/lib.rs @@ -8,7 +8,7 @@ extern crate alloc; -use alloc::boxed::Box; +use alloc::{boxed::Box, vec::Vec}; use core::mem::size_of; use litebox::platform::RawConstPointer as _; use litebox::utils::TruncateExt; @@ -999,6 +999,7 @@ impl TeeMemoryAccessRights { const TEE_ALG_AES_CTR: u32 = 0x1000_0210; const TEE_ALG_AES_GCM: u32 = 0x4000_0810; +const TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256: u32 = 0x7041_4930; const TEE_ALG_RSASSA_PKCS1_V1_5_SHA256: u32 = 0x7000_4830; const TEE_ALG_RSASSA_PKCS1_V1_5_SHA512: u32 = 0x7000_6830; const TEE_ALG_HMAC_SHA256: u32 = 0x3000_0004; @@ -1014,6 +1015,7 @@ const TEE_ALG_ILLEGAL_VALUE: u32 = 0xefff_ffff; pub enum TeeAlgorithm { AesCtr = TEE_ALG_AES_CTR, AesGcm = TEE_ALG_AES_GCM, + RsaPssSha256 = TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256, RsaPkcs1Sha256 = TEE_ALG_RSASSA_PKCS1_V1_5_SHA256, RsaPkcs1Sha512 = TEE_ALG_RSASSA_PKCS1_V1_5_SHA512, HmacSha256 = TEE_ALG_HMAC_SHA256, @@ -1055,9 +1057,9 @@ impl From for TeeAlgorithmClass { match algo { TeeAlgorithm::AesCtr | TeeAlgorithm::AesGcm => TeeAlgorithmClass::Cipher, TeeAlgorithm::HmacSha256 | TeeAlgorithm::HmacSha512 => TeeAlgorithmClass::Mac, - TeeAlgorithm::RsaPkcs1Sha256 | TeeAlgorithm::RsaPkcs1Sha512 => { - TeeAlgorithmClass::AsymmetricSignature - } + TeeAlgorithm::RsaPkcs1Sha256 + | TeeAlgorithm::RsaPkcs1Sha512 + | TeeAlgorithm::RsaPssSha256 => TeeAlgorithmClass::AsymmetricSignature, _ => TeeAlgorithmClass::Unknown, } } @@ -2646,3 +2648,125 @@ mod tests { assert!(OpteeRpcArgs::from_header_and_raw_params(&header, &[]).is_err()); } } + +/// `Shdr` from `optee_os/core/include/signed_hdr.h` +#[derive(Clone, Copy, FromBytes, IntoBytes, Immutable, KnownLayout)] +#[repr(C)] +pub struct Shdr { + pub magic: u32, + pub img_type: u32, + pub img_size: u32, + pub algo: u32, + pub hash_size: u16, + pub sig_size: u16, +} + +/// `SHDR_MAGIC` from `optee_os/core/include/signed_hdr.h` +const SHDR_MAGIC: u32 = 0x4f54_5348; + +/// From `optee_os/core/include/signed_hdr.h` +/// struct shdr_bootstrap_ta { +/// uint8_t uuid[sizeof(TEE_UUID)]; +/// uint32_t ta_version; +/// }; +/// and from `optee_os/core/include/tee_api_types.h` +/// typedef struct { +/// uint32_t timeLow; +/// uint16_t timeMid; +/// uint16_t timeHiAndVersion; +/// uint8_t clockSeqAndNode[8]; +/// } TEE_UUID; +const SHDR_UUID_LEN: usize = 16; +const SHDR_VERSION_LEN: usize = 4; + +/// An RSA public key used to verify signed `.ta` files +pub struct TaVerifyKey(rsa::RsaPublicKey); + +impl TaVerifyKey { + pub fn from_pem(pem: &str) -> Result { + use rsa::pkcs8::DecodePublicKey; + + rsa::RsaPublicKey::from_public_key_pem(pem) + .map(TaVerifyKey) + .map_err(|_| "Invalid RSA public key PEM") + } +} + +/// Parse and verify an OP-TEE signed .ta file. +/// A signed `.ta` file has the following layout: +/// shdr || hash || sig || uuid || ta_version || img +/// Return the parsed TaHead and the TA ELF binary. +pub fn parse_and_verify_ta<'a>( + ta_data: &'a [u8], + verify_key: &TaVerifyKey, +) -> Result<(TaHead, &'a [u8]), &'static str> { + let hdr_size = size_of::(); + if ta_data.len() < hdr_size { + return Err("Invalid signed TA file"); + } + let shdr = + Shdr::read_from_bytes(&ta_data[..hdr_size]).map_err(|_| "Invalid signed TA header")?; + if shdr.magic != SHDR_MAGIC { + return Err("Invalid TA magic"); + } + let hash_size = shdr.hash_size as usize; + let sig_size = shdr.sig_size as usize; + let img_size = shdr.img_size as usize; + + let hash_offset = hdr_size; + let sig_offset = hash_offset + .checked_add(hash_size) + .ok_or("Invalid signed TA file")?; + let uuid_offset = sig_offset + .checked_add(sig_size) + .ok_or("Invalid signed TA file")?; + let version_offset = uuid_offset + .checked_add(SHDR_UUID_LEN) + .ok_or("Invalid signed TA file")?; + let img_offset = version_offset + .checked_add(SHDR_VERSION_LEN) + .ok_or("Invalid signed TA file")?; + let end = img_offset + .checked_add(img_size) + .ok_or("Invalid signed TA file")?; + if end > ta_data.len() { + return Err("Invalid signed TA file"); + } + let sig = &ta_data[sig_offset..uuid_offset]; + let uuid_and_version = &ta_data[uuid_offset..img_offset]; + let img = &ta_data[img_offset..end]; + + // The signed message is shdr || uuid || ta_version || img. + let mut signed_message = Vec::with_capacity(hdr_size + uuid_and_version.len() + img.len()); + signed_message.extend_from_slice(&ta_data[..hdr_size]); + signed_message.extend_from_slice(uuid_and_version); + signed_message.extend_from_slice(img); + verify_shdr_signature(&signed_message, sig, shdr.algo, &verify_key.0)?; + let ta_head = parse_ta_head(img).ok_or("Invalid TA ELF binary")?; + + Ok((ta_head, img)) +} + +/// Verify a signed `.ta` file's signature against the given RSA public key. +/// TODO: Support more signature algorithms if needed. +fn verify_shdr_signature( + message: &[u8], + signature: &[u8], + algo: u32, + rsa_pub_key: &rsa::RsaPublicKey, +) -> Result<(), &'static str> { + use rsa::signature::Verifier; + use sha2::Sha256; + + match algo { + TEE_ALG_RSASSA_PKCS1_PSS_MGF1_SHA256 => { + let verifying_key = rsa::pss::VerifyingKey::::new(rsa_pub_key.clone()); + let sig = + rsa::pss::Signature::try_from(signature).map_err(|_| "Invalid PSS signature")?; + verifying_key + .verify(message, &sig) + .map_err(|_| "Signature verification failed") + } + _ => Err("Unsupported TA signature algorithm"), + } +} diff --git a/litebox_runner_lvbs/src/lib.rs b/litebox_runner_lvbs/src/lib.rs index 442eba6ebc..7a8ded6727 100644 --- a/litebox_runner_lvbs/src/lib.rs +++ b/litebox_runner_lvbs/src/lib.rs @@ -1419,10 +1419,27 @@ fn register_embedded_ta( shim: &litebox_shim_optee::OpteeShim, ta_binary: &'static [u8], ) -> bool { - let Some(ta_head) = litebox_common_optee::parse_ta_head(ta_binary) else { + use litebox_common_optee::{TaVerifyKey, parse_and_verify_ta}; + + // Hard-coded public key for testing + const TA_VERIFY_KEY_PEM: &[u8] = + include_bytes!("../../litebox_runner_optee_on_linux_userland/tests/signing_public_key.pem"); + let Ok(verify_key_pem) = core::str::from_utf8(TA_VERIFY_KEY_PEM) else { + debug_serial_println!("TA verification key is not valid UTF-8"); + return false; + }; + let Ok(verify_key) = TaVerifyKey::from_pem(verify_key_pem) else { + debug_serial_println!("TA verification key is invalid"); return false; }; - shim.store_ta_bin(&ta_head.uuid, ta_binary) + + match parse_and_verify_ta(ta_binary, &verify_key) { + Ok((ta_head, ta_elf)) => shim.store_ta_bin(&ta_head.uuid, ta_elf), + Err(err) => { + debug_serial_println!("parse_and_verify_ta failed: {}", err); + false + } + } } /// Register all TA binaries embedded in the runner image. From 26f3d82d4e9bb38add6cdc6847c3b0c54043b587 Mon Sep 17 00:00:00 2001 From: Angelina Vu Date: Tue, 1 Sep 2026 14:33:37 +0000 Subject: [PATCH 2/5] Embed key Signed-off-by: Angelina Vu --- litebox_common_optee/Cargo.toml | 7 +++++-- litebox_common_optee/src/lib.rs | 18 ++++++++++++++---- litebox_runner_lvbs/Cargo.toml | 1 + litebox_runner_lvbs/src/lib.rs | 25 ++++++++++++++++--------- 4 files changed, 36 insertions(+), 15 deletions(-) diff --git a/litebox_common_optee/Cargo.toml b/litebox_common_optee/Cargo.toml index ee2a700273..fcd904445e 100644 --- a/litebox_common_optee/Cargo.toml +++ b/litebox_common_optee/Cargo.toml @@ -9,9 +9,12 @@ elf = { version = "0.8.0", default-features = false } litebox = { path = "../litebox/", version = "0.1.0" } litebox_common_linux = { path = "../litebox_common_linux/", version = "0.1.0" } num_enum = { version = "0.7.3", default-features = false } -rsa = { version = "0.9.10", default-features = false, features = ["pem"] } -sha2 = { version = "0.10.9", default-features = false, features = ["oid"] } +rsa = { version = "0.9.10", default-features = false, features = ["pem"], optional = true } +sha2 = { version = "0.10.9", default-features = false, features = ["oid"], optional = true } zerocopy = { version = "0.8", features = ["derive"] } +[features] +signed-ta-rsa = ["dep:rsa", "dep:sha2"] + [lints] workspace = true diff --git a/litebox_common_optee/src/lib.rs b/litebox_common_optee/src/lib.rs index 01b876c9f7..3d60f44e36 100644 --- a/litebox_common_optee/src/lib.rs +++ b/litebox_common_optee/src/lib.rs @@ -8,7 +8,9 @@ extern crate alloc; -use alloc::{boxed::Box, vec::Vec}; +use alloc::boxed::Box; +#[cfg(feature = "signed-ta-rsa")] +use alloc::vec::Vec; use core::mem::size_of; use litebox::platform::RawConstPointer as _; use litebox::utils::TruncateExt; @@ -2650,6 +2652,7 @@ mod tests { } /// `Shdr` from `optee_os/core/include/signed_hdr.h` +#[cfg(feature = "signed-ta-rsa")] #[derive(Clone, Copy, FromBytes, IntoBytes, Immutable, KnownLayout)] #[repr(C)] pub struct Shdr { @@ -2662,6 +2665,7 @@ pub struct Shdr { } /// `SHDR_MAGIC` from `optee_os/core/include/signed_hdr.h` +#[cfg(feature = "signed-ta-rsa")] const SHDR_MAGIC: u32 = 0x4f54_5348; /// From `optee_os/core/include/signed_hdr.h` @@ -2676,19 +2680,23 @@ const SHDR_MAGIC: u32 = 0x4f54_5348; /// uint16_t timeHiAndVersion; /// uint8_t clockSeqAndNode[8]; /// } TEE_UUID; +#[cfg(feature = "signed-ta-rsa")] const SHDR_UUID_LEN: usize = 16; +#[cfg(feature = "signed-ta-rsa")] const SHDR_VERSION_LEN: usize = 4; /// An RSA public key used to verify signed `.ta` files +#[cfg(feature = "signed-ta-rsa")] pub struct TaVerifyKey(rsa::RsaPublicKey); +#[cfg(feature = "signed-ta-rsa")] impl TaVerifyKey { - pub fn from_pem(pem: &str) -> Result { + pub fn from_der(der: &[u8]) -> Result { use rsa::pkcs8::DecodePublicKey; - rsa::RsaPublicKey::from_public_key_pem(pem) + rsa::RsaPublicKey::from_public_key_der(der) .map(TaVerifyKey) - .map_err(|_| "Invalid RSA public key PEM") + .map_err(|_| "Invalid RSA public key DER") } } @@ -2696,6 +2704,7 @@ impl TaVerifyKey { /// A signed `.ta` file has the following layout: /// shdr || hash || sig || uuid || ta_version || img /// Return the parsed TaHead and the TA ELF binary. +#[cfg(feature = "signed-ta-rsa")] pub fn parse_and_verify_ta<'a>( ta_data: &'a [u8], verify_key: &TaVerifyKey, @@ -2749,6 +2758,7 @@ pub fn parse_and_verify_ta<'a>( /// Verify a signed `.ta` file's signature against the given RSA public key. /// TODO: Support more signature algorithms if needed. +#[cfg(feature = "signed-ta-rsa")] fn verify_shdr_signature( message: &[u8], signature: &[u8], diff --git a/litebox_runner_lvbs/Cargo.toml b/litebox_runner_lvbs/Cargo.toml index 5ad183ec92..ed8959dc30 100644 --- a/litebox_runner_lvbs/Cargo.toml +++ b/litebox_runner_lvbs/Cargo.toml @@ -23,6 +23,7 @@ x86_64 = { version = "0.15.2", default-features = false, features = ["instructio [features] devbox = ["litebox_platform_lvbs/devbox"] preemption_test_quantum = ["litebox_platform_lvbs/preemption_test_quantum"] +signed-ta-rsa = ["litebox_common_optee/signed-ta-rsa"] [lints] workspace = true diff --git a/litebox_runner_lvbs/src/lib.rs b/litebox_runner_lvbs/src/lib.rs index 7a8ded6727..6bdaa72eaf 100644 --- a/litebox_runner_lvbs/src/lib.rs +++ b/litebox_runner_lvbs/src/lib.rs @@ -1414,21 +1414,16 @@ const LDELF_BINARY: &[u8] = &[0u8; 0]; const TA_BINARY: &[u8] = &[0u8; 0]; const TA_BINARIES: &[&[u8]] = &[TA_BINARY]; -/// Register a TA binary embedded in the runner image. +/// Verify and register a signed TA binary embedded in the runner image. +#[cfg(feature = "signed-ta-rsa")] fn register_embedded_ta( shim: &litebox_shim_optee::OpteeShim, ta_binary: &'static [u8], ) -> bool { use litebox_common_optee::{TaVerifyKey, parse_and_verify_ta}; - // Hard-coded public key for testing - const TA_VERIFY_KEY_PEM: &[u8] = - include_bytes!("../../litebox_runner_optee_on_linux_userland/tests/signing_public_key.pem"); - let Ok(verify_key_pem) = core::str::from_utf8(TA_VERIFY_KEY_PEM) else { - debug_serial_println!("TA verification key is not valid UTF-8"); - return false; - }; - let Ok(verify_key) = TaVerifyKey::from_pem(verify_key_pem) else { + const TA_VERIFY_KEY_DER: &[u8] = include_bytes!(env!("LITEBOX_TA_VERIFY_KEY")); + let Ok(verify_key) = TaVerifyKey::from_der(TA_VERIFY_KEY_DER) else { debug_serial_println!("TA verification key is invalid"); return false; }; @@ -1442,6 +1437,18 @@ fn register_embedded_ta( } } +/// Register an unsigned TA binary embedded in the runner image. +#[cfg(not(feature = "signed-ta-rsa"))] +fn register_embedded_ta( + shim: &litebox_shim_optee::OpteeShim, + ta_binary: &'static [u8], +) -> bool { + let Some(ta_head) = litebox_common_optee::parse_ta_head(ta_binary) else { + return false; + }; + shim.store_ta_bin(&ta_head.uuid, ta_binary) +} + /// Register all TA binaries embedded in the runner image. fn register_embedded_tas(shim: &litebox_shim_optee::OpteeShim) { for ta_binary in TA_BINARIES { From 6156f5ee729b4e7d70accf66613dc40c1b54c32f Mon Sep 17 00:00:00 2001 From: Angelina Vu Date: Mon, 21 Sep 2026 15:52:46 +0000 Subject: [PATCH 3/5] Distinguish between built-in and dynamic TAs. Signed-off-by: Angelina Vu --- litebox_runner_lvbs/src/lib.rs | 11 ++++-- .../src/lib.rs | 2 +- .../src/tests.rs | 2 +- litebox_shim_optee/src/lib.rs | 37 ++++++++++++++++--- 4 files changed, 41 insertions(+), 11 deletions(-) diff --git a/litebox_runner_lvbs/src/lib.rs b/litebox_runner_lvbs/src/lib.rs index 6bdaa72eaf..8a519d71ad 100644 --- a/litebox_runner_lvbs/src/lib.rs +++ b/litebox_runner_lvbs/src/lib.rs @@ -824,13 +824,14 @@ fn open_session_new_instance( ta_req_info: &litebox_shim_optee::msg_handler::TaRequestInfo, ) -> Result<(), OpteeSmcReturnCode> { let shim = litebox_shim_optee::OpteeShimBuilder::new(platform, session_manager()).build(); - if shim.get_ta_bin(&ta_uuid).is_none() { + let Some(ta_source) = shim.get_ta_source(&ta_uuid) else { msg_args.session = 0; msg_args.ret = TeeResult::ItemNotFound; msg_args.ret_origin = TeeOrigin::Tee; write_non_ta_msg_args_to_normal_world(platform, msg_args, msg_args_phys_addr)?; return Ok(()); - } + }; + debug_serial_println!("Loading TA: uuid={:?}, source={:?}", ta_uuid, ta_source); // Token is declared before `task_pt_guard` so it drops AFTER it. // Marker only releases once CR3 is back to base. See @@ -1446,7 +1447,11 @@ fn register_embedded_ta( let Some(ta_head) = litebox_common_optee::parse_ta_head(ta_binary) else { return false; }; - shim.store_ta_bin(&ta_head.uuid, ta_binary) + shim.store_ta_bin( + &ta_head.uuid, + ta_binary, + litebox_shim_optee::TaSource::BuiltIn, + ) } /// Register all TA binaries embedded in the runner image. diff --git a/litebox_runner_optee_on_linux_userland/src/lib.rs b/litebox_runner_optee_on_linux_userland/src/lib.rs index f70ecbdde7..fbfd1a366a 100644 --- a/litebox_runner_optee_on_linux_userland/src/lib.rs +++ b/litebox_runner_optee_on_linux_userland/src/lib.rs @@ -114,7 +114,7 @@ fn run_ta_with_default_commands( let ta_uuid = litebox_common_optee::parse_ta_head(ta_bin) .expect("Failed to parse TA header from ta_bin") .uuid; - assert!(shim.store_ta_bin(&ta_uuid, ta_bin)); + assert!(shim.store_ta_bin(&ta_uuid, ta_bin, litebox_shim_optee::TaSource::BuiltIn,)); for func_id in [UteeEntryFunc::OpenSession, UteeEntryFunc::CloseSession] { let params = [const { UteeParamOwned::None }; UteeParamOwned::TEE_NUM_PARAMS]; diff --git a/litebox_runner_optee_on_linux_userland/src/tests.rs b/litebox_runner_optee_on_linux_userland/src/tests.rs index e5c6ec8bf7..548e173586 100644 --- a/litebox_runner_optee_on_linux_userland/src/tests.rs +++ b/litebox_runner_optee_on_linux_userland/src/tests.rs @@ -29,7 +29,7 @@ pub fn run_ta_with_test_commands( }; let ta_head = litebox_common_optee::parse_ta_head(ta_bin).expect("Failed to parse TA header from ta_bin"); - assert!(shim.store_ta_bin(&ta_head.uuid, ta_bin)); + assert!(shim.store_ta_bin(&ta_head.uuid, ta_bin, litebox_shim_optee::TaSource::BuiltIn,)); let mut ta_info: Option> = None; // The active session id for the TA. Set at OpenSession and reused for the // subsequent InvokeCommand entries on the same persistent session. diff --git a/litebox_shim_optee/src/lib.rs b/litebox_shim_optee/src/lib.rs index 4d96ccd24b..e69f313671 100644 --- a/litebox_shim_optee/src/lib.rs +++ b/litebox_shim_optee/src/lib.rs @@ -220,8 +220,8 @@ impl GlobalState { /// /// Returns `true` if the binary was successfully stored, `false` if the binary's /// UUID (from `.ta_head` section) doesn't match the provided UUID or parsing failed. - pub(crate) fn store_ta_bin(&self, ta_uuid: &TeeUuid, ta_bin: &[u8]) -> bool { - self.ta_uuid_map.insert(*ta_uuid, ta_bin.into()) + pub(crate) fn store_ta_bin(&self, ta_uuid: &TeeUuid, ta_bin: &[u8], source: TaSource) -> bool { + self.ta_uuid_map.insert(*ta_uuid, ta_bin.into(), source) } /// Get the TA binary associated with the given TA UUID. @@ -230,7 +230,7 @@ impl GlobalState { Some(ta_bin) } else { let ta_bin = Self::rpc_get_ta_bin(ta_uuid)?; - if !self.store_ta_bin(ta_uuid, &ta_bin) { + if !self.store_ta_bin(ta_uuid, &ta_bin, TaSource::Dynamic) { return None; } Some(ta_bin) @@ -242,6 +242,11 @@ impl GlobalState { self.ta_uuid_map.get_flags(ta_uuid).unwrap_or_default() } + /// Get how the cached TA binary was loaded. + pub(crate) fn get_ta_source(&self, ta_uuid: &TeeUuid) -> Option { + self.ta_uuid_map.get_source(ta_uuid) + } + /// Monotonic time elapsed since this instance was created, used as GP /// "system time" (`TEE_GetSystemTime`). /// @@ -360,8 +365,8 @@ impl OpteeShim { /// /// Returns `true` if the binary was successfully stored, `false` if the binary's /// UUID (from `.ta_head` section) doesn't match the provided UUID or parsing failed. - pub fn store_ta_bin(&self, ta_uuid: &TeeUuid, ta_bin: &[u8]) -> bool { - self.0.store_ta_bin(ta_uuid, ta_bin) + pub fn store_ta_bin(&self, ta_uuid: &TeeUuid, ta_bin: &[u8], source: TaSource) -> bool { + self.0.store_ta_bin(ta_uuid, ta_bin, source) } /// Get the TA binary associated with the given TA UUID. @@ -369,6 +374,11 @@ impl OpteeShim { self.0.get_ta_bin(ta_uuid) } + /// Get how the cached TA binary was loaded. + pub fn get_ta_source(&self, ta_uuid: &TeeUuid) -> Option { + self.0.get_ta_source(ta_uuid) + } + /// Release all user-space memory mappings owned by this shim instance. /// /// This must be called before switching to the base page table and deleting @@ -1401,12 +1411,21 @@ impl TaHandleMap { } } +/// Whether the TA binary was built into the runner or dynamically loaded at runtime. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum TaSource { + BuiltIn, + Dynamic, +} + /// Entry in the TA UUID map containing binary data and parsed flags. struct TaInfo { /// The raw TA binary binary: Arc<[u8]>, /// Parsed TA flags from .ta_head section flags: TaFlags, + /// How the TA binary was loaded + source: TaSource, } /// Data structure to maintain a mapping from TA UUIDs to their binary data and flags. @@ -1421,7 +1440,7 @@ impl TaUuidMap { } } - pub(crate) fn insert(&self, uuid: TeeUuid, ta_bin: Arc<[u8]>) -> bool { + pub(crate) fn insert(&self, uuid: TeeUuid, ta_bin: Arc<[u8]>, source: TaSource) -> bool { // Parse TA head from the binary's .ta_head section let Some(ta_head) = litebox_common_optee::parse_ta_head(&ta_bin) else { return false; @@ -1437,6 +1456,7 @@ impl TaUuidMap { TaInfo { binary: ta_bin, flags: ta_head.flags, + source, }, ); true @@ -1451,6 +1471,11 @@ impl TaUuidMap { self.inner.read().get(uuid).map(|info| info.flags) } + /// Get how the TA binary for a given UUID entered the cache. + pub(crate) fn get_source(&self, uuid: &TeeUuid) -> Option { + self.inner.read().get(uuid).map(|info| info.source) + } + // Lazy removal of TA binaries when they are no longer needed. pub(crate) fn remove(&self, uuid: &TeeUuid) -> Option> { self.inner.write().remove(uuid).map(|info| info.binary) From 5a2c7267727bbac12cba012df972058e77db7cab Mon Sep 17 00:00:00 2001 From: Angelina Vu Date: Mon, 21 Sep 2026 15:39:47 +0000 Subject: [PATCH 4/5] Allow built-in TAs to be either raw .elf or signed .ta binaries. Dynamic TAs must be signed and verified. Signed-off-by: Angelina Vu --- litebox_runner_lvbs/Cargo.toml | 2 +- litebox_runner_lvbs/src/lib.rs | 35 ++---------------- litebox_shim_optee/Cargo.toml | 3 ++ litebox_shim_optee/src/lib.rs | 66 ++++++++++++++++++++++++++++++++-- 4 files changed, 70 insertions(+), 36 deletions(-) diff --git a/litebox_runner_lvbs/Cargo.toml b/litebox_runner_lvbs/Cargo.toml index ed8959dc30..baa29f8e40 100644 --- a/litebox_runner_lvbs/Cargo.toml +++ b/litebox_runner_lvbs/Cargo.toml @@ -23,7 +23,7 @@ x86_64 = { version = "0.15.2", default-features = false, features = ["instructio [features] devbox = ["litebox_platform_lvbs/devbox"] preemption_test_quantum = ["litebox_platform_lvbs/preemption_test_quantum"] -signed-ta-rsa = ["litebox_common_optee/signed-ta-rsa"] +signed-ta-rsa = ["litebox_shim_optee/signed-ta-rsa"] [lints] workspace = true diff --git a/litebox_runner_lvbs/src/lib.rs b/litebox_runner_lvbs/src/lib.rs index 8a519d71ad..51b8a4de65 100644 --- a/litebox_runner_lvbs/src/lib.rs +++ b/litebox_runner_lvbs/src/lib.rs @@ -1415,43 +1415,12 @@ const LDELF_BINARY: &[u8] = &[0u8; 0]; const TA_BINARY: &[u8] = &[0u8; 0]; const TA_BINARIES: &[&[u8]] = &[TA_BINARY]; -/// Verify and register a signed TA binary embedded in the runner image. -#[cfg(feature = "signed-ta-rsa")] +/// Register a raw or signed TA binary embedded in the runner image. fn register_embedded_ta( shim: &litebox_shim_optee::OpteeShim, ta_binary: &'static [u8], ) -> bool { - use litebox_common_optee::{TaVerifyKey, parse_and_verify_ta}; - - const TA_VERIFY_KEY_DER: &[u8] = include_bytes!(env!("LITEBOX_TA_VERIFY_KEY")); - let Ok(verify_key) = TaVerifyKey::from_der(TA_VERIFY_KEY_DER) else { - debug_serial_println!("TA verification key is invalid"); - return false; - }; - - match parse_and_verify_ta(ta_binary, &verify_key) { - Ok((ta_head, ta_elf)) => shim.store_ta_bin(&ta_head.uuid, ta_elf), - Err(err) => { - debug_serial_println!("parse_and_verify_ta failed: {}", err); - false - } - } -} - -/// Register an unsigned TA binary embedded in the runner image. -#[cfg(not(feature = "signed-ta-rsa"))] -fn register_embedded_ta( - shim: &litebox_shim_optee::OpteeShim, - ta_binary: &'static [u8], -) -> bool { - let Some(ta_head) = litebox_common_optee::parse_ta_head(ta_binary) else { - return false; - }; - shim.store_ta_bin( - &ta_head.uuid, - ta_binary, - litebox_shim_optee::TaSource::BuiltIn, - ) + shim.store_embedded_ta(ta_binary) } /// Register all TA binaries embedded in the runner image. diff --git a/litebox_shim_optee/Cargo.toml b/litebox_shim_optee/Cargo.toml index 75a74e9038..97f7cc7abb 100644 --- a/litebox_shim_optee/Cargo.toml +++ b/litebox_shim_optee/Cargo.toml @@ -23,6 +23,9 @@ zerocopy = { version = "0.8", default-features = false, features = ["derive"] } zeroize = { version = "1.8", default-features = false, features = ["alloc"] } p384 = { version = "0.13.1", default-features = false, features = ["arithmetic", "ecdsa"] } +[features] +signed-ta-rsa = ["litebox_common_optee/signed-ta-rsa"] + [lints] workspace = true diff --git a/litebox_shim_optee/src/lib.rs b/litebox_shim_optee/src/lib.rs index e69f313671..3bbe551b69 100644 --- a/litebox_shim_optee/src/lib.rs +++ b/litebox_shim_optee/src/lib.rs @@ -216,12 +216,59 @@ struct GlobalState { } impl GlobalState { + /// Store a trusted TA embedded in the runner image. + /// + /// Raw ELF binaries are stored directly. Signed TAs are verified and + /// unwrapped when `signed-ta-rsa` is enabled. + pub(crate) fn store_embedded_ta(&self, ta_bin: &[u8]) -> bool { + if let Some(ta_head) = litebox_common_optee::parse_ta_head(ta_bin) { + return self + .ta_uuid_map + .insert(ta_head.uuid, ta_bin.into(), TaSource::BuiltIn); + } + + #[cfg(feature = "signed-ta-rsa")] + { + let Some((ta_uuid, ta_elf)) = verify_signed_ta(ta_bin) else { + return false; + }; + self.ta_uuid_map + .insert(ta_uuid, ta_elf.into(), TaSource::BuiltIn) + } + #[cfg(not(feature = "signed-ta-rsa"))] + { + false + } + } + /// Store the TA binary associated with the given TA UUID. /// + /// Built-in binaries are trusted raw ELF files. Dynamic binaries must be signed + /// and are verified before their inner ELF is cached. + /// /// Returns `true` if the binary was successfully stored, `false` if the binary's /// UUID (from `.ta_head` section) doesn't match the provided UUID or parsing failed. pub(crate) fn store_ta_bin(&self, ta_uuid: &TeeUuid, ta_bin: &[u8], source: TaSource) -> bool { - self.ta_uuid_map.insert(*ta_uuid, ta_bin.into(), source) + let ta_elf = match source { + TaSource::BuiltIn => ta_bin, + TaSource::Dynamic => { + #[cfg(feature = "signed-ta-rsa")] + { + let Some((verified_uuid, ta_elf)) = verify_signed_ta(ta_bin) else { + return false; + }; + if verified_uuid != *ta_uuid { + return false; + } + ta_elf + } + #[cfg(not(feature = "signed-ta-rsa"))] + { + return false; + } + } + }; + self.ta_uuid_map.insert(*ta_uuid, ta_elf.into(), source) } /// Get the TA binary associated with the given TA UUID. @@ -233,7 +280,7 @@ impl GlobalState { if !self.store_ta_bin(ta_uuid, &ta_bin, TaSource::Dynamic) { return None; } - Some(ta_bin) + self.ta_uuid_map.get(ta_uuid) } } @@ -369,6 +416,11 @@ impl OpteeShim { self.0.store_ta_bin(ta_uuid, ta_bin, source) } + /// Store a raw or signed TA embedded in the runner image. + pub fn store_embedded_ta(&self, ta_bin: &[u8]) -> bool { + self.0.store_embedded_ta(ta_bin) + } + /// Get the TA binary associated with the given TA UUID. pub fn get_ta_bin(&self, ta_uuid: &TeeUuid) -> Option> { self.0.get_ta_bin(ta_uuid) @@ -1488,6 +1540,16 @@ fn ta_uuid_map() -> &'static TaUuidMap { TA_UUID_MAP.get_or_init(|| alloc::boxed::Box::new(TaUuidMap::new())) } +#[cfg(feature = "signed-ta-rsa")] +fn verify_signed_ta(ta_bin: &[u8]) -> Option<(TeeUuid, &[u8])> { + use litebox_common_optee::{TaVerifyKey, parse_and_verify_ta}; + + const TA_VERIFY_KEY_DER: &[u8] = include_bytes!(env!("LITEBOX_TA_VERIFY_KEY")); + let verify_key = TaVerifyKey::from_der(TA_VERIFY_KEY_DER).ok()?; + let (ta_head, ta_elf) = parse_and_verify_ta(ta_bin, &verify_key).ok()?; + Some((ta_head.uuid, ta_elf)) +} + /// Per-instance TA state which can be shared between sessions if it is /// a single-instance multi-session TA. The active session id is carried /// per entry (see [`Task::current_session_id`]). From 0ee03a95d6bc6357146fc4c393779cba46ef3fbe Mon Sep 17 00:00:00 2001 From: Angelina Vu Date: Mon, 21 Sep 2026 17:21:53 +0000 Subject: [PATCH 5/5] Fix cargo clippy. Signed-off-by: Angelina Vu --- litebox_common_optee/src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/litebox_common_optee/src/lib.rs b/litebox_common_optee/src/lib.rs index 3d60f44e36..78852022fd 100644 --- a/litebox_common_optee/src/lib.rs +++ b/litebox_common_optee/src/lib.rs @@ -2670,15 +2670,15 @@ const SHDR_MAGIC: u32 = 0x4f54_5348; /// From `optee_os/core/include/signed_hdr.h` /// struct shdr_bootstrap_ta { -/// uint8_t uuid[sizeof(TEE_UUID)]; -/// uint32_t ta_version; +/// uint8_t uuid[sizeof(TEE_UUID)]; +/// uint32_t ta_version; /// }; /// and from `optee_os/core/include/tee_api_types.h` /// typedef struct { -/// uint32_t timeLow; -/// uint16_t timeMid; -/// uint16_t timeHiAndVersion; -/// uint8_t clockSeqAndNode[8]; +/// uint32_t timeLow; +/// uint16_t timeMid; +/// uint16_t timeHiAndVersion; +/// uint8_t clockSeqAndNode[8]; /// } TEE_UUID; #[cfg(feature = "signed-ta-rsa")] const SHDR_UUID_LEN: usize = 16;