Add SecurityCoordinator skeleton.

- add SecurityCoordinator component (needs mailbox support, just
  a fake which should be enabled with a feature flag)
- connect to ProcessManager & MlCoordinator - temproarily connect
  to DebugConsole to enable scecho test command
- expand Bundle to hold application information (may need more elf)
- connect ProcessManager::{install, uninstall} to SecurityCoordinator
  (no application binary yet, needs global page allocator)

Notes:
- SecurityCoordinator depends on camkes for thread synchronization
- private heap is 8KB (and could possible be less; need to tune)
- camkes interface connection uses seL4RPCOverMultiSharedData so ipc
  buffers are 4KB; the request & reply serde buffers are 2KB but could
  be near 4KB since they are used sequentially and the other params
  are a few bytes (but beware of camkes stack allocation)
- the camkes SecurityCoordinator::request rpc is defined so that the
  request param has reasonable handling but the reply param requires
  a full copy (even if only partly used); haven't found a way to
  express the desired handling

Change-Id: I686dc2d501e39bc8c27fe22db40657165a55b472
GitOrigin-RevId: db1536c241e28ddda1dc8f8da341b8c667ed6646
This commit is contained in:
Sam Leffler
2021-09-14 16:11:16 -07:00
parent 4904a61ca0
commit 0db63cfb4f
24 changed files with 698 additions and 59 deletions

View File

