Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,27 @@

## [Unreleased][]

[Unreleased]: https://github.com/trussed-dev/piv-authenticator/compare/v0.5.3...HEAD
[Unreleased]: https://github.com/trussed-dev/piv-authenticator/compare/v0.6.0...HEAD

-

## [v0.6.0][] (2026-03-25)

[v0.6.0]: https://github.com/trussed-dev/piv-authenticator/releases/tag/v0.6.0

- Replace `trussed-rsa-alloc` dependency with `trussed-rsa-types` for most use cases.
(Only the `virt` feature still requires `trussed-rsa-alloc`.)
- Update dependencies:
- `apdu-app` v0.2
- `cbor-smol` v0.5
- `flexiber` v0.2
- `heapless` v0.9
- `heapless-bytes` v0.5
- `iso7816` v0.2
- `trussed-chunked` v0.3
- `trussed-core` v0.2
- `trussed-hpke` v0.3
- `trussed-wrap-key-to-file` v0.3

## [v0.5.3][] (2025-07-31)

Expand Down
40 changes: 20 additions & 20 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "piv-authenticator"
version = "0.5.3"
version = "0.6.0"
authors = ["Nicolas Stalder <n@stalder.io>", "Nitrokey GmbH"]
edition = "2021"
license = "Apache-2.0 OR MIT"
Expand All @@ -12,30 +12,30 @@ name = "vpicc"
required-features = ["vpicc"]

[dependencies]
apdu-app = { version = "0.1", optional = true }
cbor-smol = { version = "0.5", features = ["heapless-bytes-v0-3"] }
apdu-app = { version = "0.2", optional = true }
cbor-smol = { version = "0.5", features = ["heapless-bytes-v0-5"] }
delog = { version = "0.1.5", optional = true }
flexiber = { version = "0.1", features = ["derive", "heapless"] }
heapless = "0.7"
flexiber = { version = "0.2", features = ["derive", "heapless"] }
heapless = "0.9.1"
hex-literal = "0.3"
iso7816 = "0.1.3"
iso7816 = "0.2.0"
serde = { version = "1", default-features = false, features = ["derive"] }
trussed = { version = "0.1", default-features = false, features = ["aes256-cbc", "chacha8-poly1305", "crypto-client", "ed255", "filesystem-client", "p256", "p384", "shared-secret", "serde-extensions", "tdes", "x255"], optional = true }
trussed-auth = "0.4"
trussed-auth = "0.5"
trussed-auth-backend = { version = "0.1.0", optional = true }
untrusted = "0.9"
vpicc = { version = "0.1.0", optional = true }
log = "0.4"
heapless-bytes = "0.3.0"
heapless-bytes = "0.5.0"
subtle = { version = "2", default-features = false }
# TODO: only enable rsa features when needed
trussed-core = { version = "0.1.0-rc.1", features = ["aes256-cbc", "chacha8-poly1305", "crypto-client", "ed255", "filesystem-client", "p256", "p384", "rsa2048", "rsa3072", "rsa4096", "shared-secret", "tdes", "x255"] }
trussed-rsa-types = { version = "0.1", optional = true }
trussed-rsa-alloc = { version = "0.3", features = ["raw"], optional = true }
trussed-chunked = "0.2.0"
trussed-hpke = "0.2.0"
trussed-wrap-key-to-file = "0.2.0"
trussed-staging = { version = "0.3.2", features = ["chunked", "hpke", "wrap-key-to-file"], default-features = false, optional = true }
trussed-core = { version = "0.2", features = ["aes256-cbc", "chacha8-poly1305", "crypto-client", "ed255", "filesystem-client", "p256", "p384", "rsa2048", "rsa3072", "rsa4096", "shared-secret", "tdes", "x255"] }
trussed-rsa-types = { version = "0.2", optional = true }
trussed-rsa-alloc = { version = "0.4", features = ["raw"], optional = true }
trussed-chunked = "0.3.0"
trussed-hpke = "0.3.0"
trussed-wrap-key-to-file = "0.3.0"
trussed-staging = { version = "0.4", features = ["chunked", "hpke", "wrap-key-to-file"], default-features = false, optional = true }
littlefs2-core = "0.1.0"
cfg-if = "1.0.0"

Expand All @@ -52,7 +52,7 @@ des = "0.8"
aes = "0.8.2"
stoppable_thread = "0.2.1"
expectrl = "0.7.0"
iso7816 = { version = "0.1.2", features = ["std"] }
iso7816 = { version = "0.2", features = ["std"] }

# Examples
# usbip
Expand Down Expand Up @@ -83,10 +83,10 @@ log-error = []
dangerous-test-real-card = []

