Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions libwebauthn/examples/prf_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ use tokio::sync::broadcast::Receiver;
use tracing_subscriber::{self, EnvFilter};

use libwebauthn::ops::webauthn::{
GetAssertionHmacOrPrfInput, GetAssertionRequest, GetAssertionRequestExtensions, PRFValue,
PrfInput, UserVerificationRequirement,
GetAssertionRequest, GetAssertionRequestExtensions, PRFValue, PrfInput,
UserVerificationRequirement,
};
use libwebauthn::pin::PinRequestReason;
use libwebauthn::proto::ctap2::{Ctap2PublicKeyCredentialDescriptor, Ctap2PublicKeyCredentialType};
Expand Down Expand Up @@ -126,16 +126,16 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
});

let eval_by_credential = HashMap::new();
let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf(PrfInput {
let prf = PrfInput {
eval,
eval_by_credential,
});
};

run_success_test(
&mut channel,
&credential,
&challenge,
hmac_or_prf,
prf,
"PRF output: ",
)
.await;
Expand All @@ -147,7 +147,7 @@ async fn run_success_test(
channel: &mut HidChannel<'_>,
credential: &Ctap2PublicKeyCredentialDescriptor,
challenge: &[u8; 32],
hmac_or_prf: GetAssertionHmacOrPrfInput,
prf: PrfInput,
printoutput: &str,
) {
let get_assertion = GetAssertionRequest {
Expand All @@ -156,7 +156,7 @@ async fn run_success_test(
allow: vec![credential.clone()],
user_verification: UserVerificationRequirement::Preferred,
extensions: Some(GetAssertionRequestExtensions {
hmac_or_prf: Some(hmac_or_prf),
prf: Some(prf),
..Default::default()
}),
timeout: TIMEOUT,
Expand Down
19 changes: 10 additions & 9 deletions libwebauthn/examples/webauthn_extensions_hid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ use tokio::sync::broadcast::Receiver;
use tracing_subscriber::{self, EnvFilter};

use libwebauthn::ops::webauthn::{
CredentialProtectionExtension, CredentialProtectionPolicy, GetAssertionHmacOrPrfInput,
GetAssertionRequest, GetAssertionRequestExtensions, HMACGetSecretInput, MakeCredentialRequest,
MakeCredentialsRequestExtensions, ResidentKeyRequirement, UserVerificationRequirement,
CredentialProtectionExtension, CredentialProtectionPolicy, GetAssertionRequest,
GetAssertionRequestExtensions, MakeCredentialRequest, MakeCredentialsRequestExtensions,
PRFValue, PrfInput, ResidentKeyRequirement, UserVerificationRequirement,
};
use libwebauthn::pin::PinRequestReason;
use libwebauthn::proto::ctap2::{
Expand Down Expand Up @@ -149,12 +149,13 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
user_verification: UserVerificationRequirement::Discouraged,
extensions: Some(GetAssertionRequestExtensions {
cred_blob: true,
hmac_or_prf: Some(GetAssertionHmacOrPrfInput::HmacGetSecret(
HMACGetSecretInput {
salt1: [1; 32],
salt2: None,
},
)),
prf: Some(PrfInput {
eval: Some(PRFValue {
first: [1; 32],
second: None,
}),
eval_by_credential: std::collections::HashMap::new(),
}),
..Default::default()
}),
timeout: TIMEOUT,
Expand Down
72 changes: 36 additions & 36 deletions libwebauthn/examples/webauthn_prf_hid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ use tokio::sync::broadcast::Receiver;
use tracing_subscriber::{self, EnvFilter};

use libwebauthn::ops::webauthn::{
GetAssertionHmacOrPrfInput, GetAssertionRequest, GetAssertionRequestExtensions,
MakeCredentialPrfInput, MakeCredentialRequest, MakeCredentialsRequestExtensions, PRFValue,
PrfInput, ResidentKeyRequirement, UserVerificationRequirement,
GetAssertionRequest, GetAssertionRequestExtensions, MakeCredentialPrfInput,
MakeCredentialRequest, MakeCredentialsRequestExtensions, PRFValue, PrfInput,
ResidentKeyRequirement, UserVerificationRequirement,
};
use libwebauthn::pin::PinRequestReason;
use libwebauthn::proto::ctap2::{
Expand Down Expand Up @@ -148,15 +148,15 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
second: None,
},
);
let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf(PrfInput {
let prf = PrfInput {
eval,
eval_by_credential,
});
};
run_success_test(
&mut channel,
&credential,
&challenge,
hmac_or_prf,
prf,
"eval_by_credential only",
)
.await;
Expand All @@ -175,15 +175,15 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
second: None,
},
);
let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf(PrfInput {
let prf = PrfInput {
eval,
eval_by_credential,
});
};
run_success_test(
&mut channel,
&credential,
&challenge,
hmac_or_prf,
prf,
"eval and eval_by_credential",
)
.await;
Expand All @@ -195,15 +195,15 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
});

