From ba9e0f4b6c0f3adaae00a1ca44920ed5851bb693 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Mon, 20 Feb 2023 15:47:22 +0100 Subject: [PATCH] Implement fetching the pin keys and add tests --- src/backend.rs | 22 ++++++-- src/extension.rs | 13 +++++ src/extension/reply.rs | 15 +++++- tests/backend.rs | 117 +++++++++++++++++++++++++++++++++++++++-- 4 files changed, 156 insertions(+), 11 deletions(-) diff --git a/src/backend.rs b/src/backend.rs index 7de2cdb..8660eb5 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -11,9 +11,10 @@ use sha2::Sha256; use trussed::{ backend::Backend, error::Result, + key::{Kind, Secrecy}, platform::Platform, serde_extensions::ExtensionImpl, - service::ServiceResources, + service::{Keystore, ServiceResources}, store::filestore::Filestore, types::{CoreContext, Location, PathBuf}, Bytes, @@ -23,7 +24,7 @@ use crate::{ extension::{reply, AuthExtension, AuthReply, AuthRequest}, PIN_PATH, SALT_PATH, }; -use data::{Key, PinData, Salt, SALT_LEN}; +use data::{Key, PinData, Salt, KEY_LEN, SALT_LEN}; /// max accepted length for the hardware initial key material pub const MAX_HW_KEY_LEN: usize = 64; @@ -180,6 +181,7 @@ impl ExtensionImpl for AuthBackend { let trussed_fs = &mut resources.trussed_filestore(); let rng = &mut resources.rng()?; let client_id = core_ctx.path.clone(); + let keystore = &mut resources.keystore(core_ctx)?; match request { AuthRequest::HasPin(request) => { let has_pin = fs.exists(&request.id.path(), self.location); @@ -205,10 +207,20 @@ impl ExtensionImpl for AuthBackend { self.location, |data| data.get_pin_key(&request.pin, &application_key), )??; - if verification.is_none() { - return Ok(reply::GetPinKey { result: None }.into()); + if let Some(k) = verification { + let key_id = keystore.store_key( + Location::Volatile, + Secrecy::Secret, + Kind::Symmetric(KEY_LEN), + &*k, + )?; + Ok(reply::GetPinKey { + result: Some(key_id), + } + .into()) + } else { + Ok(reply::GetPinKey { result: None }.into()) } - todo!() } AuthRequest::SetPin(request) => { let maybe_app_key = if request.derive_key { diff --git a/src/extension.rs b/src/extension.rs index a74d9ec..45a2abe 100644 --- a/src/extension.rs +++ b/src/extension.rs @@ -80,6 +80,19 @@ pub trait AuthClient: ExtensionClient { self.extension(request::CheckPin { id: id.into(), pin }) } + /// Returns a keyid if the provided PIN is correct and not blocked. + /// + /// The pin must have been created with `derive_key` set to true. + /// If the PIN is not correct and a retry counter is configured, the counter is decremented. + /// Once it reaches zero, authentication attempts for that PIN fail. If the PIN with the given + /// ID is not set, an error is returned. + fn get_pin_key(&mut self, id: I, pin: Pin) -> AuthResult<'_, reply::GetPinKey, Self> + where + I: Into, + { + self.extension(request::GetPinKey { id: id.into(), pin }) + } + /// Sets the given PIN and resets its retry counter. /// /// If the retry counter is `None`, the number of retries is not limited and the PIN will never diff --git a/src/extension/reply.rs b/src/extension/reply.rs index d1d8e09..3d992d0 100644 --- a/src/extension/reply.rs +++ b/src/extension/reply.rs @@ -44,6 +44,17 @@ impl From for AuthReply { } } +impl TryFrom for CheckPin { + type Error = Error; + + fn try_from(reply: AuthReply) -> Result { + match reply { + AuthReply::CheckPin(reply) => Ok(reply), + _ => Err(Error::InternalError), + } + } +} + #[derive(Debug, Deserialize, Serialize)] #[must_use] pub struct GetPinKey { @@ -57,12 +68,12 @@ impl From for AuthReply { } } -impl TryFrom for CheckPin { +impl TryFrom for GetPinKey { type Error = Error; fn try_from(reply: AuthReply) -> Result { match reply { - AuthReply::CheckPin(reply) => Ok(reply), + AuthReply::GetPinKey(reply) => Ok(reply), _ => Err(Error::InternalError), } } diff --git a/tests/backend.rs b/tests/backend.rs index 5f5c76d..4513580 100644 --- a/tests/backend.rs +++ b/tests/backend.rs @@ -9,9 +9,9 @@ mod dispatch { platform::Platform, serde_extensions::{ExtensionDispatch, ExtensionId, ExtensionImpl as _}, service::ServiceResources, - types::{Context, Location}, + types::{Bytes, Context, Location}, }; - use trussed_auth::{AuthBackend, AuthContext, AuthExtension}; + use trussed_auth::{AuthBackend, AuthContext, AuthExtension, MAX_HW_KEY_LEN}; pub const BACKENDS: &[BackendId] = &[BackendId::Custom(Backend::Auth), BackendId::Core]; @@ -58,6 +58,12 @@ mod dispatch { auth: AuthBackend::new(Location::Internal), } } + + pub fn with_hw_key(hw_key: Bytes) -> Self { + Self { + auth: AuthBackend::with_hw_key(Location::Internal, hw_key), + } + } } impl ExtensionDispatch

for Dispatch { @@ -111,13 +117,13 @@ mod dispatch { use rand_core::{OsRng, RngCore as _}; use trussed::{ backend::BackendId, - client::ClientImplementation, + client::{ClientImplementation, HmacSha256}, service::Service, syscall, try_syscall, types::Bytes, virt::{self, Ram}, }; -use trussed_auth::{AuthClient as _, PinId}; +use trussed_auth::{AuthClient as _, PinId, MAX_HW_KEY_LEN}; use dispatch::{Backend, Dispatch, BACKENDS}; @@ -154,6 +160,21 @@ fn run(backends: &'static [BackendId], f: F) { }) } +fn run_with_hw_key( + backends: &'static [BackendId], + hw_key: Bytes<{ MAX_HW_KEY_LEN }>, + f: F, +) { + virt::with_platform(Ram::default(), |platform| { + platform.run_client_with_backends( + "test", + Dispatch::with_hw_key(hw_key), + backends, + |mut client| f(&mut client), + ) + }) +} + fn random_pin() -> trussed_auth::Pin { let mut pin = Bytes::new(); pin.resize_to_capacity(); @@ -249,6 +270,94 @@ fn basic_wrapped() { }) } +#[test] +fn hw_key_wrapped() { + run_with_hw_key( + BACKENDS, + Bytes::from_slice(b"Some HW ikm").unwrap(), + |client| { + let pin1 = Bytes::from_slice(b"12345678").unwrap(); + let pin2 = Bytes::from_slice(b"123456").unwrap(); + + let reply = syscall!(client.has_pin(Pin::User)); + assert!(!reply.has_pin); + + syscall!(client.set_pin(Pin::User, pin1.clone(), None, true)); + + let reply = syscall!(client.has_pin(Pin::User)); + assert!(reply.has_pin); + let reply = syscall!(client.has_pin(Pin::Admin)); + assert!(!reply.has_pin); + + let reply = syscall!(client.pin_retries(Pin::User)); + assert_eq!(reply.retries, None); + + let reply = syscall!(client.check_pin(Pin::User, pin1.clone())); + assert!(reply.success); + + let reply = syscall!(client.pin_retries(Pin::User)); + assert_eq!(reply.retries, None); + + let reply = syscall!(client.check_pin(Pin::User, pin2)); + assert!(!reply.success); + + let result = try_syscall!(client.check_pin(Pin::Admin, pin1.clone())); + assert!(result.is_err()); + + let reply = syscall!(client.pin_retries(Pin::User)); + assert_eq!(reply.retries, None); + + syscall!(client.delete_pin(Pin::User)); + + let result = try_syscall!(client.check_pin(Pin::User, pin1)); + assert!(result.is_err()); + + let result = try_syscall!(client.pin_retries(Pin::User)); + assert!(result.is_err()); + }, + ) +} + +#[test] +fn pin_key() { + run_with_hw_key( + BACKENDS, + Bytes::from_slice(b"Some HW ikm").unwrap(), + |client| { + let pin1 = Bytes::from_slice(b"12345678").unwrap(); + let pin2 = Bytes::from_slice(b"123456").unwrap(); + + syscall!(client.set_pin(Pin::User, pin1.clone(), Some(3), true)); + assert!(syscall!(client.get_pin_key(Pin::User, pin2.clone())) + .result + .is_none()); + assert_eq!(syscall!(client.pin_retries(Pin::User)).retries, Some(2)); + assert!(!syscall!(client.check_pin(Pin::User, pin2.clone())).success); + assert_eq!(syscall!(client.pin_retries(Pin::User)).retries, Some(1)); + assert!(syscall!(client.check_pin(Pin::User, pin1.clone())).success); + let key = syscall!(client.get_pin_key(Pin::User, pin1.clone())) + .result + .unwrap(); + assert_eq!(syscall!(client.pin_retries(Pin::User)).retries, Some(3)); + let mac = syscall!(client.sign_hmacsha256(key, b"Some data")).signature; + let key2 = syscall!(client.get_pin_key(Pin::User, pin1.clone())) + .result + .unwrap(); + let mac2 = syscall!(client.sign_hmacsha256(key2, b"Some data")).signature; + assert_eq!(mac, mac2); + + assert!(!syscall!(client.check_pin(Pin::User, pin2.clone())).success); + assert!(!syscall!(client.check_pin(Pin::User, pin2.clone())).success); + assert!(!syscall!(client.check_pin(Pin::User, pin2.clone())).success); + assert!(!syscall!(client.check_pin(Pin::User, pin1.clone())).success); + assert!(syscall!(client.get_pin_key(Pin::User, pin1.clone())) + .result + .is_none()); + assert_eq!(syscall!(client.pin_retries(Pin::User)).retries, Some(0)); + }, + ) +} + #[test] fn blocked_pin() { run(BACKENDS, |client| {