diff --git a/Cargo.toml b/Cargo.toml index 7830561bc7c..ae7239ff074 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -131,8 +131,8 @@ trng = ["sha-1"] # syscalls default-syscalls = ["counter-client", "crypto-client-attest"] -counter-client = [] -crypto-client-attest = [] +counter-client = ["trussed-core/counter-client"] +crypto-client-attest = ["trussed-core/crypto-client-attest"] clients-1 = [] clients-2 = [] diff --git a/core/Cargo.toml b/core/Cargo.toml index 21dc3f7c222..ca07abeaecb 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -17,3 +17,5 @@ serde.workspace = true serde-indexed = "0.1" [features] +crypto-client-attest = [] +counter-client = [] diff --git a/core/src/client.rs b/core/src/client.rs new file mode 100644 index 00000000000..34461da48a7 --- /dev/null +++ b/core/src/client.rs @@ -0,0 +1,633 @@ +use core::{marker::PhantomData, task::Poll}; + +use crate::{ + api::{reply, request, NotBefore, Reply, ReplyVariant, RequestVariant}, + error::{Error, Result}, + interrupt::InterruptFlag, + types::{ + consent, reboot, Bytes, CertId, KeyId, KeySerialization, Location, Mechanism, MediumData, + Message, PathBuf, SerializedKey, ShortData, Signature, SignatureSerialization, + StorageAttributes, UserAttribute, + }, +}; + +mod mechanisms; +pub use mechanisms::{ + Aes256Cbc, Chacha8Poly1305, Ed255, HmacBlake2s, HmacSha1, HmacSha256, HmacSha512, Sha256, Tdes, + Totp, P256, P384, P521, X255, +}; + +// to be fair, this is a programmer error, +// and could also just panic +#[derive(Copy, Clone, Debug)] +pub enum ClientError { + Full, + Pending, + DataTooLarge, + SerializationFailed, +} + +pub type ClientResult<'c, T, C> = Result, ClientError>; + +/// Lowest level interface, use one of the higher level ones. +pub trait PollClient { + fn request(&mut self, req: Rq) -> ClientResult<'_, Rq::Reply, Self>; + fn poll(&mut self) -> Poll>; + fn interrupt(&self) -> Option<&'static InterruptFlag> { + None + } +} + +#[must_use = "Syscalls must be polled with the `syscall` macro"] +pub struct FutureResult<'c, T, C: ?Sized> +where + C: PollClient, +{ + // TODO: make private + pub client: &'c mut C, + __: PhantomData, +} + +impl<'c, T, C> FutureResult<'c, T, C> +where + T: ReplyVariant, + C: PollClient, +{ + pub fn new(client: &'c mut C) -> Self { + Self { + client, + __: PhantomData, + } + } + pub fn poll(&mut self) -> Poll> { + self.client + .poll() + .map(|result| result.and_then(TryFrom::try_from)) + } +} + +/// Read/Write + Delete certificates +pub trait CertificateClient: PollClient { + fn delete_certificate( + &mut self, + id: CertId, + ) -> ClientResult<'_, reply::DeleteCertificate, Self> { + self.request(request::DeleteCertificate { id }) + } + + fn read_certificate(&mut self, id: CertId) -> ClientResult<'_, reply::ReadCertificate, Self> { + self.request(request::ReadCertificate { id }) + } + + /// Currently, this writes the cert (assumed but not verified to be DER) + /// as-is. It might make sense to add attributes (such as "deletable"). + /// (On the other hand, the attn CA certs are not directly accessible to clients, + /// and generated attn certs can be regenerated). + fn write_certificate( + &mut self, + location: Location, + der: &[u8], + ) -> ClientResult<'_, reply::WriteCertificate, Self> { + let der = Message::from_slice(der).map_err(|_| ClientError::DataTooLarge)?; + self.request(request::WriteCertificate { location, der }) + } +} + +/// Trussed Client interface that Trussed apps can rely on. +pub trait CryptoClient: PollClient { + // call with any of `crate::api::request::*` + // fn request<'c>(&'c mut self, req: impl Into) + // -> core::result::Result, ClientError>; + + fn agree( + &mut self, + mechanism: Mechanism, + private_key: KeyId, + public_key: KeyId, + attributes: StorageAttributes, + ) -> ClientResult<'_, reply::Agree, Self> { + self.request(request::Agree { + mechanism, + private_key, + public_key, + attributes, + }) + } + + #[cfg(feature = "crypto-client-attest")] + fn attest( + &mut self, + signing_mechanism: Mechanism, + private_key: KeyId, + ) -> ClientResult<'_, reply::Attest, Self> { + self.request(request::Attest { + signing_mechanism, + private_key, + }) + } + + fn decrypt<'c>( + &'c mut self, + mechanism: Mechanism, + key: KeyId, + message: &[u8], + associated_data: &[u8], + nonce: &[u8], + tag: &[u8], + ) -> ClientResult<'c, reply::Decrypt, Self> { + let message = Message::from_slice(message).map_err(|_| ClientError::DataTooLarge)?; + let associated_data = + Message::from_slice(associated_data).map_err(|_| ClientError::DataTooLarge)?; + let nonce = ShortData::from_slice(nonce).map_err(|_| ClientError::DataTooLarge)?; + let tag = ShortData::from_slice(tag).map_err(|_| ClientError::DataTooLarge)?; + self.request(request::Decrypt { + mechanism, + key, + message, + associated_data, + nonce, + tag, + }) + } + + fn delete(&mut self, key: KeyId) -> ClientResult<'_, reply::Delete, Self> { + self.request(request::Delete { + key, + // mechanism, + }) + } + + /// Clear private data from the key + /// + /// This will not delete all metadata from storage. + /// Other backends can retain metadata required for `unwrap_key` to work properly + /// and delete this metadata only once `delete` is called. + fn clear(&mut self, key: KeyId) -> ClientResult<'_, reply::Clear, Self> { + self.request(request::Clear { + key, + // mechanism, + }) + } + + /// Skips deleting read-only / manufacture keys (currently, "low ID"). + fn delete_all(&mut self, location: Location) -> ClientResult<'_, reply::DeleteAllKeys, Self> { + self.request(request::DeleteAllKeys { location }) + } + + fn derive_key( + &mut self, + mechanism: Mechanism, + base_key: KeyId, + additional_data: Option, + attributes: StorageAttributes, + ) -> ClientResult<'_, reply::DeriveKey, Self> { + self.request(request::DeriveKey { + mechanism, + base_key, + additional_data, + attributes, + }) + } + + fn deserialize_key<'c>( + &'c mut self, + mechanism: Mechanism, + serialized_key: &[u8], + format: KeySerialization, + attributes: StorageAttributes, + ) -> ClientResult<'c, reply::DeserializeKey, Self> { + let serialized_key = + SerializedKey::from_slice(serialized_key).map_err(|_| ClientError::DataTooLarge)?; + self.request(request::DeserializeKey { + mechanism, + serialized_key, + format, + attributes, + }) + } + + fn encrypt<'c>( + &'c mut self, + mechanism: Mechanism, + key: KeyId, + message: &[u8], + associated_data: &[u8], + nonce: Option, + ) -> ClientResult<'c, reply::Encrypt, Self> { + let message = Message::from_slice(message).map_err(|_| ClientError::DataTooLarge)?; + let associated_data = + ShortData::from_slice(associated_data).map_err(|_| ClientError::DataTooLarge)?; + self.request(request::Encrypt { + mechanism, + key, + message, + associated_data, + nonce, + }) + } + + fn exists( + &mut self, + mechanism: Mechanism, + key: KeyId, + ) -> ClientResult<'_, reply::Exists, Self> { + self.request(request::Exists { key, mechanism }) + } + + fn generate_key( + &mut self, + mechanism: Mechanism, + attributes: StorageAttributes, + ) -> ClientResult<'_, reply::GenerateKey, Self> { + self.request(request::GenerateKey { + mechanism, + attributes, + }) + } + + fn generate_secret_key( + &mut self, + size: usize, + persistence: Location, + ) -> ClientResult<'_, reply::GenerateSecretKey, Self> { + self.request(request::GenerateSecretKey { + size, + attributes: StorageAttributes::new().set_persistence(persistence), + }) + } + + fn hash( + &mut self, + mechanism: Mechanism, + message: Message, + ) -> ClientResult<'_, reply::Hash, Self> { + self.request(request::Hash { mechanism, message }) + } + + fn random_bytes(&mut self, count: usize) -> ClientResult<'_, reply::RandomBytes, Self> { + self.request(request::RandomBytes { count }) + } + + fn serialize_key( + &mut self, + mechanism: Mechanism, + key: KeyId, + format: KeySerialization, + ) -> ClientResult<'_, reply::SerializeKey, Self> { + self.request(request::SerializeKey { + key, + mechanism, + format, + }) + } + + fn sign<'c>( + &'c mut self, + mechanism: Mechanism, + key: KeyId, + data: &[u8], + format: SignatureSerialization, + ) -> ClientResult<'c, reply::Sign, Self> { + self.request(request::Sign { + key, + mechanism, + message: Bytes::from_slice(data).map_err(|_| ClientError::DataTooLarge)?, + format, + }) + } + + fn verify<'c>( + &'c mut self, + mechanism: Mechanism, + key: KeyId, + message: &[u8], + signature: &[u8], + format: SignatureSerialization, + ) -> ClientResult<'c, reply::Verify, Self> { + self.request(request::Verify { + mechanism, + key, + message: Message::from_slice(message).expect("all good"), + signature: Signature::from_slice(signature).expect("all good"), + format, + }) + } + + fn unsafe_inject_key( + &mut self, + mechanism: Mechanism, + raw_key: &[u8], + persistence: Location, + format: KeySerialization, + ) -> ClientResult<'_, reply::UnsafeInjectKey, Self> { + self.request(request::UnsafeInjectKey { + mechanism, + raw_key: SerializedKey::from_slice(raw_key).unwrap(), + attributes: StorageAttributes::new().set_persistence(persistence), + format, + }) + } + + fn unsafe_inject_shared_key( + &mut self, + raw_key: &[u8], + location: Location, + ) -> ClientResult<'_, reply::UnsafeInjectSharedKey, Self> { + self.request(request::UnsafeInjectSharedKey { + raw_key: ShortData::from_slice(raw_key).unwrap(), + location, + }) + } + + fn unwrap_key<'c>( + &'c mut self, + mechanism: Mechanism, + wrapping_key: KeyId, + wrapped_key: Message, + associated_data: &[u8], + nonce: &[u8], + attributes: StorageAttributes, + ) -> ClientResult<'c, reply::UnwrapKey, Self> { + let associated_data = + Message::from_slice(associated_data).map_err(|_| ClientError::DataTooLarge)?; + let nonce = ShortData::from_slice(nonce).map_err(|_| ClientError::DataTooLarge)?; + self.request(request::UnwrapKey { + mechanism, + wrapping_key, + wrapped_key, + associated_data, + nonce, + attributes, + }) + } + + fn wrap_key( + &mut self, + mechanism: Mechanism, + wrapping_key: KeyId, + key: KeyId, + associated_data: &[u8], + nonce: Option, + ) -> ClientResult<'_, reply::WrapKey, Self> { + let associated_data = + Bytes::from_slice(associated_data).map_err(|_| ClientError::DataTooLarge)?; + self.request(request::WrapKey { + mechanism, + wrapping_key, + key, + associated_data, + nonce, + }) + } +} + +/// Create counters, increment existing counters. +pub trait CounterClient: PollClient { + #[cfg(feature = "counter-client")] + fn create_counter( + &mut self, + location: Location, + ) -> ClientResult<'_, reply::CreateCounter, Self> { + self.request(request::CreateCounter { location }) + } + + #[cfg(feature = "counter-client")] + fn increment_counter( + &mut self, + id: crate::types::CounterId, + ) -> ClientResult<'_, reply::IncrementCounter, Self> { + self.request(request::IncrementCounter { id }) + } +} + +/// Read/Write/Delete files, iterate over directories. +pub trait FilesystemClient: PollClient { + fn debug_dump_store(&mut self) -> ClientResult<'_, reply::DebugDumpStore, Self> { + self.request(request::DebugDumpStore {}) + } + + /// Open a directory for iteration with `read_dir_next` + /// + /// For optimization, not_before_filename can be passed to begin the iteration at that file. + fn read_dir_first( + &mut self, + location: Location, + dir: PathBuf, + not_before_filename: Option, + ) -> ClientResult<'_, reply::ReadDirFirst, Self> { + self.request(request::ReadDirFirst { + location, + dir, + not_before: NotBefore::with_filename(not_before_filename), + }) + } + + /// Open a directory for iteration with `read_dir_next` + /// + /// For optimization, not_before_filename can be passed to begin the iteration after the first file that is "alphabetically" before the original file + /// + ///
+ /// The notion used here for "alphabetical" does not correspond to the order of iteration yielded by littlefs. This function should be used with caution. If `not_before_filename` was yielded from a previous use of read_dir, it can lead to entries being repeated. + ///
+ fn read_dir_first_alphabetical( + &mut self, + location: Location, + dir: PathBuf, + not_before_filename: Option, + ) -> ClientResult<'_, reply::ReadDirFirst, Self> { + self.request(request::ReadDirFirst { + location, + dir, + not_before: NotBefore::with_filename_part(not_before_filename), + }) + } + + fn read_dir_next(&mut self) -> ClientResult<'_, reply::ReadDirNext, Self> { + self.request(request::ReadDirNext {}) + } + + fn read_dir_files_first( + &mut self, + location: Location, + dir: PathBuf, + user_attribute: Option, + ) -> ClientResult<'_, reply::ReadDirFilesFirst, Self> { + self.request(request::ReadDirFilesFirst { + dir, + location, + user_attribute, + }) + } + + fn read_dir_files_next(&mut self) -> ClientResult<'_, reply::ReadDirFilesNext, Self> { + self.request(request::ReadDirFilesNext {}) + } + + fn remove_dir( + &mut self, + location: Location, + path: PathBuf, + ) -> ClientResult<'_, reply::RemoveDir, Self> { + self.request(request::RemoveDir { location, path }) + } + + fn remove_dir_all( + &mut self, + location: Location, + path: PathBuf, + ) -> ClientResult<'_, reply::RemoveDirAll, Self> { + self.request(request::RemoveDirAll { location, path }) + } + + fn remove_file( + &mut self, + location: Location, + path: PathBuf, + ) -> ClientResult<'_, reply::RemoveFile, Self> { + self.request(request::RemoveFile { location, path }) + } + + fn read_file( + &mut self, + location: Location, + path: PathBuf, + ) -> ClientResult<'_, reply::ReadFile, Self> { + self.request(request::ReadFile { location, path }) + } + + /// Fetch the Metadata for a file or directory + /// + /// If the file doesn't exists, return None + fn entry_metadata( + &mut self, + location: Location, + path: PathBuf, + ) -> ClientResult<'_, reply::Metadata, Self> { + self.request(request::Metadata { location, path }) + } + + /// Rename a file or directory. + /// + /// If `to` exists, it must be the same type as `from` (i. e., both must be files or both must + /// be directories). If `to` is a directory, it must be empty. + fn rename( + &mut self, + location: Location, + from: PathBuf, + to: PathBuf, + ) -> ClientResult<'_, reply::Rename, Self> { + self.request(request::Rename { location, from, to }) + } + + fn locate_file( + &mut self, + location: Location, + dir: Option, + filename: PathBuf, + ) -> ClientResult<'_, reply::LocateFile, Self> { + self.request(request::LocateFile { + location, + dir, + filename, + }) + } + + fn write_file( + &mut self, + location: Location, + path: PathBuf, + data: Message, + user_attribute: Option, + ) -> ClientResult<'_, reply::WriteFile, Self> { + self.request(request::WriteFile { + location, + path, + data, + user_attribute, + }) + } +} + +/// All the other methods that are fit to expose. +pub trait ManagementClient: PollClient { + fn reboot(&mut self, to: reboot::To) -> ClientResult<'_, reply::Reboot, Self> { + self.request(request::Reboot { to }) + } + + fn uptime(&mut self) -> ClientResult<'_, reply::Uptime, Self> { + self.request(request::Uptime {}) + } +} + +/// User-interfacing functionality. +pub trait UiClient: PollClient { + fn confirm_user_present( + &mut self, + timeout_milliseconds: u32, + ) -> ClientResult<'_, reply::RequestUserConsent, Self> { + self.request(request::RequestUserConsent { + level: consent::Level::Normal, + timeout_milliseconds, + }) + } + + fn wink(&mut self, duration: core::time::Duration) -> ClientResult<'_, reply::Wink, Self> { + self.request(request::Wink { duration }) + } + + fn set_custom_status(&mut self, status: u8) -> ClientResult<'_, reply::SetCustomStatus, Self> { + self.request(request::SetCustomStatus { status }) + } +} + +// would be interesting to use proper futures, and something like +// https://github.com/dflemstr/direct-executor/blob/master/src/lib.rs#L62-L66 + +#[macro_export] +// #[deprecated] +macro_rules! block { + ($future_result:expr) => {{ + // evaluate the expression + let mut future_result = $future_result; + loop { + match future_result.poll() { + core::task::Poll::Ready(result) => { + break result; + } + core::task::Poll::Pending => {} + } + } + }}; +} + +#[macro_export] +macro_rules! syscall { + ($pre_future_result:expr) => {{ + // evaluate the expression + let mut future_result = $pre_future_result.expect("no client error"); + loop { + match future_result.poll() { + core::task::Poll::Ready(result) => { + break result.expect("no errors"); + } + core::task::Poll::Pending => {} + } + } + }}; +} + +#[macro_export] +macro_rules! try_syscall { + ($pre_future_result:expr) => {{ + // evaluate the expression + let mut future_result = $pre_future_result.expect("no client error"); + loop { + match future_result.poll() { + core::task::Poll::Ready(result) => { + break result; + } + core::task::Poll::Pending => {} + } + } + }}; +} diff --git a/core/src/client/mechanisms.rs b/core/src/client/mechanisms.rs new file mode 100644 index 00000000000..2ce16ffb387 --- /dev/null +++ b/core/src/client/mechanisms.rs @@ -0,0 +1,642 @@ +use super::{ClientError, ClientResult, CryptoClient}; +use crate::{ + api::reply, + types::{ + KeyId, KeySerialization, Location, Mechanism, MediumData, Message, ShortData, + SignatureSerialization, StorageAttributes, + }, +}; + +pub trait Aes256Cbc: CryptoClient { + fn decrypt_aes256cbc<'c>( + &'c mut self, + key: KeyId, + message: &[u8], + iv: &[u8], + ) -> ClientResult<'c, reply::Decrypt, Self> { + self.decrypt(Mechanism::Aes256Cbc, key, message, &[], iv, &[]) + } + + fn wrap_key_aes256cbc( + &mut self, + wrapping_key: KeyId, + key: KeyId, + iv: Option<&[u8; 16]>, + ) -> ClientResult<'_, reply::WrapKey, Self> { + self.wrap_key( + Mechanism::Aes256Cbc, + wrapping_key, + key, + &[], + iv.and_then(|iv| ShortData::from_slice(iv).ok()), + ) + } +} + +pub trait Chacha8Poly1305: CryptoClient { + fn decrypt_chacha8poly1305<'c>( + &'c mut self, + key: KeyId, + message: &[u8], + associated_data: &[u8], + nonce: &[u8], + tag: &[u8], + ) -> ClientResult<'c, reply::Decrypt, Self> { + self.decrypt( + Mechanism::Chacha8Poly1305, + key, + message, + associated_data, + nonce, + tag, + ) + } + + fn encrypt_chacha8poly1305<'c>( + &'c mut self, + key: KeyId, + message: &[u8], + associated_data: &[u8], + nonce: Option<&[u8; 12]>, + ) -> ClientResult<'c, reply::Encrypt, Self> { + self.encrypt( + Mechanism::Chacha8Poly1305, + key, + message, + associated_data, + nonce.and_then(|nonce| ShortData::from_slice(nonce).ok()), + ) + } + + fn generate_chacha8poly1305_key( + &mut self, + persistence: Location, + ) -> ClientResult<'_, reply::GenerateKey, Self> { + self.generate_key( + Mechanism::Chacha8Poly1305, + StorageAttributes::new().set_persistence(persistence), + ) + } + + fn unwrap_key_chacha8poly1305<'c>( + &'c mut self, + wrapping_key: KeyId, + wrapped_key: &[u8], + associated_data: &[u8], + location: Location, + ) -> ClientResult<'c, reply::UnwrapKey, Self> { + self.unwrap_key( + Mechanism::Chacha8Poly1305, + wrapping_key, + Message::from_slice(wrapped_key).map_err(|_| ClientError::DataTooLarge)?, + associated_data, + &[], + StorageAttributes::new().set_persistence(location), + ) + } + + fn wrap_key_chacha8poly1305<'c>( + &'c mut self, + wrapping_key: KeyId, + key: KeyId, + associated_data: &[u8], + nonce: Option<&[u8; 12]>, + ) -> ClientResult<'c, reply::WrapKey, Self> { + self.wrap_key( + Mechanism::Chacha8Poly1305, + wrapping_key, + key, + associated_data, + nonce.and_then(|nonce| ShortData::from_slice(nonce).ok()), + ) + } +} + +pub trait HmacBlake2s: CryptoClient { + fn hmacblake2s_derive_key( + &mut self, + base_key: KeyId, + message: &[u8], + persistence: Location, + ) -> ClientResult<'_, reply::DeriveKey, Self> { + self.derive_key( + Mechanism::HmacBlake2s, + base_key, + Some(MediumData::from_slice(message).map_err(|_| ClientError::DataTooLarge)?), + StorageAttributes::new().set_persistence(persistence), + ) + } + + fn sign_hmacblake2s<'c>( + &'c mut self, + key: KeyId, + message: &[u8], + ) -> ClientResult<'c, reply::Sign, Self> { + self.sign( + Mechanism::HmacBlake2s, + key, + message, + SignatureSerialization::Raw, + ) + } +} + +pub trait HmacSha1: CryptoClient { + fn hmacsha1_derive_key( + &mut self, + base_key: KeyId, + message: &[u8], + persistence: Location, + ) -> ClientResult<'_, reply::DeriveKey, Self> { + self.derive_key( + Mechanism::HmacSha1, + base_key, + Some(MediumData::from_slice(message).map_err(|_| ClientError::DataTooLarge)?), + StorageAttributes::new().set_persistence(persistence), + ) + } + + fn sign_hmacsha1<'c>( + &'c mut self, + key: KeyId, + message: &[u8], + ) -> ClientResult<'c, reply::Sign, Self> { + self.sign( + Mechanism::HmacSha1, + key, + message, + SignatureSerialization::Raw, + ) + } +} + +pub trait HmacSha256: CryptoClient { + fn hmacsha256_derive_key( + &mut self, + base_key: KeyId, + message: &[u8], + persistence: Location, + ) -> ClientResult<'_, reply::DeriveKey, Self> { + self.derive_key( + Mechanism::HmacSha256, + base_key, + Some(MediumData::from_slice(message).map_err(|_| ClientError::DataTooLarge)?), + StorageAttributes::new().set_persistence(persistence), + ) + } + + fn sign_hmacsha256<'c>( + &'c mut self, + key: KeyId, + message: &[u8], + ) -> ClientResult<'c, reply::Sign, Self> { + self.sign( + Mechanism::HmacSha256, + key, + message, + SignatureSerialization::Raw, + ) + } +} + +pub trait HmacSha512: CryptoClient { + fn hmacsha512_derive_key( + &mut self, + base_key: KeyId, + message: &[u8], + persistence: Location, + ) -> ClientResult<'_, reply::DeriveKey, Self> { + self.derive_key( + Mechanism::HmacSha512, + base_key, + Some(MediumData::from_slice(message).map_err(|_| ClientError::DataTooLarge)?), + StorageAttributes::new().set_persistence(persistence), + ) + } + + fn sign_hmacsha512<'c>( + &'c mut self, + key: KeyId, + message: &[u8], + ) -> ClientResult<'c, reply::Sign, Self> { + self.sign( + Mechanism::HmacSha512, + key, + message, + SignatureSerialization::Raw, + ) + } +} + +pub trait Ed255: CryptoClient { + fn generate_ed255_private_key( + &mut self, + persistence: Location, + ) -> ClientResult<'_, reply::GenerateKey, Self> { + self.generate_key( + Mechanism::Ed255, + StorageAttributes::new().set_persistence(persistence), + ) + } + + fn derive_ed255_public_key( + &mut self, + private_key: KeyId, + persistence: Location, + ) -> ClientResult<'_, reply::DeriveKey, Self> { + self.derive_key( + Mechanism::Ed255, + private_key, + None, + StorageAttributes::new().set_persistence(persistence), + ) + } + + fn deserialize_ed255_key<'c>( + &'c mut self, + serialized_key: &[u8], + format: KeySerialization, + attributes: StorageAttributes, + ) -> ClientResult<'c, reply::DeserializeKey, Self> { + self.deserialize_key(Mechanism::Ed255, serialized_key, format, attributes) + } + + fn serialize_ed255_key( + &mut self, + key: KeyId, + format: KeySerialization, + ) -> ClientResult<'_, reply::SerializeKey, Self> { + self.serialize_key(Mechanism::Ed255, key, format) + } + + fn sign_ed255<'c>( + &'c mut self, + key: KeyId, + message: &[u8], + ) -> ClientResult<'c, reply::Sign, Self> { + self.sign(Mechanism::Ed255, key, message, SignatureSerialization::Raw) + } + + fn verify_ed255<'c>( + &'c mut self, + key: KeyId, + message: &[u8], + signature: &[u8], + ) -> ClientResult<'c, reply::Verify, Self> { + self.verify( + Mechanism::Ed255, + key, + message, + signature, + SignatureSerialization::Raw, + ) + } +} + +pub trait P256: CryptoClient { + fn generate_p256_private_key( + &mut self, + persistence: Location, + ) -> ClientResult<'_, reply::GenerateKey, Self> { + self.generate_key( + Mechanism::P256, + StorageAttributes::new().set_persistence(persistence), + ) + } + + fn derive_p256_public_key( + &mut self, + private_key: KeyId, + persistence: Location, + ) -> ClientResult<'_, reply::DeriveKey, Self> { + self.derive_key( + Mechanism::P256, + private_key, + None, + StorageAttributes::new().set_persistence(persistence), + ) + } + + fn deserialize_p256_key<'c>( + &'c mut self, + serialized_key: &[u8], + format: KeySerialization, + attributes: StorageAttributes, + ) -> ClientResult<'c, reply::DeserializeKey, Self> { + self.deserialize_key(Mechanism::P256, serialized_key, format, attributes) + } + + fn serialize_p256_key( + &mut self, + key: KeyId, + format: KeySerialization, + ) -> ClientResult<'_, reply::SerializeKey, Self> { + self.serialize_key(Mechanism::P256, key, format) + } + + // generally, don't offer multiple versions of a mechanism, if possible. + // try using the simplest when given the choice. + // hashing is something users can do themselves hopefully :) + // + // on the other hand: if users need sha256, then if the service runs in secure trustzone + // domain, we'll maybe need two copies of the sha2 code + fn sign_p256<'c>( + &'c mut self, + key: KeyId, + message: &[u8], + format: SignatureSerialization, + ) -> ClientResult<'c, reply::Sign, Self> { + self.sign(Mechanism::P256, key, message, format) + } + + fn verify_p256<'c>( + &'c mut self, + key: KeyId, + message: &[u8], + signature: &[u8], + ) -> ClientResult<'c, reply::Verify, Self> { + self.verify( + Mechanism::P256, + key, + message, + signature, + SignatureSerialization::Raw, + ) + } + + fn agree_p256( + &mut self, + private_key: KeyId, + public_key: KeyId, + persistence: Location, + ) -> ClientResult<'_, reply::Agree, Self> { + self.agree( + Mechanism::P256, + private_key, + public_key, + StorageAttributes::new().set_persistence(persistence), + ) + } +} + +pub trait P384: CryptoClient { + fn generate_p384_private_key( + &mut self, + persistence: Location, + ) -> ClientResult<'_, reply::GenerateKey, Self> { + self.generate_key( + Mechanism::P384, + StorageAttributes::new().set_persistence(persistence), + ) + } + + fn derive_p384_public_key( + &mut self, + private_key: KeyId, + persistence: Location, + ) -> ClientResult<'_, reply::DeriveKey, Self> { + self.derive_key( + Mechanism::P384, + private_key, + None, + StorageAttributes::new().set_persistence(persistence), + ) + } + + fn deserialize_p384_key<'c>( + &'c mut self, + serialized_key: &[u8], + format: KeySerialization, + attributes: StorageAttributes, + ) -> ClientResult<'c, reply::DeserializeKey, Self> { + self.deserialize_key(Mechanism::P384, serialized_key, format, attributes) + } + + fn serialize_p384_key( + &mut self, + key: KeyId, + format: KeySerialization, + ) -> ClientResult<'_, reply::SerializeKey, Self> { + self.serialize_key(Mechanism::P384, key, format) + } + + // generally, don't offer multiple versions of a mechanism, if possible. + // try using the simplest when given the choice. + // hashing is something users can do themselves hopefully :) + // + // on the other hand: if users need sha256, then if the service runs in secure trustzone + // domain, we'll maybe need two copies of the sha2 code + fn sign_p384<'c>( + &'c mut self, + key: KeyId, + message: &[u8], + format: SignatureSerialization, + ) -> ClientResult<'c, reply::Sign, Self> { + self.sign(Mechanism::P384, key, message, format) + } + + fn verify_p384<'c>( + &'c mut self, + key: KeyId, + message: &[u8], + signature: &[u8], + ) -> ClientResult<'c, reply::Verify, Self> { + self.verify( + Mechanism::P384, + key, + message, + signature, + SignatureSerialization::Raw, + ) + } + + fn agree_p384( + &mut self, + private_key: KeyId, + public_key: KeyId, + persistence: Location, + ) -> ClientResult<'_, reply::Agree, Self> { + self.agree( + Mechanism::P384, + private_key, + public_key, + StorageAttributes::new().set_persistence(persistence), + ) + } +} + +pub trait P521: CryptoClient { + fn generate_p521_private_key( + &mut self, + persistence: Location, + ) -> ClientResult<'_, reply::GenerateKey, Self> { + self.generate_key( + Mechanism::P521, + StorageAttributes::new().set_persistence(persistence), + ) + } + + fn derive_p521_public_key( + &mut self, + private_key: KeyId, + persistence: Location, + ) -> ClientResult<'_, reply::DeriveKey, Self> { + self.derive_key( + Mechanism::P521, + private_key, + None, + StorageAttributes::new().set_persistence(persistence), + ) + } + + fn deserialize_p521_key<'c>( + &'c mut self, + serialized_key: &[u8], + format: KeySerialization, + attributes: StorageAttributes, + ) -> ClientResult<'c, reply::DeserializeKey, Self> { + self.deserialize_key(Mechanism::P521, serialized_key, format, attributes) + } + + fn serialize_p521_key( + &mut self, + key: KeyId, + format: KeySerialization, + ) -> ClientResult<'_, reply::SerializeKey, Self> { + self.serialize_key(Mechanism::P521, key, format) + } + + // generally, don't offer multiple versions of a mechanism, if possible. + // try using the simplest when given the choice. + // hashing is something users can do themselves hopefully :) + // + // on the other hand: if users need sha256, then if the service runs in secure trustzone + // domain, we'll maybe need two copies of the sha2 code + fn sign_p521<'c>( + &'c mut self, + key: KeyId, + message: &[u8], + format: SignatureSerialization, + ) -> ClientResult<'c, reply::Sign, Self> { + self.sign(Mechanism::P521, key, message, format) + } + + fn verify_p521<'c>( + &'c mut self, + key: KeyId, + message: &[u8], + signature: &[u8], + ) -> ClientResult<'c, reply::Verify, Self> { + self.verify( + Mechanism::P521, + key, + message, + signature, + SignatureSerialization::Raw, + ) + } + + fn agree_p521( + &mut self, + private_key: KeyId, + public_key: KeyId, + persistence: Location, + ) -> ClientResult<'_, reply::Agree, Self> { + self.agree( + Mechanism::P521, + private_key, + public_key, + StorageAttributes::new().set_persistence(persistence), + ) + } +} + +pub trait Sha256: CryptoClient { + fn sha256_derive_key( + &mut self, + shared_key: KeyId, + persistence: Location, + ) -> ClientResult<'_, reply::DeriveKey, Self> { + self.derive_key( + Mechanism::Sha256, + shared_key, + None, + StorageAttributes::new().set_persistence(persistence), + ) + } + + fn hash_sha256<'c>(&'c mut self, message: &[u8]) -> ClientResult<'c, reply::Hash, Self> { + self.hash( + Mechanism::Sha256, + Message::from_slice(message).map_err(|_| ClientError::DataTooLarge)?, + ) + } +} + +pub trait Tdes: CryptoClient { + fn decrypt_tdes<'c>( + &'c mut self, + key: KeyId, + message: &[u8], + ) -> ClientResult<'c, reply::Decrypt, Self> { + self.decrypt(Mechanism::Tdes, key, message, &[], &[], &[]) + } + + fn encrypt_tdes<'c>( + &'c mut self, + key: KeyId, + message: &[u8], + ) -> ClientResult<'c, reply::Encrypt, Self> { + self.encrypt(Mechanism::Tdes, key, message, &[], None) + } +} + +pub trait Totp: CryptoClient { + fn sign_totp(&mut self, key: KeyId, timestamp: u64) -> ClientResult<'_, reply::Sign, Self> { + self.sign( + Mechanism::Totp, + key, + timestamp.to_le_bytes().as_ref(), + SignatureSerialization::Raw, + ) + } +} + +pub trait X255: CryptoClient { + fn generate_x255_secret_key( + &mut self, + persistence: Location, + ) -> ClientResult<'_, reply::GenerateKey, Self> { + self.generate_key( + Mechanism::X255, + StorageAttributes::new().set_persistence(persistence), + ) + } + + fn derive_x255_public_key( + &mut self, + secret_key: KeyId, + persistence: Location, + ) -> ClientResult<'_, reply::DeriveKey, Self> { + self.derive_key( + Mechanism::X255, + secret_key, + None, + StorageAttributes::new().set_persistence(persistence), + ) + } + + fn agree_x255( + &mut self, + private_key: KeyId, + public_key: KeyId, + persistence: Location, + ) -> ClientResult<'_, reply::Agree, Self> { + self.agree( + Mechanism::X255, + private_key, + public_key, + StorageAttributes::new().set_persistence(persistence), + ) + } +} diff --git a/core/src/lib.rs b/core/src/lib.rs index 3c4ba9adef0..d821a8cff3f 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -7,6 +7,7 @@ //! [`trussed`]: https://docs.rs/trussed pub mod api; +pub mod client; pub mod config; pub mod error; pub mod interrupt; diff --git a/src/client.rs b/src/client.rs index 40027e00fc9..70aacfbc4ab 100644 --- a/src/client.rs +++ b/src/client.rs @@ -77,34 +77,23 @@ //! use core::{marker::PhantomData, task::Poll}; -use crate::api::{reply, request, NotBefore, Reply, ReplyVariant, RequestVariant}; +use crate::api::{Reply, RequestVariant}; use crate::backend::{BackendId, CoreOnly, Dispatch}; use crate::error::{Error, Result}; use crate::interrupt::InterruptFlag; use crate::pipe::{TrussedRequester, TRUSSED_INTERCHANGE}; use crate::service::Service; -use crate::types::{ - consent, reboot, Bytes, CertId, CounterId, KeyId, KeySerialization, Location, Mechanism, - MediumData, Message, PathBuf, Platform, SerializedKey, ShortData, Signature, - SignatureSerialization, StorageAttributes, UserAttribute, -}; +use crate::types::{PathBuf, Platform}; pub use crate::platform::Syscall; pub mod mechanisms; pub use mechanisms::*; -// to be fair, this is a programmer error, -// and could also just panic -#[derive(Copy, Clone, Debug)] -pub enum ClientError { - Full, - Pending, - DataTooLarge, - SerializationFailed, -} - -pub type ClientResult<'c, T, C> = Result, ClientError>; +pub use trussed_core::client::{ + CertificateClient, ClientError, ClientResult, CounterClient, CryptoClient, FilesystemClient, + FutureResult, ManagementClient, PollClient, UiClient, +}; /// All-in-one trait bounding on the sub-traits. pub trait Client: @@ -114,42 +103,6 @@ pub trait Client: impl Client for ClientImplementation {} -/// Lowest level interface, use one of the higher level ones. -pub trait PollClient { - fn request(&mut self, req: Rq) -> ClientResult<'_, Rq::Reply, Self>; - fn poll(&mut self) -> Poll>; - fn interrupt(&self) -> Option<&'static InterruptFlag> { - None - } -} - -#[must_use = "Syscalls must be polled with the `syscall` macro"] -pub struct FutureResult<'c, T, C: ?Sized> -where - C: PollClient, -{ - pub(crate) client: &'c mut C, - __: PhantomData, -} - -impl<'c, T, C> FutureResult<'c, T, C> -where - T: ReplyVariant, - C: PollClient, -{ - pub fn new(client: &'c mut C) -> Self { - Self { - client, - __: PhantomData, - } - } - pub fn poll(&mut self) -> Poll> { - self.client - .poll() - .map(|result| result.and_then(TryFrom::try_from)) - } -} - /// The client implementation client applications actually receive. pub struct ClientImplementation { // raw: RawClient>, @@ -262,520 +215,6 @@ impl FilesystemClient for ClientImplementation {} impl ManagementClient for ClientImplementation {} impl UiClient for ClientImplementation {} -/// Read/Write + Delete certificates -pub trait CertificateClient: PollClient { - fn delete_certificate( - &mut self, - id: CertId, - ) -> ClientResult<'_, reply::DeleteCertificate, Self> { - self.request(request::DeleteCertificate { id }) - } - - fn read_certificate(&mut self, id: CertId) -> ClientResult<'_, reply::ReadCertificate, Self> { - self.request(request::ReadCertificate { id }) - } - - /// Currently, this writes the cert (assumed but not verified to be DER) - /// as-is. It might make sense to add attributes (such as "deletable"). - /// (On the other hand, the attn CA certs are not directly accessible to clients, - /// and generated attn certs can be regenerated). - fn write_certificate( - &mut self, - location: Location, - der: &[u8], - ) -> ClientResult<'_, reply::WriteCertificate, Self> { - let der = Message::from_slice(der).map_err(|_| ClientError::DataTooLarge)?; - self.request(request::WriteCertificate { location, der }) - } -} - -/// Trussed Client interface that Trussed apps can rely on. -pub trait CryptoClient: PollClient { - // call with any of `crate::api::request::*` - // fn request<'c>(&'c mut self, req: impl Into) - // -> core::result::Result, ClientError>; - - fn agree( - &mut self, - mechanism: Mechanism, - private_key: KeyId, - public_key: KeyId, - attributes: StorageAttributes, - ) -> ClientResult<'_, reply::Agree, Self> { - self.request(request::Agree { - mechanism, - private_key, - public_key, - attributes, - }) - } - - #[cfg(feature = "crypto-client-attest")] - fn attest( - &mut self, - signing_mechanism: Mechanism, - private_key: KeyId, - ) -> ClientResult<'_, reply::Attest, Self> { - self.request(request::Attest { - signing_mechanism, - private_key, - }) - } - - fn decrypt<'c>( - &'c mut self, - mechanism: Mechanism, - key: KeyId, - message: &[u8], - associated_data: &[u8], - nonce: &[u8], - tag: &[u8], - ) -> ClientResult<'c, reply::Decrypt, Self> { - let message = Message::from_slice(message).map_err(|_| ClientError::DataTooLarge)?; - let associated_data = - Message::from_slice(associated_data).map_err(|_| ClientError::DataTooLarge)?; - let nonce = ShortData::from_slice(nonce).map_err(|_| ClientError::DataTooLarge)?; - let tag = ShortData::from_slice(tag).map_err(|_| ClientError::DataTooLarge)?; - self.request(request::Decrypt { - mechanism, - key, - message, - associated_data, - nonce, - tag, - }) - } - - fn delete(&mut self, key: KeyId) -> ClientResult<'_, reply::Delete, Self> { - self.request(request::Delete { - key, - // mechanism, - }) - } - - /// Clear private data from the key - /// - /// This will not delete all metadata from storage. - /// Other backends can retain metadata required for `unwrap_key` to work properly - /// and delete this metadata only once `delete` is called. - fn clear(&mut self, key: KeyId) -> ClientResult<'_, reply::Clear, Self> { - self.request(request::Clear { - key, - // mechanism, - }) - } - - /// Skips deleting read-only / manufacture keys (currently, "low ID"). - fn delete_all(&mut self, location: Location) -> ClientResult<'_, reply::DeleteAllKeys, Self> { - self.request(request::DeleteAllKeys { location }) - } - - fn derive_key( - &mut self, - mechanism: Mechanism, - base_key: KeyId, - additional_data: Option, - attributes: StorageAttributes, - ) -> ClientResult<'_, reply::DeriveKey, Self> { - self.request(request::DeriveKey { - mechanism, - base_key, - additional_data, - attributes, - }) - } - - fn deserialize_key<'c>( - &'c mut self, - mechanism: Mechanism, - serialized_key: &[u8], - format: KeySerialization, - attributes: StorageAttributes, - ) -> ClientResult<'c, reply::DeserializeKey, Self> { - let serialized_key = - SerializedKey::from_slice(serialized_key).map_err(|_| ClientError::DataTooLarge)?; - self.request(request::DeserializeKey { - mechanism, - serialized_key, - format, - attributes, - }) - } - - fn encrypt<'c>( - &'c mut self, - mechanism: Mechanism, - key: KeyId, - message: &[u8], - associated_data: &[u8], - nonce: Option, - ) -> ClientResult<'c, reply::Encrypt, Self> { - let message = Message::from_slice(message).map_err(|_| ClientError::DataTooLarge)?; - let associated_data = - ShortData::from_slice(associated_data).map_err(|_| ClientError::DataTooLarge)?; - self.request(request::Encrypt { - mechanism, - key, - message, - associated_data, - nonce, - }) - } - - fn exists( - &mut self, - mechanism: Mechanism, - key: KeyId, - ) -> ClientResult<'_, reply::Exists, Self> { - self.request(request::Exists { key, mechanism }) - } - - fn generate_key( - &mut self, - mechanism: Mechanism, - attributes: StorageAttributes, - ) -> ClientResult<'_, reply::GenerateKey, Self> { - self.request(request::GenerateKey { - mechanism, - attributes, - }) - } - - fn generate_secret_key( - &mut self, - size: usize, - persistence: Location, - ) -> ClientResult<'_, reply::GenerateSecretKey, Self> { - self.request(request::GenerateSecretKey { - size, - attributes: StorageAttributes::new().set_persistence(persistence), - }) - } - - fn hash( - &mut self, - mechanism: Mechanism, - message: Message, - ) -> ClientResult<'_, reply::Hash, Self> { - self.request(request::Hash { mechanism, message }) - } - - fn random_bytes(&mut self, count: usize) -> ClientResult<'_, reply::RandomBytes, Self> { - self.request(request::RandomBytes { count }) - } - - fn serialize_key( - &mut self, - mechanism: Mechanism, - key: KeyId, - format: KeySerialization, - ) -> ClientResult<'_, reply::SerializeKey, Self> { - self.request(request::SerializeKey { - key, - mechanism, - format, - }) - } - - fn sign<'c>( - &'c mut self, - mechanism: Mechanism, - key: KeyId, - data: &[u8], - format: SignatureSerialization, - ) -> ClientResult<'c, reply::Sign, Self> { - self.request(request::Sign { - key, - mechanism, - message: Bytes::from_slice(data).map_err(|_| ClientError::DataTooLarge)?, - format, - }) - } - - fn verify<'c>( - &'c mut self, - mechanism: Mechanism, - key: KeyId, - message: &[u8], - signature: &[u8], - format: SignatureSerialization, - ) -> ClientResult<'c, reply::Verify, Self> { - self.request(request::Verify { - mechanism, - key, - message: Message::from_slice(message).expect("all good"), - signature: Signature::from_slice(signature).expect("all good"), - format, - }) - } - - fn unsafe_inject_key( - &mut self, - mechanism: Mechanism, - raw_key: &[u8], - persistence: Location, - format: KeySerialization, - ) -> ClientResult<'_, reply::UnsafeInjectKey, Self> { - self.request(request::UnsafeInjectKey { - mechanism, - raw_key: SerializedKey::from_slice(raw_key).unwrap(), - attributes: StorageAttributes::new().set_persistence(persistence), - format, - }) - } - - fn unsafe_inject_shared_key( - &mut self, - raw_key: &[u8], - location: Location, - ) -> ClientResult<'_, reply::UnsafeInjectSharedKey, Self> { - self.request(request::UnsafeInjectSharedKey { - raw_key: ShortData::from_slice(raw_key).unwrap(), - location, - }) - } - - fn unwrap_key<'c>( - &'c mut self, - mechanism: Mechanism, - wrapping_key: KeyId, - wrapped_key: Message, - associated_data: &[u8], - nonce: &[u8], - attributes: StorageAttributes, - ) -> ClientResult<'c, reply::UnwrapKey, Self> { - let associated_data = - Message::from_slice(associated_data).map_err(|_| ClientError::DataTooLarge)?; - let nonce = ShortData::from_slice(nonce).map_err(|_| ClientError::DataTooLarge)?; - self.request(request::UnwrapKey { - mechanism, - wrapping_key, - wrapped_key, - associated_data, - nonce, - attributes, - }) - } - - fn wrap_key( - &mut self, - mechanism: Mechanism, - wrapping_key: KeyId, - key: KeyId, - associated_data: &[u8], - nonce: Option, - ) -> ClientResult<'_, reply::WrapKey, Self> { - let associated_data = - Bytes::from_slice(associated_data).map_err(|_| ClientError::DataTooLarge)?; - self.request(request::WrapKey { - mechanism, - wrapping_key, - key, - associated_data, - nonce, - }) - } -} - -/// Create counters, increment existing counters. -pub trait CounterClient: PollClient { - #[cfg(feature = "counter-client")] - fn create_counter( - &mut self, - location: Location, - ) -> ClientResult<'_, reply::CreateCounter, Self> { - self.request(request::CreateCounter { location }) - } - - #[cfg(feature = "counter-client")] - fn increment_counter( - &mut self, - id: CounterId, - ) -> ClientResult<'_, reply::IncrementCounter, Self> { - self.request(request::IncrementCounter { id }) - } -} - -/// Read/Write/Delete files, iterate over directories. -pub trait FilesystemClient: PollClient { - fn debug_dump_store(&mut self) -> ClientResult<'_, reply::DebugDumpStore, Self> { - self.request(request::DebugDumpStore {}) - } - - /// Open a directory for iteration with `read_dir_next` - /// - /// For optimization, not_before_filename can be passed to begin the iteration at that file. - fn read_dir_first( - &mut self, - location: Location, - dir: PathBuf, - not_before_filename: Option, - ) -> ClientResult<'_, reply::ReadDirFirst, Self> { - self.request(request::ReadDirFirst { - location, - dir, - not_before: NotBefore::with_filename(not_before_filename), - }) - } - - /// Open a directory for iteration with `read_dir_next` - /// - /// For optimization, not_before_filename can be passed to begin the iteration after the first file that is "alphabetically" before the original file - /// - ///
- /// The notion used here for "alphabetical" does not correspond to the order of iteration yielded by littlefs. This function should be used with caution. If `not_before_filename` was yielded from a previous use of read_dir, it can lead to entries being repeated. - ///
- fn read_dir_first_alphabetical( - &mut self, - location: Location, - dir: PathBuf, - not_before_filename: Option, - ) -> ClientResult<'_, reply::ReadDirFirst, Self> { - self.request(request::ReadDirFirst { - location, - dir, - not_before: NotBefore::with_filename_part(not_before_filename), - }) - } - - fn read_dir_next(&mut self) -> ClientResult<'_, reply::ReadDirNext, Self> { - self.request(request::ReadDirNext {}) - } - - fn read_dir_files_first( - &mut self, - location: Location, - dir: PathBuf, - user_attribute: Option, - ) -> ClientResult<'_, reply::ReadDirFilesFirst, Self> { - self.request(request::ReadDirFilesFirst { - dir, - location, - user_attribute, - }) - } - - fn read_dir_files_next(&mut self) -> ClientResult<'_, reply::ReadDirFilesNext, Self> { - self.request(request::ReadDirFilesNext {}) - } - - fn remove_dir( - &mut self, - location: Location, - path: PathBuf, - ) -> ClientResult<'_, reply::RemoveDir, Self> { - self.request(request::RemoveDir { location, path }) - } - - fn remove_dir_all( - &mut self, - location: Location, - path: PathBuf, - ) -> ClientResult<'_, reply::RemoveDirAll, Self> { - self.request(request::RemoveDirAll { location, path }) - } - - fn remove_file( - &mut self, - location: Location, - path: PathBuf, - ) -> ClientResult<'_, reply::RemoveFile, Self> { - self.request(request::RemoveFile { location, path }) - } - - fn read_file( - &mut self, - location: Location, - path: PathBuf, - ) -> ClientResult<'_, reply::ReadFile, Self> { - self.request(request::ReadFile { location, path }) - } - - /// Fetch the Metadata for a file or directory - /// - /// If the file doesn't exists, return None - fn entry_metadata( - &mut self, - location: Location, - path: PathBuf, - ) -> ClientResult<'_, reply::Metadata, Self> { - self.request(request::Metadata { location, path }) - } - - /// Rename a file or directory. - /// - /// If `to` exists, it must be the same type as `from` (i. e., both must be files or both must - /// be directories). If `to` is a directory, it must be empty. - fn rename( - &mut self, - location: Location, - from: PathBuf, - to: PathBuf, - ) -> ClientResult<'_, reply::Rename, Self> { - self.request(request::Rename { location, from, to }) - } - - fn locate_file( - &mut self, - location: Location, - dir: Option, - filename: PathBuf, - ) -> ClientResult<'_, reply::LocateFile, Self> { - self.request(request::LocateFile { - location, - dir, - filename, - }) - } - - fn write_file( - &mut self, - location: Location, - path: PathBuf, - data: Message, - user_attribute: Option, - ) -> ClientResult<'_, reply::WriteFile, Self> { - self.request(request::WriteFile { - location, - path, - data, - user_attribute, - }) - } -} - -/// All the other methods that are fit to expose. -pub trait ManagementClient: PollClient { - fn reboot(&mut self, to: reboot::To) -> ClientResult<'_, reply::Reboot, Self> { - self.request(request::Reboot { to }) - } - - fn uptime(&mut self) -> ClientResult<'_, reply::Uptime, Self> { - self.request(request::Uptime {}) - } -} - -/// User-interfacing functionality. -pub trait UiClient: PollClient { - fn confirm_user_present( - &mut self, - timeout_milliseconds: u32, - ) -> ClientResult<'_, reply::RequestUserConsent, Self> { - self.request(request::RequestUserConsent { - level: consent::Level::Normal, - timeout_milliseconds, - }) - } - - fn wink(&mut self, duration: core::time::Duration) -> ClientResult<'_, reply::Wink, Self> { - self.request(request::Wink { duration }) - } - - fn set_custom_status(&mut self, status: u8) -> ClientResult<'_, reply::SetCustomStatus, Self> { - self.request(request::SetCustomStatus { status }) - } -} - /// Builder for [`ClientImplementation`][]. /// /// This builder can be used to select the backends used for the client. If no backends are used, @@ -873,55 +312,3 @@ impl PreparedClient { ClientImplementation::new(self.requester, syscall, self.interrupt) } } - -// would be interesting to use proper futures, and something like -// https://github.com/dflemstr/direct-executor/blob/master/src/lib.rs#L62-L66 - -#[macro_export] -// #[deprecated] -macro_rules! block { - ($future_result:expr) => {{ - // evaluate the expression - let mut future_result = $future_result; - loop { - match future_result.poll() { - core::task::Poll::Ready(result) => { - break result; - } - core::task::Poll::Pending => {} - } - } - }}; -} - -#[macro_export] -macro_rules! syscall { - ($pre_future_result:expr) => {{ - // evaluate the expression - let mut future_result = $pre_future_result.expect("no client error"); - loop { - match future_result.poll() { - core::task::Poll::Ready(result) => { - break result.expect("no errors"); - } - core::task::Poll::Pending => {} - } - } - }}; -} - -#[macro_export] -macro_rules! try_syscall { - ($pre_future_result:expr) => {{ - // evaluate the expression - let mut future_result = $pre_future_result.expect("no client error"); - loop { - match future_result.poll() { - core::task::Poll::Ready(result) => { - break result; - } - core::task::Poll::Pending => {} - } - } - }}; -} diff --git a/src/client/mechanisms.rs b/src/client/mechanisms.rs index 8aa1213973e..b3bd605e9a5 100644 --- a/src/client/mechanisms.rs +++ b/src/client/mechanisms.rs @@ -1,684 +1,49 @@ -use super::{ClientError, ClientImplementation, ClientResult, CryptoClient}; -use crate::api::reply; +use super::ClientImplementation; use crate::platform::Syscall; -use crate::types::{ - KeyId, KeySerialization, Location, Mechanism, MediumData, Message, ShortData, - SignatureSerialization, StorageAttributes, + +pub use trussed_core::client::{ + Aes256Cbc, Chacha8Poly1305, Ed255, HmacBlake2s, HmacSha1, HmacSha256, HmacSha512, Sha256, Tdes, + Totp, P256, P384, P521, X255, }; #[cfg(feature = "aes256-cbc")] impl Aes256Cbc for ClientImplementation {} -pub trait Aes256Cbc: CryptoClient { - fn decrypt_aes256cbc<'c>( - &'c mut self, - key: KeyId, - message: &[u8], - iv: &[u8], - ) -> ClientResult<'c, reply::Decrypt, Self> { - self.decrypt(Mechanism::Aes256Cbc, key, message, &[], iv, &[]) - } - - fn wrap_key_aes256cbc( - &mut self, - wrapping_key: KeyId, - key: KeyId, - iv: Option<&[u8; 16]>, - ) -> ClientResult<'_, reply::WrapKey, Self> { - self.wrap_key( - Mechanism::Aes256Cbc, - wrapping_key, - key, - &[], - iv.and_then(|iv| ShortData::from_slice(iv).ok()), - ) - } -} - #[cfg(feature = "chacha8-poly1305")] impl Chacha8Poly1305 for ClientImplementation {} -pub trait Chacha8Poly1305: CryptoClient { - fn decrypt_chacha8poly1305<'c>( - &'c mut self, - key: KeyId, - message: &[u8], - associated_data: &[u8], - nonce: &[u8], - tag: &[u8], - ) -> ClientResult<'c, reply::Decrypt, Self> { - self.decrypt( - Mechanism::Chacha8Poly1305, - key, - message, - associated_data, - nonce, - tag, - ) - } - - fn encrypt_chacha8poly1305<'c>( - &'c mut self, - key: KeyId, - message: &[u8], - associated_data: &[u8], - nonce: Option<&[u8; 12]>, - ) -> ClientResult<'c, reply::Encrypt, Self> { - self.encrypt( - Mechanism::Chacha8Poly1305, - key, - message, - associated_data, - nonce.and_then(|nonce| ShortData::from_slice(nonce).ok()), - ) - } - - fn generate_chacha8poly1305_key( - &mut self, - persistence: Location, - ) -> ClientResult<'_, reply::GenerateKey, Self> { - self.generate_key( - Mechanism::Chacha8Poly1305, - StorageAttributes::new().set_persistence(persistence), - ) - } - - fn unwrap_key_chacha8poly1305<'c>( - &'c mut self, - wrapping_key: KeyId, - wrapped_key: &[u8], - associated_data: &[u8], - location: Location, - ) -> ClientResult<'c, reply::UnwrapKey, Self> { - self.unwrap_key( - Mechanism::Chacha8Poly1305, - wrapping_key, - Message::from_slice(wrapped_key).map_err(|_| ClientError::DataTooLarge)?, - associated_data, - &[], - StorageAttributes::new().set_persistence(location), - ) - } - - fn wrap_key_chacha8poly1305<'c>( - &'c mut self, - wrapping_key: KeyId, - key: KeyId, - associated_data: &[u8], - nonce: Option<&[u8; 12]>, - ) -> ClientResult<'c, reply::WrapKey, Self> { - self.wrap_key( - Mechanism::Chacha8Poly1305, - wrapping_key, - key, - associated_data, - nonce.and_then(|nonce| ShortData::from_slice(nonce).ok()), - ) - } -} - #[cfg(feature = "hmac-blake2s")] impl HmacBlake2s for ClientImplementation {} -pub trait HmacBlake2s: CryptoClient { - fn hmacblake2s_derive_key( - &mut self, - base_key: KeyId, - message: &[u8], - persistence: Location, - ) -> ClientResult<'_, reply::DeriveKey, Self> { - self.derive_key( - Mechanism::HmacBlake2s, - base_key, - Some(MediumData::from_slice(message).map_err(|_| ClientError::DataTooLarge)?), - StorageAttributes::new().set_persistence(persistence), - ) - } - - fn sign_hmacblake2s<'c>( - &'c mut self, - key: KeyId, - message: &[u8], - ) -> ClientResult<'c, reply::Sign, Self> { - self.sign( - Mechanism::HmacBlake2s, - key, - message, - SignatureSerialization::Raw, - ) - } -} - #[cfg(feature = "hmac-sha1")] impl HmacSha1 for ClientImplementation {} -pub trait HmacSha1: CryptoClient { - fn hmacsha1_derive_key( - &mut self, - base_key: KeyId, - message: &[u8], - persistence: Location, - ) -> ClientResult<'_, reply::DeriveKey, Self> { - self.derive_key( - Mechanism::HmacSha1, - base_key, - Some(MediumData::from_slice(message).map_err(|_| ClientError::DataTooLarge)?), - StorageAttributes::new().set_persistence(persistence), - ) - } - - fn sign_hmacsha1<'c>( - &'c mut self, - key: KeyId, - message: &[u8], - ) -> ClientResult<'c, reply::Sign, Self> { - self.sign( - Mechanism::HmacSha1, - key, - message, - SignatureSerialization::Raw, - ) - } -} - #[cfg(feature = "hmac-sha256")] impl HmacSha256 for ClientImplementation {} -pub trait HmacSha256: CryptoClient { - fn hmacsha256_derive_key( - &mut self, - base_key: KeyId, - message: &[u8], - persistence: Location, - ) -> ClientResult<'_, reply::DeriveKey, Self> { - self.derive_key( - Mechanism::HmacSha256, - base_key, - Some(MediumData::from_slice(message).map_err(|_| ClientError::DataTooLarge)?), - StorageAttributes::new().set_persistence(persistence), - ) - } - - fn sign_hmacsha256<'c>( - &'c mut self, - key: KeyId, - message: &[u8], - ) -> ClientResult<'c, reply::Sign, Self> { - self.sign( - Mechanism::HmacSha256, - key, - message, - SignatureSerialization::Raw, - ) - } -} - #[cfg(feature = "hmac-sha512")] impl HmacSha512 for ClientImplementation {} -pub trait HmacSha512: CryptoClient { - fn hmacsha512_derive_key( - &mut self, - base_key: KeyId, - message: &[u8], - persistence: Location, - ) -> ClientResult<'_, reply::DeriveKey, Self> { - self.derive_key( - Mechanism::HmacSha512, - base_key, - Some(MediumData::from_slice(message).map_err(|_| ClientError::DataTooLarge)?), - StorageAttributes::new().set_persistence(persistence), - ) - } - - fn sign_hmacsha512<'c>( - &'c mut self, - key: KeyId, - message: &[u8], - ) -> ClientResult<'c, reply::Sign, Self> { - self.sign( - Mechanism::HmacSha512, - key, - message, - SignatureSerialization::Raw, - ) - } -} - #[cfg(feature = "ed255")] impl Ed255 for ClientImplementation {} -pub trait Ed255: CryptoClient { - fn generate_ed255_private_key( - &mut self, - persistence: Location, - ) -> ClientResult<'_, reply::GenerateKey, Self> { - self.generate_key( - Mechanism::Ed255, - StorageAttributes::new().set_persistence(persistence), - ) - } - - fn derive_ed255_public_key( - &mut self, - private_key: KeyId, - persistence: Location, - ) -> ClientResult<'_, reply::DeriveKey, Self> { - self.derive_key( - Mechanism::Ed255, - private_key, - None, - StorageAttributes::new().set_persistence(persistence), - ) - } - - fn deserialize_ed255_key<'c>( - &'c mut self, - serialized_key: &[u8], - format: KeySerialization, - attributes: StorageAttributes, - ) -> ClientResult<'c, reply::DeserializeKey, Self> { - self.deserialize_key(Mechanism::Ed255, serialized_key, format, attributes) - } - - fn serialize_ed255_key( - &mut self, - key: KeyId, - format: KeySerialization, - ) -> ClientResult<'_, reply::SerializeKey, Self> { - self.serialize_key(Mechanism::Ed255, key, format) - } - - fn sign_ed255<'c>( - &'c mut self, - key: KeyId, - message: &[u8], - ) -> ClientResult<'c, reply::Sign, Self> { - self.sign(Mechanism::Ed255, key, message, SignatureSerialization::Raw) - } - - fn verify_ed255<'c>( - &'c mut self, - key: KeyId, - message: &[u8], - signature: &[u8], - ) -> ClientResult<'c, reply::Verify, Self> { - self.verify( - Mechanism::Ed255, - key, - message, - signature, - SignatureSerialization::Raw, - ) - } -} - #[cfg(feature = "p256")] impl P256 for ClientImplementation {} -pub trait P256: CryptoClient { - fn generate_p256_private_key( - &mut self, - persistence: Location, - ) -> ClientResult<'_, reply::GenerateKey, Self> { - self.generate_key( - Mechanism::P256, - StorageAttributes::new().set_persistence(persistence), - ) - } - - fn derive_p256_public_key( - &mut self, - private_key: KeyId, - persistence: Location, - ) -> ClientResult<'_, reply::DeriveKey, Self> { - self.derive_key( - Mechanism::P256, - private_key, - None, - StorageAttributes::new().set_persistence(persistence), - ) - } - - fn deserialize_p256_key<'c>( - &'c mut self, - serialized_key: &[u8], - format: KeySerialization, - attributes: StorageAttributes, - ) -> ClientResult<'c, reply::DeserializeKey, Self> { - self.deserialize_key(Mechanism::P256, serialized_key, format, attributes) - } - - fn serialize_p256_key( - &mut self, - key: KeyId, - format: KeySerialization, - ) -> ClientResult<'_, reply::SerializeKey, Self> { - self.serialize_key(Mechanism::P256, key, format) - } - - // generally, don't offer multiple versions of a mechanism, if possible. - // try using the simplest when given the choice. - // hashing is something users can do themselves hopefully :) - // - // on the other hand: if users need sha256, then if the service runs in secure trustzone - // domain, we'll maybe need two copies of the sha2 code - fn sign_p256<'c>( - &'c mut self, - key: KeyId, - message: &[u8], - format: SignatureSerialization, - ) -> ClientResult<'c, reply::Sign, Self> { - self.sign(Mechanism::P256, key, message, format) - } - - fn verify_p256<'c>( - &'c mut self, - key: KeyId, - message: &[u8], - signature: &[u8], - ) -> ClientResult<'c, reply::Verify, Self> { - self.verify( - Mechanism::P256, - key, - message, - signature, - SignatureSerialization::Raw, - ) - } - - fn agree_p256( - &mut self, - private_key: KeyId, - public_key: KeyId, - persistence: Location, - ) -> ClientResult<'_, reply::Agree, Self> { - self.agree( - Mechanism::P256, - private_key, - public_key, - StorageAttributes::new().set_persistence(persistence), - ) - } -} - #[cfg(feature = "p384")] impl P384 for ClientImplementation {} -#[cfg(feature = "p384")] -pub trait P384: CryptoClient { - fn generate_p384_private_key( - &mut self, - persistence: Location, - ) -> ClientResult<'_, reply::GenerateKey, Self> { - self.generate_key( - Mechanism::P384, - StorageAttributes::new().set_persistence(persistence), - ) - } - - fn derive_p384_public_key( - &mut self, - private_key: KeyId, - persistence: Location, - ) -> ClientResult<'_, reply::DeriveKey, Self> { - self.derive_key( - Mechanism::P384, - private_key, - None, - StorageAttributes::new().set_persistence(persistence), - ) - } - - fn deserialize_p384_key<'c>( - &'c mut self, - serialized_key: &[u8], - format: KeySerialization, - attributes: StorageAttributes, - ) -> ClientResult<'c, reply::DeserializeKey, Self> { - self.deserialize_key(Mechanism::P384, serialized_key, format, attributes) - } - - fn serialize_p384_key( - &mut self, - key: KeyId, - format: KeySerialization, - ) -> ClientResult<'_, reply::SerializeKey, Self> { - self.serialize_key(Mechanism::P384, key, format) - } - - // generally, don't offer multiple versions of a mechanism, if possible. - // try using the simplest when given the choice. - // hashing is something users can do themselves hopefully :) - // - // on the other hand: if users need sha256, then if the service runs in secure trustzone - // domain, we'll maybe need two copies of the sha2 code - fn sign_p384<'c>( - &'c mut self, - key: KeyId, - message: &[u8], - format: SignatureSerialization, - ) -> ClientResult<'c, reply::Sign, Self> { - self.sign(Mechanism::P384, key, message, format) - } - - fn verify_p384<'c>( - &'c mut self, - key: KeyId, - message: &[u8], - signature: &[u8], - ) -> ClientResult<'c, reply::Verify, Self> { - self.verify( - Mechanism::P384, - key, - message, - signature, - SignatureSerialization::Raw, - ) - } - - fn agree_p384( - &mut self, - private_key: KeyId, - public_key: KeyId, - persistence: Location, - ) -> ClientResult<'_, reply::Agree, Self> { - self.agree( - Mechanism::P384, - private_key, - public_key, - StorageAttributes::new().set_persistence(persistence), - ) - } -} - #[cfg(feature = "p521")] impl P521 for ClientImplementation {} -pub trait P521: CryptoClient { - fn generate_p521_private_key( - &mut self, - persistence: Location, - ) -> ClientResult<'_, reply::GenerateKey, Self> { - self.generate_key( - Mechanism::P521, - StorageAttributes::new().set_persistence(persistence), - ) - } - - fn derive_p521_public_key( - &mut self, - private_key: KeyId, - persistence: Location, - ) -> ClientResult<'_, reply::DeriveKey, Self> { - self.derive_key( - Mechanism::P521, - private_key, - None, - StorageAttributes::new().set_persistence(persistence), - ) - } - - fn deserialize_p521_key<'c>( - &'c mut self, - serialized_key: &[u8], - format: KeySerialization, - attributes: StorageAttributes, - ) -> ClientResult<'c, reply::DeserializeKey, Self> { - self.deserialize_key(Mechanism::P521, serialized_key, format, attributes) - } - - fn serialize_p521_key( - &mut self, - key: KeyId, - format: KeySerialization, - ) -> ClientResult<'_, reply::SerializeKey, Self> { - self.serialize_key(Mechanism::P521, key, format) - } - - // generally, don't offer multiple versions of a mechanism, if possible. - // try using the simplest when given the choice. - // hashing is something users can do themselves hopefully :) - // - // on the other hand: if users need sha256, then if the service runs in secure trustzone - // domain, we'll maybe need two copies of the sha2 code - fn sign_p521<'c>( - &'c mut self, - key: KeyId, - message: &[u8], - format: SignatureSerialization, - ) -> ClientResult<'c, reply::Sign, Self> { - self.sign(Mechanism::P521, key, message, format) - } - - fn verify_p521<'c>( - &'c mut self, - key: KeyId, - message: &[u8], - signature: &[u8], - ) -> ClientResult<'c, reply::Verify, Self> { - self.verify( - Mechanism::P521, - key, - message, - signature, - SignatureSerialization::Raw, - ) - } - - fn agree_p521( - &mut self, - private_key: KeyId, - public_key: KeyId, - persistence: Location, - ) -> ClientResult<'_, reply::Agree, Self> { - self.agree( - Mechanism::P521, - private_key, - public_key, - StorageAttributes::new().set_persistence(persistence), - ) - } -} - #[cfg(feature = "sha256")] impl Sha256 for ClientImplementation {} -pub trait Sha256: CryptoClient { - fn sha256_derive_key( - &mut self, - shared_key: KeyId, - persistence: Location, - ) -> ClientResult<'_, reply::DeriveKey, Self> { - self.derive_key( - Mechanism::Sha256, - shared_key, - None, - StorageAttributes::new().set_persistence(persistence), - ) - } - - fn hash_sha256<'c>(&'c mut self, message: &[u8]) -> ClientResult<'c, reply::Hash, Self> { - self.hash( - Mechanism::Sha256, - Message::from_slice(message).map_err(|_| ClientError::DataTooLarge)?, - ) - } -} - #[cfg(feature = "tdes")] impl Tdes for ClientImplementation {} -pub trait Tdes: CryptoClient { - fn decrypt_tdes<'c>( - &'c mut self, - key: KeyId, - message: &[u8], - ) -> ClientResult<'c, reply::Decrypt, Self> { - self.decrypt(Mechanism::Tdes, key, message, &[], &[], &[]) - } - - fn encrypt_tdes<'c>( - &'c mut self, - key: KeyId, - message: &[u8], - ) -> ClientResult<'c, reply::Encrypt, Self> { - self.encrypt(Mechanism::Tdes, key, message, &[], None) - } -} - #[cfg(feature = "totp")] impl Totp for ClientImplementation {} -pub trait Totp: CryptoClient { - fn sign_totp(&mut self, key: KeyId, timestamp: u64) -> ClientResult<'_, reply::Sign, Self> { - self.sign( - Mechanism::Totp, - key, - timestamp.to_le_bytes().as_ref(), - SignatureSerialization::Raw, - ) - } -} - #[cfg(feature = "x255")] impl X255 for ClientImplementation {} - -pub trait X255: CryptoClient { - fn generate_x255_secret_key( - &mut self, - persistence: Location, - ) -> ClientResult<'_, reply::GenerateKey, Self> { - self.generate_key( - Mechanism::X255, - StorageAttributes::new().set_persistence(persistence), - ) - } - - fn derive_x255_public_key( - &mut self, - secret_key: KeyId, - persistence: Location, - ) -> ClientResult<'_, reply::DeriveKey, Self> { - self.derive_key( - Mechanism::X255, - secret_key, - None, - StorageAttributes::new().set_persistence(persistence), - ) - } - - fn agree_x255( - &mut self, - private_key: KeyId, - public_key: KeyId, - persistence: Location, - ) -> ClientResult<'_, reply::Agree, Self> { - self.agree( - Mechanism::X255, - private_key, - public_key, - StorageAttributes::new().set_persistence(persistence), - ) - } -} diff --git a/src/lib.rs b/src/lib.rs index 2360377421e..8b148c58937 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,7 +46,7 @@ pub use error::Error; pub use platform::Platform; pub use service::Service; -pub use trussed_core::{api, error, interrupt}; +pub use trussed_core::{api, block, error, interrupt, syscall, try_syscall}; pub use cbor_smol::cbor_deserialize; pub use heapless_bytes::Bytes;