let eval_by_credential = HashMap::new();
let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf(PrfInput {
let prf = PrfInput {
eval,
eval_by_credential,
});
};
run_success_test(
&mut channel,
&credential,
&challenge,
hmac_or_prf,
prf,
"eval only",
)
.await;
Expand Down Expand Up @@ -243,15 +243,15 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
second: None,
},
);
let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf(PrfInput {
let prf = PrfInput {
eval,
eval_by_credential,
});
};
run_success_test(
&mut channel,
&credential,
&challenge,
hmac_or_prf,
prf,
"eval and full list of eval_by_credential",
)
.await;
Expand Down Expand Up @@ -284,15 +284,15 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
second: Some([8; 32]),
},
);
let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf(PrfInput {
let prf = PrfInput {
eval,
eval_by_credential,
});
};
run_success_test(
&mut channel,
&credential,
&challenge,
hmac_or_prf,
prf,
"eval and non-fitting list of eval_by_credential",
)
.await;
Expand Down Expand Up @@ -322,15 +322,15 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
second: Some([8; 32]),
},
);
let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf(PrfInput {
let prf = PrfInput {
eval,
eval_by_credential,
});
};
run_success_test(
&mut channel,
&credential,
&challenge,
hmac_or_prf,
prf,
"No eval and non-fitting list of eval_by_credential (should have no extension output)",
)
.await;
Expand All @@ -349,15 +349,15 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
second: None,
},
);
let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf(PrfInput {
let prf = PrfInput {
eval,
eval_by_credential,
});
};
run_failed_test(
&mut channel,
Some(&credential),
&challenge,
hmac_or_prf,
prf,
"Wrongly encoded credential_id",
WebAuthnError::Platform(PlatformError::SyntaxError),
)
Expand All @@ -373,15 +373,15 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
second: None,
},
);
let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf(PrfInput {
let prf = PrfInput {
eval,
eval_by_credential,
});
};
run_failed_test(
&mut channel,
Some(&credential),
&challenge,
hmac_or_prf,
prf,
"Empty credential_id",
WebAuthnError::Platform(PlatformError::SyntaxError),
)
Expand All @@ -397,15 +397,15 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
second: None,
},
);
let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf(PrfInput {
let prf = PrfInput {
eval,
eval_by_credential,
});
};
run_failed_test(
&mut channel,
None,
&challenge,
hmac_or_prf,
prf,
"Empty allow_list, set eval_by_credential",
WebAuthnError::Platform(PlatformError::NotSupported),
)
Expand All @@ -418,7 +418,7 @@ async fn run_success_test(
channel: &mut HidChannel<'_>,
credential: &Ctap2PublicKeyCredentialDescriptor,
challenge: &[u8; 32],
hmac_or_prf: GetAssertionHmacOrPrfInput,
prf: PrfInput,
printoutput: &str,
) {
let get_assertion = GetAssertionRequest {
Expand All @@ -427,7 +427,7 @@ async fn run_success_test(
allow: vec![credential.clone()],
user_verification: UserVerificationRequirement::Discouraged,
extensions: Some(GetAssertionRequestExtensions {
hmac_or_prf: Some(hmac_or_prf),
prf: Some(prf),
..Default::default()
}),
timeout: TIMEOUT,
Expand All @@ -444,7 +444,7 @@ async fn run_success_test(
break Err(WebAuthnError::Ctap(ctap_error));
}
Err(err) => break Err(err),
};
}
}
.unwrap();
for (num, assertion) in response.assertions.iter().enumerate() {
Expand All @@ -459,7 +459,7 @@ async fn run_failed_test(
channel: &mut HidChannel<'_>,
credential: Option<&Ctap2PublicKeyCredentialDescriptor>,
challenge: &[u8; 32],
hmac_or_prf: GetAssertionHmacOrPrfInput,
prf: PrfInput,
printoutput: &str,
expected_error: WebAuthnError,
) {
Expand All @@ -469,7 +469,7 @@ async fn run_failed_test(
allow: credential.map(|x| vec![x.clone()]).unwrap_or_default(),
user_verification: UserVerificationRequirement::Discouraged,
extensions: Some(GetAssertionRequestExtensions {
hmac_or_prf: Some(hmac_or_prf),
prf: Some(prf),
..Default::default()
}),
timeout: TIMEOUT,
Expand All @@ -486,7 +486,7 @@ async fn run_failed_test(
break Err(WebAuthnError::Ctap(ctap_error));
}
Err(err) => break Err(err),
};
}
};