@@ -55,6 +55,18 @@ DeclareCAmkESComponent(MlCoordinator
INCLUDES interfaces
)
RustAddLibrary(
kata_security_coordinator
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/components/SecurityCoordinator
TARGET "riscv32imc-unknown-none-elf"
LIB_FILENAME libkata_security_coordinator.a
)
DeclareCAmkESComponent(SecurityCoordinator
LIBS kata_security_coordinator
INCLUDES interfaces
)
DeclareCAmkESComponent(LogFibonacci
SOURCES
components/LogFibonacci/src/main.c

View File

@@ -2,6 +2,7 @@ import <LoggerInterface.camkes>;
import <ProcessControlInterface.camkes>;
import <PackageManagementInterface.camkes>;
import <MlCoordinatorInterface.camkes>;
import <SecurityCoordinatorInterface.camkes>;
import <SeL4DebugInterface.camkes>;
component DebugConsole {
@@ -18,6 +19,8 @@ component DebugConsole {
provides LoggerInterface logger;
uses ProcessControlInterface proc_ctrl;
uses PackageManagementInterface pkg_mgmt;
// TODO(sleffler): for debugging
uses SecurityCoordinatorInterface security;
uses SeL4DebugInterface sel4debug;
uses MlCoordinatorInterface mlcoord;
}

View File

@@ -9,5 +9,6 @@ cstr_core = "0.2"
kata-io = { path = "../kata-io" }
kata-line-reader = { path = "../kata-line-reader" }
kata-proc-common = { path = "../../ProcessManager/kata-proc-common" }
kata-security-common = { path = "../../SecurityCoordinator/kata-security-common" }
log = "0.4"
postcard = { version = "0.7", features = ["alloc"] }

View File

@@ -82,6 +82,7 @@ fn dispatch_command(cmdline: &str, output: &mut dyn io::Write) {
"install" => install_command(&mut args, output),
"loglevel" => loglevel_command(&mut args, output),
"ps" => ps_command(),
"scecho" => scecho_command(cmdline, output),
"start" => start_command(&mut args, output),
"stop" => stop_command(&mut args, output),
"uninstall" => uninstall_command(&mut args, output),
@@ -117,6 +118,33 @@ fn echo_command(cmdline: &str, output: &mut dyn io::Write) -> Result<(), Command
}
}
/// Implements an "scecho" command that sends arguments to the Security Core's echo service.
fn scecho_command(cmdline: &str, output: &mut dyn io::Write) -> Result<(), CommandError> {
use kata_security_common::*;
let (_, request) = cmdline.split_at(7); // 'scecho'
let reply = &mut [0u8; SECURITY_REPLY_DATA_SIZE];
match unsafe {
security_request(
SecurityRequest::SrEcho,
request.len() as u32,
request.as_ptr(),
reply as *mut _,
)
} {
SecurityRequestError::SreSuccess => {
writeln!(
output,
"{}",
String::from_utf8_lossy(&reply[..request.len()])
)?;
}
status => {
writeln!(output, "ECHO replied {:?}", status)?;
}
}
Ok(())
}
// Set/display the max log level for the DebugConsole.
fn loglevel_command(
args: &mut dyn Iterator<Item = &str>,

View File

@@ -1,9 +1,11 @@
import <LoggerInterface.camkes>;
import <MlCoordinatorInterface.camkes>;
import <SecurityCoordinatorInterface.camkes>;
component MlCoordinator {
provides MlCoordinatorInterface mlcoord;
uses LoggerInterface logger;
uses SecurityCoordinatorInterface security;
uses VectorCoreInterface vctop;
}

View File

@@ -4,6 +4,7 @@ import <LoggerInterface.camkes>;
import <PackageManagementInterface.camkes>;
import <ProcessControlInterface.camkes>;
import <SeL4DebugInterface.camkes>;
import <SecurityCoordinatorInterface.camkes>;
component ProcessManager {
provides PackageManagementInterface pkg_mgmt;
@@ -11,4 +12,5 @@ component ProcessManager {
uses LoggerInterface logger;
uses SeL4DebugInterface sel4debug;
uses SecurityCoordinatorInterface security;
}

View File

@@ -4,4 +4,6 @@ version = "0.1.0"
edition = "2018"
[dependencies]
kata-security-common = { path = "../../SecurityCoordinator/kata-security-common" }
postcard = { version = "0.7", features = ["alloc"] }
serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] }

View File

@@ -6,45 +6,68 @@ extern crate alloc;
use alloc::string::String;
use alloc::vec::Vec;
use core::str;
use serde::{Deserialize, Serialize};
pub type BundleIdArray = Vec<String>;
// NB: struct's marked repr(C) are processed by cbindgen to get a .h file
// used in camkes C interfaces.
// BundleId capcity before spillover to the heap.
// TODO(sleffler): hide this; it's part of the implementation
pub const DEFAULT_BUNDLE_ID_CAPACITY: usize = 64;
// Size of the data buffer used to pass BundleIdArray data between Rust <> C.
// Size of the data buffer used to pass a serialized BundleIdArray between Rust <> C.
// The data structure size is bounded by the camkes ipc buffer (120 bytes!)
// and also by it being allocated on the stack of the rpc glue code.
// So we need to balance these against being able to return all values.
pub const RAW_BUNDLE_ID_DATA_SIZE: usize = 100;
pub type RawBundleIdData = [u8; RAW_BUNDLE_ID_DATA_SIZE];
// TODO(sleffler): fill-in
#[derive(Clone, Debug)]
// BundleId capacity before spillover to the heap.
// TODO(sleffler): hide this; it's part of the implementation
pub const DEFAULT_BUNDLE_ID_CAPACITY: usize = 64;
mod ptr_helper {
use super::*;
use serde::{Deserializer, Serializer};
pub fn serialize<T, S>(ptr: &*const T, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
(*ptr as usize).serialize(serializer)
}
pub fn deserialize<'de, T, D>(deserializer: D) -> Result<*const T, D::Error>
where
D: Deserializer<'de>,
{
Ok(usize::deserialize(deserializer)? as *const T)
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Bundle {
// NB: application & ML binaries use well-known paths relative to bundle_id
// NB: ProcessManager owns loaded application's memory
// Bundle id extracted from manifest
pub app_id: String,
pub data: [u8; 64], // TODO(sleffler): placeholder
// Raw memory address of loaded application
#[serde(with = "ptr_helper")]
pub app_memory_address: *const u8,
// Size (bytes) of loaded application
pub app_memory_size: u32,
}
impl Bundle {
pub fn new() -> Self {
Bundle {
app_id: String::with_capacity(DEFAULT_BUNDLE_ID_CAPACITY),
data: [0u8; 64],
app_memory_address: 0 as *const u8,
app_memory_size: 0u32,
}
}
pub fn as_ptr(&self) -> *const u8 {
self.data.as_ptr()
}
pub fn as_mut_ptr(&mut self) -> *mut u8 {
self.data.as_mut_ptr()
}
}
// NB: struct's marked repr(C) are processed by cbindgen to get a .h file
// used in camkes C interfaces.
#[repr(C)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ProcessManagerError {
@@ -70,7 +93,7 @@ pub trait ProcessManagerInterface {
&mut self,
pkg_buffer: *const u8,
pkg_buffer_size: u32,
) -> Result<Bundle, ProcessManagerError>;
) -> Result<String, ProcessManagerError>;
fn uninstall(&mut self, bundle_id: &str) -> Result<(), ProcessManagerError>;
fn start(&mut self, bundle: &Bundle) -> Result<(), ProcessManagerError>;
fn stop(&mut self, bundle: &Bundle) -> Result<(), ProcessManagerError>;

View File

@@ -5,9 +5,10 @@ description = "Kata OS ProcessManager services"
edition = "2018"
[dependencies]
cstr_core = { version = "0.2.3", default-features = false, features = ["alloc"] }
hashbrown = { version = "0.11", features = ["ahash-compile-time-rng"] }
kata-proc-common = { path = "../kata-proc-common" }
kata-security-common = { path = "../../SecurityCoordinator/kata-security-common" }
log = "0.4"
postcard = "0.7"
smallstr = "0.2"
spin = "0.9"

View File

@@ -3,9 +3,11 @@
#![cfg_attr(not(test), no_std)]
extern crate alloc;
use alloc::string::{String, ToString};
use kata_proc_common as proc;
use proc::*;
use alloc::string::String;
use kata_proc_common::*;
use kata_security_common::*;
use log::trace;
use postcard;
use spin::Mutex;
mod proc_manager;
@@ -77,48 +79,99 @@ impl ProcessManagerInterface for KataManagerInterface {
fn install(
&mut self,
pkg_buffer: *const u8,
_pkg_buffer_size: u32,
) -> Result<Bundle, ProcessManagerError> {
pkg_buffer_size: u32,
) -> Result<String, ProcessManagerError> {
// Package contains: application manifest, application binary, and
// (optional) ML workload binary to run on vector core.
// Manifest contains bundle_id.
// Resulting flash file/pathname is fixed (1 app / bundle), only need bundle_id.
// Store a generated "access key" (for Tock) for start ops; this is
// "bound via capability badging to seL4 capabilities".
let bundle = Bundle {
// NB: temporarily fill-in app_id
app_id: (pkg_buffer as usize).to_string(),
data: [0u8; 64],
};
Ok(bundle)
// Pass opaque package contents through; get back bundle_id.
// This is handled by the SecurityCoordinator.
let reply = &mut [0u8; SECURITY_REPLY_DATA_SIZE];
match unsafe {
security_request(
SecurityRequest::SrInstall,
pkg_buffer_size,
pkg_buffer,
reply as *mut _,
)
} {
SecurityRequestError::SreSuccess => {
fn deserialize_failure(e: postcard::Error) -> ProcessManagerError {
trace!("install failed: deserialize {:?}", e);
ProcessManagerError::BundleDataInvalid
}
postcard::from_bytes::<String>(reply).map_err(deserialize_failure)
}
status => {
trace!("install failed: {:?}", status);
Err(ProcessManagerError::InstallFailed)
}
}
}
fn uninstall(&mut self, _bundle_id: &str) -> Result<(), ProcessManagerError> {
// This is handled with the StorageManager::Installer::uninstall.
Ok(())
fn uninstall(&mut self, bundle_id: &str) -> Result<(), ProcessManagerError> {
fn serialize_failure(e: postcard::Error) -> ProcessManagerError {
trace!("uninstall failed: serialize {:?}", e);
ProcessManagerError::UninstallFailed
}
// NB: the caller has already checked no running application exists
// NB: the Security Core is assumed to invalidate/remove any kv store
// This is handled by the SecurityCoordinator.
let mut request_data = [0u8; SECURITY_REQUEST_DATA_SIZE];
let _ = postcard::to_slice(&bundle_id, &mut request_data).map_err(serialize_failure)?;
let reply = &mut [0u8; SECURITY_REPLY_DATA_SIZE];
match unsafe {
security_request(
SecurityRequest::SrUninstall,
request_data.len() as u32,
request_data.as_ptr(),
reply as *mut _,
)
} {
SecurityRequestError::SreSuccess => Ok(()),
status => {
trace!("uninstall failed: {:?}", status);
Err(ProcessManagerError::UninstallFailed)
}
}
}
fn start(&mut self, _bundle: &Bundle) -> Result<(), ProcessManagerError> {
// 1. Allocate shared memory for the program image
// 2. Poke security core (via mailbox) to VerifyAndLoad data and load
// into shared memory
// 3. Security core responds (via mailbox) with success/failure &
// mailbox handler sends interrupt
// 4. Request completed with validated program in shared memory.
// 5. On success allocate seL4 resources: VSpace, TCB & necessary
// capabiltiies; setup application system context and start thread
// (or should resources be allocated before Verify?).
// TODO: set up access to StorageManager? (badge seL4 cap w/ bundle_id)
// 1. Ask security core for application footprint with SizeBuffer
// 2. Ask security core for manifest (maybe piggyback on SizeBuffer)
// and parse for necessary info (e.g. whether kv Storage is
// required, other privileges/capabilities)
// 3. Ask MemoryManager for shared memory pages for the application
// (model handled separately by MlCoordinator since we do not know
// which model will be used)
// 4. Allocate other seL4 resources:
// - VSpace, TCB & necessary capabiltiies
// 5. Ask security core to VerifyAndLoad app into shared memory pages
// 6. Complete seL4 setup:
// - Setup application system context and start thread
// - Badge seL4 recv cap w/ bundle_id for (optional) StorageManager
// access
//
// Applications with an ML workload use the MlCoordinator to request
// data be loaded for the vector core.
//
// TBD where stuff normally in ELF headers comes from (e.g. starting pc,
// text size for marking pages executable, bss size).
//
// May want stack size parameterized.
//
// Applications with an ML workload use the MLCoordinator to request
// data be written to the vector core.
// TODO(sleffler): fill-in
// Err(ProcessManagerError::StartFailed)
Ok(())
}
fn stop(&mut self, _bundle: &Bundle) -> Result<(), ProcessManagerError> {
// 1. If thread is running, notify application so it can do cleanup;
// e.g. ask the MLCoordinator to stop any ML workloads
// 2. If thread notified, wait some period of time for ack.
// 3. If thread is running, stop thread.
// 0. Assume thread is running (caller verifies)
// 1. Notify application so it can do cleanup; e.g. ask the
// MLCoordinator to stop any ML workloads
// 2. Wait some period of time for an ack from application
// 3. Stop thread
// 4. Reclaim seL4 resources: TCB, VSpace, memory, capabilities, etc.
// TODO(sleffler): fill-in
// Err(ProcessManagerError::StopFailed)

View File

@@ -6,6 +6,7 @@ use alloc::string::String;
use core::convert::TryFrom;
use core::marker::Sync;
use hashbrown::HashMap;
use kata_proc_common::{Bundle, DEFAULT_BUNDLE_ID_CAPACITY};
use log::trace;
use smallstr::SmallString;
@@ -81,21 +82,23 @@ impl PackageManagementInterface for ProcessManager {
// We assume the seL4 capability for the memory associated with
// pkg_buffer has been setup for us so we can pass it along (as needed)
// to the StorageManager.
// to the Security Core.
//
// NB: defer to StorageManager for handling an install of a previously
// installed app. We do not have the app_id to check locally so if the
// StorageManager disallows re-install then we'll return it's error;
// otherwise we update the returned Bundle state.
// TODO(sleffler): owner's public key?
let bundle = self.manager.install(pkg_buffer, pkg_buffer_size)?;
let bundle_id = self.manager.install(pkg_buffer, pkg_buffer_size)?;
trace!(
"install pkg {:p}:{} => bundle_id:{}",
pkg_buffer,
pkg_buffer_size,
&bundle.app_id
bundle_id
);
let mut bundle = Bundle::new();
bundle.app_id = bundle_id;
assert!(self
.bundles
.insert(BundleId::from_str(&bundle.app_id), BundleData::new(&bundle))
@@ -315,6 +318,4 @@ mod tests {
let running = mgr.get_running_bundles().unwrap();
assert_eq!(running.len(), 0);
}
// TODO(sleffler): check uninstall stops a running thread
}

View File

@@ -0,0 +1,23 @@
[workspace]
members = [
"kata-security-common",
"kata-security-component",
"kata-security-coordinator",
]
[profile.dev]
opt-level = 0
debug = true
lto = "fat"
codegen-units = 1
[profile.release]
opt-level = "z"
lto = "fat"
codegen-units = 1
split-debuginfo = "unpacked"
[profile.release.build-override]
opt-level = "z"
codegen-units = 1

View File

@@ -0,0 +1,10 @@
// Kata OS SecurityCoordinator services.
import <LoggerInterface.camkes>;
import <SecurityCoordinatorInterface.camkes>;
component SecurityCoordinator {
provides SecurityCoordinatorInterface security;
uses LoggerInterface logger;
}

View File

@@ -0,0 +1,7 @@
language = "C"
include_guard = "__SECURITY_COORDINATOR_BINDINGS_H__"
autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
no_includes = true
[export]
include = ["SecurityRequestData", "SecurityReplyData", "SecurityRequest", "SecurityRequestError"]

View File

@@ -0,0 +1,8 @@
[package]
name = "kata-security-common"
version = "0.1.0"
edition = "2018"
[dependencies]
serde = { version = "1.0", default-features = false, features = ["alloc", "derive"] }
serde-big-array = "0.3"

View File

@@ -0,0 +1,174 @@
//! Kata OS Security Coordinator support
#![cfg_attr(not(test), no_std)]
extern crate alloc;
use alloc::string::String;
use core::str;
use serde::{Deserialize, Serialize};
// NB: serde helper for arrays w/ >32 elements
// c.f. https://github.com/serde-rs/serde/pull/1860
use serde_big_array::big_array;
big_array! { BigArray; }
// Size of the buffers used to pass serialized data between Rust <> C.
// The data structure size is bounded by the camkes ipc buffer (2K bytes!)
// and also by it being allocated on the stack of the rpc glue code.
// So we need to balance these against being able to handle all values.
pub const SECURITY_REQUEST_DATA_SIZE: usize = 2048;
pub type SecurityRequestData = [u8; SECURITY_REQUEST_DATA_SIZE];
pub const SECURITY_REPLY_DATA_SIZE: usize = 2048;
pub type SecurityReplyData = [u8; SECURITY_REPLY_DATA_SIZE];
// NB: struct's marked repr(C) are processed by cbindgen to get a .h file
// used in camkes C interfaces.
mod mut_ptr_helper {
use super::*;
use serde::{Deserializer, Serializer};
pub fn serialize<T, S>(ptr: &*mut T, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
(*ptr as usize).serialize(serializer)
}
pub fn deserialize<'de, T, D>(deserializer: D) -> Result<*mut T, D::Error>
where
D: Deserializer<'de>,
{
Ok(usize::deserialize(deserializer)? as *mut T)
}
}
// NB: SecurityRequestInstall is handled specially.
// SecurityRequestUninstall
#[derive(Debug, Serialize, Deserialize)]
pub struct UninstallRequest {
pub bundle_id: String,
}
// SecurityRequestSizeBuffer
#[derive(Debug, Serialize, Deserialize)]
pub struct SizeBufferRequest {
pub bundle_id: String,
}
// SecurityRequestGetManifest
#[derive(Debug, Serialize, Deserialize)]
pub struct GetManifestRequest {
pub bundle_id: String,
}
// SecurityRequestLoadApplication
#[derive(Debug, Serialize, Deserialize)]
pub struct LoadApplicationRequest {
pub bundle_id: String,
// Scatter-list of shared memory pages where application should be loaded.
// TODO(sleffler) scatter list
#[serde(with = "mut_ptr_helper")]
pub app_binary: *mut u8,
}
// SecurityRequestLoadModel
#[derive(Debug, Serialize, Deserialize)]
pub struct LoadModelRequest {
pub bundle_id: String,
pub model_id: String,
// Scatter-list of shared memory pages where model should be loaded.
// TODO(sleffler) scatter list
#[serde(with = "mut_ptr_helper")]
pub model_binary: *mut u8,
}
// SecurityRequestReadKey
#[derive(Debug, Serialize, Deserialize)]
pub struct ReadKeyRequest {
pub bundle_id: String,
pub key: String,
}
// SecurityRequestWriteKey
#[derive(Debug, Serialize, Deserialize)]
pub struct WriteKeyRequest<'a> {
pub bundle_id: String,
pub key: String,
pub value: &'a [u8],
}
// SecurityRequestDeleteKey
#[derive(Debug, Serialize, Deserialize)]
pub struct DeleteKeyRequest {
pub bundle_id: String,
pub key: String,
}
// NB: this is the union of InstallInterface & StorageInterface because
// the camkes-generated interface code uses basic C which does not
// tolerate overlapping member names.
#[repr(C)]
#[derive(Debug, Eq, PartialEq)]
pub enum SecurityRequestError {
SreSuccess = 0,
SreBundleIdInvalid,
SreBundleDataInvalid,
SreBundleNotFound,
SreKeyNotFound,
SrePackageBufferLenInvalid,
SreValueInvalid,
SreKeyInvalid,
// Generic errors, mostly used in unit tests
SreInstallFailed,
SreUninstallFailed,
SreReadFailed,
SreWriteFailed,
SreDeleteFailed,
}
#[repr(C)]
#[derive(Debug, Eq, PartialEq)]
pub enum SecurityRequest {
SrEcho = 0, // Security core replies with request payload
SrInstall, // Install package [pkg_buffer] -> bundle_id
SrUninstall, // Uninstall package [bundle_id]
SrSizeBuffer, // Size application image [bundle_id] -> u32
SrGetManifest, // Return application manifest [bundle_id] -> String
SrLoadApplication, // Load application [bundle_id]
// TODO(sleffler): define <tag>?
SrLoadModel, // Load ML model [bundle_id, <tag>]
SrReadKey, // Read key value [bundle_id, key] -> value
SrWriteKey, // Write key value [bundle_id, key, value]
SrDeleteKey, // Delete key [bundle_id, key]
}
// Interface to underlying facilities; also used to inject fakes for unit tests.
pub trait SecurityCoordinatorInterface {
fn request(
&mut self,
request_id: SecurityRequest,
request_buffer: &[u8],
reply_buffer: &mut [u8],
) -> Result<(), SecurityRequestError>;
}
// Camkes-generated rpc api.
// NB: this requires the SecurityCoordinator component be named "security".
#[allow(dead_code)]
extern "C" {
pub fn security_request(
c_request: SecurityRequest,
c_request_buffer_len: u32,
c_request_buffer: *const u8,
c_reply_buffer: *mut SecurityReplyData,
) -> SecurityRequestError;
}

View File

@@ -0,0 +1,17 @@
[package]
name = "kata-security-component"
version = "0.1.0"
edition = "2018"
[dependencies]
kata-allocator = { path = "../../DebugConsole/kata-allocator" }
kata-logger = { path = "../../DebugConsole/kata-logger" }
kata-panic = { path = "../../DebugConsole/kata-panic" }
kata-security-common = { path = "../kata-security-common" }
kata-security-coordinator = { path = "../kata-security-coordinator" }
log = "0.4"
[lib]
name = "kata_security_coordinator"
path = "src/run.rs"
crate-type = ["staticlib"]

View File

@@ -0,0 +1,55 @@
//! Kata OS Security Coordinator component support.
// Code here binds the camkes component to the rust code.
#![no_std]
use core::slice;
use kata_allocator;
use kata_logger::KataLogger;
extern crate kata_panic;
use kata_security_common::*;
use kata_security_coordinator::KATA_SECURITY;
use log::trace;
#[no_mangle]
pub extern "C" fn pre_init() {
static KATA_LOGGER: KataLogger = KataLogger;
log::set_logger(&KATA_LOGGER).unwrap();
// NB: set to max; the LoggerInterface will filter
log::set_max_level(log::LevelFilter::Trace);
// TODO(sleffler): temp until we integrate with seL4
// TODO(sleffler): should be used rarely
static mut HEAP_MEMORY: [u8; 8 * 1024] = [0; 8 * 1024];
unsafe {
kata_allocator::ALLOCATOR.init(HEAP_MEMORY.as_mut_ptr() as usize, HEAP_MEMORY.len());
trace!(
"setup heap: start_addr {:p} size {}",
HEAP_MEMORY.as_ptr(),
HEAP_MEMORY.len()
);
}
// Complete KATA_SECURITY setup. This is as early as we can do it given that
// it needs the GlobalAllocator.
unsafe {
KATA_SECURITY.init();
}
}
#[no_mangle]
pub extern "C" fn security_request(
c_request: SecurityRequest,
c_request_buffer_len: u32,
c_request_buffer: *const u8,
c_reply_buffer: *mut SecurityReplyData,
) -> SecurityRequestError {
unsafe {
KATA_SECURITY.request(
c_request,
slice::from_raw_parts(c_request_buffer, c_request_buffer_len as usize),
&mut (*c_reply_buffer)[..],
)
}
.map_or_else(|e| e, |_v| SecurityRequestError::SreSuccess)
}

View File

@@ -0,0 +1,9 @@
[package]
name = "kata-security-coordinator"
version = "0.1.0"
edition = "2018"
[dependencies]
kata-security-common = { path = "../kata-security-common" }
log = "0.4"
postcard = "0.7"

View File

@@ -0,0 +1,150 @@
//! Kata OS security coordinator support
#![cfg_attr(not(test), no_std)]
// NB: "error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable"
#![feature(const_fn_trait_bound)]
extern crate alloc;
use alloc::boxed::Box;
use alloc::string::ToString;
use kata_security_common::*;
use log::trace;
use postcard;
#[cfg(not(test))]
pub static mut KATA_SECURITY: KataSecurityCoordinator = KataSecurityCoordinator::empty();
// KataSecurityCoordinator bundles an instance of the SecurityCoordinator that operates
// on KataOS interfaces. There is a two-step dance to setup an instance because we want
// KATA_STORAGE static.
// NB: no locking is done; we assume the caller/user is single-threaded
pub struct KataSecurityCoordinator {
manager: Option<Box<dyn SecurityCoordinatorInterface + Sync>>,
// TODO(sleffler): mailbox ipc state
}
impl KataSecurityCoordinator {
// Constructs a partially-initialized instance; to complete call init().
// This is needed because we need a const fn for static setup.
const fn empty() -> KataSecurityCoordinator {
KataSecurityCoordinator { manager: None }
}
pub fn init(&mut self) {
self.manager = Some(Box::new(KataSecurityCoordinatorInterface));
}
}
impl SecurityCoordinatorInterface for KataSecurityCoordinator {
fn request(
&mut self,
request_id: SecurityRequest,
request_buffer: &[u8],
reply_buffer: &mut [u8],
) -> Result<(), SecurityRequestError> {
self.manager
.as_mut()
.unwrap()
.request(request_id, request_buffer, reply_buffer)
}
}
struct KataSecurityCoordinatorInterface;
// TODO(sleffler): move this to a feature-controlled fake
impl SecurityCoordinatorInterface for KataSecurityCoordinatorInterface {
fn request(
&mut self,
request_id: SecurityRequest,
request_buffer: &[u8],
reply_buffer: &mut [u8],
) -> Result<(), SecurityRequestError> {
fn serialize_failure(e: postcard::Error) -> SecurityRequestError {
trace!("serialize failed: {:?}", e);
SecurityRequestError::SreBundleDataInvalid
}
fn deserialize_failure(e: postcard::Error) -> SecurityRequestError {
trace!("deserialize failed: {:?}", e);
SecurityRequestError::SreBundleDataInvalid
}
// TODO(sleffler): mailbox ipc
match request_id {
SecurityRequest::SrEcho => {
trace!("ECHO {:?}", request_buffer);
reply_buffer[0..request_buffer.len()].copy_from_slice(&request_buffer[..]);
Ok(())
}
SecurityRequest::SrInstall => {
trace!(
"INSTALL addr {:p} len {}",
request_buffer.as_ptr(),
request_buffer.len()
);
let _ = postcard::to_slice(
&(request_buffer.as_ptr() as usize).to_string(),
reply_buffer,
)
.map_err(serialize_failure)?;
Ok(())
}
SecurityRequest::SrUninstall => {
let request = postcard::from_bytes::<UninstallRequest>(&request_buffer[..])
.map_err(deserialize_failure)?;
trace!("UNINSTALL {}", request.bundle_id);
Ok(())
}
SecurityRequest::SrSizeBuffer => {
let request = postcard::from_bytes::<SizeBufferRequest>(&request_buffer[..])
.map_err(deserialize_failure)?;
trace!("SIZE BUFFER bundle_id {}", request.bundle_id);
let _ = postcard::to_slice(
&0u32, // TODO(sleffler): fill-in
reply_buffer
).map_err(serialize_failure)?;
Ok(())
}
SecurityRequest::SrGetManifest => {
let request = postcard::from_bytes::<SizeBufferRequest>(&request_buffer[..])
.map_err(deserialize_failure)?;
trace!("GET MANIFEST bundle_id {}", request.bundle_id);
let _ = postcard::to_slice(
"# Comments like this
[Manifest]
BundleId=com.google.cerebra.hw.HelloWorld
[Binaries]
App=HelloWorldBin
Model=NeuralNetworkName
[Storage]
Required=1
", // TODO(sleffler): fill-in
reply_buffer
).map_err(serialize_failure)?;
Ok(())
}
SecurityRequest::SrLoadApplication => {
let request = postcard::from_bytes::<LoadApplicationRequest>(&request_buffer[..])
.map_err(deserialize_failure)?;
trace!(
"LOAD APPLICATION bundle_id {} addr {:p}",
request.bundle_id,
request.app_binary
);
Ok(())
}
SecurityRequest::SrLoadModel => {
let request = postcard::from_bytes::<LoadModelRequest>(&request_buffer[..])
.map_err(deserialize_failure)?;
trace!(
"LOAD MODEL bundle_id {} model_id {} addr {:p}",
request.bundle_id,
request.model_id,
request.model_binary
);
Ok(())
}
SecurityRequest::SrReadKey => Err(SecurityRequestError::SreReadFailed),
SecurityRequest::SrWriteKey => Err(SecurityRequestError::SreWriteFailed),
SecurityRequest::SrDeleteKey => Err(SecurityRequestError::SreDeleteFailed),
}
}
}

View File

@@ -3,10 +3,10 @@
/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */
#define DEFAULT_BUNDLE_ID_CAPACITY 64
#define RAW_BUNDLE_ID_DATA_SIZE 100
#define DEFAULT_BUNDLE_ID_CAPACITY 64
typedef enum ProcessManagerError {
Success = 0,
BundleIdInvalid,

View File

@@ -0,0 +1,41 @@
#ifndef __SECURITY_COORDINATOR_BINDINGS_H__
#define __SECURITY_COORDINATOR_BINDINGS_H__
/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */
#define SECURITY_REQUEST_DATA_SIZE 2048
#define SECURITY_REPLY_DATA_SIZE 2048
typedef enum SecurityRequest {
SrEcho = 0,
SrInstall,
SrUninstall,
SrLoadApplication,
SrLoadModel,
SrReadKey,
SrWriteKey,
SrDeleteKey,
} SecurityRequest;
typedef enum SecurityRequestError {
SreSuccess = 0,
SreBundleIdInvalid,
SreBundleDataInvalid,
SreBundleNotFound,
SreKeyNotFound,
SrePackageBufferLenInvalid,
SreValueInvalid,
SreKeyInvalid,
SreInstallFailed,
SreUninstallFailed,
SreReadFailed,
SreWriteFailed,
SreDeleteFailed,
} SecurityRequestError;
typedef uint8_t SecurityRequestData[SECURITY_REQUEST_DATA_SIZE];
typedef uint8_t SecurityReplyData[SECURITY_REPLY_DATA_SIZE];
#endif /* __SECURITY_COORDINATOR_BINDINGS_H__ */

View File

@@ -0,0 +1,5 @@
procedure SecurityCoordinatorInterface {
include <SecurityCoordinatorBindings.h>;
SecurityRequestError request(in int request_id, in char request[], out SecurityReplyData reply);
};

View File

@@ -20,6 +20,7 @@ import "components/DebugConsole/DebugConsole.camkes";
import "components/ProcessManager/ProcessManager.camkes";
import "components/MlCoordinator/MlCoordinator.camkes";
import "components/SeL4Debug/SeL4Debug.camkes";
import "components/SecurityCoordinator/SecurityCoordinator.camkes";
import "components/VectorCoreDriver/VectorCoreDriver.camkes";
component OpenTitanUART {
@@ -49,6 +50,7 @@ assembly {
component ProcessManager process_manager;
component MlCoordinator ml_coordinator;
component DebugConsole debug_console;
component SecurityCoordinator security_coordinator;
// OpenTitanUARTDriver
connection seL4HardwareMMIO uart_mem(from uart_driver.mmio_region,
@@ -73,6 +75,15 @@ assembly {
connection seL4RPCCall shell_ml(from debug_console.mlcoord,
to ml_coordinator.mlcoord);
// Connect the SecurityCoordinatorInterface to each component that needs
// access to the Security Core. Note this allocates a 4KB shared memory
// region to each component and copies data between components.
connection seL4RPCOverMultiSharedData multi_security(
from debug_console.security, // NB: for debug/test
from process_manager.security,
from ml_coordinator.security, // NB: for LoadModel but not in design
to security_coordinator.security);
// Connect the DebugConsole to the OpenTitanUARTDriver.
connection seL4SharedData tx_channel(
from debug_console.tx_dataport, to uart_driver.tx_dataport);
@@ -89,6 +100,7 @@ assembly {
connection seL4RPCOverMultiSharedData multi_logger(
from process_manager.logger,
from ml_coordinator.logger,
from security_coordinator.logger,
to debug_console.logger);
// Connect the SeL4Debug interface of each component that needs access.