Skip to main content
Zondax Github LinkZondax Github Link
Theme SwitchTheme Switch

Hello Rustee

A Trusted application runs in a TEE (trusted execution environment) that is isolated from the normal REE (Rich execution environment) where the normal OS is running. To talk to a TA, you need a host application (HA) that executes in the non-secure world (REE = Rich execution environment).

Hello-rustee demonstrates how we can run both a host and trusted apps using OPTEE and Rust in these devices. In order to simplify trusted app development we have created a set of tools to facilitate not only compilation but also emulation (QEMU) and testing.

Overview

It is a simple echo program, where the client sends a message to the trusted app which received the message and sends it back to the client. Although it is simple, it allows to verify basic but important optee framework functionalities:

  • Open a session to our Trusted Application(TA)
  • Send commands to the TA
  • Send data to the TA validating we can write buffers and send them to the TA through the Optee framework
  • The TA process commands correctly thanks to a well functioning Optee framework.
  • The TA is able to received the data coming from the external world(REE) and send data to the external world in a secure way.

Ensuring that the client or REE application can open sessions to Trusted Applications is important as any optee program requires this functionality as well as a proper communication where both the secure and non-secure worlds can exchange data flowlessly.

Below we can see part of the code that our tee-base framework calls to start the application.
The function attributes no_mangle tells the compiler to use the function name "run" instead of changing it. The extern C attribute indicates the compiler to use C abi to make this function callable from C.. sent to the TA:

#[no_mangle]
#[cfg(not(feature = "ci"))]
pub extern "C" fn run() -> u32 {
env_logger::init(); //can use any logger impl if you want

/* ****************
Place your logic from here....
You could do it directly in this crate or separate it out to another crate so it's easier to test
Below is some example code
****************** */
let msg = "been to trusted and back";
info!("[TEE] << {}", msg);

assert_eq!(logic::echo(msg.as_bytes()), Ok(true));
0
}

We have below the invoke_command function, that processes data comming from the REE world. It uses our custom bindings for safety and simplicity, this means that this function is called by another rust function as a result there is not a no_mangle or extern C attributes.

fn invoke_command(_cmd_id: u32, param_types: u32, parameters: &mut [TEE_Param; 4]) -> u32 {
let mut params = Parameters::from_raw(parameters, param_types);

// This check would depend on the opretion defined by cmd_id
// We might decide to limit the params to be only two, an input and output
// slice.
let expected_param_types = TEE_PARAM_TYPES(
ParamType::MemRefInput as u32,
ParamType::MemRefOutput as u32,
ParamType::None as u32,
ParamType::None as u32,
);
if param_types != expected_param_types {
error!("[ERROR] Bad parameters");
return Error::BadParameters as _;
}

let mut imemref = unsafe {
params
.0
.as_memref()
.expect("this is safe, the type was previously checked")
};
let mut omemref = unsafe {
params
.1
.as_memref()
.expect("this is safe, the type was previously checked")
};

/* **********
Place your logic from here...
You could do it directly in this crate or separate it out to another crate so it's easier to test
Below is some example code
************ */

//let's ignore the cmd_id since we only accept 1 command
//which is the echo command

let input = imemref.buffer();
let out = omemref.buffer();

if input.len() > out.len() {
return Error::BadFormat as _;
}

out.copy_from_slice(&input[..input.len()]);

0
}

The hello-rustee app also serves as a template that developers can use to build new applications as its structure is know by our build system, making it easy to deploy new applications.

Code structure

The typical project structure looks like this (some files have been removed for the sake of clarity)

.
├── framework
│   ├── crates
│   │   ├── Cargo.lock
│   │   ├── Cargo.toml
│   │   ├── zondee
│   │   ├── zondee-macros
│   │   ├── zondee-teec
│   │   └── zondee-utee
│   ├── host
│   │   ├── Makefile
│   │   ├── src
│   │   └── target
│   ├── LICENSE
│   ├── Makefile
│   ├── proj.mk
│   ├── ta
│   │   ├── Makefile
│   │   ├── src
│   │   └── target
│   └── tee-template
│   ├── REE
│   └── TEE
├── Makefile
├── REE
│   ├── Cargo.lock
│   ├── Cargo.toml
│   └── lib
│   ├── Cargo.toml
│   ├── include
│   └── src
└── TEE
├── Cargo.lock
├── Cargo.toml
└── lib
├── Cargo.toml
├── include
└── src

The framework directory contains the TEE-BASE framework that as described in this section, contains the bootstrapping C code that calls the user code in REE and TEE, which is written in Rust.

note

Please go to this section that gives instructions on how to test using this app