diff --git a/apps/system/CMakeLists.txt b/apps/system/CMakeLists.txt index 5d74eae..1e83818 100644 --- a/apps/system/CMakeLists.txt +++ b/apps/system/CMakeLists.txt @@ -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 diff --git a/apps/system/components/DebugConsole/DebugConsole.camkes b/apps/system/components/DebugConsole/DebugConsole.camkes index fb569a2..e1d3dfe 100644 --- a/apps/system/components/DebugConsole/DebugConsole.camkes +++ b/apps/system/components/DebugConsole/DebugConsole.camkes @@ -2,6 +2,7 @@ import ; import ; import ; import ; +import ; import ; 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; } diff --git a/apps/system/components/DebugConsole/kata-shell/Cargo.toml b/apps/system/components/DebugConsole/kata-shell/Cargo.toml index 2dd04df..c620646 100644 --- a/apps/system/components/DebugConsole/kata-shell/Cargo.toml +++ b/apps/system/components/DebugConsole/kata-shell/Cargo.toml @@ -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"] } diff --git a/apps/system/components/DebugConsole/kata-shell/src/lib.rs b/apps/system/components/DebugConsole/kata-shell/src/lib.rs index ea71a3c..f1535c2 100644 --- a/apps/system/components/DebugConsole/kata-shell/src/lib.rs +++ b/apps/system/components/DebugConsole/kata-shell/src/lib.rs @@ -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, diff --git a/apps/system/components/MlCoordinator/MlCoordinator.camkes b/apps/system/components/MlCoordinator/MlCoordinator.camkes index 18e0dcf..dea151b 100644 --- a/apps/system/components/MlCoordinator/MlCoordinator.camkes +++ b/apps/system/components/MlCoordinator/MlCoordinator.camkes @@ -1,9 +1,11 @@ import ; import ; +import ; component MlCoordinator { provides MlCoordinatorInterface mlcoord; uses LoggerInterface logger; + uses SecurityCoordinatorInterface security; uses VectorCoreInterface vctop; } diff --git a/apps/system/components/ProcessManager/ProcessManager.camkes b/apps/system/components/ProcessManager/ProcessManager.camkes index 74cd831..952e3a6 100644 --- a/apps/system/components/ProcessManager/ProcessManager.camkes +++ b/apps/system/components/ProcessManager/ProcessManager.camkes @@ -4,6 +4,7 @@ import ; import ; import ; import ; +import ; component ProcessManager { provides PackageManagementInterface pkg_mgmt; @@ -11,4 +12,5 @@ component ProcessManager { uses LoggerInterface logger; uses SeL4DebugInterface sel4debug; + uses SecurityCoordinatorInterface security; } diff --git a/apps/system/components/ProcessManager/kata-proc-common/Cargo.toml b/apps/system/components/ProcessManager/kata-proc-common/Cargo.toml index 60c01c6..26c5a0d 100644 --- a/apps/system/components/ProcessManager/kata-proc-common/Cargo.toml +++ b/apps/system/components/ProcessManager/kata-proc-common/Cargo.toml @@ -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"] } diff --git a/apps/system/components/ProcessManager/kata-proc-common/src/lib.rs b/apps/system/components/ProcessManager/kata-proc-common/src/lib.rs index bf6998a..0fc1dd5 100644 --- a/apps/system/components/ProcessManager/kata-proc-common/src/lib.rs +++ b/apps/system/components/ProcessManager/kata-proc-common/src/lib.rs @@ -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; -// 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(ptr: &*const T, serializer: S) -> Result + 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; + ) -> Result; 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>; diff --git a/apps/system/components/ProcessManager/kata-proc-manager/Cargo.toml b/apps/system/components/ProcessManager/kata-proc-manager/Cargo.toml index fc123b6..6e25ec8 100644 --- a/apps/system/components/ProcessManager/kata-proc-manager/Cargo.toml +++ b/apps/system/components/ProcessManager/kata-proc-manager/Cargo.toml @@ -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" diff --git a/apps/system/components/ProcessManager/kata-proc-manager/src/lib.rs b/apps/system/components/ProcessManager/kata-proc-manager/src/lib.rs index 96c4c45..5801f39 100644 --- a/apps/system/components/ProcessManager/kata-proc-manager/src/lib.rs +++ b/apps/system/components/ProcessManager/kata-proc-manager/src/lib.rs @@ -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 { + pkg_buffer_size: u32, + ) -> Result { // 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::(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) diff --git a/apps/system/components/ProcessManager/kata-proc-manager/src/proc_manager/mod.rs b/apps/system/components/ProcessManager/kata-proc-manager/src/proc_manager/mod.rs index 331b369..567dbad 100644 --- a/apps/system/components/ProcessManager/kata-proc-manager/src/proc_manager/mod.rs +++ b/apps/system/components/ProcessManager/kata-proc-manager/src/proc_manager/mod.rs @@ -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 } diff --git a/apps/system/components/SecurityCoordinator/Cargo.toml b/apps/system/components/SecurityCoordinator/Cargo.toml new file mode 100644 index 0000000..03d1b9f --- /dev/null +++ b/apps/system/components/SecurityCoordinator/Cargo.toml @@ -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 diff --git a/apps/system/components/SecurityCoordinator/SecurityCoordinator.camkes b/apps/system/components/SecurityCoordinator/SecurityCoordinator.camkes new file mode 100644 index 0000000..d5bd882 --- /dev/null +++ b/apps/system/components/SecurityCoordinator/SecurityCoordinator.camkes @@ -0,0 +1,10 @@ +// Kata OS SecurityCoordinator services. + +import ; +import ; + +component SecurityCoordinator { + provides SecurityCoordinatorInterface security; + + uses LoggerInterface logger; +} diff --git a/apps/system/components/SecurityCoordinator/cbindgen.toml b/apps/system/components/SecurityCoordinator/cbindgen.toml new file mode 100644 index 0000000..e7ab415 --- /dev/null +++ b/apps/system/components/SecurityCoordinator/cbindgen.toml @@ -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"] diff --git a/apps/system/components/SecurityCoordinator/kata-security-common/Cargo.toml b/apps/system/components/SecurityCoordinator/kata-security-common/Cargo.toml new file mode 100644 index 0000000..5e9ea49 --- /dev/null +++ b/apps/system/components/SecurityCoordinator/kata-security-common/Cargo.toml @@ -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" diff --git a/apps/system/components/SecurityCoordinator/kata-security-common/src/lib.rs b/apps/system/components/SecurityCoordinator/kata-security-common/src/lib.rs new file mode 100644 index 0000000..72c0ca5 --- /dev/null +++ b/apps/system/components/SecurityCoordinator/kata-security-common/src/lib.rs @@ -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(ptr: &*mut T, serializer: S) -> Result + 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 ? + SrLoadModel, // Load ML model [bundle_id, ] + + 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; +} diff --git a/apps/system/components/SecurityCoordinator/kata-security-component/Cargo.toml b/apps/system/components/SecurityCoordinator/kata-security-component/Cargo.toml new file mode 100644 index 0000000..25c478e --- /dev/null +++ b/apps/system/components/SecurityCoordinator/kata-security-component/Cargo.toml @@ -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"] diff --git a/apps/system/components/SecurityCoordinator/kata-security-component/src/run.rs b/apps/system/components/SecurityCoordinator/kata-security-component/src/run.rs new file mode 100644 index 0000000..bbc93d9 --- /dev/null +++ b/apps/system/components/SecurityCoordinator/kata-security-component/src/run.rs @@ -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) +} diff --git a/apps/system/components/SecurityCoordinator/kata-security-coordinator/Cargo.toml b/apps/system/components/SecurityCoordinator/kata-security-coordinator/Cargo.toml new file mode 100644 index 0000000..a196f43 --- /dev/null +++ b/apps/system/components/SecurityCoordinator/kata-security-coordinator/Cargo.toml @@ -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" diff --git a/apps/system/components/SecurityCoordinator/kata-security-coordinator/src/lib.rs b/apps/system/components/SecurityCoordinator/kata-security-coordinator/src/lib.rs new file mode 100644 index 0000000..47575c3 --- /dev/null +++ b/apps/system/components/SecurityCoordinator/kata-security-coordinator/src/lib.rs @@ -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>, + // 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::(&request_buffer[..]) + .map_err(deserialize_failure)?; + trace!("UNINSTALL {}", request.bundle_id); + Ok(()) + } + SecurityRequest::SrSizeBuffer => { + let request = postcard::from_bytes::(&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::(&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::(&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::(&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), + } + } +} diff --git a/apps/system/interfaces/ProcessManagerBindings.h b/apps/system/interfaces/ProcessManagerBindings.h index ab8dbdf..d079c15 100644 --- a/apps/system/interfaces/ProcessManagerBindings.h +++ b/apps/system/interfaces/ProcessManagerBindings.h @@ -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, diff --git a/apps/system/interfaces/SecurityCoordinatorBindings.h b/apps/system/interfaces/SecurityCoordinatorBindings.h new file mode 100644 index 0000000..a6d75bf --- /dev/null +++ b/apps/system/interfaces/SecurityCoordinatorBindings.h @@ -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__ */ diff --git a/apps/system/interfaces/SecurityCoordinatorInterface.camkes b/apps/system/interfaces/SecurityCoordinatorInterface.camkes new file mode 100644 index 0000000..6e63ca6 --- /dev/null +++ b/apps/system/interfaces/SecurityCoordinatorInterface.camkes @@ -0,0 +1,5 @@ +procedure SecurityCoordinatorInterface { + include ; + + SecurityRequestError request(in int request_id, in char request[], out SecurityReplyData reply); +}; diff --git a/apps/system/system.camkes b/apps/system/system.camkes index 689dcb3..fee64a1 100644 --- a/apps/system/system.camkes +++ b/apps/system/system.camkes @@ -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.