From c70b1bbcc877f1d392d3dcdfce60e0d719eac979 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Me=C3=9Fmer?= Date: Mon, 13 Nov 2023 22:05:08 -0800 Subject: [PATCH] Feature/configurable timeout (#385) --- .../examples/authenticate.rs | 3 ++- webauthn-rs-core/src/constants.rs | 5 +++-- webauthn-rs-core/src/core.rs | 19 +++++++++++++------ webauthn-rs/src/lib.rs | 13 ++++++++++++- 4 files changed, 30 insertions(+), 10 deletions(-) diff --git a/webauthn-authenticator-rs/examples/authenticate.rs b/webauthn-authenticator-rs/examples/authenticate.rs index a48cbed7..d74d4416 100644 --- a/webauthn-authenticator-rs/examples/authenticate.rs +++ b/webauthn-authenticator-rs/examples/authenticate.rs @@ -4,6 +4,7 @@ extern crate tracing; #[cfg(feature = "softtoken")] use std::fs::OpenOptions; use std::io::{stdin, stdout, Write}; +use std::time::Duration; use clap::clap_derive::ValueEnum; #[cfg(any(feature = "cable", feature = "softtoken"))] @@ -236,7 +237,7 @@ async fn main() { "https://localhost:8080/auth", "localhost", vec![url::Url::parse("https://localhost:8080").unwrap()], - Some(1), + Some(Duration::from_millis(1)), None, None, ); diff --git a/webauthn-rs-core/src/constants.rs b/webauthn-rs-core/src/constants.rs index db188827..0ba5661b 100644 --- a/webauthn-rs-core/src/constants.rs +++ b/webauthn-rs-core/src/constants.rs @@ -1,4 +1,5 @@ +use std::time::Duration; + // Can this ever change? pub const CHALLENGE_SIZE_BYTES: usize = 32; -// Allegedly this is milliseconds? -pub const AUTHENTICATOR_TIMEOUT: u32 = 60000; +pub const DEFAULT_AUTHENTICATOR_TIMEOUT: Duration = Duration::from_millis(60000); diff --git a/webauthn-rs-core/src/core.rs b/webauthn-rs-core/src/core.rs index fb0e8ff1..285a3eee 100644 --- a/webauthn-rs-core/src/core.rs +++ b/webauthn-rs-core/src/core.rs @@ -18,6 +18,7 @@ use rand::prelude::*; use std::collections::BTreeSet; use std::convert::TryFrom; +use std::time::Duration; use url::Url; use crate::attestation::{ @@ -25,7 +26,7 @@ use crate::attestation::{ verify_apple_anonymous_attestation, verify_attestation_ca_chain, verify_fidou2f_attestation, verify_packed_attestation, verify_tpm_attestation, AttestationFormat, }; -use crate::constants::{AUTHENTICATOR_TIMEOUT, CHALLENGE_SIZE_BYTES}; +use crate::constants::{CHALLENGE_SIZE_BYTES, DEFAULT_AUTHENTICATOR_TIMEOUT}; use crate::crypto::compute_sha256; use crate::error::WebauthnError; use crate::internals::*; @@ -54,7 +55,7 @@ pub struct WebauthnCore { rp_id: String, rp_id_hash: [u8; 32], allowed_origins: Vec, - authenticator_timeout: u32, + authenticator_timeout: Duration, require_valid_counter_value: bool, #[allow(unused)] ignore_unsupported_attestation_formats: bool, @@ -84,7 +85,7 @@ impl WebauthnCore { rp_name: &str, rp_id: &str, allowed_origins: Vec, - authenticator_timeout: Option, + authenticator_timeout: Option, allow_subdomains_origin: Option, allow_any_port: Option, ) -> Self { @@ -94,7 +95,7 @@ impl WebauthnCore { rp_id: rp_id.to_string(), rp_id_hash, allowed_origins, - authenticator_timeout: authenticator_timeout.unwrap_or(AUTHENTICATOR_TIMEOUT), + authenticator_timeout: authenticator_timeout.unwrap_or(DEFAULT_AUTHENTICATOR_TIMEOUT), require_valid_counter_value: true, ignore_unsupported_attestation_formats: true, allow_cross_origin: false, @@ -213,6 +214,9 @@ impl WebauthnCore { Some(ResidentKeyRequirement::Discouraged) }; + let timeout_millis = + u32::try_from(self.authenticator_timeout.as_millis()).expect("Timeout too large"); + let c = CreationChallengeResponse { public_key: PublicKeyCredentialCreationOptions { rp: RelyingParty { @@ -232,7 +236,7 @@ impl WebauthnCore { alg: *alg as i64, }) .collect(), - timeout: Some(self.authenticator_timeout), + timeout: Some(timeout_millis), attestation: Some(attestation), exclude_credentials: exclude_credentials.as_ref().map(|creds| { creds @@ -935,12 +939,15 @@ impl WebauthnCore { // Extract the appid from the extensions to store it in the AuthenticationState let appid = extensions.as_ref().and_then(|e| e.appid.clone()); + let timeout_millis = + u32::try_from(self.authenticator_timeout.as_millis()).expect("Timeout too large"); + // Store the chal associated to the user. // Now put that into the correct challenge format let r = RequestChallengeResponse { public_key: PublicKeyCredentialRequestOptions { challenge: chal.clone().into(), - timeout: Some(self.authenticator_timeout), + timeout: Some(timeout_millis), rp_id: self.rp_id.clone(), allow_credentials: ac, user_verification: policy, diff --git a/webauthn-rs/src/lib.rs b/webauthn-rs/src/lib.rs index 6e19dbc6..034c0625 100644 --- a/webauthn-rs/src/lib.rs +++ b/webauthn-rs/src/lib.rs @@ -185,6 +185,7 @@ extern crate tracing; mod interface; +use std::time::Duration; use url::Url; use uuid::Uuid; use webauthn_rs_core::error::{WebauthnError, WebauthnResult}; @@ -225,6 +226,7 @@ pub struct WebauthnBuilder<'a> { allowed_origins: Vec, allow_subdomains: bool, allow_any_port: bool, + timeout: Option, algorithms: Vec, user_presence_only_security_keys: bool, } @@ -280,6 +282,7 @@ impl<'a> WebauthnBuilder<'a> { allowed_origins: vec![rp_origin.to_owned()], allow_subdomains: false, allow_any_port: false, + timeout: None, algorithms: COSEAlgorithm::secure_algs(), user_presence_only_security_keys: false, }) @@ -315,6 +318,14 @@ impl<'a> WebauthnBuilder<'a> { self } + /// Set the timeout value to use for credential creation and authentication challenges. + /// + /// If not set, defaults to [webauthn_rs_core::constants::DEFAULT_AUTHENTICATOR_TIMEOUT]. + pub fn timeout(mut self, timeout: Duration) -> Self { + self.timeout = Some(timeout); + self + } + /// Set the relying party name. This may be shown to the user. This value can be changed in /// the future without affecting credentials that have already registered. /// @@ -356,7 +367,7 @@ impl<'a> WebauthnBuilder<'a> { self.rp_name.unwrap_or(self.rp_id), self.rp_id, self.allowed_origins, - None, + self.timeout, Some(self.allow_subdomains), Some(self.allow_any_port), ),