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
1 change: 1 addition & 0 deletions litebox_shim_optee/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ p384 = { version = "0.13.1", default-features = false, features = ["arithmetic",

[features]
default = ["platform_lvbs"]
idks-production = []
platform_linux_userland = ["litebox_platform_multiplex/platform_linux_userland_with_optee_syscall"]
platform_lvbs = ["litebox_platform_multiplex/platform_lvbs_with_optee_syscall"]

Expand Down
202 changes: 185 additions & 17 deletions litebox_shim_optee/src/idk.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,24 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

use crate::NormalWorldMutPtr;
use litebox::{mm::linux::PAGE_SIZE, platform::CrngProvider, utils::TruncateExt};
use crate::syscalls::pta::PTA_DEFAULT_FLAGS;
use crate::{
NormalWorldMutPtr, TA_DIGEST_LEN, TaDigest, Task, UserConstPtr, UserMutPtr, syscalls::Cleanup,
};
use alloc::vec::Vec;
use litebox::{
mm::linux::PAGE_SIZE,
platform::{CrngProvider, RawConstPointer as _, RawMutPointer as _},
utils::TruncateExt,
};
use litebox_common_linux::errno::Errno;
use litebox_common_optee::{TaFlags, TeeParamType, TeeResult, TeeUuid, UteeParams};
use num_enum::TryFromPrimitive;
use p384::{NonZeroScalar, elliptic_curve::sec1::ToEncodedPoint};
use p384::{
NonZeroScalar,
ecdsa::{Signature, SigningKey, signature::Signer},
elliptic_curve::sec1::ToEncodedPoint,
};
use spin::Once;
use zeroize::Zeroizing;

Expand All @@ -15,11 +28,155 @@ const KEY_ALGORITHM_MASK: u64 = 0xff00;
const KEY_VARIANT_MASK: u64 = 0xff;
const KEY_ALGORITHM_VALUE_MASK: u64 = KEY_ALGORITHM_MASK | KEY_VARIANT_MASK;
const MAX_KEYGEN_ATTEMPT: usize = 256;
const IDKS_ENDORSEMENT_DATA_MAX_SIZE: usize = 8 * 1024 * 1024;
const IDKS_ENDORSEMENT_MAGIC: &[u8; 4] = b"IDKS";
const IDKS_ENDORSEMENT_VERSION: u32 = 1;
#[cfg(not(feature = "idks-production"))]
const IDKS_DEBUG_FLAG: u8 = 1;
#[cfg(feature = "idks-production")]
const IDKS_DEBUG_FLAG: u8 = 0;
const ISOLATION_SOLUTION: &[u8] = b"LVBS";
pub(crate) const IDKS_ENDORSEMENT_SIGNATURE_LEN: usize = 96;
const IDKS_ENDORSEMENT_METADATA_LEN: usize = IDKS_ENDORSEMENT_MAGIC.len()
+ size_of::<u32>()
+ size_of::<TeeUuid>()
+ size_of::<u32>()
+ TA_DIGEST_LEN
+ size_of::<u8>()
+ ISOLATION_SOLUTION.len();
pub(crate) struct IdksPta;

#[derive(Clone, Copy, TryFromPrimitive)]
#[repr(u32)]
pub(crate) enum IdksCommandId {
EndorseData = 0,
}

impl IdksPta {
pub(crate) const FLAGS: TaFlags = PTA_DEFAULT_FLAGS.union(TaFlags::CONCURRENT);
pub(crate) const UUID: TeeUuid = TeeUuid {
time_low: 0xfd79_8211,
time_mid: 0x38a3,
time_hi_and_version: 0x474a,
clock_seq_and_node: [0xab, 0x6c, 0x75, 0x61, 0x0d, 0x45, 0x35, 0x93],
};

pub(crate) fn open_session(params: &UteeParams) -> Result<u32, TeeResult> {
crate::syscalls::pta::open_default_pta_session(params)
}

pub(crate) fn close_session(_task: &Task, _session_id: u32) {}

pub(crate) fn invoke_command(
task: &Task,
cmd_id: u32,
params: &mut UteeParams,
) -> Result<Cleanup, TeeResult> {
match IdksCommandId::try_from(cmd_id).map_err(|_| TeeResult::BadParameters)? {
IdksCommandId::EndorseData => Self::endorse_data(task, params).map(|()| Cleanup::None),
}
}

fn endorse_data(task: &Task, params: &mut UteeParams) -> Result<(), TeeResult> {
use TeeParamType::{MemrefInput, MemrefOutput, None};

if !params.has_types([MemrefInput, MemrefOutput, None, None]) {
return Err(TeeResult::BadParameters);
}

let (ta_data_addr, ta_data_size) = params
.get_values(0)
.map_err(|_| TeeResult::BadParameters)?
.ok_or(TeeResult::BadParameters)?;
let ta_data_size = usize::try_from(ta_data_size).map_err(|_| TeeResult::BadParameters)?;
if ta_data_size > IDKS_ENDORSEMENT_DATA_MAX_SIZE {
return Err(TeeResult::BadParameters);
}
if ta_data_size > 0 && ta_data_addr == 0 {
return Err(TeeResult::BadParameters);
}

let (endorsement_addr, endorsement_size) = params
.get_values(1)
.map_err(|_| TeeResult::BadParameters)?
.ok_or(TeeResult::BadParameters)?;
let required_endorsement_size = ta_data_size
.checked_add(IDKS_ENDORSEMENT_METADATA_LEN)
.and_then(|size| size.checked_add(IDKS_ENDORSEMENT_SIGNATURE_LEN))
.ok_or(TeeResult::BadParameters)?;
let required_endorsement_size_u64 =
u64::try_from(required_endorsement_size).map_err(|_| TeeResult::BadParameters)?;
if endorsement_size < required_endorsement_size_u64 {
params
.set_values(1, endorsement_addr, required_endorsement_size_u64)
.map_err(|_| TeeResult::BadParameters)?;
return Err(TeeResult::ShortBuffer);
}
if endorsement_addr == 0 {
return Err(TeeResult::BadParameters);
}

let ta_data = if ta_data_size == 0 {
Vec::new().into_boxed_slice()
} else {
UserConstPtr::<u8>::from_usize(
usize::try_from(ta_data_addr).map_err(|_| TeeResult::BadParameters)?,
)
.to_owned_slice(ta_data_size)
.ok_or(TeeResult::BadParameters)?
};
let mut endorsement =
build_endorsement_data(&ta_data, &task.ta_app_id, task.ta_svn, &task.ta_digest)
.ok_or(TeeResult::BadParameters)?;
let key_pair = get_identity_signing_key_pair().map_err(|_| TeeResult::GenericError)?;
let signature = endorse_data_with(&endorsement, &key_pair.private_key)
.map_err(|_| TeeResult::GenericError)?;
endorsement.extend_from_slice(&signature);
UserMutPtr::<u8>::from_usize(
usize::try_from(endorsement_addr).map_err(|_| TeeResult::BadParameters)?,
)
.copy_from_slice(0, &endorsement)
.ok_or(TeeResult::AccessDenied)?;
params
.set_values(1, endorsement_addr, required_endorsement_size_u64)
.map_err(|_| TeeResult::BadParameters)
}
}

fn build_endorsement_data(
ta_data: &[u8],
ta_uuid: &TeeUuid,
ta_svn: u32,
ta_digest: &TaDigest,
) -> Option<Vec<u8>> {
// MAGIC || VERSION || TA_DATA || TA_UUID || TA_SVN || TA_DIGEST || DEBUG || ISOLATION_SOLUTION
let capacity = ta_data.len().checked_add(IDKS_ENDORSEMENT_METADATA_LEN)?;
let mut endorsement = Vec::with_capacity(capacity);
endorsement.extend_from_slice(IDKS_ENDORSEMENT_MAGIC);
endorsement.extend_from_slice(&IDKS_ENDORSEMENT_VERSION.to_le_bytes());
endorsement.extend_from_slice(ta_data);
endorsement.extend_from_slice(&ta_uuid.to_le_bytes());
endorsement.extend_from_slice(&ta_svn.to_le_bytes());
endorsement.extend_from_slice(ta_digest);
endorsement.push(IDKS_DEBUG_FLAG);
endorsement.extend_from_slice(ISOLATION_SOLUTION);
Some(endorsement)
}
Comment on lines +146 to +164

@sangho2 Sangho Lee (sangho2) Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function would be the most important one within this PR. Need to decide the data to include and the claim format. Don't prefer to use any specific format (e.g., C-struct, JSON, CBOR/COSE, ...) here because it heavily depends on what the verifier expects.


fn endorse_data_with(
endorsement_data: &[u8],
private_key: &[u8; IDENTITY_SIGNING_PRIVATE_KEY_LEN],
) -> Result<[u8; IDKS_ENDORSEMENT_SIGNATURE_LEN], Errno> {
let signing_key = SigningKey::from_slice(private_key).map_err(|_| Errno::EINVAL)?;
let signature: Signature = signing_key.sign(endorsement_data);
let mut signature_bytes = [0u8; IDKS_ENDORSEMENT_SIGNATURE_LEN];
signature_bytes.copy_from_slice(&signature.to_bytes());
Ok(signature_bytes)
}

static IDENTITY_SIGNING_KEY_PAIR: Once<IdentitySigningKeyPair> = Once::new();

struct IdentitySigningKeyPair {
#[allow(dead_code, reason = "retained for future IDK_S signing operations")]
private_key: Zeroizing<[u8; IDENTITY_SIGNING_PRIVATE_KEY_LEN]>,
public_key: [u8; IDENTITY_SIGNING_PUBLIC_KEY_LEN],
}
Expand Down Expand Up @@ -150,24 +307,35 @@ mod tests {
use super::*;

#[test]
fn identity_signing_private_key_signs_and_verifies_message() {
use crate::syscalls::tests::init_platform;
use p384::ecdsa::{
Signature, SigningKey, VerifyingKey,
signature::{Signer, Verifier},
fn endorsement_signature_covers_plaintext_layout() {
use p384::ecdsa::{Signature, VerifyingKey, signature::Verifier};

let mut private_key = [0u8; IDENTITY_SIGNING_PRIVATE_KEY_LEN];
private_key[IDENTITY_SIGNING_PRIVATE_KEY_LEN - 1] = 1;
let ta_data = b"TA public key";
let ta_uuid = TeeUuid {
time_low: 0x1122_3344,
time_mid: 0x5566,
time_hi_and_version: 0x7788,
clock_seq_and_node: [0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00],
};

let message = b"IDK_S signing test message";
let ta_svn = 7u32;
let ta_digest = [
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd,
0xee, 0xff, 0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b,
0x3c, 0x2d, 0x1e, 0x0f,
];
let expected_plaintext =
build_endorsement_data(ta_data, &ta_uuid, ta_svn, &ta_digest).unwrap();

let _task = init_platform();
let private_key = generate_identity_signing_private_key().unwrap();
assert!(is_valid_identity_signing_private_key(&private_key));
let signing_key = SigningKey::from_slice(&private_key[..]).unwrap();
let signature = endorse_data_with(&expected_plaintext, &private_key).unwrap();
let public_key = identity_signing_public_key_from_private_key(&private_key).unwrap();
let verifying_key = VerifyingKey::from_sec1_bytes(&public_key).unwrap();
let signature = Signature::from_slice(&signature).unwrap();

let signature: Signature = signing_key.sign(message);

verifying_key.verify(message, &signature).unwrap();
verifying_key
.verify(&expected_plaintext, &signature)
.unwrap();
}
}
47 changes: 31 additions & 16 deletions litebox_shim_optee/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,22 @@ use litebox_common_optee::{
TeeObjectInfo, TeeObjectType, TeeOperationMode, TeeResult, TeeUuid, UteeAttribute,
};
use litebox_platform_multiplex::Platform;
use sha2::{Digest, Sha256};

pub mod loader;
pub mod session;
pub(crate) mod syscalls;

pub mod msg_handler;

#[cfg(feature = "platform_lvbs")]
pub mod idk;

// Re-export session management types for convenience
pub use session::{OpenSessionTarget, SessionManager, SessionToken, TaInstance};

const MAX_KERNEL_BUF_SIZE: usize = 0x80_000;
pub(crate) const TA_DIGEST_LEN: usize = 32;
pub(crate) type TaDigest = [u8; TA_DIGEST_LEN];

pub struct OpteeShimEntrypoints {
task: Task,
Expand Down Expand Up @@ -203,11 +205,6 @@ impl GlobalState {
}
}

/// Get the TA flags associated with the given TA UUID.
pub(crate) fn get_ta_flags(&self, ta_uuid: &TeeUuid) -> TaFlags {
self.ta_uuid_map.get_flags(ta_uuid).unwrap_or_default()
}

/// Monotonic time elapsed since this instance was created, used as GP
/// "system time" (`TEE_GetSystemTime`).
///
Expand Down Expand Up @@ -259,12 +256,25 @@ impl OpteeShim {
ta_uuid: TeeUuid,
ta_bin: Option<&[u8]>,
) -> Result<LoadedProgram, loader::elf::ElfLoaderError> {
if let Some(ta_bin) = ta_bin
&& !self.0.store_ta_bin(&ta_uuid, ta_bin)
{
return Err(loader::elf::ElfLoaderError::InvalidUuid);
}
let (ta_flags, ta_digest) = self
.0
.ta_uuid_map
.get_metadata(&ta_uuid)
.ok_or(loader::elf::ElfLoaderError::OpenError(Errno::ENOENT))?;
let entrypoints = crate::OpteeShimEntrypoints {
_not_send: core::marker::PhantomData,
task: Task {
global: self.0.clone(),
thread: ThreadState::new(),
ta_app_id: ta_uuid,
// TODO: Populate this from trusted TA version metadata when available.
ta_svn: 0,
ta_digest,
tee_cryp_state_map: TeeCrypStateMap::new(),
tee_obj_map: TeeObjMap::new(),
ta_handle_map: TaHandleMap::new(),
Expand All @@ -276,11 +286,6 @@ impl OpteeShim {
tls_base_addr: Cell::new(0),
},
};
if let Some(ta_bin) = ta_bin
&& !entrypoints.task.global.store_ta_bin(&ta_uuid, ta_bin)
{
return Err(loader::elf::ElfLoaderError::InvalidUuid);
}
let elf_loader = loader::elf::ElfLoader::new(&entrypoints.task, ldelf_bin, true)?;
entrypoints.task.load_ldelf(elf_loader, ta_uuid)?;
let params_address = if entrypoints.task.get_ta_stack_base_addr().is_some() {
Expand All @@ -295,8 +300,6 @@ impl OpteeShim {
} else {
None
};
// Get TA flags from the stored binary
let ta_flags = entrypoints.task.global.get_ta_flags(&ta_uuid);
Ok(LoadedProgram {
entrypoints: Some(entrypoints),
params_address,
Expand Down Expand Up @@ -1317,6 +1320,8 @@ struct TaInfo {
binary: alloc::boxed::Box<[u8]>,
/// Parsed TA flags from .ta_head section
flags: TaFlags,
/// SHA-256 digest of the raw TA binary
digest: TaDigest,
}

/// Data structure to maintain a mapping from TA UUIDs to their binary data and flags.
Expand All @@ -1342,12 +1347,14 @@ impl TaUuidMap {
return false;
}

let digest = Sha256::digest(&ta_bin).into();
let mut inner = self.inner.lock();
inner.insert(
uuid,
TaInfo {
binary: ta_bin,
flags: ta_head.flags,
digest,
},
);
true
Expand All @@ -1357,9 +1364,11 @@ impl TaUuidMap {
self.inner.lock().get(uuid).map(|info| info.binary.clone())
}

/// Get the TA flags for a given UUID.
pub(crate) fn get_flags(&self, uuid: &TeeUuid) -> Option<TaFlags> {
self.inner.lock().get(uuid).map(|info| info.flags)
fn get_metadata(&self, uuid: &TeeUuid) -> Option<(TaFlags, TaDigest)> {
self.inner
.lock()
.get(uuid)
.map(|info| (info.flags, info.digest))
}

// Lazy removal of TA binaries when they are no longer needed.
Expand All @@ -1376,6 +1385,10 @@ struct Task {
thread: ThreadState,
/// TA UUID
ta_app_id: TeeUuid,
/// TA security version number
ta_svn: u32,
/// SHA-256 digest of the raw TA binary.
ta_digest: TaDigest,
/// TEE cryptography state map
tee_cryp_state_map: TeeCrypStateMap,
/// TEE object map
Expand Down Expand Up @@ -1550,6 +1563,8 @@ mod test_utils {
global: self.clone(),
thread: ThreadState::new(),
ta_app_id: TeeUuid::default(),
ta_svn: 0,
ta_digest: [0; TA_DIGEST_LEN],
tee_cryp_state_map: TeeCrypStateMap::new(),
tee_obj_map: TeeObjMap::new(),
ta_handle_map: TaHandleMap::new(),
Expand Down
Loading