Skip to content

Commit 2fec80f

Browse files
Merge branch 'release-039' into fix-admin-auth
2 parents 41220cb + 98680ed commit 2fec80f

10 files changed

+577
-135
lines changed

CHANGELOG.md

+10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# Changelog
22

3+
## [v0.3.9][] (2025-02-11)
4+
5+
[v0.3.9]: https://github.com/Nitrokey/piv-authenticator/releases/tag/v0.3.9
6+
7+
- Fix admin authentication
8+
9+
### Security
10+
11+
- Fix [CVE-2025-25201](https://github.com/Nitrokey/piv-authenticator/security/advisories/GHSA-28p6-c99x-fg8j)
12+
313
## [v0.3.8][] (2024-10-17)
414

515
[v0.3.8]: https://github.com/Nitrokey/piv-authenticator/releases/tag/v0.3.8

Cargo.toml

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "piv-authenticator"
3-
version = "0.3.8"
3+
version = "0.3.9"
44
authors = ["Nicolas Stalder <[email protected]>", "Nitrokey GmbH"]
55
edition = "2021"
66
license = "Apache-2.0 OR MIT"
@@ -72,8 +72,9 @@ rsa = ["trussed-rsa-alloc", "alloc"]
7272

7373
log-all = []
7474
log-none = []
75-
log-info = []
75+
log-trace = []
7676
log-debug = []
77+
log-info = []
7778
log-warn = []
7879
log-error = []
7980

src/lib.rs

+38-29
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ where
255255
}
256256
}
257257