assert_eq!(response, Err(expected_error), "{printoutput}:");
Expand Down
31 changes: 14 additions & 17 deletions libwebauthn/src/ops/webauthn/get_assertion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,18 +81,11 @@ impl FromInnerModel<PublicKeyCredentialRequestOptionsJSON, GetAssertionRequestPa
rpid: &RelyingPartyId,
inner: PublicKeyCredentialRequestOptionsJSON,
) -> Result<Self, GetAssertionRequestParsingError> {
let hmac_or_prf = match inner.extensions.clone() {
Some(ext) => {
if let Some(prf) = ext.prf {
let prf_input = PrfInput::try_from(prf)?;
Some(GetAssertionHmacOrPrfInput::Prf(prf_input))
} else if let Some(hmac) = ext.hmac_get_secret {
let hmac_input = HMACGetSecretInput::try_from(hmac)?;
Some(GetAssertionHmacOrPrfInput::HmacGetSecret(hmac_input))
} else {
None
}
}
let prf = match inner.extensions.as_ref() {
Some(ext) => match &ext.prf {
Some(prf_json) => Some(PrfInput::try_from(prf_json.clone())?),
None => None,
},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should only do this change here and keep HMAC available for the Rust-API, which isn't a strict webauthn-compliant interface as opposed to the JSON-interface?

I've seen some questions of potential users of this extension floating around, but I'm not sure if they have changed by now to PRF.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm OK with the changes too, so I'm going to go ahead and merge.

I see that the Ctap2GetAssertion model still has HmacOrPrf; was there another part of the Rust-API that you think we should preserve?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking about leaving the option in GetAssertionRequestExtensions, as that is what a user would interact with, if they do not use JSON. So the Rust API might be a bit more 'powerful' in that sense, and the JSON-API more strict regarding webauthn-spec.

None => None,
};

Expand All @@ -106,7 +99,7 @@ impl FromInnerModel<PublicKeyCredentialRequestOptionsJSON, GetAssertionRequestPa
.large_blob
.clone()
.and_then(|lb| GetAssertionLargeBlobExtension::try_from(lb).ok()),
hmac_or_prf: hmac_or_prf.clone(),
prf: prf.clone(),
});

let timeout: Duration = inner
Expand Down Expand Up @@ -137,6 +130,9 @@ impl FromInnerModel<PublicKeyCredentialRequestOptionsJSON, GetAssertionRequestPa
}
}

/// Internal enum for CTAP-level HMAC/PRF handling.
/// At WebAuthn level, only PRF is exposed. This enum is used internally
/// to support both PRF (WebAuthn) and raw HMAC (CTAP testing).
#[derive(Debug, Clone, PartialEq)]
pub enum GetAssertionHmacOrPrfInput {
HmacGetSecret(HMACGetSecretInput),
Expand Down Expand Up @@ -279,7 +275,8 @@ pub struct GetAssertionLargeBlobExtensionOutput {
#[derive(Debug, Default, Clone, PartialEq)]
pub struct GetAssertionRequestExtensions {
pub cred_blob: bool,
pub hmac_or_prf: Option<GetAssertionHmacOrPrfInput>,
/// PRF extension input. At the CTAP level, this is converted to HMAC secret.
pub prf: Option<PrfInput>,
pub large_blob: Option<GetAssertionLargeBlobExtension>,
}

Expand Down Expand Up @@ -570,11 +567,11 @@ mod tests {

let req: GetAssertionRequest = GetAssertionRequest::from_json(&rpid, &req_json).unwrap();
if let Some(GetAssertionRequestExtensions {
hmac_or_prf:
Some(GetAssertionHmacOrPrfInput::Prf(PrfInput {
prf:
Some(PrfInput {
eval: Some(ref prf_value),
..
})),
}),
..
}) = &req.extensions
{
Expand Down
Loading
Loading