Merge pull request #14 from Nitrokey/trussed-auth

Use trussed-auth for pin authentication
This commit is contained in:
sosthene-nitrokey
2023-04-13 09:19:31 +02:00
committed by GitHub
10 changed files with 372 additions and 197 deletions
+7 -4
View File
@@ -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]
+20 -11
View File
@@ -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<trussed_usbip::Service<Ram, dispatch::Dispatch>, 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<ClientImplementation<Syscall<virt::Platform<Ram>>>>,
piv: piv::Authenticator<VirtClient>,
}
impl trussed_usbip::Apps<ClientImplementation<Syscall<virt::Platform<Ram>>>, ()> for PivApp {
fn new(
make_client: impl Fn(&str) -> ClientImplementation<Syscall<virt::Platform<Ram>>>,
_data: (),
) -> Self {
impl trussed_usbip::Apps<VirtClient, Dispatch> for PivApp {
type Data = ();
fn new<B: ClientBuilder<VirtClient, Dispatch>>(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<dyn trussed::platform::UserInterface + Send + Sync> =
Box::new(UserInterface::new());
platform.user_interface().set_inner(ui);
})
.exec::<PivApp, _, _>(|_platform| {});
.build::<PivApp>()
.exec(|_platform| {});
}
+2 -2
View File
@@ -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");
+2 -1
View File
@@ -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<T> App<{ command::SIZE }, { response::SIZE }> for Authenticator<T>
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))
+23 -49
View File
@@ -26,6 +26,8 @@ mod tlv;
pub use piv_types::{AsymmetricAlgorithms, Pin, Puk};
#[cfg(feature = "virt")]
pub mod virt;
#[cfg(feature = "vpicc")]
pub mod vpicc;
@@ -36,6 +38,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 +65,7 @@ impl Default for Options {
impl Options {
pub fn storage(self, storage: Location) -> Self {
Self { storage, ..self }
Self { storage }
}
pub fn url(self, url: &'static [u8]) -> Self {
Self { url, ..self }
@@ -97,7 +100,7 @@ impl<T> iso7816::App for Authenticator<T> {
impl<T> Authenticator<T>
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 +219,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<const R: usize>(
&mut self,
data: &[u8],
@@ -280,24 +283,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 +314,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 +330,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 +951,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(())
}
+123 -121
View File
@@ -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<T: trussed::Client + AuthClient>(
&mut self,
client: &mut impl trussed::Client,
client: &mut T,
storage: Location,
) -> Result<LoadedState<'_>, Status> {
if self.persistent.is_none() {
@@ -202,9 +203,9 @@ impl State {
})
}
pub fn persistent(
pub fn persistent<T: trussed::Client + AuthClient>(
&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<PinType> 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<T: trussed::Client + AuthClient>(&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<T: trussed::Client + AuthClient>(&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<T: trussed::Client + AuthClient>(
&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<T: trussed::Client + AuthClient>(
&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<T: trussed::Client + AuthClient>(
&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<T: trussed::Client + AuthClient>(
&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<T: trussed::Client + AuthClient>(
&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<T: trussed::Client + AuthClient>(
&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<T: trussed::Client + AuthClient>(
&mut self,
new_pin: Pin,
client: &mut T,
) -> Result<(), Status> {
self.set_pin(new_pin, client)
}
pub fn reset_puk<T: trussed::Client + AuthClient>(
&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<T: trussed::Client + AuthClient>(
client: &mut T,
storage: Location,
) -> Result<Self, Status> {
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<T: trussed::Client + AuthClient>(
client: &mut T,
storage: Location,
) -> Result<Self, Status> {
// 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| {
+183
View File
@@ -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<Backend>] = &[
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<Extension> for u8 {
fn from(extension: Extension) -> Self {
match extension {
Extension::Auth => 0,
}
}
}
impl TryFrom<u8> for Extension {
type Error = Error;
fn try_from(id: u8) -> Result<Self, Self::Error> {
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<MAX_HW_KEY_LEN>) -> 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<P: Platform>(
&mut self,
backend: &Self::BackendId,
ctx: &mut Context<Self::Context>,
request: &Request,
resources: &mut ServiceResources<P>,
) -> Result<Reply, Error> {
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<P: Platform>(
&mut self,
backend: &Self::BackendId,
extension: &Self::ExtensionId,
ctx: &mut Context<Self::Context>,
request: &request::SerdeExtension,
resources: &mut ServiceResources<P>,
) -> Result<reply::SerdeExtension, Error> {
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<AuthExtension> 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<S> = Client<S, dispatch::Dispatch>;
/// Run a client using a provided store
pub fn with_client<S, R, F>(store: S, client_id: &str, f: F) -> R
where
F: FnOnce(VirtClient<S>) -> 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<P, R, F>(internal: P, client_id: &str, f: F) -> R
where
F: FnOnce(VirtClient<Filesystem>) -> R,
P: Into<PathBuf>,
{
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<R, F>(client_id: &str, f: F) -> R
where
F: FnOnce(VirtClient<Ram>) -> R,
{
with_client(Ram::default(), client_id, f)
}
+4 -3
View File
@@ -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<REQUEST_LEN>,
response_buffer: ResponseBuffer<RESPONSE_LEN>,
card: Authenticator<Client<Ram>>,
card: Authenticator<VirtClient<Ram>>,
}
impl VpiccCard {
/// Creates a new virtual smart card from the given card.
pub fn new(card: Authenticator<Client<Ram>>) -> Self {
pub fn new(card: Authenticator<VirtClient<Ram>>) -> Self {
Self {
request_buffer: Default::default(),
response_buffer: Default::default(),
+2 -2
View File
@@ -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<F: FnOnce() -> 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(());
+6 -4
View File
@@ -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<Client<Ram>>;
pub type Piv = piv_authenticator::Authenticator<VirtClient<Ram>>;
pub fn piv<R>(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)
})