Skip to content

Commit d285dc0

Browse files
fix(ctap2): decode unsignedExtensionOutputs at GetAssertion 0x08
GetAssertion response index 0x08 is unsignedExtensionOutputs, a CBOR map, not enterprise attestation. Decoding it as Option<bool> made the whole response fail to parse. Decode it as an optional map and remove the speculative enterprise_attestation and attestation_statement fields, which no spec defines for GetAssertion.
1 parent ba35dde commit d285dc0

3 files changed

Lines changed: 29 additions & 17 deletions

File tree

libwebauthn/src/ops/u2f.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,7 @@ impl UpgradableResponse<GetAssertionResponse, SignRequest> for SignResponse {
216216
credentials_count: None,
217217
user_selected: None,
218218
large_blob_key: None,
219-
enterprise_attestation: None,
220-
attestation_statement: None,
219+
unsigned_extension_outputs: None,
221220
};
222221

223222
// This isn't great, but we have no access to the original request, and need to construct

libwebauthn/src/ops/webauthn/get_assertion.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ use crate::{
2525
},
2626
pin::PinUvAuthProtocol,
2727
proto::ctap2::{
28-
Ctap2AttestationStatement, Ctap2GetAssertionResponseExtensions,
29-
Ctap2PublicKeyCredentialDescriptor, Ctap2PublicKeyCredentialUserEntity,
28+
Ctap2GetAssertionResponseExtensions, Ctap2PublicKeyCredentialDescriptor,
29+
Ctap2PublicKeyCredentialUserEntity,
3030
},
3131
webauthn::CtapError,
3232
};
@@ -452,8 +452,6 @@ pub struct Assertion {
452452
pub credentials_count: Option<u32>,
453453
pub user_selected: Option<bool>,
454454
pub unsigned_extensions_output: Option<GetAssertionResponseUnsignedExtensions>,
455-
pub enterprise_attestation: Option<bool>,
456-
pub attestation_statement: Option<Ctap2AttestationStatement>,
457455
}
458456

459457
impl WebAuthnIDLResponse for Assertion {
@@ -1229,8 +1227,6 @@ mod tests {
12291227
credentials_count: None,
12301228
user_selected: None,
12311229
unsigned_extensions_output: None,
1232-
enterprise_attestation: None,
1233-
attestation_statement: None,
12341230
}
12351231
}
12361232

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

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -370,11 +370,7 @@ pub struct Ctap2GetAssertionResponse {
370370

371371
#[serde(skip_serializing_if = "Option::is_none")]
372372
#[serde(index = 0x08)]
373-
pub enterprise_attestation: Option<bool>,
374-
375-
#[serde(skip_serializing_if = "Option::is_none")]
376-
#[serde(index = 0x09)]
377-
pub attestation_statement: Option<Ctap2AttestationStatement>,
373+
pub unsigned_extension_outputs: Option<BTreeMap<Value, Value>>,
378374
}
379375

380376
impl Ctap2UserVerifiableRequest for Ctap2GetAssertionRequest {
@@ -462,8 +458,6 @@ impl Ctap2GetAssertionResponse {
462458
credentials_count: self.credentials_count,
463459
user_selected: self.user_selected,
464460
unsigned_extensions_output,
465-
enterprise_attestation: self.enterprise_attestation,
466-
attestation_statement: self.attestation_statement,
467461
}
468462
}
469463
}
@@ -575,8 +569,7 @@ mod tests {
575569
credentials_count: None,
576570
user_selected: None,
577571
large_blob_key: None,
578-
enterprise_attestation: None,
579-
attestation_statement: None,
572+
unsigned_extension_outputs: None,
580573
}
581574
}
582575

@@ -660,4 +653,28 @@ mod tests {
660653

661654
assert!(large_blob.blob.is_none());
662655
}
656+
657+
#[test]
658+
fn decodes_unsigned_extension_outputs_at_index_0x08() {
659+
// 0x08 is unsignedExtensionOutputs (a CBOR map), not enterprise attestation.
660+
let mut auth_data = vec![0u8; 37];
661+
auth_data[32] = AuthenticatorDataFlags::USER_PRESENT.bits();
662+
663+
let mut ueo = BTreeMap::new();
664+
ueo.insert(
665+
Value::Text("thirdPartyPayment".to_string()),
666+
Value::Bool(true),
667+
);
668+
669+
let mut response: BTreeMap<u64, Value> = BTreeMap::new();
670+
response.insert(0x02, Value::Bytes(auth_data));
671+
response.insert(0x03, Value::Bytes(vec![0xAAu8; 64]));
672+
response.insert(0x08, Value::Map(ueo.clone()));
673+
674+
let bytes = crate::proto::ctap2::cbor::to_vec(&response).unwrap();
675+
let parsed: Ctap2GetAssertionResponse =
676+
crate::proto::ctap2::cbor::from_slice(&bytes).unwrap();
677+
678+
assert_eq!(parsed.unsigned_extension_outputs, Some(ueo));
679+
}
663680
}

0 commit comments

Comments
 (0)