From b755de46fb38b38b11579ed11d8961eba1150d75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Wed, 5 Apr 2023 10:54:19 +0200 Subject: [PATCH 1/3] Add GetApplicationKey syscall --- src/backend.rs | 16 ++++++++++++ src/backend/data.rs | 55 ++++++++++++++++++++++++++++++++++++++-- src/extension.rs | 3 +++ src/extension/reply.rs | 23 +++++++++++++++++ src/extension/request.rs | 13 +++++++++- 5 files changed, 107 insertions(+), 3 deletions(-) diff --git a/src/backend.rs b/src/backend.rs index ffbd316..c426206 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -21,6 +21,7 @@ use trussed::{ }; use crate::{ + backend::data::{expand_app_key, get_app_salt}, extension::{reply, AuthExtension, AuthReply, AuthRequest}, BACKEND_DIR, }; @@ -331,6 +332,21 @@ impl ExtensionImpl for AuthBackend { let retries = PinData::load(fs, self.location, request.id)?.retries_left(); Ok(reply::PinRetries { retries }.into()) } + AuthRequest::GetApplicationKey(request) => { + let salt = get_app_salt(fs, rng, self.location)?; + let key = expand_app_key( + &salt, + &self.get_app_key(client_id, trussed_fs, ctx, rng)?, + &request.info, + ); + let key_id = keystore.store_key( + Location::Volatile, + Secrecy::Secret, + Kind::Symmetric(KEY_LEN), + &*key, + )?; + Ok(reply::GetApplicationKey { key: key_id }.into()) + } } } } diff --git a/src/backend/data.rs b/src/backend/data.rs index 100c82d..060fb9b 100644 --- a/src/backend/data.rs +++ b/src/backend/data.rs @@ -12,11 +12,12 @@ use subtle::ConstantTimeEq as _; use trussed::{ platform::{CryptoRng, RngCore}, store::filestore::Filestore, - types::Location, + types::{Location, PathBuf}, + Bytes, }; use super::Error; -use crate::{Pin, PinId, MAX_PIN_LENGTH}; +use crate::{Pin, PinId, BACKEND_DIR, MAX_PIN_LENGTH}; pub(crate) const SIZE: usize = 256; pub(crate) const CHACHA_TAG_LEN: usize = 16; @@ -493,6 +494,56 @@ fn pin_len(pin: &Pin) -> u8 { pin.len() as u8 } +fn app_salt_path() -> PathBuf { + const SALT_PATH: &str = "application_salt"; + + let mut path = PathBuf::new(); + path.push(&PathBuf::from(BACKEND_DIR)); + path.push(&PathBuf::from(SALT_PATH)); + path +} + +pub(crate) fn get_app_salt( + fs: &mut S, + rng: &mut R, + location: Location, +) -> Result { + if !fs.exists(&app_salt_path(), location) { + create_app_salt(fs, rng, location) + } else { + load_app_salt(fs, location) + } +} + +fn create_app_salt( + fs: &mut S, + rng: &mut R, + location: Location, +) -> Result { + let mut salt = Salt::default(); + rng.fill_bytes(&mut *salt); + fs.write(&app_salt_path(), location, &*salt) + .map_err(|_| Error::WriteFailed)?; + Ok(salt) +} + +fn load_app_salt(fs: &mut S, location: Location) -> Result { + fs.read(&app_salt_path(), location) + .map_err(|_| Error::ReadFailed) + .and_then(|b: Bytes| (**b).try_into().map_err(|_| Error::ReadFailed)) +} + +pub fn expand_app_key(salt: &Salt, application_key: &Key, info: &[u8]) -> Key { + #[allow(clippy::expect_used)] + let mut hmac = Hmac::::new_from_slice(&**application_key) + .expect("Slice will always be of acceptable size"); + hmac.update(&**salt); + hmac.update(&(info.len() as u64).to_be_bytes()); + hmac.update(info); + let tmp: [_; HASH_LEN] = hmac.finalize().into_bytes().into(); + tmp.into() +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/extension.rs b/src/extension.rs index eeaf298..f2032cf 100644 --- a/src/extension.rs +++ b/src/extension.rs @@ -28,12 +28,14 @@ impl Extension for AuthExtension { type Reply = AuthReply; } +#[allow(clippy::large_enum_variant)] #[derive(Debug, Deserialize, Serialize)] #[allow(missing_docs)] pub enum AuthRequest { HasPin(request::HasPin), CheckPin(request::CheckPin), GetPinKey(request::GetPinKey), + GetApplicationKey(request::GetApplicationKey), SetPin(request::SetPin), SetPinWithKey(request::SetPinWithKey), ChangePin(request::ChangePin), @@ -48,6 +50,7 @@ pub enum AuthReply { HasPin(reply::HasPin), CheckPin(reply::CheckPin), GetPinKey(reply::GetPinKey), + GetApplicationKey(reply::GetApplicationKey), SetPin(reply::SetPin), SetPinWithKey(reply::SetPinWithKey), ChangePin(reply::ChangePin), diff --git a/src/extension/reply.rs b/src/extension/reply.rs index ed2129f..7fbd32b 100644 --- a/src/extension/reply.rs +++ b/src/extension/reply.rs @@ -79,6 +79,29 @@ impl TryFrom for GetPinKey { } } +#[derive(Debug, Deserialize, Serialize)] +#[must_use] +pub struct GetApplicationKey { + pub key: KeyId, +} + +impl From for AuthReply { + fn from(reply: GetApplicationKey) -> Self { + Self::GetApplicationKey(reply) + } +} + +impl TryFrom for GetApplicationKey { + type Error = Error; + + fn try_from(reply: AuthReply) -> Result { + match reply { + AuthReply::GetApplicationKey(reply) => Ok(reply), + _ => Err(Error::InternalError), + } + } +} + #[derive(Debug, Deserialize, Serialize)] pub struct SetPin; diff --git a/src/extension/request.rs b/src/extension/request.rs index 300c8b2..923eebe 100644 --- a/src/extension/request.rs +++ b/src/extension/request.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 or MIT use serde::{Deserialize, Serialize}; -use trussed::types::KeyId; +use trussed::types::{KeyId, Message}; use super::AuthRequest; use crate::{Pin, PinId}; @@ -42,6 +42,17 @@ impl From for AuthRequest { } } +#[derive(Debug, Deserialize, Serialize)] +pub struct GetApplicationKey { + pub info: Message, +} + +impl From for AuthRequest { + fn from(request: GetApplicationKey) -> Self { + Self::GetApplicationKey(request) + } +} + #[derive(Debug, Deserialize, Serialize)] pub struct SetPin { pub id: PinId, From 00a20e08aa7eff1f5bd10567cebd3991aad7b041 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Wed, 5 Apr 2023 11:15:52 +0200 Subject: [PATCH 2/3] Add get_application_key method to clients --- src/extension.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/extension.rs b/src/extension.rs index f2032cf..059088e 100644 --- a/src/extension.rs +++ b/src/extension.rs @@ -9,7 +9,7 @@ pub mod request; use serde::{Deserialize, Serialize}; use trussed::{ serde_extensions::{Extension, ExtensionClient, ExtensionResult}, - types::KeyId, + types::{KeyId, Message}, }; use crate::{Pin, PinId}; @@ -172,6 +172,14 @@ pub trait AuthClient: ExtensionClient { fn pin_retries>(&mut self, id: I) -> AuthResult<'_, reply::PinRetries, Self> { self.extension(request::PinRetries { id: id.into() }) } + + /// Returns a keyid that is persistent given the "info" parameter + fn get_application_key( + &mut self, + info: Message, + ) -> AuthResult<'_, reply::GetApplicationKey, Self> { + self.extension(request::GetApplicationKey { info }) + } } impl> AuthClient for C {} From ab526e4f28f81c6c0a0d2706ae9b452985b1434a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Wed, 5 Apr 2023 11:16:32 +0200 Subject: [PATCH 3/3] Add tests for get_application_key --- src/backend.rs | 2 +- tests/backend.rs | 29 ++++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/backend.rs b/src/backend.rs index c426206..562ccda 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -336,7 +336,7 @@ impl ExtensionImpl for AuthBackend { let salt = get_app_salt(fs, rng, self.location)?; let key = expand_app_key( &salt, - &self.get_app_key(client_id, trussed_fs, ctx, rng)?, + &self.get_app_key(client_id, global_fs, ctx, rng)?, &request.info, ); let key_id = keystore.store_key( diff --git a/tests/backend.rs b/tests/backend.rs index 8194806..5b7f4f6 100644 --- a/tests/backend.rs +++ b/tests/backend.rs @@ -125,7 +125,7 @@ use trussed::{ client::{ClientImplementation, FilesystemClient, HmacSha256}, service::Service, syscall, try_syscall, - types::{Bytes, Location, PathBuf}, + types::{Bytes, Location, Message, PathBuf}, virt::{self, Ram}, }; use trussed_auth::{AuthClient as _, PinId, MAX_HW_KEY_LEN}; @@ -662,3 +662,30 @@ fn delete_all_pins() { assert!(result.is_err()); }) } + +#[test] +fn application_key() { + run(BACKENDS, |client| { + let info1 = Message::from_slice(b"test1").unwrap(); + let info2 = Message::from_slice(b"test2").unwrap(); + let app_key1 = syscall!(client.get_application_key(info1.clone())).key; + let app_key2 = syscall!(client.get_application_key(info2)).key; + let mac1 = syscall!(client.sign_hmacsha256(app_key1, b"Some data")).signature; + let mac2 = syscall!(client.sign_hmacsha256(app_key2, b"Some data")).signature; + // Different info leads to different keys + assert_ne!(mac1, mac2); + + let app_key1_again = syscall!(client.get_application_key(info1.clone())).key; + let mac1_again = syscall!(client.sign_hmacsha256(app_key1_again, b"Some data")).signature; + // Same info leads to same key + assert_eq!(mac1, mac1_again); + + syscall!(client.delete_all_pins()); + + // After deletion same info leads to different keys + let app_key1_after_delete = syscall!(client.get_application_key(info1)).key; + let mac1_after_delete = + syscall!(client.sign_hmacsha256(app_key1_after_delete, b"Some data")).signature; + assert_ne!(mac1, mac1_after_delete); + }) +}