[patch.crates-io]
trussed = { git = "https://github.com/trussed-dev/trussed", rev = "6bba8fde36d05c0227769eb63345744e87d84b2b" }
trussed-rsa-alloc = { git = "https://github.com/trussed-dev/trussed-rsa-backend.git", tag = "v0.3.0" }
trussed-staging = { git = "https://github.com/trussed-dev/trussed-staging.git", rev = "1e1ca03a3a62ea9b802f4070ea4bce002eeb4bec" }
trussed-auth-backend = { git = "https://github.com/trussed-dev/trussed-auth", tag = "v0.4.0" }
trussed = { git = "https://github.com/trussed-dev/trussed", rev = "0f8df68be879acdde1f8cf428c11e5d29692a47b" }
trussed-auth-backend = { git = "https://github.com/trussed-dev/trussed-auth", tag = "backend-v0.1.0" }
trussed-rsa-alloc = { git = "https://github.com/trussed-dev/trussed-rsa-backend.git", tag = "v0.4.0" }
trussed-staging = { git = "https://github.com/trussed-dev/trussed-staging.git", tag = "v0.4.0" }

[profile.dev.package.rsa]
opt-level = 2
Expand Down
14 changes: 10 additions & 4 deletions src/dispatch.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
use crate::{reply::Reply, Authenticator, /*constants::PIV_AID,*/ Result};

use apdu_app::{App, CommandView, Data};
use apdu_app::{App, CommandView};
use heapless::VecView;
use iso7816::{Interface, Status};

