Skip to content

Commit de69cd1

Browse files
0x0ecerobin-nitrokey
authored andcommitted
ctap2: accept up=true on MakeCredential; LargeBlobs Set accepts both PIN protocols
1 parent 61326c4 commit de69cd1

3 files changed

Lines changed: 10 additions & 13 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2020
- Indicate support for `FIDO_2_3` in `get_info`.
2121
- Load full credential from filesstem for getAssertion if an allow list is used with a discoverable credential.
2222
- Use UTF-8 code points instead of bytes when checking the minimum length for PINs.
23+
- Accept `up = true` in makeCredential.
24+
- Fix PIN verification in `large_blobs_set`.
2325

2426
## [v0.3.0](https://github.com/trussed-dev/fido-authenticator/releases/tag/v0.3.0) (2026-03-25)
2527

src/ctap2.rs

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,9 @@ impl<UP: UserPresence, T: TrussedRequirements> Authenticator for crate::Authenti
214214

215215
// 1-4.
216216
if let Some(options) = parameters.options.as_ref() {
217-
// up option is not valid for make_credential
218-
if options.up.is_some() {
217+
// CTAP 2.1 §6.1.2: MakeCredential allows `up` only with value
218+
// true (UP is implicit and required); `up=false` is invalid.
219+
if options.up == Some(false) {
219220
return Err(Error::InvalidOption);
220221
}
221222
}
@@ -2427,15 +2428,10 @@ impl<UP: UserPresence, T: TrussedRequirements> crate::Authenticator<UP, T> {
24272428
let Some(pin_uv_auth_protocol) = request.pin_uv_auth_protocol else {
24282429
return Err(Error::PinRequired);
24292430
};
2430-
if pin_uv_auth_protocol != 1 {
2431-
return Err(Error::PinAuthInvalid);
2432-
}
24332431
let pin_protocol = self.parse_pin_protocol(pin_uv_auth_protocol)?;
2434-
// TODO: check pinUvAuthToken
2435-
let pin_auth: [u8; 16] = pin_uv_auth_param
2436-
.as_ref()
2437-
.try_into()
2438-
.map_err(|_| Error::PinAuthInvalid)?;
2432+
// verify_pin_token truncates per protocol (16 B for v1, 32 B
2433+
// for v2), so pass the full param.
2434+
let pin_auth: &[u8] = pin_uv_auth_param.as_ref();
24392435

24402436
let mut auth_data: Bytes<70> = Bytes::new();
24412437
// 32x 0xff
@@ -2451,7 +2447,7 @@ impl<UP: UserPresence, T: TrussedRequirements> crate::Authenticator<UP, T> {
24512447
auth_data.extend_from_slice(&Sha256::digest(data)).unwrap();
24522448

24532449
let mut pin_protocol = self.pin_protocol(pin_protocol);
2454-
let pin_token = pin_protocol.verify_pin_token(&pin_auth, &auth_data)?;
2450+
let pin_token = pin_protocol.verify_pin_token(&auth_data, pin_auth)?;
24552451
pin_token.require_permissions(Permissions::LARGE_BLOB_WRITE)?;
24562452
}
24572453

tests/basic.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -571,8 +571,7 @@ struct TestMakeCredential {
571571
impl TestMakeCredential {
572572
fn expected_error(&self) -> Option<u8> {
573573
if let Some(options) = self.options {
574-
// TODO: this is the current implementation, but the spec allows Some(true)
575-
if options.up.is_some() {
574+
if options.up == Some(false) {
576575
return Some(0x2c);
577576
}
578577
if !matches!(self.pin_auth, PinAuth::PinToken(_)) && options.uv == Some(true) {

0 commit comments

Comments
 (0)