mirror of
https://github.com/trussed-dev/piv-authenticator.git
synced 2026-06-20 04:16:15 -07:00
Migrate to upstream trussed and trussed-staging for streaming writes
This commit is contained in:
+4
-3
@@ -36,6 +36,7 @@ log = "0.4"
|
||||
heapless-bytes = "0.3.0"
|
||||
subtle = { version = "2", default-features = false }
|
||||
trussed-rsa-alloc = { version = "0.1.0", features = ["raw"] }
|
||||
trussed-staging = { version = "0.1.0", features = ["chunked", "encrypted-chunked"]}
|
||||
|
||||
[dev-dependencies]
|
||||
littlefs2 = "0.3.2"
|
||||
@@ -74,10 +75,10 @@ log-warn = []
|
||||
log-error = []
|
||||
|
||||
[patch.crates-io]
|
||||
trussed = { git = "https://github.com/Nitrokey/trussed", tag = "v0.1.0-nitrokey.9" }
|
||||
trussed-auth = { git = "https://github.com/trussed-dev/trussed-auth", tag = "v0.2.1"}
|
||||
trussed = { git = "https://github.com/trussed-dev/trussed" , rev = "55ea391367fce4bf5093ff2d3c79041d7aef0485" }
|
||||
trussed-auth = { git = "https://github.com/trussed-dev/trussed-auth.git", tag = "v0.2.2"}
|
||||
trussed-rsa-alloc = { git = "https://github.com/Nitrokey/trussed-rsa-backend.git", tag = "v0.1.0"}
|
||||
littlefs2 = { git = "https://github.com/Nitrokey/littlefs2", tag = "v0.3.2-nitrokey-2" }
|
||||
trussed-staging = { git = "https://github.com/Nitrokey/trussed-staging", tag = "v0.1.0" }
|
||||
|
||||
[profile.dev.package.rsa]
|
||||
opt-level = 2
|
||||
|
||||
+1
-3
@@ -4,13 +4,11 @@
|
||||
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 + AuthClient + client::Ed255 + client::Tdes,
|
||||
T: crate::Client,
|
||||
{
|
||||
fn select(&mut self, _apdu: &Command, reply: &mut response::Data) -> Result {
|
||||
self.select(Reply(reply))
|
||||
|
||||
+13
-2
@@ -25,6 +25,7 @@ pub mod state;
|
||||
mod tlv;
|
||||
|
||||
pub use piv_types::{AsymmetricAlgorithms, Pin, Puk};
|
||||
use trussed_staging::streaming::ChunkedClient;
|
||||
|
||||
#[cfg(feature = "virt")]
|
||||
pub mod virt;
|
||||
@@ -100,7 +101,7 @@ impl<T> iso7816::App for Authenticator<T> {
|
||||
|
||||
impl<T> Authenticator<T>
|
||||
where
|
||||
T: client::Client + AuthClient + client::Ed255 + client::Tdes,
|
||||
T: Client,
|
||||
{
|
||||
pub fn new(trussed: T, options: Options) -> Self {
|
||||
// seems like RefCell is not the right thing, we want something like `Rc` instead,
|
||||
@@ -219,7 +220,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: trussed::Client + AuthClient + trussed::client::Ed255> LoadedAuthenticator<'a, T> {
|
||||
impl<'a, T: Client> LoadedAuthenticator<'a, T> {
|
||||
pub fn yubico_set_administration_key<const R: usize>(
|
||||
&mut self,
|
||||
data: &[u8],
|
||||
@@ -991,3 +992,13 @@ impl<'a, T: trussed::Client + AuthClient + trussed::client::Ed255> LoadedAuthent
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Super trait with all trussed extensions required by opcard
|
||||
pub trait Client:
|
||||
trussed::Client + AuthClient + ChunkedClient + trussed::client::Ed255 + client::Tdes
|
||||
{
|
||||
}
|
||||
impl<C: trussed::Client + AuthClient + ChunkedClient + trussed::client::Ed255 + client::Tdes> Client
|
||||
for C
|
||||
{
|
||||
}
|
||||
|
||||
+40
-48
@@ -8,15 +8,13 @@ use flexiber::EncodableHeapless;
|
||||
use heapless::Vec;
|
||||
use heapless_bytes::Bytes;
|
||||
use iso7816::Status;
|
||||
use trussed::types::OpenSeekFrom;
|
||||
use trussed::{
|
||||
api::reply::Metadata,
|
||||
config::MAX_MESSAGE_LENGTH,
|
||||
syscall, try_syscall,
|
||||
types::{KeyId, KeySerialization, Location, Mechanism, PathBuf, StorageAttributes},
|
||||
utils,
|
||||
};
|
||||
use trussed_auth::AuthClient;
|
||||
use trussed_staging::streaming::utils;
|
||||
|
||||
use crate::piv_types::CardHolderUniqueIdentifier;
|
||||
use crate::reply::Reply;
|
||||
@@ -192,7 +190,7 @@ pub struct State {
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn load<T: trussed::Client + AuthClient>(
|
||||
pub fn load<T: crate::Client>(
|
||||
&mut self,
|
||||
client: &mut T,
|
||||
storage: Location,
|
||||
@@ -206,7 +204,7 @@ impl State {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn persistent<T: trussed::Client + AuthClient>(
|
||||
pub fn persistent<T: crate::Client>(
|
||||
&mut self,
|
||||
client: &mut T,
|
||||
storage: Location,
|
||||
@@ -334,41 +332,33 @@ impl Persistent {
|
||||
const DEFAULT_PIN: Pin = Pin(*b"123456\xff\xff");
|
||||
const DEFAULT_PUK: Puk = Puk(*b"12345678");
|
||||
|
||||
pub fn remaining_pin_retries<T: trussed::Client + AuthClient>(&self, client: &mut T) -> u8 {
|
||||
pub fn remaining_pin_retries<T: crate::Client>(&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<T: trussed::Client + AuthClient>(&self, client: &mut T) -> u8 {
|
||||
pub fn remaining_puk_retries<T: crate::Client>(&self, client: &mut T) -> u8 {
|
||||
try_syscall!(client.pin_retries(PinType::Puk))
|
||||
.map(|r| r.retries.unwrap_or_default())
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
pub fn verify_pin<T: trussed::Client + AuthClient>(
|
||||
&mut self,
|
||||
value: &Pin,
|
||||
client: &mut T,
|
||||
) -> bool {
|
||||
pub fn verify_pin<T: crate::Client>(&mut self, 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 verify_puk<T: trussed::Client + AuthClient>(
|
||||
&mut self,
|
||||
value: &Puk,
|
||||
client: &mut T,
|
||||
) -> bool {
|
||||
pub fn verify_puk<T: crate::Client>(&mut self, 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 change_pin<T: trussed::Client + AuthClient>(
|
||||
pub fn change_pin<T: crate::Client>(
|
||||
&mut self,
|
||||
old_value: &Pin,
|
||||
new_value: &Pin,
|
||||
@@ -381,7 +371,7 @@ impl Persistent {
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn change_puk<T: trussed::Client + AuthClient>(
|
||||
pub fn change_puk<T: crate::Client>(
|
||||
&mut self,
|
||||
old_value: &Puk,
|
||||
new_value: &Puk,
|
||||
@@ -394,7 +384,7 @@ impl Persistent {
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn set_pin<T: trussed::Client + AuthClient>(
|
||||
pub fn set_pin<T: crate::Client>(
|
||||
&mut self,
|
||||
new_pin: Pin,
|
||||
client: &mut T,
|
||||
@@ -413,7 +403,7 @@ impl Persistent {
|
||||
.map(drop)
|
||||
}
|
||||
|
||||
pub fn set_puk<T: trussed::Client + AuthClient>(
|
||||
pub fn set_puk<T: crate::Client>(
|
||||
&mut self,
|
||||
new_puk: Puk,
|
||||
client: &mut T,
|
||||
@@ -426,14 +416,14 @@ impl Persistent {
|
||||
})
|
||||
.map(drop)
|
||||
}
|
||||
pub fn reset_pin<T: trussed::Client + AuthClient>(
|
||||
pub fn reset_pin<T: crate::Client>(
|
||||
&mut self,
|
||||
new_pin: Pin,
|
||||
client: &mut T,
|
||||
) -> Result<(), Status> {
|
||||
self.set_pin(new_pin, client)
|
||||
}
|
||||
pub fn reset_puk<T: trussed::Client + AuthClient>(
|
||||
pub fn reset_puk<T: crate::Client>(
|
||||
&mut self,
|
||||
new_puk: Puk,
|
||||
client: &mut T,
|
||||
@@ -441,7 +431,7 @@ impl Persistent {
|
||||
self.set_puk(new_puk, client)
|
||||
}
|
||||
|
||||
pub fn reset_administration_key(&mut self, client: &mut impl trussed::Client) {
|
||||
pub fn reset_administration_key(&mut self, client: &mut impl crate::Client) {
|
||||
self.set_administration_key(
|
||||
YUBICO_DEFAULT_MANAGEMENT_KEY,
|
||||
YUBICO_DEFAULT_MANAGEMENT_KEY_ALG,
|
||||
@@ -453,7 +443,7 @@ impl Persistent {
|
||||
&mut self,
|
||||
management_key: &[u8],
|
||||
alg: AdministrationAlgorithm,
|
||||
client: &mut impl trussed::Client,
|
||||
client: &mut impl crate::Client,
|
||||
) {
|
||||
// let new_management_key = syscall!(self.trussed.unsafe_inject_tdes_key(
|
||||
let id = syscall!(client.unsafe_inject_key(
|
||||
@@ -483,7 +473,7 @@ impl Persistent {
|
||||
&mut self,
|
||||
key: AsymmetricKeyReference,
|
||||
alg: AsymmetricAlgorithms,
|
||||
client: &mut impl trussed::Client,
|
||||
client: &mut impl crate::Client,
|
||||
) -> KeyId {
|
||||
let id = syscall!(client.generate_key(
|
||||
alg.key_mechanism(),
|
||||
@@ -498,10 +488,7 @@ impl Persistent {
|
||||
id
|
||||
}
|
||||
|
||||
pub fn initialize<T: trussed::Client + AuthClient>(
|
||||
client: &mut T,
|
||||
storage: Location,
|
||||
) -> Result<Self, Status> {
|
||||
pub fn initialize<T: crate::Client>(client: &mut T, storage: Location) -> Result<Self, Status> {
|
||||
info!("initializing PIV state");
|
||||
let administration = KeyWithAlg {
|
||||
id: syscall!(client.unsafe_inject_key(
|
||||
@@ -566,7 +553,7 @@ impl Persistent {
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
pub fn load_or_initialize<T: trussed::Client + AuthClient>(
|
||||
pub fn load_or_initialize<T: crate::Client>(
|
||||
client: &mut T,
|
||||
storage: Location,
|
||||
) -> Result<Self, Status> {
|
||||
@@ -584,13 +571,13 @@ impl Persistent {
|
||||
Ok(parsed)
|
||||
}
|
||||
|
||||
pub fn save(&mut self, client: &mut impl trussed::Client) {
|
||||
pub fn save(&mut self, client: &mut impl crate::Client) {
|
||||
let data: trussed::types::Message = trussed::cbor_serialize_bytes(&self).unwrap();
|
||||
|
||||
syscall!(client.write_file(self.storage, PathBuf::from(Self::FILENAME), data, None,));
|
||||
}
|
||||
|
||||
pub fn timestamp(&mut self, client: &mut impl trussed::Client) -> u32 {
|
||||
pub fn timestamp(&mut self, client: &mut impl crate::Client) -> u32 {
|
||||
self.timestamp += 1;
|
||||
self.save(client);
|
||||
self.timestamp
|
||||
@@ -598,7 +585,7 @@ impl Persistent {
|
||||
}
|
||||
|
||||
fn load_if_exists(
|
||||
client: &mut impl trussed::Client,
|
||||
client: &mut impl crate::Client,
|
||||
location: Location,
|
||||
path: &PathBuf,
|
||||
) -> Result<Option<Bytes<MAX_MESSAGE_LENGTH>>, Status> {
|
||||
@@ -622,26 +609,30 @@ fn load_if_exists(
|
||||
|
||||
/// Returns false if the file does not exist
|
||||
fn load_if_exists_streaming<const R: usize>(
|
||||
client: &mut impl trussed::Client,
|
||||
client: &mut impl crate::Client,
|
||||
location: Location,
|
||||
path: &PathBuf,
|
||||
mut buffer: Reply<'_, R>,
|
||||
) -> Result<bool, Status> {
|
||||
let mut read_len = 0;
|
||||
let file_len;
|
||||
match try_syscall!(client.read_file_chunk(location, path.clone(), OpenSeekFrom::Start(0))) {
|
||||
match try_syscall!(client.start_chunked_read(location, path.clone())) {
|
||||
Ok(r) => {
|
||||
read_len += r.data.len();
|
||||
file_len = r.len;
|
||||
buffer.append_len(file_len)?;
|
||||
buffer.expand(&r.data)?;
|
||||
if !r.data.is_full() {
|
||||
debug_assert_eq!(read_len, file_len);
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
Err(_) => match try_syscall!(client.entry_metadata(location, path.clone())) {
|
||||
Err(_err) => match try_syscall!(client.entry_metadata(location, path.clone())) {
|
||||
Ok(Metadata { metadata: None }) => return Ok(false),
|
||||
Ok(Metadata {
|
||||
metadata: Some(_metadata),
|
||||
}) => {
|
||||
error!("File {path} exists but couldn't be read: {_metadata:?}");
|
||||
error!("File {path} exists but couldn't be read: {_metadata:?}, {_err:?}");
|
||||
return Err(Status::UnspecifiedPersistentExecutionError);
|
||||
}
|
||||
Err(_err) => {
|
||||
@@ -651,15 +642,16 @@ fn load_if_exists_streaming<const R: usize>(
|
||||
},
|
||||
}
|
||||
|
||||
while read_len < file_len {
|
||||
match try_syscall!(client.read_file_chunk(
|
||||
location,
|
||||
path.clone(),
|
||||
OpenSeekFrom::Start(read_len as u32)
|
||||
)) {
|
||||
loop {
|
||||
match try_syscall!(client.read_file_chunk()) {
|
||||
Ok(r) => {
|
||||
debug_assert_eq!(r.len, file_len);
|
||||
read_len += r.data.len();
|
||||
buffer.expand(&r.data)?;
|
||||
if !r.data.is_full() {
|
||||
debug_assert_eq!(read_len, file_len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(_err) => {
|
||||
error!("Failed to read chunk: {:?}", _err);
|
||||
@@ -732,7 +724,7 @@ impl ContainerStorage {
|
||||
|
||||
pub fn exists(
|
||||
self,
|
||||
client: &mut impl trussed::Client,
|
||||
client: &mut impl crate::Client,
|
||||
storage: Location,
|
||||
) -> Result<bool, Status> {
|
||||
match try_syscall!(client.entry_metadata(storage, self.path())) {
|
||||
@@ -759,7 +751,7 @@ impl ContainerStorage {
|
||||
// Write the length of the file and write
|
||||
pub fn load<const R: usize>(
|
||||
self,
|
||||
client: &mut impl trussed::Client,
|
||||
client: &mut impl crate::Client,
|
||||
storage: Location,
|
||||
mut reply: Reply<'_, R>,
|
||||
) -> Result<bool, Status> {
|
||||
@@ -778,11 +770,11 @@ impl ContainerStorage {
|
||||
|
||||
pub fn save(
|
||||
self,
|
||||
client: &mut impl trussed::Client,
|
||||
client: &mut impl crate::Client,
|
||||
bytes: &[u8],
|
||||
storage: Location,
|
||||
) -> Result<(), Status> {
|
||||
utils::write_all(client, storage, self.path(), bytes, None).map_err(|_err| {
|
||||
utils::write_all(client, storage, self.path(), bytes, None, None).map_err(|_err| {
|
||||
error!("Failed to write data object: {:?}", _err);
|
||||
Status::UnspecifiedNonpersistentExecutionError
|
||||
})
|
||||
|
||||
+37
-4
@@ -10,16 +10,17 @@ pub mod dispatch {
|
||||
backend::{Backend as _, BackendId},
|
||||
error::Error,
|
||||
platform::Platform,
|
||||
serde_extensions::{ExtensionDispatch, ExtensionId, ExtensionImpl as _},
|
||||
serde_extensions::{ExtensionDispatch, ExtensionId, ExtensionImpl},
|
||||
service::ServiceResources,
|
||||
types::{Bytes, Context, Location},
|
||||
};
|
||||
use trussed_auth::{AuthBackend, AuthContext, AuthExtension, MAX_HW_KEY_LEN};
|
||||
|
||||
use trussed_rsa_alloc::SoftwareRsa;
|
||||
use trussed_staging::{streaming::ChunkedExtension, StagingBackend, StagingContext};
|
||||
|
||||
/// Backends used by opcard
|
||||
pub const BACKENDS: &[BackendId<Backend>] = &[
|
||||
BackendId::Custom(Backend::Staging),
|
||||
BackendId::Custom(Backend::Auth),
|
||||
BackendId::Custom(Backend::Rsa),
|
||||
BackendId::Core,
|
||||
@@ -29,17 +30,20 @@ pub mod dispatch {
|
||||
pub enum Backend {
|
||||
Auth,
|
||||
Rsa,
|
||||
Staging,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Extension {
|
||||
Auth,
|
||||
Chunked,
|
||||
}
|
||||
|
||||
impl From<Extension> for u8 {
|
||||
fn from(extension: Extension) -> Self {
|
||||
match extension {
|
||||
Extension::Auth => 0,
|
||||
Extension::Chunked => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,6 +54,7 @@ pub mod dispatch {
|
||||
fn try_from(id: u8) -> Result<Self, Self::Error> {
|
||||
match id {
|
||||
0 => Ok(Extension::Auth),
|
||||
1 => Ok(Extension::Chunked),
|
||||
_ => Err(Error::InternalError),
|
||||
}
|
||||
}
|
||||
@@ -59,24 +64,28 @@ pub mod dispatch {
|
||||
#[derive(Debug)]
|
||||
pub struct Dispatch {
|
||||
auth: AuthBackend,
|
||||
staging: StagingBackend,
|
||||
}
|
||||
|
||||
/// Dispatch context for the backends required by opcard
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default)]
|
||||
pub struct DispatchContext {
|
||||
auth: AuthContext,
|
||||
staging: StagingContext,
|
||||
}
|
||||
|
||||
impl Dispatch {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
auth: AuthBackend::new(Location::Internal),
|
||||
staging: StagingBackend::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_hw_key(hw_key: Bytes<MAX_HW_KEY_LEN>) -> Self {
|
||||
Self {
|
||||
auth: AuthBackend::with_hw_key(Location::Internal, hw_key),
|
||||
staging: StagingBackend::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,6 +113,12 @@ pub mod dispatch {
|
||||
self.auth
|
||||
.request(&mut ctx.core, &mut ctx.backends.auth, request, resources)
|
||||
}
|
||||
Backend::Staging => self.staging.request(
|
||||
&mut ctx.core,
|
||||
&mut ctx.backends.staging,
|
||||
request,
|
||||
resources,
|
||||
),
|
||||
Backend::Rsa => SoftwareRsa.request(&mut ctx.core, &mut (), request, resources),
|
||||
}
|
||||
}
|
||||
@@ -124,7 +139,20 @@ pub mod dispatch {
|
||||
request,
|
||||
resources,
|
||||
),
|
||||
},
|
||||
Extension::Chunked => Err(Error::RequestNotAvailable),
|
||||
}
|
||||
Backend::Staging =>match extension {
|
||||
Extension::Chunked => {
|
||||
<StagingBackend as ExtensionImpl<ChunkedExtension>>::extension_request_serialized(
|
||||
&mut self.staging,
|
||||
&mut ctx.core,
|
||||
&mut ctx.backends.staging,
|
||||
request,
|
||||
resources
|
||||
)
|
||||
}
|
||||
Extension::Auth => Err(Error::RequestNotAvailable),
|
||||
}
|
||||
Backend::Rsa => Err(Error::RequestNotAvailable),
|
||||
}
|
||||
}
|
||||
@@ -135,6 +163,11 @@ pub mod dispatch {
|
||||
|
||||
const ID: Self::Id = Self::Id::Auth;
|
||||
}
|
||||
impl ExtensionId<ChunkedExtension> for Dispatch {
|
||||
type Id = Extension;
|
||||
|
||||
const ID: Self::Id = Self::Id::Chunked;
|
||||
}
|
||||
}
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
Reference in New Issue
Block a user