pub use rand_core::{RngCore, SeedableRng};
use interchange::Responder;
use littlefs2::path::PathBuf;
use chacha20::ChaCha8Rng;
use heapless_bytes::Unsigned;
use crate::api::*;
use crate::Bytes;
use crate::platform::*;
use crate::config::*;
use crate::error::Error;
pub use crate::key;
use crate::mechanisms;
use crate::pipe::TrussedInterchange;
pub use crate::store::{
filestore::{ClientFilestore, Filestore, ReadDirState, ReadDirFilesState},
certstore::{ClientCertstore, Certstore as _},
counterstore::{ClientCounterstore, Counterstore as _},
keystore::{ClientKeystore, Keystore},
};
use crate::types::*;
pub use crate::pipe::ServiceEndpoint;
pub mod attest;
macro_rules! rpc_trait { ($($Name:ident, $name:ident,)*) => { $(
pub trait $Name {
fn $name(_keystore: &mut impl Keystore, _request: &request::$Name)
-> Result<reply::$Name, Error> { Err(Error::MechanismNotAvailable) }
}
)* } }
rpc_trait! {
Agree, agree,
Decrypt, decrypt,
DeriveKey, derive_key,
DeserializeKey, deserialize_key,
Encrypt, encrypt,
Exists, exists,
GenerateKey, generate_key,
Hash, hash,
SerializeKey, serialize_key,
Sign, sign,
UnsafeInjectKey, unsafe_inject_key,
UnwrapKey, unwrap_key,
Verify, verify,
WrapKey, wrap_key,
}
pub struct ServiceResources<P>
where P: Platform
{
pub(crate) platform: P,
read_dir_files_state: Option<ReadDirFilesState>,
read_dir_state: Option<ReadDirState>,
rng_state: Option<ChaCha8Rng>,
}
impl<P: Platform> ServiceResources<P> {
pub fn new(platform: P) -> Self {
Self {
platform,
read_dir_files_state: None,
read_dir_state: None,
rng_state: None,
}
}
}
pub struct Service<P> where P: Platform {
eps: Vec<ServiceEndpoint, {MAX_SERVICE_CLIENTS::USIZE}>,
resources: ServiceResources<P>,
}
unsafe impl<P: Platform> Send for Service<P> {}
impl<P: Platform> ServiceResources<P> {
#[inline(never)]
pub fn reply_to(&mut self, client_id: PathBuf, request: &Request) -> Result<Reply, Error> {
let full_store = self.platform.store();
let mut keystore: ClientKeystore<P> = ClientKeystore::new(
client_id.clone(),
self.rng().map_err(|_| Error::EntropyMalfunction)?,
full_store,
);
let keystore = &mut keystore;
let mut certstore: ClientCertstore<P::S> = ClientCertstore::new(
client_id.clone(),
self.rng().map_err(|_| Error::EntropyMalfunction)?,
full_store,
);
let certstore = &mut certstore;
let mut counterstore: ClientCounterstore<P::S> = ClientCounterstore::new(
client_id.clone(),
self.rng().map_err(|_| Error::EntropyMalfunction)?,
full_store,
);
let counterstore = &mut counterstore;
let mut filestore: ClientFilestore<P::S> = ClientFilestore::new(
client_id,
full_store,
);
let filestore = &mut filestore;
debug_now!("TRUSSED {:?}", request);
match request {
Request::DummyRequest => {
Ok(Reply::DummyReply)
},
Request::Agree(request) => {
match request.mechanism {
Mechanism::P256 => mechanisms::P256::agree(keystore, request),
Mechanism::X255 => mechanisms::X255::agree(keystore, request),
_ => Err(Error::MechanismNotAvailable),
}.map(Reply::Agree)
},
Request::Attest(request) => {
let mut attn_keystore: ClientKeystore<P> = ClientKeystore::new(
PathBuf::from("attn"),
self.rng().map_err(|_| Error::EntropyMalfunction)?,
full_store,
);
attest::try_attest(&mut attn_keystore, certstore, keystore, request).map(Reply::Attest)
}
Request::Decrypt(request) => {
match request.mechanism {
Mechanism::Aes256Cbc => mechanisms::Aes256Cbc::decrypt(keystore, request),
Mechanism::Chacha8Poly1305 => mechanisms::Chacha8Poly1305::decrypt(keystore, request),
Mechanism::Tdes => mechanisms::Tdes::decrypt(keystore, request),
_ => Err(Error::MechanismNotAvailable),
}.map(Reply::Decrypt)
},
Request::DeriveKey(request) => {
match request.mechanism {
Mechanism::HmacBlake2s => mechanisms::HmacBlake2s::derive_key(keystore, request),
Mechanism::HmacSha1 => mechanisms::HmacSha1::derive_key(keystore, request),
Mechanism::HmacSha256 => mechanisms::HmacSha256::derive_key(keystore, request),
Mechanism::HmacSha512 => mechanisms::HmacSha512::derive_key(keystore, request),
Mechanism::Ed255 => mechanisms::Ed255::derive_key(keystore, request),
Mechanism::P256 => mechanisms::P256::derive_key(keystore, request),
Mechanism::Sha256 => mechanisms::Sha256::derive_key(keystore, request),
Mechanism::X255 => mechanisms::X255::derive_key(keystore, request),
_ => Err(Error::MechanismNotAvailable),
}.map(Reply::DeriveKey)
},
Request::DeserializeKey(request) => {
match request.mechanism {
Mechanism::Ed255 => mechanisms::Ed255::deserialize_key(keystore, request),
Mechanism::P256 => mechanisms::P256::deserialize_key(keystore, request),
Mechanism::X255 => mechanisms::X255::deserialize_key(keystore, request),
_ => Err(Error::MechanismNotAvailable),
}.map(Reply::DeserializeKey)
}
Request::Encrypt(request) => {
match request.mechanism {
Mechanism::Aes256Cbc => mechanisms::Aes256Cbc::encrypt(keystore, request),
Mechanism::Chacha8Poly1305 => mechanisms::Chacha8Poly1305::encrypt(keystore, request),
Mechanism::Tdes => mechanisms::Tdes::encrypt(keystore, request),
_ => Err(Error::MechanismNotAvailable),
}.map(Reply::Encrypt)
},
Request::Delete(request) => {
let success = keystore.delete_key(&request.key);
Ok(Reply::Delete(reply::Delete { success } ))
},
Request::DeleteAllKeys(request) => {
let count = keystore.delete_all(request.location)?;
Ok(Reply::DeleteAllKeys(reply::DeleteAllKeys { count } ))
},
Request::Exists(request) => {
match request.mechanism {
Mechanism::Ed255 => mechanisms::Ed255::exists(keystore, request),
Mechanism::P256 => mechanisms::P256::exists(keystore, request),
Mechanism::Totp => mechanisms::Totp::exists(keystore, request),
Mechanism::X255 => mechanisms::X255::exists(keystore, request),
_ => Err(Error::MechanismNotAvailable),
}.map(Reply::Exists)
},
Request::GenerateKey(request) => {
match request.mechanism {
Mechanism::Chacha8Poly1305 => mechanisms::Chacha8Poly1305::generate_key(keystore, request),
Mechanism::Ed255 => mechanisms::Ed255::generate_key(keystore, request),
Mechanism::P256 => mechanisms::P256::generate_key(keystore, request),
Mechanism::X255 => mechanisms::X255::generate_key(keystore, request),
_ => Err(Error::MechanismNotAvailable),
}.map(Reply::GenerateKey)
},
Request::GenerateSecretKey(request) => {
let mut secret_key = MediumData::new();
let size = request.size;
secret_key.resize_default(request.size).map_err(|_| Error::ImplementationError)?;
keystore.rng().fill_bytes(&mut secret_key[..size]);
let key_id = keystore.store_key(
request.attributes.persistence,
key::Secrecy::Secret,
key::Kind::Symmetric(size),
&secret_key[..size],
)?;
Ok(Reply::GenerateSecretKey(reply::GenerateSecretKey { key: key_id }))
},
Request::UnsafeInjectKey(_request) => {
Err(Error::MechanismNotAvailable)
},
Request::UnsafeInjectSharedKey(request) => {
let key_id = keystore.store_key(
request.location,
key::Secrecy::Secret,
key::Kind::Shared(request.raw_key.len()),
&request.raw_key,
)?;
Ok(Reply::UnsafeInjectSharedKey(reply::UnsafeInjectSharedKey { key: key_id } ))
},
Request::Hash(request) => {
match request.mechanism {
Mechanism::Sha256 => mechanisms::Sha256::hash(keystore, request),
_ => Err(Error::MechanismNotAvailable),
}.map(Reply::Hash)
},
Request::LocateFile(request) => {
let path = filestore.locate_file(request.location, request.dir.clone(), request.filename.clone())?;
Ok(Reply::LocateFile(reply::LocateFile { path }) )
}
Request::DebugDumpStore(_request) => {
info_now!(":: PERSISTENT");
recursively_list(self.platform.store().ifs(), PathBuf::from("/"));
info_now!(":: VOLATILE");
recursively_list(self.platform.store().vfs(), PathBuf::from("/"));
fn recursively_list<S: 'static + crate::types::LfsStorage>(fs: &'static crate::store::Fs<S>, path: PathBuf) {
fs.read_dir_and_then(&path, |dir| {
for (i, entry) in dir.enumerate() {
let entry = entry.unwrap();
if i < 2 {
continue;
}
info_now!("{:?} p({:?})", entry.path(), &path);
if entry.file_type().is_dir() {
recursively_list(fs, PathBuf::from(entry.path()));
}
if entry.file_type().is_file() {
let _contents: Vec<u8, 256> = fs.read(entry.path()).unwrap();
}
}
Ok(())
}).unwrap();
}
Ok(Reply::DebugDumpStore(reply::DebugDumpStore {}) )
}
Request::ReadDirFirst(request) => {
let maybe_entry = match filestore.read_dir_first(&request.dir, request.location, request.not_before_filename.as_ref())? {
Some((entry, read_dir_state)) => {
self.read_dir_state = Some(read_dir_state);
Some(entry)
}
None => {
self.read_dir_state = None;
None
}
};
Ok(Reply::ReadDirFirst(reply::ReadDirFirst { entry: maybe_entry } ))
}
Request::ReadDirNext(_request) => {
let read_dir_state = self.read_dir_state.take();
let maybe_entry = match read_dir_state {
None => None,
Some(state) => {
match filestore.read_dir_next(state)? {
Some((entry, read_dir_state)) => {
self.read_dir_state = Some(read_dir_state);
Some(entry)
}
None => {
self.read_dir_state = None;
None
}
}
}
};
Ok(Reply::ReadDirNext(reply::ReadDirNext { entry: maybe_entry } ))
}
Request::ReadDirFilesFirst(request) => {
let maybe_data = match filestore.read_dir_files_first(&request.dir, request.location, request.user_attribute.clone())? {
Some((data, state)) => {
self.read_dir_files_state = Some(state);
data
}
None => {
self.read_dir_files_state = None;
None
}
};
Ok(Reply::ReadDirFilesFirst(reply::ReadDirFilesFirst { data: maybe_data } ))
}
Request::ReadDirFilesNext(_request) => {
let read_dir_files_state = self.read_dir_files_state.take();
let maybe_data = match read_dir_files_state {
None => None,
Some(state) => {
match filestore.read_dir_files_next(state)? {
Some((data, state)) => {
self.read_dir_files_state = Some(state);
data
}
None => {
self.read_dir_files_state = None;
None
}
}
}
};
Ok(Reply::ReadDirFilesNext(reply::ReadDirFilesNext { data: maybe_data } ))
}
Request::RemoveDir(request) => {
filestore.remove_dir(&request.path, request.location)?;
Ok(Reply::RemoveDir(reply::RemoveDir {} ))
}
Request::RemoveDirAll(request) => {
let count = filestore.remove_dir_all(&request.path, request.location)?;
Ok(Reply::RemoveDirAll(reply::RemoveDirAll { count } ))
}
Request::RemoveFile(request) => {
filestore.remove_file(&request.path, request.location)?;
Ok(Reply::RemoveFile(reply::RemoveFile {} ))
}
Request::ReadFile(request) => {
Ok(Reply::ReadFile(reply::ReadFile {
data: filestore.read(&request.path, request.location)?
}))
}
Request::RandomBytes(request) => {
if request.count < 1024 {
let mut bytes = Message::new();
bytes.resize_default(request.count).unwrap();
self.rng()?.fill_bytes(&mut bytes);
Ok(Reply::RandomBytes(reply::RandomBytes { bytes } ))
} else {
Err(Error::MechanismNotAvailable)
}
}
Request::SerializeKey(request) => {
match request.mechanism {
Mechanism::Ed255 => mechanisms::Ed255::serialize_key(keystore, request),
Mechanism::P256 => mechanisms::P256::serialize_key(keystore, request),
Mechanism::X255 => mechanisms::X255::serialize_key(keystore, request),
_ => Err(Error::MechanismNotAvailable),
}.map(Reply::SerializeKey)
}
Request::Sign(request) => {
match request.mechanism {
Mechanism::Ed255 => mechanisms::Ed255::sign(keystore, request),
Mechanism::HmacBlake2s => mechanisms::HmacBlake2s::sign(keystore, request),
Mechanism::HmacSha1 => mechanisms::HmacSha1::sign(keystore, request),
Mechanism::HmacSha256 => mechanisms::HmacSha256::sign(keystore, request),
Mechanism::HmacSha512 => mechanisms::HmacSha512::sign(keystore, request),
Mechanism::P256 => mechanisms::P256::sign(keystore, request),
Mechanism::P256Prehashed => mechanisms::P256Prehashed::sign(keystore, request),
Mechanism::Totp => mechanisms::Totp::sign(keystore, request),
_ => Err(Error::MechanismNotAvailable),
}.map(Reply::Sign)
},
Request::WriteFile(request) => {
filestore.write(&request.path, request.location, &request.data)?;
Ok(Reply::WriteFile(reply::WriteFile {} ))
}
Request::UnwrapKey(request) => {
match request.mechanism {
Mechanism::Chacha8Poly1305 => mechanisms::Chacha8Poly1305::unwrap_key(keystore, request),
_ => Err(Error::MechanismNotAvailable),
}.map(Reply::UnwrapKey)
}
Request::Verify(request) => {
match request.mechanism {
Mechanism::Ed255 => mechanisms::Ed255::verify(keystore, request),
Mechanism::P256 => mechanisms::P256::verify(keystore, request),
_ => Err(Error::MechanismNotAvailable),
}.map(Reply::Verify)
},
Request::WrapKey(request) => {
match request.mechanism {
Mechanism::Aes256Cbc => mechanisms::Aes256Cbc::wrap_key(keystore, request),
Mechanism::Chacha8Poly1305 => mechanisms::Chacha8Poly1305::wrap_key(keystore, request),
_ => Err(Error::MechanismNotAvailable),
}.map(Reply::WrapKey)
},
Request::RequestUserConsent(request) => {
let starttime = self.platform.user_interface().uptime();
let timeout = core::time::Duration::from_millis(request.timeout_milliseconds as u64);
let previous_status = self.platform.user_interface().status();
self.platform.user_interface().set_status(ui::Status::WaitingForUserPresence);
loop {
self.platform.user_interface().refresh();
let nowtime = self.platform.user_interface().uptime();
if (nowtime - starttime) > timeout {
let result = Err(consent::Error::TimedOut);
return Ok(Reply::RequestUserConsent(reply::RequestUserConsent { result } ));
}
let up = self.platform.user_interface().check_user_presence();
match request.level {
consent::Level::Normal => {
if up == consent::Level::Normal ||
up == consent::Level::Strong {
break;
}
},
consent::Level::Strong => {
if up == consent::Level::Strong {
break;
}
}
_ => {
break;
}
}
}
self.platform.user_interface().set_status(previous_status);
let result = Ok(());
Ok(Reply::RequestUserConsent(reply::RequestUserConsent { result } ))
}
Request::Reboot(request) => {
self.platform.user_interface().reboot(request.to);
}
Request::Uptime(_request) => {
Ok(Reply::Uptime(reply::Uptime { uptime: self.platform.user_interface().uptime() }))
}
Request::Wink(request) => {
self.platform.user_interface().wink(request.duration);
Ok(Reply::Wink(reply::Wink {}))
}
Request::CreateCounter(request) => {
counterstore.create(request.location)
.map(|id| Reply::CreateCounter(reply::CreateCounter { id } ))
}
Request::IncrementCounter(request) => {
counterstore.increment(request.id)
.map(|counter| Reply::IncrementCounter(reply::IncrementCounter { counter } ))
}
Request::DeleteCertificate(request) => {
certstore.delete_certificate(request.id)
.map(|_| Reply::DeleteCertificate(reply::DeleteCertificate {} ))
}
Request::ReadCertificate(request) => {
certstore.read_certificate(request.id)
.map(|der| Reply::ReadCertificate(reply::ReadCertificate { der } ))
}
Request::WriteCertificate(request) => {
certstore.write_certificate(request.location, &request.der)
.map(|id| Reply::WriteCertificate(reply::WriteCertificate { id } ))
}
}
}
pub fn rng(&mut self) -> Result<ChaCha8Rng, Error> {
let mut rng = match self.rng_state.take() {
Some(rng) => rng,
None => {
let mut filestore: ClientFilestore<P::S> = ClientFilestore::new(
PathBuf::from("trussed"),
self.platform.store(),
);
let path = PathBuf::from("rng-state.bin");
let mixin_seed = if ! filestore.exists(&path, Location::Internal) {
[0u8; 32]
} else {
let mixin_bytes: Bytes<32> = filestore.read(&path, Location::Internal)?;
let mut mixin_seed = [0u8; 32];
mixin_seed.clone_from_slice(&mixin_bytes);
mixin_seed
};
let mut entropy = [0u8; 32];
self.platform.rng().try_fill_bytes(&mut entropy)
.map_err(|_| Error::EntropyMalfunction)?;
let mut our_seed = [0u8; 32];
for i in 0..32 {
our_seed[i] = mixin_seed[i] ^ entropy[i];
}
let mut rng = chacha20::ChaCha8Rng::from_seed(our_seed);
let mut seed_to_store = [0u8; 32];
rng.fill_bytes(&mut seed_to_store);
filestore.write(&path, Location::Internal, seed_to_store.as_ref()).unwrap();
Ok(rng)
}?
};
let split_rng = ChaCha8Rng::from_rng(&mut rng).map_err(|_| Error::EntropyMalfunction);
self.rng_state = Some(rng);
split_rng
}
pub fn fill_random_bytes(&mut self, bytes: &mut[u8]) -> Result<(), Error> {
self.rng()?.fill_bytes(bytes);
Ok(())
}
}
impl<P: Platform> Service<P> {
pub fn new(platform: P) -> Self {
let resources = ServiceResources::new(platform);
Self { eps: Vec::new(), resources }
}
#[allow(clippy::result_unit_err)]
pub fn try_new_client<S: crate::platform::Syscall>(&mut self, client_id: &str, syscall: S)
-> Result<crate::client::ClientImplementation<S>, ()>
{
use interchange::Interchange;
let (requester, responder) = TrussedInterchange::claim().ok_or(())?;
let client_id = ClientId::from(client_id.as_bytes());
self.add_endpoint(responder, client_id).map_err(|_service_endpoint| ())?;
Ok(crate::client::ClientImplementation::new(requester, syscall))
}
#[allow(clippy::result_unit_err)]
pub fn try_as_new_client(&mut self, client_id: &str)
-> Result<crate::client::ClientImplementation<&mut Service<P>>, ()>
{
use interchange::Interchange;
let (requester, responder) = TrussedInterchange::claim().ok_or(())?;
let client_id = ClientId::from(client_id.as_bytes());
self.add_endpoint(responder, client_id).map_err(|_service_endpoint| ())?;
Ok(crate::client::ClientImplementation::new(requester, self))
}
pub fn add_endpoint(&mut self, interchange: Responder<TrussedInterchange>, client_id: ClientId) -> Result<(), ServiceEndpoint> {
if client_id == PathBuf::from("trussed") {
panic!("trussed is a reserved client ID");
}
self.eps.push(ServiceEndpoint { interchange, client_id })
}
pub fn set_seed_if_uninitialized(&mut self, seed: &[u8; 32]) {
let mut filestore: ClientFilestore<P::S> = ClientFilestore::new(
PathBuf::from("trussed"),
self.resources.platform.store(),
);
let filestore = &mut filestore;
let path = PathBuf::from("rng-state.bin");
if !filestore.exists(&path, Location::Internal) {
filestore.write(&path, Location::Internal, seed.as_ref()).unwrap();
}
}
pub fn update_ui(&mut self) {
self.resources.platform.user_interface().refresh();
}
pub fn process(&mut self) {
let eps = &mut self.eps;
let resources = &mut self.resources;
for ep in eps.iter_mut() {
if let Some(request) = ep.interchange.take_request() {
resources.platform.user_interface().set_status(ui::Status::Processing);
let reply_result = resources.reply_to(ep.client_id.clone(), &request);
resources.platform.user_interface().set_status(ui::Status::Idle);
ep.interchange.respond(&reply_result).ok();
}
}
debug_now!("I/E/V : {}/{}/{} >",
self.resources.platform.store().ifs().available_blocks().unwrap(),
self.resources.platform.store().efs().available_blocks().unwrap(),
self.resources.platform.store().vfs().available_blocks().unwrap(),
);
}
}
impl<P> crate::client::Syscall for &mut Service<P>
where P: Platform
{
fn syscall(&mut self) {
self.process();
}
}