Skip to content

Commit 605f30e

Browse files
fix(pin): require exact pinUvAuthToken length per protocol
The boundary check accepted any token of at least min_token_len bytes, so protocol one accepted e.g. a 20-byte token and protocol two a 33-byte one. CTAP 2.1 requires the decrypted pinUvAuthToken to be exactly 16 or 32 bytes for protocol one and exactly 32 bytes for protocol two. Replace the minimum-length check with an exact-length helper and unit-test it.
1 parent 9236103 commit 605f30e

1 file changed

Lines changed: 28 additions & 10 deletions

File tree

libwebauthn/src/webauthn/pin_uv_auth_token.rs

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ pub(crate) async fn select_uv_proto(
4949
None
5050
}
5151

52+
/// Spec-valid decrypted pinUvAuthToken length: 16 or 32 bytes for protocol one, exactly 32 for protocol two.
53+
fn pin_uv_auth_token_len_valid(version: Ctap2PinUvAuthProtocol, len: usize) -> bool {
54+
match version {
55+
Ctap2PinUvAuthProtocol::One => len == 16 || len == 32,
56+
Ctap2PinUvAuthProtocol::Two => len == 32,
57+
}
58+
}
59+
5260
#[instrument(skip_all)]
5361
pub(crate) async fn user_verification<R, C>(
5462
channel: &mut C,
@@ -359,18 +367,12 @@ where
359367
let uv_auth_token =
360368
uv_proto.decrypt(&shared_secret, &encrypted_pin_uv_auth_token)?;
361369

362-
// pinUvAuthToken is 16 bytes for PUAP1 and 32 bytes for PUAP2.
363-
// Reject a shorter token before it is used as a key downstream.
364-
let min_token_len = match uv_proto.version() {
365-
Ctap2PinUvAuthProtocol::One => 16,
366-
Ctap2PinUvAuthProtocol::Two => 32,
367-
};
368-
if uv_auth_token.len() < min_token_len {
370+
// Reject a spec-invalid token length before it is used as a key downstream.
371+
if !pin_uv_auth_token_len_valid(uv_proto.version(), uv_auth_token.len()) {
369372
error!(
370373
protocol = ?uv_proto.version(),
371374
token_len = uv_auth_token.len(),
372-
min_expected = min_token_len,
373-
"Decrypted pinUvAuthToken is shorter than required"
375+
"Decrypted pinUvAuthToken has an invalid length"
374376
);
375377
return Err(Error::Ctap(CtapError::Other));
376378
}
@@ -563,9 +565,25 @@ mod test {
563565
UvUpdate,
564566
};
565567

566-
use super::{user_verification, Error};
568+
use super::{pin_uv_auth_token_len_valid, user_verification, Error};
567569
const TIMEOUT: Duration = Duration::from_secs(1);
568570

571+
#[test]
572+
fn pin_uv_auth_token_len_valid_enforces_spec_lengths() {
573+
use Ctap2PinUvAuthProtocol::{One, Two};
574+
// Protocol one: the authenticator may pick 16 or 32 bytes, nothing else.
575+
assert!(pin_uv_auth_token_len_valid(One, 16));
576+
assert!(pin_uv_auth_token_len_valid(One, 32));
577+
for bad in [0, 15, 17, 20, 31, 33, 64] {
578+
assert!(!pin_uv_auth_token_len_valid(One, bad), "len {bad}");
579+
}
580+
// Protocol two: exactly 32 bytes.
581+
assert!(pin_uv_auth_token_len_valid(Two, 32));
582+
for bad in [0, 15, 16, 17, 31, 33, 64] {
583+
assert!(!pin_uv_auth_token_len_valid(Two, bad), "len {bad}");
584+
}
585+
}
586+
569587
fn create_info(
570588
options: &[(&'static str, bool)],
571589
extensions: Option<&[&'static str]>,

0 commit comments

Comments
 (0)