Skip to content
Draft
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
52 changes: 51 additions & 1 deletion litebox_common_optee/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,17 @@ pub struct TeeUuid {
}

impl TeeUuid {
/// The nil UUID (all zeros, RFC 4122 S4.1.7).
///
/// Used for anonymous clients (e.g. ,`TeeLogin::Public`) that carry no
/// REE-derived identity.
pub const NIL: Self = Self {
time_low: 0,
time_mid: 0,
time_hi_and_version: 0,
clock_seq_and_node: [0; 8],
};

/// Converts a UUID from a 16-byte array in RFC 4122 format (big-endian for numeric fields).
///
/// The byte layout is:
Expand Down Expand Up @@ -820,8 +831,13 @@ const TEE_LOGIN_APPLICATION: u32 = 0x4;
const TEE_LOGIN_APPLICATION_USER: u32 = 0x5;
const TEE_LOGIN_APPLICATION_GROUP: u32 = 0x6;
const TEE_LOGIN_TRUSTED_APP: u32 = 0xf000_0000;
// Private OP-TEE login for in-kernel REE clients (from
// `tee_api_defines_extensions.h`). Userspace cannot send it. It uses
// the nil client UUID.
const TEE_LOGIN_REE_KERNEL: u32 = 0x8000_0000;

/// `TEE Login type` from `optee_os/lib/libutee/include/tee_api_defines.h`
/// (plus the `TEE_LOGIN_REE_KERNEL` extension).
#[derive(Clone, Copy, PartialEq, TryFromPrimitive, Immutable, IntoBytes)]
#[repr(u32)]
pub enum TeeLogin {
Expand All @@ -831,6 +847,7 @@ pub enum TeeLogin {
Application = TEE_LOGIN_APPLICATION,
ApplicationUser = TEE_LOGIN_APPLICATION_USER,
ApplicationGroup = TEE_LOGIN_APPLICATION_GROUP,
ReeKernel = TEE_LOGIN_REE_KERNEL,
TrustedApp = TEE_LOGIN_TRUSTED_APP,
}

Expand Down Expand Up @@ -1457,6 +1474,11 @@ const OPTEE_MSG_ATTR_TYPE_TMEM_INOUT: u8 = 0xb;
// Note: `OPTEE_MSG_ATTR_TYPE_FMEM_*` are aliases of `OPTEE_MSG_ATTR_TYPE_RMEM_*`.
// Whether it is RMEM of FMEM depends on the conduit.

/// Meta-parameter marker (bit 8) of the attribute word. Set on the `OpenSession`
/// TA-UUID and client-identity params, which are consumed by the secure world
/// and not delivered to the TA.
const OPTEE_MSG_ATTR_META: u64 = 1 << 8;

#[non_exhaustive]
#[derive(Debug, PartialEq, TryFromPrimitive)]
#[repr(u8)]
Expand All @@ -1480,11 +1502,17 @@ pub enum OpteeMsgAttrType {
/// - bit 8 – meta
/// - bit 9 – noncontig
/// - bits \[63:10\] – reserved (zero)
#[derive(Clone, Copy, Default, FromBytes, IntoBytes, Immutable, KnownLayout)]
#[derive(Clone, Copy, Default, PartialEq, Eq, FromBytes, IntoBytes, Immutable, KnownLayout)]
#[repr(transparent)]
pub struct OpteeMsgAttr(u64);

impl OpteeMsgAttr {
/// The exact attribute word an `OpenSession` meta value parameter must carry
/// (`OPTEE_MSG_ATTR_META | OPTEE_MSG_ATTR_TYPE_VALUE_INPUT`, all other bits
/// zero). See [`OpteeMsgArgs::get_meta_param_value`].
pub const META_VALUE_INPUT: Self =
Self(OPTEE_MSG_ATTR_META | OPTEE_MSG_ATTR_TYPE_VALUE_INPUT as u64);

/// Returns the attribute type (bits 0–7).
#[allow(clippy::cast_possible_truncation)]
pub fn attr_type(&self) -> u8 {
Expand Down Expand Up @@ -1745,6 +1773,28 @@ impl OpteeMsgArgs {
.ok_or(OpteeSmcReturnCode::EBadCmd)?)
}
}

/// Read a value parameter that must be tagged as an `OpenSession` meta parameter.
///
/// `OpenSession` conveys the TA UUID and client identity in the first two
/// params, each marked exactly [`OpteeMsgAttr::META_VALUE_INPUT`]. This
/// mirrors OP-TEE OS `get_open_session_meta()`, which rejects the request
/// unless the full attr word of both meta params equals that value. Plain
/// `get_param_value` ignores these bits, so it must not be used for this.
pub fn get_meta_param_value(
&self,
index: usize,
) -> Result<OpteeMsgParamValue, OpteeSmcReturnCode> {
if index >= self.num_params as usize {
return Err(OpteeSmcReturnCode::ENotAvail);
}
let param = &self.params[index];
if param.attr != OpteeMsgAttr::META_VALUE_INPUT {
return Err(OpteeSmcReturnCode::EBadCmd);
}
param.get_param_value().ok_or(OpteeSmcReturnCode::EBadCmd)
}

pub fn set_param_value(
&mut self,
index: usize,
Expand Down
90 changes: 88 additions & 2 deletions litebox_runner_optee_on_linux_userland/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@

use litebox::platform::RawConstPointer;
use litebox::utils::TruncateExt;
use litebox_common_optee::{TeeParamType, UteeEntryFunc, UteeParamOwned, UteeParams};
use litebox_common_optee::{
TeeIdentity, TeeLogin, TeeParamType, TeeUuid, UteeEntryFunc, UteeParamOwned, UteeParams,
};
use litebox_shim_optee::session::allocate_session_id;
use litebox_shim_optee::{LoadedProgram, UserConstPtr};
use serde::Deserialize;
Expand Down Expand Up @@ -49,12 +51,22 @@ pub fn run_ta_with_test_commands(
if func_id == UteeEntryFunc::OpenSession {
let ta_head = litebox_common_optee::parse_ta_head(ta_bin)
.expect("Failed to parse TA header from ta_bin");
// Emulate the client identity a real REE client would present. The
// secure world normally derives this from the OpenSession meta
// params; here the test file states it, defaulting to a `user` login.
let client_identity = cmd.client_identity.as_ref().map_or(
TeeIdentity {
login: TeeLogin::User,
uuid: TeeUuid::NIL,
},
ClientIdentityJson::to_tee_identity,
);
let loaded = shim
.load_ldelf(
ldelf_bin,
ta_head.uuid,
Some(ta_bin),
None,
Some(client_identity),
allocate_session_id().unwrap(),
)
.map_err(|_| {
Expand Down Expand Up @@ -173,6 +185,80 @@ pub struct TaCommandBase64 {
cmd_id: u32,
#[serde(default)]
args: Vec<TaCommandParamsBase64>,
/// Client identity presented for an `OpenSession`, mirroring the
/// `TEE_Identity` a real REE client would supply (the secure world derives
/// this from the message meta params; this userland harness has no such
/// client, so the test states it here). Ignored for other commands. When
/// absent it defaults to a `user` login with the nil UUID, which is what a
/// typical REE user client presents.
#[serde(default)]
client_identity: Option<ClientIdentityJson>,
}

/// Client identity for an `OpenSession`, parsed from the test JSON.
#[derive(Debug, Deserialize)]
struct ClientIdentityJson {
#[serde(default)]
login: ClientLoginJson,
/// RFC 4122 UUID string (hyphens optional). Defaults to the nil UUID.
#[serde(default)]
uuid: Option<String>,
}

/// JSON mirror of [`TeeLogin`], so test files can name logins (e.g. `"user"`)
/// instead of raw `TEE_LOGIN_*` constants.
#[derive(Debug, Default, Clone, Copy, Deserialize)]
#[serde(rename_all = "snake_case")]
enum ClientLoginJson {
Public,
#[default]
User,
Group,
Application,
ApplicationUser,
ApplicationGroup,
ReeKernel,
TrustedApp,
}

impl From<ClientLoginJson> for TeeLogin {
fn from(login: ClientLoginJson) -> Self {
match login {
ClientLoginJson::Public => TeeLogin::Public,
ClientLoginJson::User => TeeLogin::User,
ClientLoginJson::Group => TeeLogin::Group,
ClientLoginJson::Application => TeeLogin::Application,
ClientLoginJson::ApplicationUser => TeeLogin::ApplicationUser,
ClientLoginJson::ApplicationGroup => TeeLogin::ApplicationGroup,
ClientLoginJson::ReeKernel => TeeLogin::ReeKernel,
ClientLoginJson::TrustedApp => TeeLogin::TrustedApp,
}
}
}

impl ClientIdentityJson {
fn to_tee_identity(&self) -> TeeIdentity {
let uuid = self
.uuid
.as_deref()
.map_or(TeeUuid::NIL, parse_uuid_or_panic);
TeeIdentity {
login: self.login.into(),
uuid,
}
}
}

/// Parse an RFC 4122 UUID string (hyphens optional) into a [`TeeUuid`].
fn parse_uuid_or_panic(s: &str) -> TeeUuid {
let hex: String = s.chars().filter(|&c| c != '-').collect();
assert_eq!(hex.len(), 32, "client uuid must be 32 hex digits: {s:?}");
let mut bytes = [0u8; 16];
for (i, byte) in bytes.iter_mut().enumerate() {
*byte = u8::from_str_radix(&hex[i * 2..i * 2 + 2], 16)
.unwrap_or_else(|_| panic!("invalid hex in client uuid: {s:?}"));
}
TeeUuid::from_bytes(bytes)
}

#[derive(Debug, Deserialize)]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
[
{
"func_id": "open_session"
"func_id": "open_session",
"client_identity": {
"login": "user"
}
},
{
"func_id": "invoke_command",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
[
{
"func_id": "open_session"
"func_id": "open_session",
"client_identity": {
"login": "user"
}
},
{
"func_id": "invoke_command",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
[
{
"func_id": "open_session",
"client_identity": {
"login": "user"
},
"args": [
{
"param_type": "value_input",
Expand Down Expand Up @@ -31,6 +34,9 @@
},
{
"func_id": "open_session",
"client_identity": {
"login": "user"
},
"args": [
{
"param_type": "value_input",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
[
{
"func_id": "open_session"
"func_id": "open_session",
"client_identity": {
"login": "user"
}
},
{
"func_id": "invoke_command",
Expand Down
10 changes: 6 additions & 4 deletions litebox_shim_optee/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,9 +237,11 @@ impl OpteeShim {
thread: ThreadState::new(),
session_id,
ta_app_id: ta_uuid,
// Fall back to the anonymous public identity when no client is
// supplied (matches OP-TEE OS / the Linux driver).
client_identity: client.unwrap_or(TeeIdentity {
login: TeeLogin::User,
uuid: TeeUuid::default(),
login: TeeLogin::Public,
uuid: TeeUuid::NIL,
}),
tee_cryp_state_map: TeeCrypStateMap::new(),
tee_obj_map: TeeObjMap::new(),
Expand Down Expand Up @@ -1428,8 +1430,8 @@ mod test_utils {
session_id: SessionIdPool::allocate().unwrap(),
ta_app_id: TeeUuid::default(),
client_identity: TeeIdentity {
login: TeeLogin::User,
uuid: TeeUuid::default(),
login: TeeLogin::Public,
uuid: TeeUuid::NIL,
},
tee_cryp_state_map: TeeCrypStateMap::new(),
tee_obj_map: TeeObjMap::new(),
Expand Down
34 changes: 26 additions & 8 deletions litebox_shim_optee/src/msg_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -408,25 +408,43 @@ pub fn decode_ta_request(

let (ta_uuid, client_identity, skip): (Option<TeeUuid>, Option<TeeIdentity>, usize) =
if ta_entry_func == UteeEntryFunc::OpenSession {
// If it is an OpenSession request, extract UUIDs and login from params[0] and params[1]
// Based on observed Linux kernel behavior:
// If it is an OpenSession request, extract the TA UUID, client UUID,
// and login from the two meta params. Wire layout (per the Linux
// OP-TEE driver):
// - params[0].a/b = TA UUID (two little-endian u64 values)
// - params[1].a/b = client UUID (two little-endian u64 values)
// - params[1].a/b = client UUID, but only meaningful for the
// user/group/application logins; PUBLIC/REE_KERNEL ignore it and
// report the nil UUID (see the match below).
// - params[1].c = client login type (TEE_LOGIN_*)
let param0 = msg_args.get_param_value(0)?;
let param0 = msg_args.get_meta_param_value(0)?;
let ta_data = [param0.a, param0.b];

let param1 = msg_args.get_param_value(1)?;
let client_data = [param1.a, param1.b];
let param1 = msg_args.get_meta_param_value(1)?;
let login: u32 = param1.c.trunc();
let login = TeeLogin::try_from(login).unwrap_or(TeeLogin::Public);
// Reject unknown login methods
let login = TeeLogin::try_from(login).map_err(|_| OpteeSmcReturnCode::EBadCmd)?;

// Only the REE-derived user/group/application logins carry a
// meaningful client UUID. PUBLIC and REE_KERNEL clients are anonymous
// and must report the nil UUID (OP-TEE OS memsets it to zero).
// TRUSTED_APP identifies a TA-to-TA caller and is established
// internally, never from a normal-world message.
let client_uuid = match login {
TeeLogin::Public | TeeLogin::ReeKernel => TeeUuid::NIL,
TeeLogin::User
| TeeLogin::Group
| TeeLogin::Application
| TeeLogin::ApplicationUser
| TeeLogin::ApplicationGroup => TeeUuid::from_u64_array([param1.a, param1.b]),
TeeLogin::TrustedApp => return Err(OpteeSmcReturnCode::EBadCmd),
};

// Skip the first two parameters as they convey TA and client UUIDs
(
Some(TeeUuid::from_u64_array(ta_data)),
Some(TeeIdentity {
login,
uuid: TeeUuid::from_u64_array(client_data),
uuid: client_uuid,
}),
2,
)
Expand Down
Loading