commit 22b48502a5726985237faea0df5e2e5d2541ce4c
parent e0087e5726455892f7df87ab29951b672dac68e4
Author: Zack Newman <zack@philomathiclife.com>
Date: Fri, 8 May 2026 10:42:16 -0600
update deps. require uv for prf
Diffstat:
9 files changed, 190 insertions(+), 109 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
@@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0"
name = "webauthn_rp"
readme = "README.md"
repository = "https://git.philomathiclife.com/repos/webauthn_rp/"
-rust-version = "1.94.1"
+rust-version = "1.95.0"
version = "0.4.0+spec-3"
[lints.rust]
@@ -115,24 +115,24 @@ targets = [
]
[dependencies]
-base64url_nopad = { version = "0.1.4", default-features = false }
-ed25519-dalek = { version = "3.0.0-pre.6", default-features = false }
-hashbrown = { version = "0.16.1", default-features = false }
-ml-dsa = { version = "0.1.0-rc.8", default-features = false }
-p256 = { version = "0.14.0-rc.8", default-features = false, features = ["ecdsa"] }
-p384 = { version = "0.14.0-rc.8", default-features = false, features = ["ecdsa"] }
-rand = { version = "0.10.0", default-features = false, features = ["thread_rng"] }
-rsa = { version = "0.10.0-rc.17", default-features = false, features = ["encoding", "sha2"] }
+base64url_nopad = { version = "0.1.5", default-features = false }
+ed25519-dalek = { version = "3.0.0-pre.7", default-features = false }
+hashbrown = { version = "0.17.0", default-features = false }
+ml-dsa = { version = "0.1.0-rc.9", default-features = false }
+p256 = { version = "0.14.0-rc.9", default-features = false, features = ["ecdsa"] }
+p384 = { version = "0.14.0-rc.9", default-features = false, features = ["ecdsa"] }
+rand = { version = "0.10.1", default-features = false, features = ["thread_rng"] }
+rsa = { version = "0.10.0-rc.18", default-features = false, features = ["encoding", "sha2"] }
serde = { version = "1.0.228", default-features = false, features = ["alloc"], optional = true }
serde_json = { version = "1.0.149", default-features = false, features = ["alloc"], optional = true }
url = { version = "2.5.8", default-features = false }
[dev-dependencies]
-base64url_nopad = { version = "0.1.4", default-features = false, features = ["alloc"] }
-ed25519-dalek = { version = "3.0.0-pre.6", default-features = false, features = ["alloc", "pkcs8"] }
-ml-dsa = { version = "0.1.0-rc.8", default-features = false, features = ["alloc", "pkcs8"] }
-p256 = { version = "0.14.0-rc.8", default-features = false, features = ["pem"] }
-p384 = { version = "0.14.0-rc.8", default-features = false, features = ["pkcs8"] }
+base64url_nopad = { version = "0.1.5", default-features = false, features = ["alloc"] }
+ed25519-dalek = { version = "3.0.0-pre.7", default-features = false, features = ["alloc", "pkcs8"] }
+ml-dsa = { version = "0.1.0-rc.9", default-features = false, features = ["alloc", "pkcs8"] }
+p256 = { version = "0.14.0-rc.9", default-features = false, features = ["pem"] }
+p384 = { version = "0.14.0-rc.9", default-features = false, features = ["pkcs8"] }
serde_json = { version = "1.0.149", default-features = false, features = ["preserve_order"] }
diff --git a/src/lib.rs b/src/lib.rs
@@ -559,7 +559,9 @@ use crate::{
};
use crate::{
request::{
- auth::error::{InvalidTimeout, NonDiscoverableCredentialRequestOptionsErr},
+ auth::error::{
+ DiscoverableCredentialRequestOptionsErr, NonDiscoverableCredentialRequestOptionsErr,
+ },
error::{AsciiDomainErr, DomainOriginParseErr, PortParseErr, SchemeParseErr, UrlErr},
register::{
ResidentKeyRequirement, USER_HANDLE_MAX_LEN, UserHandle, error::CreationOptionsErr,
@@ -694,9 +696,9 @@ pub enum CredentialErr {
/// Variant when [`CredentialProtectionPolicy::UserVerificationRequired`], but
/// [`DynamicState::user_verified`] is `false`.
CredProtectUserVerificationRequiredWithoutUserVerified,
- /// Variant when [`AuthenticatorExtensionOutput::hmac_secret`] is `Some(true)` and
- /// [`DynamicState::user_verified`] is `false`.
- HmacSecretWithoutUserVerified,
+ /// Variant when [`ClientExtensionsOutputs::prf`] is
+ /// `Some(AuthenticationExtensionsPRFOutputs { enabled: true })` and [`DynamicState::user_verified`] is `false`.
+ PrfWithoutUserVerified,
/// Variant when [`AuthenticatorExtensionOutput::hmac_secret`] is `Some(true)`, but
/// [`ClientExtensionsOutputs::prf`] is `Some(AuthenticationExtensionsPRFOutputs { enabled: false })`
/// or `AuthenticatorExtensionOutput::hmac_secret` is `Some`, but
@@ -717,9 +719,7 @@ impl Display for CredentialErr {
Self::CredProtectUserVerificationRequiredWithoutUserVerified => {
"credProtect requires user verification, but the user is not verified"
}
- Self::HmacSecretWithoutUserVerified => {
- "hmac-secret was enabled, but the user is not verified"
- }
+ Self::PrfWithoutUserVerified => "prf is enabled, but the user is not verified",
Self::HmacSecretWithoutPrf => "hmac-secret was enabled but prf was not",
Self::PrfWithoutHmacSecret => "prf was enabled, but hmac-secret was not",
Self::ResidentKeyRequiredServerCredentialCreated => {
@@ -746,11 +746,11 @@ fn verify_static_and_dynamic_state<T>(
) {
Err(CredentialErr::CredProtectUserVerificationRequiredWithoutUserVerified)
} else if static_state
- .extensions
- .hmac_secret
- .is_some_and(convert::identity)
+ .client_extension_results
+ .prf
+ .is_some_and(|prf| prf.enabled)
{
- Err(CredentialErr::HmacSecretWithoutUserVerified)
+ Err(CredentialErr::PrfWithoutUserVerified)
} else {
Ok(())
}
@@ -1131,12 +1131,10 @@ pub enum AggErr {
DomainOrigin(DomainOriginParseErr),
/// Variant when [`Port::from_str`] errors.
Port(PortParseErr),
- /// Variant when [`DiscoverableCredentialRequestOptions::start_ceremony`] or
- /// [`NonDiscoverableCredentialRequestOptions::start_ceremony`]
- /// error.
- InvalidTimeout(InvalidTimeout),
/// Variant when [`CredentialCreationOptions::start_ceremony`] errors.
CreationOptions(CreationOptionsErr),
+ /// Variant when [`DiscoverableCredentialRequestOptions::start_ceremony`] errors.
+ DiscoverableCredentialRequestOptions(DiscoverableCredentialRequestOptionsErr),
/// Variant when [`NonDiscoverableCredentialRequestOptions::start_ceremony`] errors.
NonDiscoverableCredentialRequestOptions(NonDiscoverableCredentialRequestOptionsErr),
/// Variant when [`RegistrationServerState::verify`] errors.
@@ -1234,18 +1232,18 @@ impl From<PortParseErr> for AggErr {
Self::Port(value)
}
}
-impl From<InvalidTimeout> for AggErr {
- #[inline]
- fn from(value: InvalidTimeout) -> Self {
- Self::InvalidTimeout(value)
- }
-}
impl From<CreationOptionsErr> for AggErr {
#[inline]
fn from(value: CreationOptionsErr) -> Self {
Self::CreationOptions(value)
}
}
+impl From<DiscoverableCredentialRequestOptionsErr> for AggErr {
+ #[inline]
+ fn from(value: DiscoverableCredentialRequestOptionsErr) -> Self {
+ Self::DiscoverableCredentialRequestOptions(value)
+ }
+}
impl From<NonDiscoverableCredentialRequestOptionsErr> for AggErr {
#[inline]
fn from(value: NonDiscoverableCredentialRequestOptionsErr) -> Self {
@@ -1387,8 +1385,8 @@ impl Display for AggErr {
Self::Scheme(err) => err.fmt(f),
Self::DomainOrigin(ref err) => err.fmt(f),
Self::Port(ref err) => err.fmt(f),
- Self::InvalidTimeout(err) => err.fmt(f),
Self::CreationOptions(err) => err.fmt(f),
+ Self::DiscoverableCredentialRequestOptions(err) => err.fmt(f),
Self::NonDiscoverableCredentialRequestOptions(err) => err.fmt(f),
Self::RegCeremony(ref err) => err.fmt(f),
Self::AuthCeremony(ref err) => err.fmt(f),
diff --git a/src/request/auth.rs b/src/request/auth.rs
@@ -30,7 +30,9 @@ use super::{
CredentialMediationRequirement, Credentials, ExtensionReq, FIVE_MINUTES, Hints, Origin,
PrfInput, PublicKeyCredentialDescriptor, RpId, SentChallenge, TimedCeremony,
UserVerificationRequirement,
- auth::error::{InvalidTimeout, NonDiscoverableCredentialRequestOptionsErr},
+ auth::error::{
+ DiscoverableCredentialRequestOptionsErr, NonDiscoverableCredentialRequestOptionsErr,
+ },
};
use core::{
borrow::Borrow,
@@ -317,6 +319,34 @@ impl From<Vec<PublicKeyCredentialDescriptor<Box<[u8]>>>> for AllowedCredentials
creds
}
}
+/// Helper that verifies the overlap of [`DiscoverableCredentialRequestOptions::start_ceremony`] and
+/// [`DiscoverableAuthenticationServerState::decode`].
+///
+/// Returns `true` iff the options are valid.
+const fn validate_discoverable_options_helper(
+ ext: ServerExtensionInfo,
+ uv: UserVerificationRequirement,
+) -> bool {
+ // If PRF is set, the user has to verify themselves.
+ matches!(ext.prf, ServerPrfInfo::None) || matches!(uv, UserVerificationRequirement::Required)
+}
+/// Helper that verifies the overlap of [`NonDiscoverableCredentialRequestOptions::start_ceremony`] and
+/// [`NonDiscoverableAuthenticationServerState::decode`].
+fn validate_non_discoverable_options_helper(
+ uv: UserVerificationRequirement,
+ creds: &[CredInfo],
+) -> Result<(), NonDiscoverableCredentialRequestOptionsErr> {
+ creds.iter().try_fold((), |(), cred| {
+ // If PRF is set, the user has to verify themselves.
+ if matches!(cred.ext.prf, ServerPrfInfo::None)
+ || matches!(uv, UserVerificationRequirement::Required)
+ {
+ Ok(())
+ } else {
+ Err(NonDiscoverableCredentialRequestOptionsErr::PrfWithoutUserVerification)
+ }
+ })
+}
/// [`CredentialUiMode`](https://www.w3.org/TR/credential-management-1/#enumdef-credentialuimode)
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CredentialUiMode {
@@ -385,27 +415,32 @@ impl<'rp_id, 'prf_first, 'prf_second>
DiscoverableAuthenticationServerState,
DiscoverableAuthenticationClientState<'rp_id, 'prf_first, 'prf_second>,
),
- InvalidTimeout,
+ DiscoverableCredentialRequestOptionsErr,
> {
- #[cfg(not(feature = "serializable_server_state"))]
- let res = Instant::now();
- #[cfg(feature = "serializable_server_state")]
- let res = SystemTime::now();
- res.checked_add(Duration::from_millis(
- NonZeroU64::from(self.public_key.timeout).get(),
- ))
- .ok_or(InvalidTimeout)
- .map(|expiration| {
- (
- DiscoverableAuthenticationServerState(AuthenticationServerState {
- challenge: SentChallenge(self.public_key.challenge.0),
- user_verification: self.public_key.user_verification,
- extensions: self.public_key.extensions.into(),
- expiration,
- }),
- DiscoverableAuthenticationClientState(self),
- )
- })
+ let extensions = self.public_key.extensions.into();
+ if validate_discoverable_options_helper(extensions, self.public_key.user_verification) {
+ #[cfg(not(feature = "serializable_server_state"))]
+ let res = Instant::now();
+ #[cfg(feature = "serializable_server_state")]
+ let res = SystemTime::now();
+ res.checked_add(Duration::from_millis(
+ NonZeroU64::from(self.public_key.timeout).get(),
+ ))
+ .ok_or(DiscoverableCredentialRequestOptionsErr::InvalidTimeout)
+ .map(|expiration| {
+ (
+ DiscoverableAuthenticationServerState(AuthenticationServerState {
+ challenge: SentChallenge(self.public_key.challenge.0),
+ user_verification: self.public_key.user_verification,
+ extensions,
+ expiration,
+ }),
+ DiscoverableAuthenticationClientState(self),
+ )
+ })
+ } else {
+ Err(DiscoverableCredentialRequestOptionsErr::PrfWithoutUserVerification)
+ }
}
/// Same as [`Self::start_ceremony`] except the raw challenge is returned instead of
/// [`DiscoverableAuthenticationClientState`].
@@ -422,7 +457,10 @@ impl<'rp_id, 'prf_first, 'prf_second>
#[inline]
pub fn start_ceremony_challenge_only(
self,
- ) -> Result<(DiscoverableAuthenticationServerState, [u8; 16]), InvalidTimeout> {
+ ) -> Result<
+ (DiscoverableAuthenticationServerState, [u8; 16]),
+ DiscoverableCredentialRequestOptionsErr,
+ > {
self.start_ceremony()
.map(|(server, client)| (server, client.0.public_key.challenge.into_array()))
}
@@ -525,28 +563,40 @@ impl<'rp_id, 'prf_first, 'prf_second>
} else if matches!(self.mediation, CredentialMediationRequirement::Conditional) {
Err(NonDiscoverableCredentialRequestOptionsErr::ConditionalMediationRequested)
} else {
- #[cfg(not(feature = "serializable_server_state"))]
- let res = Instant::now();
- #[cfg(feature = "serializable_server_state")]
- let res = SystemTime::now();
- res.checked_add(Duration::from_millis(
- NonZeroU64::from(self.options.timeout).get(),
- ))
- .ok_or(NonDiscoverableCredentialRequestOptionsErr::InvalidTimeout)
- .map(|expiration| {
- (
- NonDiscoverableAuthenticationServerState {
- state: AuthenticationServerState {
- challenge: SentChallenge(self.options.challenge.0),
- user_verification: self.options.user_verification,
- extensions: self.options.extensions.into(),
- expiration,
- },
- allow_credentials: Box::from(&self.allow_credentials),
- },
- NonDiscoverableAuthenticationClientState(self),
+ let extensions = self.options.extensions.into();
+ if validate_discoverable_options_helper(extensions, self.options.user_verification) {
+ let allow_credentials = Box::from(&self.allow_credentials);
+ validate_non_discoverable_options_helper(
+ self.options.user_verification,
+ &allow_credentials,
)
- })
+ .and_then(|()| {
+ #[cfg(not(feature = "serializable_server_state"))]
+ let res = Instant::now();
+ #[cfg(feature = "serializable_server_state")]
+ let res = SystemTime::now();
+ res.checked_add(Duration::from_millis(
+ NonZeroU64::from(self.options.timeout).get(),
+ ))
+ .ok_or(NonDiscoverableCredentialRequestOptionsErr::InvalidTimeout)
+ .map(|expiration| {
+ (
+ NonDiscoverableAuthenticationServerState {
+ state: AuthenticationServerState {
+ challenge: SentChallenge(self.options.challenge.0),
+ user_verification: self.options.user_verification,
+ extensions,
+ expiration,
+ },
+ allow_credentials,
+ },
+ NonDiscoverableAuthenticationClientState(self),
+ )
+ })
+ })
+ } else {
+ Err(NonDiscoverableCredentialRequestOptionsErr::PrfWithoutUserVerification)
+ }
}
}
}
diff --git a/src/request/auth/error.rs b/src/request/auth/error.rs
@@ -11,18 +11,27 @@ use core::{
#[cfg(doc)]
use std::time::{Instant, SystemTime};
/// Error returned by [`DiscoverableCredentialRequestOptions::start_ceremony`].
-///
-/// This happens when [`PublicKeyCredentialRequestOptions::timeout`] could not be added to [`Instant::now`] or
-/// [`SystemTime::now`].
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub struct InvalidTimeout;
-impl Display for InvalidTimeout {
+pub enum DiscoverableCredentialRequestOptionsErr {
+ /// Error when [`Extension::prf`] is [`Some`] but [`PublicKeyCredentialRequestOptions::user_verification`] is
+ /// not [`UserVerificationRequirement::Required`].
+ PrfWithoutUserVerification,
+ /// Variant when [`PublicKeyCredentialRequestOptions::timeout`] could not be added to [`Instant::now`] or
+ /// [`SystemTime::now`].
+ InvalidTimeout,
+}
+impl Display for DiscoverableCredentialRequestOptionsErr {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- f.write_str("the timeout could not be added to the current Instant")
+ f.write_str(match *self {
+ Self::PrfWithoutUserVerification => {
+ "prf extension was requested without requiring user verification"
+ }
+ Self::InvalidTimeout => "the timeout could not be added to the current Instant",
+ })
}
}
-impl Error for InvalidTimeout {}
+impl Error for DiscoverableCredentialRequestOptionsErr {}
/// Error returned by [`NonDiscoverableCredentialRequestOptions::start_ceremony`].
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum NonDiscoverableCredentialRequestOptionsErr {
@@ -32,6 +41,10 @@ pub enum NonDiscoverableCredentialRequestOptionsErr {
/// Variant when [`NonDiscoverableCredentialRequestOptions::mediation`] is
/// [`CredentialMediationRequirement::Conditional`].
ConditionalMediationRequested,
+ /// Error when [`Extension::prf`] or [`CredentialSpecificExtension::prf`] is [`Some`] but
+ /// [`PublicKeyCredentialRequestOptions::user_verification`] is not
+ /// [`UserVerificationRequirement::Required`].
+ PrfWithoutUserVerification,
/// Variant when [`PublicKeyCredentialRequestOptions::timeout`] could not be added to [`Instant::now`] or
/// [`SystemTime::now`].
InvalidTimeout,
@@ -46,6 +59,9 @@ impl Display for NonDiscoverableCredentialRequestOptionsErr {
Self::ConditionalMediationRequested => {
"non-discoverable requests are not allowed to use conditional mediation"
}
+ Self::PrfWithoutUserVerification => {
+ "prf extension was requested without requiring user verification"
+ }
Self::InvalidTimeout => "the timeout could not be added to the current Instant",
})
}
diff --git a/src/request/auth/ser_server_state.rs b/src/request/auth/ser_server_state.rs
@@ -229,12 +229,16 @@ impl<'a> DecodeBuffer<'a> for AuthenticationServerState {
SentChallenge::decode_from_buffer(data).and_then(|challenge| {
UserVerificationRequirement::decode_from_buffer(data).and_then(|user_verification| {
ServerExtensionInfo::decode_from_buffer(data).and_then(|extensions| {
- SystemTime::decode_from_buffer(data).map(|expiration| Self {
- challenge,
- user_verification,
- extensions,
- expiration,
- })
+ if super::validate_discoverable_options_helper(extensions, user_verification) {
+ SystemTime::decode_from_buffer(data).map(|expiration| Self {
+ challenge,
+ user_verification,
+ extensions,
+ expiration,
+ })
+ } else {
+ Err(EncDecErr)
+ }
})
})
})
@@ -304,19 +308,26 @@ impl Decode for NonDiscoverableAuthenticationServerState {
AuthenticationServerState::decode_from_buffer(&mut input)
.map_err(|_e| DecodeNonDiscoverableAuthenticationServerStateErr::Other)
.and_then(|state| {
- Box::<[_]>::decode_from_buffer(&mut input)
+ Box::decode_from_buffer(&mut input)
.map_err(|_e| DecodeNonDiscoverableAuthenticationServerStateErr::Other)
.and_then(|allow_credentials| {
- if allow_credentials.is_empty() {
- Err(DecodeNonDiscoverableAuthenticationServerStateErr::Other)
- } else if input.is_empty() {
- Ok(Self {
- state,
- allow_credentials,
- })
- } else {
- Err(DecodeNonDiscoverableAuthenticationServerStateErr::TrailingData)
- }
+ super::validate_non_discoverable_options_helper(
+ state.user_verification,
+ &allow_credentials,
+ )
+ .map_err(|_e| DecodeNonDiscoverableAuthenticationServerStateErr::Other)
+ .and({
+ if allow_credentials.is_empty() {
+ Err(DecodeNonDiscoverableAuthenticationServerStateErr::Other)
+ } else if input.is_empty() {
+ Ok(Self {
+ state,
+ allow_credentials,
+ })
+ } else {
+ Err(DecodeNonDiscoverableAuthenticationServerStateErr::TrailingData)
+ }
+ })
})
})
}
diff --git a/src/request/register.rs b/src/request/register.rs
@@ -977,6 +977,8 @@ const fn validate_options_helper(
UserVerificationRequirement::Required
) {
Ok(())
+ } else if !matches!(extensions.prf, ServerPrfInfo::None) {
+ Err(CreationOptionsErr::PrfWithoutUserVerification)
} else if matches!(
extensions.cred_protect,
CredProtect::UserVerificationRequired(_, _)
diff --git a/src/request/register/error.rs b/src/request/register/error.rs
@@ -13,6 +13,9 @@ use std::time::{Instant, SystemTime};
/// Error returned by [`CredentialCreationOptions::start_ceremony`].
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CreationOptionsErr {
+ /// Error when [`Extension::prf`] is [`Some`] but [`AuthenticatorSelectionCriteria::user_verification`] is not
+ /// [`UserVerificationRequirement::Required`].
+ PrfWithoutUserVerification,
/// Error when [`Extension::cred_protect`] is [`CredProtect::UserVerificationRequired`] but [`AuthenticatorSelectionCriteria::user_verification`] is not
/// [`UserVerificationRequirement::Required`].
CredProtectRequiredWithoutUserVerification,
@@ -26,6 +29,7 @@ impl Display for CreationOptionsErr {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(match *self {
+ Self::PrfWithoutUserVerification => "prf extension was requested without requiring user verification",
Self::CredProtectRequiredWithoutUserVerification => "credProtect extension with a value of user verification required was requested without requiring user verification",
Self::HintsIncompatibleWithAuthAttachment => "hints are not compatible with the requested authenticator attachment modality",
Self::InvalidTimeout => "the timeout could not be added to the current Instant",
diff --git a/src/request/register/tests.rs b/src/request/register/tests.rs
@@ -846,7 +846,7 @@ fn prf() -> Result<(), AggErr> {
opts.response.user_verified = false;
opts.response.hmac = HmacSecret::Enabled;
opts.response.prf = Some(true);
- assert!(validate(opts).is_err_and(|e| matches!(e, AggErr::RegCeremony(err) if matches!(err, RegCeremonyErr::Credential(cred_err) if matches!(cred_err, CredentialErr::HmacSecretWithoutUserVerified)))));
+ assert!(validate(opts).is_err_and(|e| matches!(e, AggErr::RegCeremony(err) if matches!(err, RegCeremonyErr::Credential(cred_err) if matches!(cred_err, CredentialErr::PrfWithoutUserVerified)))));
opts.response.prf = None;
opts.response.hmac = HmacSecret::None;
validate(opts)?;
diff --git a/src/response.rs b/src/response.rs
@@ -33,14 +33,14 @@ use ser_relaxed::SerdeJsonErr;
/// # use core::convert;
/// # use webauthn_rp::{
/// # hash::hash_set::{InsertRemoveExpired, MaxLenHashSet},
-/// # request::{auth::{error::InvalidTimeout, DiscoverableAuthenticationClientState, DiscoverableCredentialRequestOptions, AuthenticationVerificationOptions}, register::{BackupReq, UserHandle, USER_HANDLE_MAX_LEN, UserHandle64}, RpId},
+/// # request::{auth::{error::DiscoverableCredentialRequestOptionsErr, DiscoverableAuthenticationClientState, DiscoverableCredentialRequestOptions, AuthenticationVerificationOptions}, register::{BackupReq, UserHandle, USER_HANDLE_MAX_LEN, UserHandle64}, RpId},
/// # response::{auth::{error::AuthCeremonyErr, DiscoverableAuthentication64}, error::CollectedClientDataErr, register::{AuthenticatorExtensionOutputStaticState, ClientExtensionsOutputsStaticState, CredentialProtectionPolicy, DynamicState, Ed25519PubKey, CompressedPubKeyOwned, StaticState}, AuthenticatorAttachment, Backup, CollectedClientData, CredentialId},
/// # AuthenticatedCredential, CredentialErr
/// # };
/// # #[derive(Debug)]
/// # enum E {
/// # CollectedClientData(CollectedClientDataErr),
-/// # InvalidTimeout(InvalidTimeout),
+/// # DiscoverableCredentialRequestOptions(DiscoverableCredentialRequestOptionsErr),
/// # SerdeJson(serde_json::Error),
/// # MissingUserHandle,
/// # MissingCeremony,
@@ -53,9 +53,9 @@ use ser_relaxed::SerdeJsonErr;
/// # Self::CollectedClientData(value)
/// # }
/// # }
-/// # impl From<InvalidTimeout> for E {
-/// # fn from(value: InvalidTimeout) -> Self {
-/// # Self::InvalidTimeout(value)
+/// # impl From<DiscoverableCredentialRequestOptionsErr> for E {
+/// # fn from(value: DiscoverableCredentialRequestOptionsErr) -> Self {
+/// # Self::DiscoverableCredentialRequestOptions(value)
/// # }
/// # }
/// # impl From<serde_json::Error> for E {