#[cfg(feature = "apdu-dispatch")]
impl<T, const R: usize> App<R> for Authenticator<T>
impl<T> App for Authenticator<T>
where
T: crate::Client,
{
fn select(
&mut self,
interface: Interface,
_apdu: CommandView<'_>,
reply: &mut Data<R>,
reply: &mut VecView<u8>,
) -> Result {
if interface != Interface::Contact {
return Err(Status::ConditionsOfUseNotSatisfied);
Expand All @@ -24,7 +25,12 @@ where
self.deselect()
}

fn call(&mut self, interface: Interface, apdu: CommandView<'_>, reply: &mut Data<R>) -> Result {
fn call(
&mut self,
interface: Interface,
apdu: CommandView<'_>,
reply: &mut VecView<u8>,
) -> Result {
if interface != Interface::Contact {
return Err(Status::ConditionsOfUseNotSatisfied);
}
Expand Down
65 changes: 27 additions & 38 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ mod reply;
pub mod state;
mod tlv;

use heapless::VecView;
pub use piv_types::{AsymmetricAlgorithms, Pin, Puk};
use trussed_chunked::ChunkedClient;
use trussed_hpke::HpkeClient;
Expand All @@ -35,7 +36,7 @@ use core::convert::TryInto;

use flexiber::EncodableHeapless;
use heapless_bytes::Bytes;
use iso7816::{Data, Status};
use iso7816::Status;
use trussed_auth::AuthClient;
use trussed_core::mechanisms::Tdes;
use trussed_core::types::{KeySerialization, Location, Mechanism, PathBuf, StorageAttributes};
Expand Down Expand Up @@ -136,7 +137,7 @@ where
// The way apdu-dispatch currently works, this would deselect, resetting security indicators.
pub fn deselect(&mut self) {}

pub fn select<const R: usize>(&mut self, mut reply: Reply<'_, R>) -> Result {
pub fn select(&mut self, mut reply: Reply<'_>) -> Result {
use piv_types::Algorithms::*;
info!("selecting PIV maybe");

Expand All @@ -154,10 +155,10 @@ where
Ok(())
}

pub fn respond<const R: usize>(
pub fn respond(
&mut self,
command: iso7816::command::CommandView<'_>,
reply: &mut Data<R>,
reply: &mut VecView<u8>,
) -> Result {
let just_verified = self.state.volatile.app_security_status.pin_just_verified;
self.state.volatile.app_security_status.pin_just_verified = false;
Expand Down Expand Up @@ -192,11 +193,11 @@ where
}
}

pub fn yubico_piv_extension<const R: usize>(
pub fn yubico_piv_extension(
&mut self,
data: &[u8],
instruction: YubicoPivExtension,
mut reply: Reply<'_, R>,
mut reply: Reply<'_>,
) -> Result {
info!("yubico extension: {:?}", &instruction);
match instruction {
Expand Down Expand Up @@ -256,11 +257,11 @@ where
}

impl<T: Client> LoadedAuthenticator<'_, T> {
pub fn yubico_set_administration_key<const R: usize>(
pub fn yubico_set_administration_key(
&mut self,
data: &[u8],
_touch_policy: TouchPolicy,
_reply: Reply<'_, R>,
_reply: Reply<'_>,
) -> Result {
// cmd := apdu{
// instruction: insSetMGMKey,
Expand Down Expand Up @@ -424,12 +425,12 @@ impl<T: Client> LoadedAuthenticator<'_, T> {
// - 9000, 61XX for success
// - 6982 security status
// - 6A80, 6A86 for data, P1/P2 issue
pub fn general_authenticate<const R: usize>(
pub fn general_authenticate(
&mut self,
auth: GeneralAuthenticate,
data: &[u8],
just_verified: bool,
mut reply: Reply<'_, R>,
mut reply: Reply<'_>,
) -> Result {
// For "SSH", we need implement A.4.2 in SP-800-73-4 Part 2, ECDSA signatures
//
Expand Down Expand Up @@ -562,11 +563,7 @@ impl<T: Client> LoadedAuthenticator<'_, T> {
Ok(self.state.persistent.keys.administration)
}

fn single_auth_1<const R: usize>(
&mut self,
auth: GeneralAuthenticate,
mut reply: Reply<'_, R>,
) -> Result {
fn single_auth_1(&mut self, auth: GeneralAuthenticate, mut reply: Reply<'_>) -> Result {
info!("Single auth 1");
let key = self.validate_auth_management(auth)?;
let plaintext = syscall!(self.trussed.random_bytes(key.alg.challenge_length())).bytes;
Expand All @@ -576,7 +573,7 @@ impl<T: Client> LoadedAuthenticator<'_, T> {
.encrypt(key.alg.mechanism(), key.id, &plaintext, &[], None))
.ciphertext;
self.state.volatile.command_cache = Some(CommandCache::SingleAuthChallengeReference(
Bytes::from_slice(&ciphertext).unwrap(),
Bytes::try_from(&*ciphertext).unwrap(),
));

reply.expand(&[0x7C])?;
Expand Down Expand Up @@ -619,11 +616,7 @@ impl<T: Client> LoadedAuthenticator<'_, T> {
Ok(())
}

fn mutual_auth_1<const R: usize>(
&mut self,
auth: GeneralAuthenticate,
mut reply: Reply<'_, R>,
) -> Result {
fn mutual_auth_1(&mut self, auth: GeneralAuthenticate, mut reply: Reply<'_>) -> Result {
info!("Mutual auth 1");
let key = self.validate_auth_management(auth)?;
let plaintext = syscall!(self.trussed.random_bytes(key.alg.challenge_length())).bytes;
Expand All @@ -635,7 +628,7 @@ impl<T: Client> LoadedAuthenticator<'_, T> {
.ciphertext;

self.state.volatile.command_cache = Some(CommandCache::MutualAuthWitnessReference(
Bytes::from_slice(&plaintext).unwrap(),
Bytes::try_from(&*plaintext).unwrap(),
));

reply.expand(&[0x7C])?;
Expand All @@ -649,12 +642,12 @@ impl<T: Client> LoadedAuthenticator<'_, T> {
Ok(())
}

fn mutual_auth_2<const R: usize>(
fn mutual_auth_2(
&mut self,
auth: GeneralAuthenticate,
response: &[u8],
challenge: &[u8],
mut reply: Reply<'_, R>,
mut reply: Reply<'_>,
) -> Result {
use subtle::ConstantTimeEq;

Expand Down Expand Up @@ -703,12 +696,12 @@ impl<T: Client> LoadedAuthenticator<'_, T> {
}

// Sign a message. For RSA, since the key is exposed as a raw key, so it can also be used for decryption
fn sign<const R: usize>(
fn sign(
&mut self,
auth: GeneralAuthenticate,
message: &[u8],
just_verified: bool,
mut reply: Reply<'_, R>,
mut reply: Reply<'_>,
) -> Result {
debug!("Request for sign, data length: {}, data:", message.len());
// error!("{}", delog::hexstr!(message));
Expand Down Expand Up @@ -754,11 +747,11 @@ impl<T: Client> LoadedAuthenticator<'_, T> {
)
}

fn key_agreement<const R: usize>(
fn key_agreement(
&mut self,
auth: GeneralAuthenticate,
data: &[u8],
mut reply: Reply<'_, R>,
mut reply: Reply<'_>,
just_verified: bool,
) -> Result {
info!("Request for exponentiation");
Expand Down Expand Up @@ -839,11 +832,11 @@ impl<T: Client> LoadedAuthenticator<'_, T> {
)
}

pub fn generate_asymmetric_keypair<const R: usize>(
pub fn generate_asymmetric_keypair(
&mut self,
reference: GenerateKeyReference,
data: &[u8],
mut reply: Reply<'_, R>,
mut reply: Reply<'_>,
) -> Result {
if !self
.state
Expand Down Expand Up @@ -967,11 +960,7 @@ impl<T: Client> LoadedAuthenticator<'_, T> {
Ok(())
}

fn get_data<const R: usize>(
&mut self,
container: Container,
mut reply: Reply<'_, R>,
) -> Result {
fn get_data(&mut self, container: Container, mut reply: Reply<'_>) -> Result {
let read_valid =
self.state
.volatile
Expand Down Expand Up @@ -1045,7 +1034,7 @@ impl<T: Client> LoadedAuthenticator<'_, T> {
Ok(())
}

fn get_key_history_object<const R: usize>(&mut self, mut reply: Reply<'_, R>) -> Result {
fn get_key_history_object(&mut self, mut reply: Reply<'_>) -> Result {
use state::ContainerStorage;

let mut num_certs = 0;
Expand All @@ -1063,12 +1052,12 @@ impl<T: Client> LoadedAuthenticator<'_, T> {
Ok(())
}

fn import_asymmetric_key<const R: usize>(
fn import_asymmetric_key(
&mut self,
algo: AsymmetricAlgorithms,
key: AsymmetricKeyReference,
#[cfg_attr(not(feature = "rsa"), allow(unused))] data: &[u8],
mut _reply: Reply<'_, R>,
mut _reply: Reply<'_>,
) -> Result {
if !self
.state
Expand Down
Loading