258-
impl<'a, T: Client> LoadedAuthenticator<'a, T> {
258+
impl<T: Client> LoadedAuthenticator<'_, T> {
259259
pub fn yubico_set_administration_key<const R: usize>(
260260
&mut self,
261261
data: &[u8],
@@ -482,7 +482,7 @@ impl<'a, T: Client> LoadedAuthenticator<'a, T> {
482482
exponentiation: tlv::get_do(&[0x85], input),
483483
};
484484

485-
error!(
485+
debug!(
486486
"witness: {}, challenge: {}, response: {}, exponentiation: {}",
487487
&parsed.witness.is_some(),
488488
&parsed.challenge.is_some(),
@@ -569,21 +569,22 @@ impl<'a, T: Client> LoadedAuthenticator<'a, T> {
569569
) -> Result {
570570
info!("Single auth 1");
571571
let key = self.validate_auth_management(auth)?;
572-
let pl = syscall!(self.trussed.random_bytes(key.alg.challenge_length())).bytes;
573-
self.state.volatile.command_cache = Some(CommandCache::SingleAuthChallenge(
574-
Bytes::from_slice(&pl).unwrap(),
572+
let plaintext = syscall!(self.trussed.random_bytes(key.alg.challenge_length())).bytes;
573+
let ciphertext =
574+
syscall!(self
575+
.trussed
576+
.encrypt(key.alg.mechanism(), key.id, &plaintext, &[], None))
577+
.ciphertext;
578+
self.state.volatile.command_cache = Some(CommandCache::SingleAuthChallengeReference(
579+
Bytes::from_slice(&ciphertext).unwrap(),
575580
));
576-
let data = syscall!(self
577-
.trussed
578-
.encrypt(key.alg.mechanism(), key.id, &pl, &[], None))
579-
.ciphertext;
580581

581582
reply.expand(&[0x7C])?;
582583
let offset = reply.len();
583584
{
584585
reply.expand(&[0x81])?;
585-
reply.append_len(data.len())?;
586-
reply.expand(&data)?;
586+
reply.append_len(plaintext.len())?;
587+
reply.expand(&plaintext)?;
587588
}
588589
reply.prepend_len(offset)?;
589590
Ok(())
@@ -599,14 +600,15 @@ impl<'a, T: Client> LoadedAuthenticator<'a, T> {
599600
return Err(Status::IncorrectDataParameter);
600601
}
601602

602-
let Some(plaintext_challenge) = self.state.volatile.take_single_challenge() else {
603+
let Some(challenge_reference) = self.state.volatile.take_single_challenge_reference()
604+
else {
603605
warn!("Missing cached challenge for auth");
604606
return Err(Status::ConditionsOfUseNotSatisfied);
605607
};
606608

607-
let is_eq: bool = response.ct_eq(&plaintext_challenge).into();
608-
if is_eq {
609-
warn!("Bad auth challenge");
609+
let is_eq: bool = response.ct_eq(&challenge_reference).into();
610+
if !is_eq {
611+
warn!("Failed admin authentication. Challenge did not match");
610612
return Err(Status::IncorrectDataParameter);
611613
}
612614

@@ -616,31 +618,37 @@ impl<'a, T: Client> LoadedAuthenticator<'a, T> {
616618
.administrator_verified = true;
617619
Ok(())
618620
}
621+
619622
fn mutual_auth_1<const R: usize>(
620623
&mut self,
621624
auth: GeneralAuthenticate,
622625
mut reply: Reply<'_, R>,
623626
) -> Result {
624627
info!("Mutual auth 1");
625628
let key = self.validate_auth_management(auth)?;
626-
let pl = syscall!(self.trussed.random_bytes(key.alg.challenge_length())).bytes;
627-
self.state.volatile.command_cache = Some(CommandCache::MutualAuthChallenge(
628-
Bytes::from_slice(&pl).unwrap(),
629+
let plaintext = syscall!(self.trussed.random_bytes(key.alg.challenge_length())).bytes;
630+
631+
let ciphertext =
632+
syscall!(self
633+
.trussed
634+
.encrypt(key.alg.mechanism(), key.id, &plaintext, &[], None))
635+
.ciphertext;
636+
637+
self.state.volatile.command_cache = Some(CommandCache::MutualAuthWitnessReference(
638+
Bytes::from_slice(&plaintext).unwrap(),
629639
));
630-
let data = syscall!(self
631-
.trussed
632-
.encrypt(key.alg.mechanism(), key.id, &pl, &[], None))
633-
.ciphertext;
640+
634641
reply.expand(&[0x7C])?;
635642
let offset = reply.len();
636643
{
637644
reply.expand(&[0x80])?;
638-
reply.append_len(data.len())?;
639-
reply.expand(&data)?;
645+
reply.append_len(ciphertext.len())?;
646+
reply.expand(&ciphertext)?;
640647
}
641648
reply.prepend_len(offset)?;
642649
Ok(())
643650
}
651+
644652
fn mutual_auth_2<const R: usize>(
645653
&mut self,
646654
auth: GeneralAuthenticate,
@@ -661,13 +669,14 @@ impl<'a, T: Client> LoadedAuthenticator<'a, T> {
661669
return Err(Status::IncorrectDataParameter);
662670
}
663671

664-
let Some(plaintext_challenge) = self.state.volatile.take_mutual_challenge() else {
672+
let Some(witness_reference) = self.state.volatile.take_mutual_witness_reference() else {
665673
warn!("Missing cached challenge for auth");
666674
return Err(Status::ConditionsOfUseNotSatisfied);
667675
};
668676

669-
if challenge.ct_eq(&plaintext_challenge).into() {
670-
warn!("Bad auth challenge");
677+
let is_eq: bool = response.ct_eq(&witness_reference).into();
678+
if !is_eq {
679+
warn!("Failed admin authentication. Challenge did not match");
671680
return Err(Status::IncorrectDataParameter);
672681
}
673682

@@ -701,7 +710,7 @@ impl<'a, T: Client> LoadedAuthenticator<'a, T> {
701710
just_verified: bool,
702711
mut reply: Reply<'_, R>,
703712
) -> Result {
704-
error!("Request for sign, data length: {}, data:", message.len());
713+
debug!("Request for sign, data length: {}, data:", message.len());
705714
// error!("{}", delog::hexstr!(message));
706715

707716
let Ok(key_ref) = auth.key_reference.try_into() else {
@@ -737,7 +746,7 @@ impl<'a, T: Client> LoadedAuthenticator<'a, T> {
737746
reply.append_len(response.len())?;
738747
reply.expand(&response)?;
739748
}
740-
error!("Signed data len: {}, Data:", response.len());
749+
debug!("Signed data len: {}, Data:", response.len());
741750
// error!("{}", delog::hexstr!(&response));
742751

743752
reply.prepend_len(offset)?;

src/reply.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ impl<'v, const R: usize> Deref for Reply<'v, R> {
1212
}
1313
}
1414

15-
impl<'v, const R: usize> DerefMut for Reply<'v, R> {
15+
impl<const R: usize> DerefMut for Reply<'_, R> {
1616
fn deref_mut(&mut self) -> &mut Self::Target {
1717
&mut self.0
1818
}
1919
}
2020

21-
impl<'v, const R: usize> Reply<'v, R> {
21+
impl<const R: usize> Reply<'_, R> {
2222
/// Extend the reply and return an error otherwise
2323
/// The MoreAvailable and GET RESPONSE mechanisms are handled by adpu_dispatch
2424
///

src/state.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ impl Drop for UseValidKey {
305305
}
306306
}
307307

308-
impl<'t> LoadedState<'t> {
308+
impl LoadedState<'_> {
309309
pub fn key_exists(
310310
&self,
311311
client: &mut impl crate::Client,
@@ -508,17 +508,17 @@ impl Volatile {
508508

509509
Ok(ReadValid::Encrypted(unsealed_key.key))
510510
}
511-
pub fn take_single_challenge(&mut self) -> Option<Bytes<16>> {
511+
pub fn take_single_challenge_reference(&mut self) -> Option<Bytes<16>> {
512512
match self.command_cache.take() {
513-
Some(CommandCache::SingleAuthChallenge(b)) => return Some(b),
513+
Some(CommandCache::SingleAuthChallengeReference(b)) => return Some(b),
514514
old => self.command_cache = old,
515515
};
516516
None
517517
}
518518

519-
pub fn take_mutual_challenge(&mut self) -> Option<Bytes<16>> {
519+
pub fn take_mutual_witness_reference(&mut self) -> Option<Bytes<16>> {
520520
match self.command_cache.take() {
521-
Some(CommandCache::MutualAuthChallenge(b)) => return Some(b),
521+
Some(CommandCache::MutualAuthWitnessReference(b)) => return Some(b),
522522
old => self.command_cache = old,
523523
};
524524
None
@@ -535,8 +535,8 @@ pub struct AppSecurityStatus {
535535

536536
#[derive(Clone, Debug, Eq, PartialEq)]
537537
pub enum CommandCache {
538-
SingleAuthChallenge(Bytes<16>),
539-
MutualAuthChallenge(Bytes<16>),
538+
SingleAuthChallengeReference(Bytes<16>),
539+
MutualAuthWitnessReference(Bytes<16>),
540540
}
541541

542542
impl Persistent {

tests/bad_admin_key

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
��������

0 commit comments

Comments
 (0)