From e9b8077fc2b01b4520d9d5ed6b93d033c8c38d88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Wed, 5 Apr 2023 16:38:46 +0200 Subject: [PATCH 1/2] Add trussed-auth as backend dependency --- Cargo.toml | 11 ++- examples/usbip.rs | 31 +++++--- examples/vpicc.rs | 4 +- src/lib.rs | 2 + src/virt.rs | 183 +++++++++++++++++++++++++++++++++++++++++++++ src/vpicc.rs | 7 +- tests/card/mod.rs | 4 +- tests/setup/mod.rs | 10 ++- 8 files changed, 226 insertions(+), 26 deletions(-) create mode 100644 src/virt.rs diff --git a/Cargo.toml b/Cargo.toml index 902a8d1..88d11a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ required-features = ["vpicc"] [[example]] name = "usbip" -required-features = ["apdu-dispatch"] +required-features = ["apdu-dispatch", "virt"] [dependencies] apdu-dispatch = { version = "0.1", optional = true } @@ -29,6 +29,7 @@ interchange = "0.2.2" iso7816 = "0.1" serde = { version = "1", default-features = false, features = ["derive"] } trussed = { version = "0.1" } +trussed-auth = { version = "0.2" } untrusted = "0.9" vpicc = { version = "0.1.0", optional = true } log = "0.4" @@ -52,7 +53,7 @@ stoppable_thread = "0.2.1" expectrl = "0.6.0" # Examples -trussed-usbip = { git = "https://github.com/trussed-dev/pc-usbip-runner", default-features = false, features = ["ccid"], rev = "d2957b6c24c2b0cafbbfacd6fecd62c80943630b"} +trussed-usbip = { git = "https://github.com/trussed-dev/pc-usbip-runner", default-features = false, features = ["ccid"], rev = "f3a680ca4c9a1411838ae0774f1713f79d4c2979"} usbd-ccid = { version = "0.2.0", features = ["highspeed-usb"]} rand = "0.8.5" @@ -60,7 +61,8 @@ rand = "0.8.5" default = [] strict-pin = [] std = [] -vpicc = ["std", "dep:vpicc", "trussed-rsa-alloc/virt"] +vpicc = ["std", "dep:vpicc", "virt"] +virt = ["std"] pivy-tests = [] opensc-tests = [] @@ -72,7 +74,8 @@ log-warn = [] log-error = [] [patch.crates-io] - trussed = { git = "https://github.com/Nitrokey/trussed", tag = "v0.1.0-nitrokey.8"} +trussed = { git = "https://github.com/Nitrokey/trussed", tag = "v0.1.0-nitrokey.8"} +trussed-auth = { git = "https://github.com/trussed-dev/trussed-auth", tag = "v0.2.1"} littlefs2 = { git = "https://github.com/Nitrokey/littlefs2", tag = "v0.3.2-nitrokey-2" } [profile.dev.package.rsa] diff --git a/examples/usbip.rs b/examples/usbip.rs index fa62ce9..065eb86 100644 --- a/examples/usbip.rs +++ b/examples/usbip.rs @@ -3,9 +3,15 @@ use trussed::virt::{self, Ram, UserInterface}; use trussed::{ClientImplementation, Platform}; +use trussed_usbip::ClientBuilder; -use piv_authenticator as piv; -use trussed_usbip::Syscall; +use piv_authenticator::{ + self as piv, + virt::dispatch::{self, Dispatch}, +}; + +type VirtClient = + ClientImplementation, dispatch::Dispatch>; const MANUFACTURER: &str = "Nitrokey"; const PRODUCT: &str = "Nitrokey 3"; @@ -13,16 +19,17 @@ const VID: u16 = 0x20a0; const PID: u16 = 0x42b2; struct PivApp { - piv: piv::Authenticator>>>, + piv: piv::Authenticator, } -impl trussed_usbip::Apps>>, ()> for PivApp { - fn new( - make_client: impl Fn(&str) -> ClientImplementation>>, - _data: (), - ) -> Self { +impl trussed_usbip::Apps for PivApp { + type Data = (); + fn new>(builder: &B, _data: ()) -> Self { PivApp { - piv: piv::Authenticator::new(make_client("piv"), piv::Options::default()), + piv: piv::Authenticator::new( + builder.build("piv", dispatch::BACKENDS), + piv::Options::default(), + ), } } @@ -44,11 +51,13 @@ fn main() { vid: VID, pid: PID, }; - trussed_usbip::Runner::new(virt::Ram::default(), options) + trussed_usbip::Builder::new(virt::Ram::default(), options) + .dispatch(Dispatch::new()) .init_platform(move |platform| { let ui: Box = Box::new(UserInterface::new()); platform.user_interface().set_inner(ui); }) - .exec::(|_platform| {}); + .build::() + .exec(|_platform| {}); } diff --git a/examples/vpicc.rs b/examples/vpicc.rs index e4002d8..687eed5 100644 --- a/examples/vpicc.rs +++ b/examples/vpicc.rs @@ -11,12 +11,12 @@ // TODO: add CLI -use piv_authenticator::{Authenticator, Options}; +use piv_authenticator::{virt::with_ram_client, Authenticator, Options}; fn main() { env_logger::init(); - trussed_rsa_alloc::virt::with_ram_client("piv-authenticator", |client| { + with_ram_client("piv-authenticator", |client| { let card = Authenticator::new(client, Options::default()); let mut vpicc_card = piv_authenticator::vpicc::VpiccCard::new(card); let vpicc = vpicc::connect().expect("failed to connect to vpicc"); diff --git a/src/lib.rs b/src/lib.rs index 08a7c58..7f6bec4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,6 +28,8 @@ mod tlv; pub use piv_types::{AsymmetricAlgorithms, Pin, Puk}; +#[cfg(feature = "virt")] +pub mod virt; #[cfg(feature = "vpicc")] pub mod vpicc; diff --git a/src/virt.rs b/src/virt.rs new file mode 100644 index 0000000..b5d21c7 --- /dev/null +++ b/src/virt.rs @@ -0,0 +1,183 @@ +// Copyright (C) 2022 Nitrokey GmbH +// SPDX-License-Identifier: LGPL-3.0-only + +//! Virtual trussed client (mostly for testing) + +pub mod dispatch { + + use trussed::{ + api::{reply, request, Reply, Request}, + backend::{Backend as _, BackendId}, + error::Error, + platform::Platform, + serde_extensions::{ExtensionDispatch, ExtensionId, ExtensionImpl as _}, + service::ServiceResources, + types::{Bytes, Context, Location}, + }; + use trussed_auth::{AuthBackend, AuthContext, AuthExtension, MAX_HW_KEY_LEN}; + + use trussed_rsa_alloc::SoftwareRsa; + + /// Backends used by opcard + pub const BACKENDS: &[BackendId] = &[ + BackendId::Custom(Backend::Auth), + BackendId::Custom(Backend::Rsa), + BackendId::Core, + ]; + + #[derive(Debug, Clone, Copy)] + pub enum Backend { + Auth, + Rsa, + } + + #[derive(Debug, Clone, Copy)] + pub enum Extension { + Auth, + } + + impl From for u8 { + fn from(extension: Extension) -> Self { + match extension { + Extension::Auth => 0, + } + } + } + + impl TryFrom for Extension { + type Error = Error; + + fn try_from(id: u8) -> Result { + match id { + 0 => Ok(Extension::Auth), + _ => Err(Error::InternalError), + } + } + } + + /// Dispatch implementation with the backends required by opcard + #[derive(Debug)] + pub struct Dispatch { + auth: AuthBackend, + } + + /// Dispatch context for the backends required by opcard + #[derive(Default, Debug)] + pub struct DispatchContext { + auth: AuthContext, + } + + impl Dispatch { + pub fn new() -> Self { + Self { + 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 Default for Dispatch { + fn default() -> Self { + Self::new() + } + } + + impl ExtensionDispatch for Dispatch { + type BackendId = Backend; + type Context = DispatchContext; + type ExtensionId = Extension; + + fn core_request( + &mut self, + backend: &Self::BackendId, + ctx: &mut Context, + request: &Request, + resources: &mut ServiceResources

