Skip to content
Open
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
4 changes: 3 additions & 1 deletion libwebauthn/examples/prf_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,9 @@ async fn run_success_test(
) {
let get_assertion = GetAssertionRequest {
relying_party_id: "demo.yubico.com".to_owned(),
hash: Vec::from(challenge),
challenge: Vec::from(challenge),
origin: "demo.yubico.com".to_string(),
cross_origin: None,
allow: vec![credential.clone()],
user_verification: UserVerificationRequirement::Preferred,
extensions: Some(GetAssertionRequestExtensions {
Expand Down
7 changes: 5 additions & 2 deletions libwebauthn/examples/webauthn_cable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,9 @@ pub async fn main() -> Result<(), Box<dyn Error>> {

// Make Credentials ceremony
let make_credentials_request = MakeCredentialRequest {
challenge: Vec::from(challenge),
origin: "example.org".to_owned(),
hash: Vec::from(challenge),
cross_origin: None,
relying_party: Ctap2PublicKeyCredentialRpEntity::new("example.org", "example.org"),
user: Ctap2PublicKeyCredentialUserEntity::new(&user_id, "mario.rossi", "Mario Rossi"),
resident_key: Some(ResidentKeyRequirement::Discouraged),
Expand Down Expand Up @@ -159,7 +160,9 @@ pub async fn main() -> Result<(), Box<dyn Error>> {

let get_assertion = GetAssertionRequest {
relying_party_id: "example.org".to_owned(),
hash: Vec::from(challenge),
challenge: Vec::from(challenge),
origin: "example.org".to_string(),
cross_origin: None,
allow: vec![credential],
user_verification: UserVerificationRequirement::Discouraged,
extensions: Some(GetAssertionRequestExtensions::default()),
Expand Down
7 changes: 5 additions & 2 deletions libwebauthn/examples/webauthn_extensions_hid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,9 @@ pub async fn main() -> Result<(), Box<dyn Error>> {

// Make Credentials ceremony
let make_credentials_request = MakeCredentialRequest {
challenge: Vec::from(challenge),
origin: "example.org".to_owned(),
hash: Vec::from(challenge),
cross_origin: None,
relying_party: Ctap2PublicKeyCredentialRpEntity::new("example.org", "example.org"),
user: Ctap2PublicKeyCredentialUserEntity::new(&user_id, "mario.rossi", "Mario Rossi"),
resident_key: Some(ResidentKeyRequirement::Required),
Expand Down Expand Up @@ -144,7 +145,9 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
(&response.authenticator_data).try_into().unwrap();
let get_assertion = GetAssertionRequest {
relying_party_id: "example.org".to_owned(),
hash: Vec::from(challenge),
challenge: Vec::from(challenge),
origin: "example.org".to_string(),
cross_origin: None,
allow: vec![credential],
user_verification: UserVerificationRequirement::Discouraged,
extensions: Some(GetAssertionRequestExtensions {
Expand Down
7 changes: 5 additions & 2 deletions libwebauthn/examples/webauthn_hid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,9 @@ pub async fn main() -> Result<(), Box<dyn Error>> {

// Make Credentials ceremony
let make_credentials_request = MakeCredentialRequest {
challenge: Vec::from(challenge),
origin: "example.org".to_owned(),
hash: Vec::from(challenge),
cross_origin: None,
relying_party: Ctap2PublicKeyCredentialRpEntity::new("example.org", "example.org"),
user: Ctap2PublicKeyCredentialUserEntity::new(&user_id, "mario.rossi", "Mario Rossi"),
resident_key: Some(ResidentKeyRequirement::Discouraged),
Expand Down Expand Up @@ -126,7 +127,9 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
(&response.authenticator_data).try_into().unwrap();
let get_assertion = GetAssertionRequest {
relying_party_id: "example.org".to_owned(),
hash: Vec::from(challenge),
challenge: Vec::from(challenge),
origin: "example.org".to_string(),
cross_origin: None,
allow: vec![credential],
user_verification: UserVerificationRequirement::Discouraged,
extensions: Some(GetAssertionRequestExtensions::default()),
Expand Down
29 changes: 28 additions & 1 deletion libwebauthn/examples/webauthn_json_hid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ use tokio::sync::broadcast::Receiver;
use tracing_subscriber::{self, EnvFilter};

use libwebauthn::ops::webauthn::{
GetAssertionRequest, MakeCredentialRequest, RelyingPartyId, WebAuthnIDL as _,
GetAssertionRequest, JsonFormat, MakeCredentialRequest, RelyingPartyId,
WebAuthnIDL as _, WebAuthnIDLResponse as _,
};
use libwebauthn::pin::PinRequestReason;
use libwebauthn::transport::hid::list_devices;
Expand Down Expand Up @@ -132,6 +133,20 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
.unwrap();
println!("WebAuthn MakeCredential response: {:?}", response);

// Serialize the response back to JSON using the original request as context.
// The request contains the client_data_json bytes that were built during parsing.
match response.to_json(&make_credentials_request, JsonFormat::Prettified) {
Ok(response_json) => {
println!(
"WebAuthn MakeCredential response (JSON):\n{}",
response_json
);
}
Err(e) => {
eprintln!("Failed to serialize MakeCredential response: {:?}", e);
}
}

let request_json = r#"
{
"challenge": "Y3JlZGVudGlhbHMtZm9yLWxpbnV4L2xpYndlYmF1dGhu",
Expand Down Expand Up @@ -160,6 +175,18 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
}
.unwrap();
println!("WebAuthn GetAssertion response: {:?}", response);

// Serialize the response back to JSON using the original request as context.
for assertion in &response.assertions {
match assertion.to_json(&get_assertion, JsonFormat::Prettified) {
Ok(assertion_json) => {
println!("WebAuthn GetAssertion response (JSON):\n{}", assertion_json);
}
Err(e) => {
eprintln!("Failed to serialize GetAssertion response: {:?}", e);
}
}
}
}

Ok(())
Expand Down
7 changes: 5 additions & 2 deletions libwebauthn/examples/webauthn_preflight_hid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,9 @@ async fn make_credential_call(
) -> Result<Ctap2PublicKeyCredentialDescriptor, WebAuthnError> {
let challenge: [u8; 32] = thread_rng().gen();
let make_credentials_request = MakeCredentialRequest {
challenge: Vec::from(challenge),
origin: "example.org".to_owned(),
hash: Vec::from(challenge),
cross_origin: None,
relying_party: Ctap2PublicKeyCredentialRpEntity::new("example.org", "example.org"),
user: Ctap2PublicKeyCredentialUserEntity::new(&user_id, "mario.rossi", "Mario Rossi"),
resident_key: Some(ResidentKeyRequirement::Discouraged),
Expand Down Expand Up @@ -199,7 +200,9 @@ async fn get_assertion_call(
let challenge: [u8; 32] = thread_rng().gen();
let get_assertion = GetAssertionRequest {
relying_party_id: "example.org".to_owned(),
hash: Vec::from(challenge),
challenge: Vec::from(challenge),
origin: "example.org".to_string(),
cross_origin: None,
allow: allow_list,
user_verification: UserVerificationRequirement::Discouraged,
extensions: Some(GetAssertionRequestExtensions::default()),
Expand Down
15 changes: 10 additions & 5 deletions libwebauthn/examples/webauthn_prf_hid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ use tracing_subscriber::{self, EnvFilter};

use libwebauthn::ops::webauthn::{
GetAssertionHmacOrPrfInput, GetAssertionRequest, GetAssertionRequestExtensions,
MakeCredentialPrfInput, MakeCredentialRequest, MakeCredentialsRequestExtensions, PRFValue,
PrfInput, ResidentKeyRequirement, UserVerificationRequirement,
MakeCredentialPrfInput, MakeCredentialRequest, MakeCredentialsRequestExtensions,
PRFValue, PrfInput, ResidentKeyRequirement, UserVerificationRequirement,
};
use libwebauthn::pin::PinRequestReason;
use libwebauthn::proto::ctap2::{
Expand Down Expand Up @@ -99,8 +99,9 @@ pub async fn main() -> Result<(), Box<dyn Error>> {

// Make Credentials ceremony
let make_credentials_request = MakeCredentialRequest {
challenge: Vec::from(challenge),
origin: "example.org".to_owned(),
hash: Vec::from(challenge),
cross_origin: None,
relying_party: Ctap2PublicKeyCredentialRpEntity::new("example.org", "example.org"),
user: Ctap2PublicKeyCredentialUserEntity::new(&user_id, "mario.rossi", "Mario Rossi"),
resident_key: Some(ResidentKeyRequirement::Required),
Expand Down Expand Up @@ -423,7 +424,9 @@ async fn run_success_test(
) {
let get_assertion = GetAssertionRequest {
relying_party_id: "example.org".to_owned(),
hash: Vec::from(challenge),
challenge: Vec::from(challenge),
origin: "example.org".to_string(),
cross_origin: None,
allow: vec![credential.clone()],
user_verification: UserVerificationRequirement::Discouraged,
extensions: Some(GetAssertionRequestExtensions {
Expand Down Expand Up @@ -465,7 +468,9 @@ async fn run_failed_test(
) {
let get_assertion = GetAssertionRequest {
relying_party_id: "example.org".to_owned(),
hash: Vec::from(challenge),
challenge: Vec::from(challenge),
origin: "example.org".to_string(),
cross_origin: None,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we have any tests where cross_origin is not None?

allow: credential.map(|x| vec![x.clone()]).unwrap_or_default(),
user_verification: UserVerificationRequirement::Discouraged,
extensions: Some(GetAssertionRequestExtensions {
Expand Down
10 changes: 6 additions & 4 deletions libwebauthn/src/ops/u2f.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ use x509_parser::nom::AsBytes;
use super::webauthn::MakeCredentialRequest;
use crate::fido::{AttestedCredentialData, AuthenticatorData, AuthenticatorDataFlags};
use crate::ops::webauthn::{
GetAssertionRequest, GetAssertionResponse,
MakeCredentialResponse, UserVerificationRequirement,
GetAssertionRequest, GetAssertionResponse, MakeCredentialResponse,
UserVerificationRequirement,
};
use crate::proto::ctap1::{Ctap1RegisterRequest, Ctap1SignRequest};
use crate::proto::ctap1::{Ctap1RegisterResponse, Ctap1SignResponse};
Expand Down Expand Up @@ -133,7 +133,7 @@ impl UpgradableResponse<MakeCredentialResponse, MakeCredentialRequest> for Regis
// states a different length range.
let attestation_statement = Ctap2AttestationStatement::FidoU2F(FidoU2fAttestationStmt {
signature: ByteBuf::from(self.signature.clone()),
certificate: ByteBuf::from(self.attestation.clone()),
certificates: vec![ByteBuf::from(self.attestation.clone())],
});

// Let attestationObject be a CBOR map (see "attObj" in Generating an Attestation Object [WebAuthn]) with the
Expand Down Expand Up @@ -201,7 +201,9 @@ impl UpgradableResponse<GetAssertionResponse, SignRequest> for SignResponse {
// something like that here. In reality, we only need `extensions: None` currently.
let orig_request = GetAssertionRequest {
relying_party_id: String::new(), // We don't have access to that info here, but we don't need it either
hash: request.app_id_hash.clone(),
challenge: Vec::new(), // U2F path doesn't use client_data for response serialization
origin: String::new(),
cross_origin: None,
allow: vec![Ctap2PublicKeyCredentialDescriptor {
r#type: Ctap2PublicKeyCredentialType::PublicKey,
id: request.key_handle.clone().into(),
Expand Down
11 changes: 7 additions & 4 deletions libwebauthn/src/ops/webauthn/client_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ pub struct ClientData {
}

impl ClientData {
pub fn hash(&self) -> Vec<u8> {
/// Returns the canonical JSON representation of the client data.
pub fn to_json_bytes(&self) -> Vec<u8> {
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 it would be more versatile to make this to_json() and return a String. Those who need the bytes can then call into_bytes() on the result.

let op_str = match &self.operation {
Operation::MakeCredential => "webauthn.create",
Operation::GetAssertion => "webauthn.get",
Expand All @@ -25,11 +26,13 @@ impl ClientData {
} else {
"false"
};
let json =
format!("{{\"type\":\"{op_str}\",\"challenge\":\"{challenge_str}\",\"origin\":\"{origin_str}\",\"crossOrigin\":{cross_origin_str}}}");
format!("{{\"type\":\"{op_str}\",\"challenge\":\"{challenge_str}\",\"origin\":\"{origin_str}\",\"crossOrigin\":{cross_origin_str}}}").into_bytes()
}

pub fn hash(&self) -> Vec<u8> {
let json_bytes = self.to_json_bytes();
let mut hasher = Sha256::new();
hasher.update(json.as_bytes());
hasher.update(&json_bytes);
hasher.finalize().to_vec()
}
}
Expand Down
Loading