Skip to content

Commit 6a56218

Browse files
fix(get_info): graceful early-return in Ctap2GetInfoResponse::uv_operation
A malicious or buggy authenticator can advertise `pinUvAuthToken=true` without `clientPin` set (or supported). CTAP 2.2 §6.4 makes these options independent and the platform must tolerate any combination. The current `assert!(self.option_enabled("clientPin"))` panics on this path, taking down the host process via every `make_credential` / `get_assertion` / `change_pin` flow that calls into `select_uv_proto`. Replace the assertion with a debug log + early return of `Ctap2UserVerificationOperation::OnlyForSharedSecret`, matching the existing handling for "pinUvAuthToken supported but PIN not set". The caller can still establish a shared secret (e.g., for hmac-secret), while not attempting a PIN-token ceremony that the device cannot service. Add a regression test.
1 parent 99fa265 commit 6a56218

1 file changed

Lines changed: 23 additions & 1 deletion

File tree

libwebauthn/src/proto/ctap2/model/get_info.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,16 @@ impl Ctap2GetInfoResponse {
246246

247247
// If we do have a PIN, check if we need to use legacy getPinToken or new getPinUvAuthToken..-command
248248
if self.option_enabled("pinUvAuthToken") {
249-
assert!(self.option_enabled("clientPin"));
249+
// `pinUvAuthToken` and `clientPin` are independent options
250+
// (CTAP 2.2 §6.4). Tolerate the combination without `clientPin`
251+
// and fall back to a shared-secret-only flow.
252+
if !self.option_enabled("clientPin") {
253+
debug!(
254+
"Device advertises pinUvAuthToken without clientPin; \
255+
falling back to OnlyForSharedSecret"
256+
);
257+
return Some(Ctap2UserVerificationOperation::OnlyForSharedSecret);
258+
}
250259
debug!("getPinUvAuthTokenUsingPinWithPermissions");
251260
Some(Ctap2UserVerificationOperation::GetPinUvAuthTokenUsingPinWithPermissions)
252261
} else if self.option_enabled("clientPin") {
@@ -574,4 +583,17 @@ mod test {
574583
Some(Ctap2UserVerificationOperation::OnlyForSharedSecret)
575584
);
576585
}
586+
587+
#[test]
588+
fn device_pin_uv_auth_token_without_client_pin_does_not_panic() {
589+
let info = create_info(&[("pinUvAuthToken", true)]);
590+
assert_eq!(
591+
info.uv_operation(false),
592+
Some(Ctap2UserVerificationOperation::OnlyForSharedSecret)
593+
);
594+
assert_eq!(
595+
info.uv_operation(true),
596+
Some(Ctap2UserVerificationOperation::OnlyForSharedSecret)
597+
);
598+
}
577599
}

0 commit comments

Comments
 (0)