, + ) -> Result { + match backend { + Backend::Auth => { + self.auth + .request(&mut ctx.core, &mut ctx.backends.auth, request, resources) + } + Backend::Rsa => SoftwareRsa.request(&mut ctx.core, &mut (), request, resources), + } + } + + fn extension_request( + &mut self, + backend: &Self::BackendId, + extension: &Self::ExtensionId, + ctx: &mut Context, + request: &request::SerdeExtension, + resources: &mut ServiceResources

, + ) -> Result { + match backend { + Backend::Auth => match extension { + Extension::Auth => self.auth.extension_request_serialized( + &mut ctx.core, + &mut ctx.backends.auth, + request, + resources, + ), + }, + Backend::Rsa => Err(Error::RequestNotAvailable), + } + } + } + + impl ExtensionId for Dispatch { + type Id = Extension; + + const ID: Self::Id = Self::Id::Auth; + } +} + +use std::path::PathBuf; +use trussed::{ + types::Bytes, + virt::{self, Client, Filesystem, Ram, StoreProvider}, +}; + +/// Client type using a dispatcher with the backends required by opcard +pub type VirtClient = Client; + +/// Run a client using a provided store +pub fn with_client(store: S, client_id: &str, f: F) -> R +where + F: FnOnce(VirtClient) -> R, + S: StoreProvider, +{ + #[allow(clippy::unwrap_used)] + virt::with_platform(store, |platform| { + platform.run_client_with_backends( + client_id, + dispatch::Dispatch::with_hw_key(Bytes::from_slice(b"some bytes").unwrap()), + dispatch::BACKENDS, + f, + ) + }) +} + +/// Run the backend with the extensions required by opcard +/// using storage backed by a file +pub fn with_fs_client(internal: P, client_id: &str, f: F) -> R +where + F: FnOnce(VirtClient) -> R, + P: Into, +{ + with_client(Filesystem::new(internal), client_id, f) +} + +/// Run the backend with the extensions required by opcard +/// using a RAM file storage +pub fn with_ram_client(client_id: &str, f: F) -> R +where + F: FnOnce(VirtClient) -> R, +{ + with_client(Ram::default(), client_id, f) +} diff --git a/src/vpicc.rs b/src/vpicc.rs index f2a2b9c..1000997 100644 --- a/src/vpicc.rs +++ b/src/vpicc.rs @@ -3,7 +3,8 @@ use iso7816::{command::FromSliceError, Command, Status}; use trussed::virt::Ram; -use trussed_rsa_alloc::virt::Client; + +use crate::virt::VirtClient; use std::convert::{TryFrom, TryInto}; @@ -19,12 +20,12 @@ const RESPONSE_LEN: usize = 7609; pub struct VpiccCard { request_buffer: RequestBuffer, response_buffer: ResponseBuffer, - card: Authenticator>, + card: Authenticator>, } impl VpiccCard { /// Creates a new virtual smart card from the given card. - pub fn new(card: Authenticator>) -> Self { + pub fn new(card: Authenticator>) -> Self { Self { request_buffer: Default::default(), response_buffer: Default::default(), diff --git a/tests/card/mod.rs b/tests/card/mod.rs index 6c22b8b..e206ad2 100644 --- a/tests/card/mod.rs +++ b/tests/card/mod.rs @@ -1,7 +1,7 @@ // Copyright (C) 2022 Nitrokey GmbH // SPDX-License-Identifier: LGPL-3.0-only -use piv_authenticator::{vpicc::VpiccCard, Authenticator, Options}; +use piv_authenticator::{virt::with_ram_client, vpicc::VpiccCard, Authenticator, Options}; use std::{sync::mpsc, thread::sleep, time::Duration}; use stoppable_thread::spawn; @@ -17,7 +17,7 @@ pub fn with_vsc R, R>(f: F) -> R { let (tx, rx) = mpsc::channel(); let handle = spawn(move |stopped| { - trussed_rsa_alloc::virt::with_ram_client("opcard", |client| { + with_ram_client("opcard", |client| { let card = Authenticator::new(client, Options::default()); let mut vpicc_card = VpiccCard::new(card); let mut result = Ok(()); diff --git a/tests/setup/mod.rs b/tests/setup/mod.rs index a355692..3321e63 100644 --- a/tests/setup/mod.rs +++ b/tests/setup/mod.rs @@ -11,14 +11,16 @@ macro_rules! cmd { }; } -use piv_authenticator::{Authenticator, Options}; +use piv_authenticator::{ + virt::{with_ram_client, VirtClient}, + Authenticator, Options, +}; use trussed::virt::Ram; -use trussed_rsa_alloc::virt::Client; -pub type Piv = piv_authenticator::Authenticator>; +pub type Piv = piv_authenticator::Authenticator>; pub fn piv(test: impl FnOnce(&mut Piv) -> R) -> R { - trussed_rsa_alloc::virt::with_ram_client("test", |client| { + with_ram_client("test", |client| { let mut piv_app = Authenticator::new(client, Options::default()); test(&mut piv_app) }) From 29bf6cc2f7923f784be101bf380861a6da413d1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Wed, 5 Apr 2023 17:55:19 +0200 Subject: [PATCH 2/2] Use trussed-auth for pin verification --- src/dispatch.rs | 3 +- src/lib.rs | 70 +++++--------- src/state.rs | 244 ++++++++++++++++++++++++------------------------ 3 files changed, 146 insertions(+), 171 deletions(-) diff --git a/src/dispatch.rs b/src/dispatch.rs index d1d8142..ad33cb7 100644 --- a/src/dispatch.rs +++ b/src/dispatch.rs @@ -5,11 +5,12 @@ use crate::{reply::Reply, Authenticator, /*constants::PIV_AID,*/ Result}; use apdu_dispatch::{app::App, command, response, Command}; use trussed::client; +use trussed_auth::AuthClient; #[cfg(feature = "apdu-dispatch")] impl App<{ command::SIZE }, { response::SIZE }> for Authenticator where - T: client::Client + client::Ed255 + client::Tdes, + T: client::Client + AuthClient + client::Ed255 + client::Tdes, { fn select(&mut self, _apdu: &Command, reply: &mut response::Data) -> Result { self.select(Reply(reply)) diff --git a/src/lib.rs b/src/lib.rs index 7f6bec4..8bf1d52 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,6 +40,7 @@ use heapless_bytes::Bytes; use iso7816::{Data, Status}; use trussed::types::{KeySerialization, Location, PathBuf, StorageAttributes}; use trussed::{client, syscall, try_syscall}; +use trussed_auth::AuthClient; use constants::*; @@ -62,7 +63,7 @@ impl Default for Options { impl Options { pub fn storage(self, storage: Location) -> Self { - Self { storage, ..self } + Self { storage } } } @@ -91,7 +92,7 @@ impl iso7816::App for Authenticator { impl Authenticator where - T: client::Client + client::Ed255 + client::Tdes, + T: client::Client + AuthClient + client::Ed255 + client::Tdes, { pub fn new(trussed: T, options: Options) -> Self { // seems like RefCell is not the right thing, we want something like `Rc` instead, @@ -216,7 +217,7 @@ where } } -impl<'a, T: trussed::Client + trussed::client::Ed255> LoadedAuthenticator<'a, T> { +impl<'a, T: trussed::Client + AuthClient + trussed::client::Ed255> LoadedAuthenticator<'a, T> { pub fn yubico_set_administration_key( &mut self, data: &[u8], @@ -280,24 +281,13 @@ impl<'a, T: trussed::Client + trussed::client::Ed255> LoadedAuthenticator<'a, T> // maybe reserve this for the case VerifyLogin::PivPin? pub fn login(&mut self, login: commands::VerifyLogin) -> Result { if let commands::VerifyLogin::PivPin(pin) = login { - // the actual PIN verification - if self.state.persistent.remaining_pin_retries() == 0 { - return Err(Status::OperationBlocked); - } - if self.state.persistent.verify_pin(&pin, self.trussed) { - self.state - .persistent - .reset_consecutive_pin_mismatches(self.trussed); self.state.volatile.app_security_status.pin_verified = true; Ok(()) } else { - let remaining = self - .state - .persistent - .increment_consecutive_pin_mismatches(self.trussed); // should we logout here? self.state.volatile.app_security_status.pin_verified = false; + let remaining = self.state.persistent.remaining_pin_retries(self.trussed); Err(Status::RemainingRetries(remaining)) } } else { @@ -322,7 +312,7 @@ impl<'a, T: trussed::Client + trussed::client::Ed255> LoadedAuthenticator<'a, T> if self.state.volatile.app_security_status.pin_verified { Ok(()) } else { - let retries = self.state.persistent.remaining_pin_retries(); + let retries = self.state.persistent.remaining_pin_retries(self.trussed); Err(Status::RemainingRetries(retries)) } } @@ -338,45 +328,25 @@ impl<'a, T: trussed::Client + trussed::client::Ed255> LoadedAuthenticator<'a, T> } pub fn change_pin(&mut self, old_pin: commands::Pin, new_pin: commands::Pin) -> Result { - if self.state.persistent.remaining_pin_retries() == 0 { - return Err(Status::OperationBlocked); - } - - if !self.state.persistent.verify_pin(&old_pin, self.trussed) { - let remaining = self - .state - .persistent - .increment_consecutive_pin_mismatches(self.trussed); - self.state.volatile.app_security_status.pin_verified = false; - return Err(Status::RemainingRetries(remaining)); - } - - self.state + if !self + .state .persistent - .reset_consecutive_pin_mismatches(self.trussed); - self.state.persistent.set_pin(new_pin, self.trussed); + .change_pin(&old_pin, &new_pin, self.trussed) + { + return Err(Status::VerificationFailed); + } self.state.volatile.app_security_status.pin_verified = true; Ok(()) } pub fn change_puk(&mut self, old_puk: commands::Puk, new_puk: commands::Puk) -> Result { - if self.state.persistent.remaining_puk_retries() == 0 { - return Err(Status::OperationBlocked); - } - - if !self.state.persistent.verify_puk(&old_puk, self.trussed) { - let remaining = self - .state - .persistent - .increment_consecutive_puk_mismatches(self.trussed); - self.state.volatile.app_security_status.puk_verified = false; - return Err(Status::RemainingRetries(remaining)); - } - - self.state + if !self + .state .persistent - .reset_consecutive_puk_mismatches(self.trussed); - self.state.persistent.set_puk(new_puk, self.trussed); + .change_puk(&old_puk, &new_puk, self.trussed) + { + return Err(Status::VerificationFailed); + } self.state.volatile.app_security_status.puk_verified = true; Ok(()) } @@ -979,7 +949,9 @@ impl<'a, T: trussed::Client + trussed::client::Ed255> LoadedAuthenticator<'a, T> { return Err(Status::VerificationFailed); } - self.state.persistent.set_pin(Pin(data.pin), self.trussed); + self.state + .persistent + .reset_pin(Pin(data.pin), self.trussed)?; Ok(()) } diff --git a/src/state.rs b/src/state.rs index 507829e..93c145e 100644 --- a/src/state.rs +++ b/src/state.rs @@ -14,6 +14,7 @@ use trussed::{ syscall, try_syscall, types::{KeyId, KeySerialization, Location, Mechanism, PathBuf, StorageAttributes}, }; +use trussed_auth::AuthClient; use crate::piv_types::CardHolderUniqueIdentifier; use crate::{constants::*, piv_types::AsymmetricAlgorithms}; @@ -188,9 +189,9 @@ pub struct State { } impl State { - pub fn load( + pub fn load( &mut self, - client: &mut impl trussed::Client, + client: &mut T, storage: Location, ) -> Result, Status> { if self.persistent.is_none() { @@ -202,9 +203,9 @@ impl State { }) } - pub fn persistent( + pub fn persistent( &mut self, - client: &mut impl trussed::Client, + client: &mut T, storage: Location, ) -> Result<&mut Persistent, Status> { Ok(self.load(client, storage)?.persistent) @@ -226,18 +227,23 @@ fn volatile() -> Location { Location::Volatile } +enum PinType { + Puk, + UserPin, +} + +impl From for trussed_auth::PinId { + fn from(value: PinType) -> Self { + match value { + PinType::UserPin => 0.into(), + PinType::Puk => 1.into(), + } + } +} + #[derive(Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] pub struct Persistent { pub keys: Keys, - consecutive_pin_mismatches: u8, - consecutive_puk_mismatches: u8, - // the PIN can be 6-8 digits, padded with 0xFF if <8 - // we just store all of them for now. - pin: Pin, - // the PUK should be 8 digits, but it seems Yubico allows 6-8 - // like for PIN - puk: Puk, - // pin_hash: Option<[u8; 16]>, // Ideally, we'd dogfood a "Monotonic Counter" from `trussed`. timestamp: u32, #[serde(skip, default = "volatile")] @@ -322,119 +328,114 @@ impl Persistent { // hmm...! pub const PUK_RETRIES_DEFAULT: u8 = 5; const FILENAME: &'static [u8] = b"persistent-state.cbor"; - const DEFAULT_PIN: &'static [u8] = b"123456\xff\xff"; - const DEFAULT_PUK: &'static [u8] = b"12345678"; + const DEFAULT_PIN: Pin = Pin(*b"123456\xff\xff"); + const DEFAULT_PUK: Puk = Puk(*b"12345678"); - pub fn remaining_pin_retries(&self) -> u8 { - if self.consecutive_pin_mismatches >= Self::PIN_RETRIES_DEFAULT { - 0 - } else { - Self::PIN_RETRIES_DEFAULT - self.consecutive_pin_mismatches - } + pub fn remaining_pin_retries(&self, client: &mut T) -> u8 { + try_syscall!(client.pin_retries(PinType::UserPin)) + .map(|r| r.retries.unwrap_or_default()) + .unwrap_or(0) } - pub fn remaining_puk_retries(&self) -> u8 { - if self.consecutive_puk_mismatches >= Self::PUK_RETRIES_DEFAULT { - 0 - } else { - Self::PUK_RETRIES_DEFAULT - self.consecutive_puk_mismatches - } + pub fn remaining_puk_retries(&self, client: &mut T) -> u8 { + try_syscall!(client.pin_retries(PinType::Puk)) + .map(|r| r.retries.unwrap_or_default()) + .unwrap_or(0) } - // FIXME: revisit with trussed pin management - pub fn verify_pin(&mut self, other_pin: &Pin, client: &mut impl trussed::Client) -> bool { - if self.remaining_pin_retries() == 0 { - return false; - } - self.consecutive_pin_mismatches += 1; - self.save(client); - if self.pin == *other_pin { - self.consecutive_pin_mismatches = 0; - true - } else { - false - } - } - - // FIXME: revisit with trussed pin management - pub fn verify_puk(&mut self, other_puk: &Puk, client: &mut impl trussed::Client) -> bool { - if self.remaining_puk_retries() == 0 { - return false; - } - self.consecutive_puk_mismatches += 1; - self.save(client); - if self.puk == *other_puk { - self.consecutive_puk_mismatches = 0; - true - } else { - false - } - } - - pub fn set_pin(&mut self, new_pin: Pin, client: &mut impl trussed::Client) { - self.pin = new_pin; - self.consecutive_pin_mismatches = 0; - self.save(client); - } - - pub fn set_puk(&mut self, new_puk: Puk, client: &mut impl trussed::Client) { - self.puk = new_puk; - self.consecutive_puk_mismatches = 0; - self.save(client); - } - - pub fn reset_pin(&mut self, client: &mut impl trussed::Client) { - self.set_pin(Pin::try_from(Self::DEFAULT_PIN).unwrap(), client); - self.reset_consecutive_pin_mismatches(client); - } - - pub fn reset_puk(&mut self, client: &mut impl trussed::Client) { - self.set_puk(Puk::try_from(Self::DEFAULT_PUK).unwrap(), client); - self.reset_consecutive_puk_mismatches(client); - } - - pub fn increment_consecutive_pin_mismatches( + pub fn verify_pin( &mut self, - client: &mut impl trussed::Client, - ) -> u8 { - if self.consecutive_pin_mismatches >= Self::PIN_RETRIES_DEFAULT { - return 0; - } - - self.consecutive_pin_mismatches += 1; - self.save(client); - Self::PIN_RETRIES_DEFAULT - self.consecutive_pin_mismatches + value: &Pin, + client: &mut T, + ) -> bool { + let pin = Bytes::from_slice(&value.0).expect("Convertion of static array"); + try_syscall!(client.check_pin(PinType::UserPin, pin)) + .map(|r| r.success) + .unwrap_or(false) } - pub fn increment_consecutive_puk_mismatches( + pub fn verify_puk( &mut self, - client: &mut impl trussed::Client, - ) -> u8 { - if self.consecutive_puk_mismatches >= Self::PUK_RETRIES_DEFAULT { - return 0; - } - - self.consecutive_puk_mismatches += 1; - self.save(client); - Self::PUK_RETRIES_DEFAULT - self.consecutive_puk_mismatches + value: &Puk, + client: &mut T, + ) -> bool { + let puk = Bytes::from_slice(&value.0).expect("Convertion of static array"); + try_syscall!(client.check_pin(PinType::Puk, puk)) + .map(|r| r.success) + .unwrap_or(false) } - pub fn reset_consecutive_pin_mismatches(&mut self, client: &mut impl trussed::Client) -> u8 { - if self.consecutive_pin_mismatches != 0 { - self.consecutive_pin_mismatches = 0; - self.save(client); - } - - Self::PIN_RETRIES_DEFAULT + pub fn change_pin( + &mut self, + old_value: &Pin, + new_value: &Pin, + client: &mut T, + ) -> bool { + let old_pin = Bytes::from_slice(&old_value.0).expect("Convertion of static array"); + let new_pin = Bytes::from_slice(&new_value.0).expect("Convertion of static array"); + try_syscall!(client.change_pin(PinType::UserPin, old_pin, new_pin)) + .map(|r| r.success) + .unwrap_or(false) } - pub fn reset_consecutive_puk_mismatches(&mut self, client: &mut impl trussed::Client) -> u8 { - if self.consecutive_puk_mismatches != 0 { - self.consecutive_puk_mismatches = 0; - self.save(client); - } + pub fn change_puk( + &mut self, + old_value: &Puk, + new_value: &Puk, + client: &mut T, + ) -> bool { + let old_puk = Bytes::from_slice(&old_value.0).expect("Convertion of static array"); + let new_puk = Bytes::from_slice(&new_value.0).expect("Convertion of static array"); + try_syscall!(client.change_pin(PinType::UserPin, old_puk, new_puk)) + .map(|r| r.success) + .unwrap_or(false) + } - Self::PUK_RETRIES_DEFAULT + pub fn set_pin( + &mut self, + new_pin: Pin, + client: &mut T, + ) -> Result<(), Status> { + let new_pin = Bytes::from_slice(&new_pin.0).expect("Convertion of static array"); + try_syscall!(client.set_pin( + PinType::UserPin, + new_pin, + Some(Self::PIN_RETRIES_DEFAULT), + true + )) + .map_err(|_err| { + error!("Failed to set pin"); + Status::UnspecifiedPersistentExecutionError + }) + .map(drop) + } + + pub fn set_puk( + &mut self, + new_puk: Puk, + client: &mut T, + ) -> Result<(), Status> { + let new_puk = Bytes::from_slice(&new_puk.0).expect("Convertion of static array"); + try_syscall!(client.set_pin(PinType::Puk, new_puk, Some(Self::PUK_RETRIES_DEFAULT), true)) + .map_err(|_err| { + error!("Failed to set puk"); + Status::UnspecifiedPersistentExecutionError + }) + .map(drop) + } + pub fn reset_pin( + &mut self, + new_pin: Pin, + client: &mut T, + ) -> Result<(), Status> { + self.set_pin(new_pin, client) + } + pub fn reset_puk( + &mut self, + new_puk: Puk, + client: &mut T, + ) -> Result<(), Status> { + self.set_puk(new_puk, client) } pub fn reset_administration_key(&mut self, client: &mut impl trussed::Client) { @@ -494,7 +495,10 @@ impl Persistent { id } - pub fn initialize(client: &mut impl trussed::Client, storage: Location) -> Self { + pub fn initialize( + client: &mut T, + storage: Location, + ) -> Result { info!("initializing PIV state"); let administration = KeyWithAlg { id: syscall!(client.unsafe_inject_key( @@ -549,26 +553,24 @@ impl Persistent { let mut state = Self { keys, - consecutive_pin_mismatches: 0, - consecutive_puk_mismatches: 0, - pin: Pin::try_from(Self::DEFAULT_PIN).unwrap(), - puk: Puk::try_from(Self::DEFAULT_PUK).unwrap(), timestamp: 0, // In case of forgotten to rebind, ensure the bug is found storage: Location::Volatile, }; state.save(client); - state + state.reset_pin(Self::DEFAULT_PIN, client)?; + state.reset_puk(Self::DEFAULT_PUK, client)?; + Ok(state) } - pub fn load_or_initialize( - client: &mut impl trussed::Client, + pub fn load_or_initialize( + client: &mut T, storage: Location, ) -> Result { // todo: can't seem to combine load + initialize without code repetition let data = load_if_exists(client, storage, &PathBuf::from(Self::FILENAME))?; let Some(bytes) = data else { - return Ok( Self::initialize(client, storage)); + return Self::initialize(client, storage); }; let mut parsed: Self = trussed::cbor_deserialize(&bytes).map_err(|_err| {