Skip to content

Commit

Permalink
Add support for Webauthn L3 options (#435)
Browse files Browse the repository at this point in the history
* Add support for Webauthn L3 options

* fix up

* Apply feedback

* Authenticate is a builder

* Feedback
  • Loading branch information
Firstyear committed Apr 27, 2024
1 parent e7d7cef commit 3b80be7
Show file tree
Hide file tree
Showing 21 changed files with 350 additions and 152 deletions.
1 change: 1 addition & 0 deletions compat_tester/webauthn-rs-demo-shared/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ rust-version = "1.70.0"
core = ["webauthn-rs-core"]

[dependencies]
base64urlsafedata.workspace = true
serde.workspace = true

webauthn-rs-core = { workspace = true, optional = true }
Expand Down
4 changes: 3 additions & 1 deletion compat_tester/webauthn-rs-demo-shared/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#![deny(warnings)]
#![warn(unused_extern_crates)]

use base64urlsafedata::HumanBinaryData;
use serde::{Deserialize, Serialize};
#[cfg(feature = "core")]
use webauthn_rs_core::error::WebauthnError;

pub use webauthn_rs_core::proto::CredentialID;
pub use webauthn_rs_proto::{
AttestationConveyancePreference, AuthenticationExtensions, AuthenticatorAttachment,
COSEAlgorithm, CreationChallengeResponse, CredProtect, CredentialProtectionPolicy, ExtnState,
Expand All @@ -14,6 +14,8 @@ pub use webauthn_rs_proto::{
UserVerificationPolicy,
};

pub type CredentialID = HumanBinaryData;

#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub enum AttestationLevel {
None,
Expand Down
6 changes: 3 additions & 3 deletions compat_tester/webauthn-rs-demo/pkg/webauthn_rs_demo_wasm.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ export interface InitOutput {
readonly __wbindgen_malloc: (a: number, b: number) => number;
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
readonly __wbindgen_export_2: WebAssembly.Table;
readonly wasm_bindgen__convert__closures__invoke1_mut_ref__h015a6d4beac911b9: (a: number, b: number, c: number) => void;
readonly wasm_bindgen__convert__closures__invoke1__h9385f9b96e74d99b: (a: number, b: number, c: number) => void;
readonly _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hcb3dcc208685cd98: (a: number, b: number, c: number) => void;
readonly wasm_bindgen__convert__closures__invoke1_mut_ref__h0f42758389f9ef59: (a: number, b: number, c: number) => void;
readonly wasm_bindgen__convert__closures__invoke1__h3626c9e41e52d750: (a: number, b: number, c: number) => void;
readonly _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h458d161319ed8eb6: (a: number, b: number, c: number) => void;
readonly __wbindgen_add_to_stack_pointer: (a: number) => number;
readonly __wbindgen_exn_store: (a: number) => void;
readonly __wbindgen_free: (a: number, b: number, c: number) => void;
Expand Down
18 changes: 9 additions & 9 deletions compat_tester/webauthn-rs-demo/pkg/webauthn_rs_demo_wasm.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ function addBorrowedObject(obj) {
}
function __wbg_adapter_48(arg0, arg1, arg2) {
try {
wasm.wasm_bindgen__convert__closures__invoke1_mut_ref__h015a6d4beac911b9(arg0, arg1, addBorrowedObject(arg2));
wasm.wasm_bindgen__convert__closures__invoke1_mut_ref__h0f42758389f9ef59(arg0, arg1, addBorrowedObject(arg2));
} finally {
heap[stack_pointer++] = undefined;
}
Expand Down Expand Up @@ -260,11 +260,11 @@ function makeClosure(arg0, arg1, dtor, f) {
return real;
}
function __wbg_adapter_51(arg0, arg1, arg2) {
wasm.wasm_bindgen__convert__closures__invoke1__h9385f9b96e74d99b(arg0, arg1, addHeapObject(arg2));
wasm.wasm_bindgen__convert__closures__invoke1__h3626c9e41e52d750(arg0, arg1, addHeapObject(arg2));
}

function __wbg_adapter_54(arg0, arg1, arg2) {
wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hcb3dcc208685cd98(arg0, arg1, addHeapObject(arg2));
wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h458d161319ed8eb6(arg0, arg1, addHeapObject(arg2));
}

/**
Expand Down Expand Up @@ -944,16 +944,16 @@ function __wbg_get_imports() {
const ret = wasm.memory;
return addHeapObject(ret);
};
imports.wbg.__wbindgen_closure_wrapper1109 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 529, __wbg_adapter_48);
imports.wbg.__wbindgen_closure_wrapper1147 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 541, __wbg_adapter_48);
return addHeapObject(ret);
};
imports.wbg.__wbindgen_closure_wrapper1612 = function(arg0, arg1, arg2) {
const ret = makeClosure(arg0, arg1, 644, __wbg_adapter_51);
imports.wbg.__wbindgen_closure_wrapper1653 = function(arg0, arg1, arg2) {
const ret = makeClosure(arg0, arg1, 654, __wbg_adapter_51);
return addHeapObject(ret);
};
imports.wbg.__wbindgen_closure_wrapper1703 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 659, __wbg_adapter_54);
imports.wbg.__wbindgen_closure_wrapper1743 = function(arg0, arg1, arg2) {
const ret = makeMutClosure(arg0, arg1, 669, __wbg_adapter_54);
return addHeapObject(ret);
};

Expand Down
Binary file modified compat_tester/webauthn-rs-demo/pkg/webauthn_rs_demo_wasm_bg.wasm
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ export function run_app(a: number): void;
export function __wbindgen_malloc(a: number, b: number): number;
export function __wbindgen_realloc(a: number, b: number, c: number, d: number): number;
export const __wbindgen_export_2: WebAssembly.Table;
export function wasm_bindgen__convert__closures__invoke1_mut_ref__h015a6d4beac911b9(a: number, b: number, c: number): void;
export function wasm_bindgen__convert__closures__invoke1__h9385f9b96e74d99b(a: number, b: number, c: number): void;
export function _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hcb3dcc208685cd98(a: number, b: number, c: number): void;
export function wasm_bindgen__convert__closures__invoke1_mut_ref__h0f42758389f9ef59(a: number, b: number, c: number): void;
export function wasm_bindgen__convert__closures__invoke1__h3626c9e41e52d750(a: number, b: number, c: number): void;
export function _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h458d161319ed8eb6(a: number, b: number, c: number): void;
export function __wbindgen_add_to_stack_pointer(a: number): number;
export function __wbindgen_exn_store(a: number): void;
export function __wbindgen_free(a: number, b: number, c: number): void;
8 changes: 6 additions & 2 deletions compat_tester/webauthn-rs-demo/src/actors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -367,11 +367,15 @@ impl WebauthnActor {
.ok_or(WebauthnError::CredentialNotFound)?;

self.wan
.generate_challenge_authenticate(vec![cred], uv, extensions, None)
.new_challenge_authenticate_builder(vec![cred], uv)
.map(|builder| builder.extensions(extensions))
.and_then(|b| self.wan.generate_challenge_authenticate(b))
}
None => self
.wan
.generate_challenge_authenticate(creds, None, extensions, None),
.new_challenge_authenticate_builder(creds, None)
.map(|builder| builder.extensions(extensions))
.and_then(|b| self.wan.generate_challenge_authenticate(b)),
}?;

debug!("complete ChallengeAuthenticate -> {:?}", acr);
Expand Down
9 changes: 4 additions & 5 deletions sshkey-attest/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,14 @@ use uuid::Uuid;
pub use webauthn_rs_core::error::WebauthnError;
use webauthn_rs_core::{
attestation::{
assert_packed_attest_req, validate_extension, verify_attestation_ca_chain,
AttestationFormat, FidoGenCeAaguid,
assert_packed_attest_req, validate_extension, verify_attestation_ca_chain, FidoGenCeAaguid,
},
crypto::{compute_sha256, verify_signature},
internals::AuthenticatorData,
proto::{
AttestationCaList, AttestationMetadata, COSEAlgorithm, COSEKey, COSEKeyType,
CredentialProtectionPolicy, ExtnState, ParsedAttestation, ParsedAttestationData,
RegisteredExtensions, Registration,
AttestationCaList, AttestationFormat, AttestationMetadata, COSEAlgorithm, COSEKey,
COSEKeyType, CredentialProtectionPolicy, ExtnState, ParsedAttestation,
ParsedAttestationData, RegisteredExtensions, Registration,
},
};

Expand Down
3 changes: 1 addition & 2 deletions sshkey-attest/src/proto.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
//! Serialisable formats of attested ssh keys

use serde::{Deserialize, Serialize};
use webauthn_rs_core::attestation::AttestationFormat;
use webauthn_rs_core::proto::{ParsedAttestation, RegisteredExtensions};
use webauthn_rs_core::proto::{AttestationFormat, ParsedAttestation, RegisteredExtensions};

pub use sshkeys::PublicKey;

Expand Down
13 changes: 6 additions & 7 deletions webauthn-authenticator-rs/examples/authenticate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,16 +280,15 @@ async fn main() {
.connect_provider(CableRequestType::GetAssertion, &ui)
.await;
let (chal, auth_state) = wan
.generate_challenge_authenticate(
vec![cred.clone()],
None,
Some(RequestAuthenticationExtensions {
.new_challenge_authenticate_builder(vec![cred.clone()], None)
.map(|builder| {
builder.extensions(Some(RequestAuthenticationExtensions {
appid: Some("example.app.id".to_string()),
uvm: None,
hmac_get_secret: None,
}),
None,
)
}))
})
.and_then(|b| wan.generate_challenge_authenticate(b))
.unwrap();

let r = u
Expand Down
3 changes: 3 additions & 0 deletions webauthn-authenticator-rs/src/authenticator_hashed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,9 @@ pub fn perform_register_with_request(
timeout: Some(timeout_ms),
exclude_credentials: Some(request.exclude_list),
// TODO
hints: None,
attestation: None,
attestation_formats: None,
authenticator_selection: None,
extensions: None,
};
Expand Down Expand Up @@ -192,6 +194,7 @@ pub fn perform_auth_with_request(
rp_id: request.rp_id,
allow_credentials: request.allow_list,
// TODO
hints: None,
user_verification: webauthn_rs_proto::UserVerificationPolicy::Preferred,
extensions: None,
};
Expand Down
3 changes: 2 additions & 1 deletion webauthn-authenticator-rs/src/softpasskey.rs
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,8 @@ mod tests {
let cred = wan.register_credential(&r, &reg_state, None).unwrap();

let (chal, auth_state) = wan
.generate_challenge_authenticate(vec![cred], None, None, None)
.new_challenge_authenticate_builder(vec![cred], None)
.and_then(|b| wan.generate_challenge_authenticate(b))
.unwrap();

let r = wa
Expand Down
6 changes: 4 additions & 2 deletions webauthn-authenticator-rs/src/softtoken.rs
Original file line number Diff line number Diff line change
Expand Up @@ -928,7 +928,8 @@ mod tests {
info!("Credential -> {:?}", cred);

let (chal, auth_state) = wan
.generate_challenge_authenticate(vec![cred], None, None, None)
.new_challenge_authenticate_builder(vec![cred], None)
.and_then(|b| wan.generate_challenge_authenticate(b))
.unwrap();

let r = wa
Expand Down Expand Up @@ -1014,7 +1015,8 @@ mod tests {
let mut wa = WebauthnAuthenticator::new(soft_token);

let (chal, auth_state) = wan
.generate_challenge_authenticate(vec![cred], None, None, None)
.new_challenge_authenticate_builder(vec![cred], None)
.and_then(|b| wan.generate_challenge_authenticate(b))
.unwrap();

let r = wa
Expand Down
48 changes: 2 additions & 46 deletions webauthn-rs-core/src/attestation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,52 +341,6 @@ where
})
}

/// The type of attestation on the credential
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, Hash)]
pub enum AttestationFormat {
/// Packed attestation
Packed,
/// TPM attestation (like Micrsoft)
Tpm,
/// Android hardware attestation
AndroidKey,
/// Older Android Safety Net
AndroidSafetyNet,
/// Old U2F attestation type
FIDOU2F,
/// Apple touchID/faceID
AppleAnonymous,
/// No attestation
None,
}

impl AttestationFormat {
/// Only a small number of devices correctly report their transports. These are
/// limited to attested devices, and exclusively packed (fido2) and tpms. Most
/// other devices/browsers will get this wrong, meaning that authentication will
/// fail or not offer the correct transports to the user.
pub(crate) fn transports_valid(&self) -> bool {
matches!(self, AttestationFormat::Packed | AttestationFormat::Tpm)
}
}

impl TryFrom<&str> for AttestationFormat {
type Error = WebauthnError;

fn try_from(a: &str) -> Result<AttestationFormat, Self::Error> {
match a {
"packed" => Ok(AttestationFormat::Packed),
"tpm" => Ok(AttestationFormat::Tpm),
"android-key" => Ok(AttestationFormat::AndroidKey),
"android-safetynet" => Ok(AttestationFormat::AndroidSafetyNet),
"fido-u2f" => Ok(AttestationFormat::FIDOU2F),
"apple" => Ok(AttestationFormat::AppleAnonymous),
"none" => Ok(AttestationFormat::None),
_ => Err(WebauthnError::AttestationNotSupported),
}
}
}

// Perform the Verification procedure for 8.2. Packed Attestation Statement Format
// https://w3c.github.io/webauthn/#sctn-packed-attestation
pub(crate) fn verify_packed_attestation(
Expand Down Expand Up @@ -1432,9 +1386,11 @@ pub fn verify_attestation_ca_chain<'a>(
ParsedAttestationData::AnonCa(chain) => chain,
ParsedAttestationData::Self_ | ParsedAttestationData::None => {
// nothing to check
debug!("No attestation present");
return Ok(None);
}
ParsedAttestationData::ECDAA | ParsedAttestationData::Uncertain => {
debug!("attestation is an unsupported format");
return Err(WebauthnError::AttestationNotVerifiable);
}
};
Expand Down
Loading

0 comments on commit 3b80be7

Please sign in to comment.