Skip to content

Commit aec13ac

Browse files
Fix #137: Add RP ID validation with IDNA normalization
1 parent 9672300 commit aec13ac

File tree

5 files changed

+402
-13
lines changed

5 files changed

+402
-13
lines changed

libwebauthn/Cargo.lock

Lines changed: 217 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

libwebauthn/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ libnfc = [
2727
base64-url = "3.0.0"
2828
dbus = "0.9.5"
2929
tracing = "0.1.29"
30+
idna = "1.0.3"
3031
maplit = "1.0.2"
3132
sha2 = "0.10.2"
3233
uuid = { version = "1.5.0", features = ["serde", "v4"] }

libwebauthn/src/ops/webauthn/get_assertion.rs

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,12 @@ pub enum GetAssertionRequestParsingError {
8383

8484
#[error("Not supported: {0}")]
8585
NotSupported(String),
86+
87+
#[error("Invalid relying party ID: {0}")]
88+
InvalidRelyingPartyId(String),
89+
90+
#[error("Mismatching relying party ID: {0} != {1}")]
91+
MismatchingRelyingPartyId(String, String),
8692
}
8793

8894
impl WebAuthnIDL<GetAssertionRequestParsingError> for GetAssertionRequest {
@@ -107,6 +113,18 @@ impl FromInnerModel<PublicKeyCredentialRequestOptionsJSON, GetAssertionRequestPa
107113
rpid: &RelyingPartyId,
108114
inner: PublicKeyCredentialRequestOptionsJSON,
109115
) -> Result<Self, GetAssertionRequestParsingError> {
116+
if let Some(relying_party_id) = inner.relying_party_id.as_deref() {
117+
let parsed = RelyingPartyId::try_from(relying_party_id).map_err(|err| {
118+
GetAssertionRequestParsingError::InvalidRelyingPartyId(err.to_string())
119+
})?;
120+
// TODO(#160): Add support for related origin per WebAuthn Level 3.
121+
if parsed.0 != rpid.0 {
122+
return Err(GetAssertionRequestParsingError::MismatchingRelyingPartyId(
123+
parsed.0,
124+
rpid.0.to_string(),
125+
));
126+
}
127+
}
110128
let hmac_or_prf = match inner.extensions.clone() {
111129
Some(ext) => {
112130
if let Some(prf) = ext.prf {
@@ -624,13 +642,30 @@ mod tests {
624642
}
625643

626644
#[test]
627-
fn test_request_from_json_ignore_request_rp_id() {
645+
fn test_request_from_json_invalid_rp_id() {
628646
let rpid = RelyingPartyId::try_from("example.org").unwrap();
629-
let req_json = json_field_rm(REQUEST_BASE_JSON, "rpId");
630-
let req_json = json_field_add(&req_json, "rpId", r#""another-example.org""#);
647+
let req_json = json_field_add(&REQUEST_BASE_JSON, "rpId", r#""example.org.""#);
631648

632-
let req: GetAssertionRequest = GetAssertionRequest::from_json(&rpid, &req_json).unwrap();
633-
assert_eq!(req, request_base());
649+
let result = GetAssertionRequest::from_json(&rpid, &req_json);
650+
assert!(matches!(
651+
result,
652+
Err(GetAssertionRequestParsingError::InvalidRelyingPartyId(_))
653+
));
654+
}
655+
656+
#[test]
657+
fn test_request_from_json_mismatching_rp_id() {
658+
let rpid = RelyingPartyId::try_from("example.org").unwrap();
659+
let req_json = json_field_add(&REQUEST_BASE_JSON, "rpId", r#""other.example.org""#);
660+
661+
let result = GetAssertionRequest::from_json(&rpid, &req_json);
662+
assert!(matches!(
663+
result,
664+
Err(GetAssertionRequestParsingError::MismatchingRelyingPartyId(
665+
_,
666+
_
667+
))
668+
));
634669
}
635670

636671
#[test]

0 commit comments

Comments
 (0)