webauthn_rp

WebAuthn Level 3 RP library.
git clone https://git.philomathiclife.com/repos/webauthn_rp
Log | Files | Refs | README

error.rs (14422B)


      1 #[cfg(feature = "serde_relaxed")]
      2 use super::super::SerdeJsonErr;
      3 use super::super::{
      4     super::{CredentialErr, CredentialId},
      5     AuthRespErr, AuthenticatorDataErr as AuthDataErr, CeremonyErr, CollectedClientDataErr,
      6     PubKeyErr, RpId,
      7 };
      8 #[cfg(doc)]
      9 use super::{
     10     super::{
     11         super::{
     12             request::{
     13                 auth::{
     14                     AllowedCredential, AllowedCredentials, AuthenticationServerState,
     15                     AuthenticationVerificationOptions, Extension,
     16                     PublicKeyCredentialRequestOptions,
     17                 },
     18                 BackupReq, UserVerificationRequirement,
     19             },
     20             AuthenticatedCredential, DynamicState, StaticState,
     21         },
     22         Backup,
     23     },
     24     Authentication, AuthenticatorAssertion, AuthenticatorAttachment, AuthenticatorData,
     25     AuthenticatorExtensionOutput, CollectedClientData, CompressedPubKey, Flag, HmacSecret,
     26     Signature,
     27 };
     28 use core::{
     29     convert::Infallible,
     30     error::Error,
     31     fmt::{self, Display, Formatter},
     32 };
     33 /// Error returned in [`AuthenticatorDataErr::AuthenticatorExtension`].
     34 #[derive(Clone, Copy, Debug)]
     35 pub enum AuthenticatorExtensionOutputErr {
     36     /// The `slice` had an invalid length.
     37     Len,
     38     /// The first byte did not represent a map of one key pair.
     39     CborHeader,
     40     /// `hmac-secret` was not a byte string with additional info 24.
     41     HmacSecretType,
     42     /// `hmac-secret` was not a byte string of length 48 or 80.
     43     HmacSecretValue,
     44     /// An unsupported extension existed.
     45     Unsupported,
     46     /// Fewer extensions existed than expected.
     47     Missing,
     48 }
     49 impl Display for AuthenticatorExtensionOutputErr {
     50     #[inline]
     51     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
     52         f.write_str(match *self {
     53             Self::Len => "CBOR authenticator extensions had an invalid length",
     54             Self::CborHeader => {
     55                 "CBOR authenticator extensions did not represent a map of one key pair"
     56             }
     57             Self::HmacSecretType => "CBOR authenticator extension 'hmac-secret' was not a byte string with additional info 24",
     58             Self::HmacSecretValue => "CBOR authenticator extension 'hmac-secret' was not a byte string of length 48 or 80",
     59             Self::Unsupported => "CBOR authenticator extension had an unsupported extension",
     60             Self::Missing => "CBOR authenticator extensions had fewer extensions than expected",
     61         })
     62     }
     63 }
     64 impl Error for AuthenticatorExtensionOutputErr {}
     65 /// Error returned from [`AuthenticatorData::try_from`].
     66 #[derive(Clone, Copy, Debug)]
     67 pub enum AuthenticatorDataErr {
     68     /// The `slice` had an invalid length.
     69     Len,
     70     /// [`Flag::user_present`] was `false`.
     71     UserNotPresent,
     72     /// Bit 1 in [`flags`](https://www.w3.org/TR/webauthn-3/#authdata-flags) is not 0.
     73     FlagsBit1Not0,
     74     /// Bit 5 in [`flags`](https://www.w3.org/TR/webauthn-3/#authdata-flags) is not 0.
     75     FlagsBit5Not0,
     76     /// [AT](https://www.w3.org/TR/webauthn-3/#authdata-flags-at) was 1.
     77     AttestedCredentialDataIncluded,
     78     /// [BE](https://www.w3.org/TR/webauthn-3/#authdata-flags-be) and
     79     /// [BS](https://www.w3.org/TR/webauthn-3/#authdata-flags-bs) bits were 0 and 1 respectively.
     80     BackupWithoutEligibility,
     81     /// Error returned when [`AuthenticatorExtensionOutput`] is malformed.
     82     AuthenticatorExtension(AuthenticatorExtensionOutputErr),
     83     /// [ED](https://www.w3.org/TR/webauthn-3/#authdata-flags-ed) bit was 0, but
     84     /// [`extensions`](https://www.w3.org/TR/webauthn-3/#authdata-extensions) existed.
     85     NoExtensionBitWithData,
     86     /// [ED](https://www.w3.org/TR/webauthn-3/#authdata-flags-ed) bit was 1, but
     87     /// [`extensions`](https://www.w3.org/TR/webauthn-3/#authdata-extensions) did not exist.
     88     ExtensionBitWithoutData,
     89     /// There was data remaining that could not be deserialized.
     90     TrailingData,
     91 }
     92 impl Display for AuthenticatorDataErr {
     93     #[inline]
     94     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
     95         match *self {
     96             Self::Len => AuthDataErr::<(), Infallible, AuthenticatorExtensionOutputErr>::Len.fmt(f),
     97             Self::UserNotPresent => f.write_str("the user was not present"),
     98             Self::FlagsBit1Not0 => {
     99                 AuthDataErr::<(), Infallible, AuthenticatorExtensionOutputErr>::FlagsBit1Not0.fmt(f)
    100             }
    101             Self::FlagsBit5Not0 => {
    102                 AuthDataErr::<(), Infallible, AuthenticatorExtensionOutputErr>::FlagsBit5Not0.fmt(f)
    103             }
    104             Self::AttestedCredentialDataIncluded => {
    105                 f.write_str("attested credential data was included")
    106             }
    107             Self::BackupWithoutEligibility => AuthDataErr::<
    108                 (),
    109                 Infallible,
    110                 AuthenticatorExtensionOutputErr,
    111             >::BackupWithoutEligibility
    112                 .fmt(f),
    113             Self::AuthenticatorExtension(err) => err.fmt(f),
    114             Self::NoExtensionBitWithData => AuthDataErr::<
    115                 (),
    116                 Infallible,
    117                 AuthenticatorExtensionOutputErr,
    118             >::NoExtensionBitWithData
    119                 .fmt(f),
    120             Self::ExtensionBitWithoutData => AuthDataErr::<
    121                 (),
    122                 Infallible,
    123                 AuthenticatorExtensionOutputErr,
    124             >::ExtensionBitWithoutData
    125                 .fmt(f),
    126             Self::TrailingData => {
    127                 AuthDataErr::<(), Infallible, AuthenticatorExtensionOutputErr>::TrailingData.fmt(f)
    128             }
    129         }
    130     }
    131 }
    132 impl Error for AuthenticatorDataErr {}
    133 /// One or two.
    134 #[derive(Clone, Copy, Debug)]
    135 pub enum OneOrTwo {
    136     /// One.
    137     One,
    138     /// Two.
    139     Two,
    140 }
    141 impl Display for OneOrTwo {
    142     #[inline]
    143     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
    144         f.write_str(match *self {
    145             Self::One => "one",
    146             Self::Two => "two",
    147         })
    148     }
    149 }
    150 /// Error in [`AuthCeremonyErr::Extension`].
    151 #[derive(Clone, Copy, Debug)]
    152 pub enum ExtensionErr {
    153     /// [`AuthenticatorExtensionOutput::hmac_secret`] was sent from the client but was not supposed to be.
    154     ForbiddenHmacSecret,
    155     /// [`AuthenticatorExtensionOutput::hmac_secret`] was sent from the client for a credential that is not PRF
    156     /// capable.
    157     HmacSecretForPrfIncapableCred,
    158     /// [`Extension::prf`] was requested, but the required response was not sent back.
    159     MissingHmacSecret,
    160     /// [`Extension::prf`] was requested with the first number of PRF inputs, but the second number of
    161     /// [`hmac-secret`](https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#sctn-hmac-secret-extension)
    162     /// outputs were sent.
    163     InvalidHmacSecretValue(OneOrTwo, OneOrTwo),
    164 }
    165 impl Display for ExtensionErr {
    166     #[inline]
    167     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
    168         match *self {
    169             Self::ForbiddenHmacSecret => {
    170                 f.write_str("hmac-secret info was sent from the client, but it is not allowed")
    171             }
    172             Self::HmacSecretForPrfIncapableCred => f.write_str(
    173                 "hmac-secret info was sent from the client for a PRF-incapable credential",
    174             ),
    175             Self::MissingHmacSecret => f.write_str("hmac-secret was not sent from the client"),
    176             Self::InvalidHmacSecretValue(sent, recv) => write!(
    177                 f,
    178                 "{sent} PRF input(s) were sent, but {recv} hmac-secret output(s) were received"
    179             ),
    180         }
    181     }
    182 }
    183 impl Error for ExtensionErr {}
    184 /// Error returned by [`AuthenticationServerState::verify`].
    185 #[derive(Debug)]
    186 pub enum AuthCeremonyErr {
    187     /// [`PublicKeyCredentialRequestOptions::timeout`] was exceeded.
    188     Timeout,
    189     /// [`AuthenticatorAssertion::client_data_json`] could not be parsed by
    190     /// [`CollectedClientData::from_client_data_json`].
    191     CollectedClientData(CollectedClientDataErr),
    192     /// [`AuthenticatorAssertion::client_data_json`] could not be parsed by
    193     /// [`CollectedClientData::from_client_data_json_relaxed`].
    194     #[cfg_attr(docsrs, doc(cfg(feature = "serde_relaxed")))]
    195     #[cfg(feature = "serde_relaxed")]
    196     CollectedClientDataRelaxed(SerdeJsonErr),
    197     /// [`AuthenticatorAssertion::authenticator_data`] could not be parsed into an
    198     /// [`AuthenticatorData`].
    199     AuthenticatorData(AuthenticatorDataErr),
    200     /// [`CompressedPubKey`] was not valid.
    201     PubKey(PubKeyErr),
    202     /// [`CompressedPubKey`] was not able to verify [`AuthenticatorAssertion::signature`].
    203     AssertionSignature,
    204     /// [`CollectedClientData::origin`] does not match one of the values in
    205     /// [`AuthenticationVerificationOptions::allowed_origins`].
    206     OriginMismatch,
    207     /// [`CollectedClientData::cross_origin`] was `true`, but
    208     /// [`AuthenticationVerificationOptions::allowed_top_origins`] was `None`.
    209     CrossOrigin,
    210     /// [`CollectedClientData::top_origin`] does not match one of the values in
    211     /// [`AuthenticationVerificationOptions::allowed_top_origins`].
    212     TopOriginMismatch,
    213     /// [`PublicKeyCredentialRequestOptions::challenge`] and [`CollectedClientData::challenge`] don't match.
    214     ChallengeMismatch,
    215     /// The SHA-256 hash of the [`RpId`] does not match [`AuthenticatorData::rp_id_hash`].
    216     RpIdHashMismatch,
    217     /// [`PublicKeyCredentialRequestOptions::user_verification`] was set to
    218     /// [`UserVerificationRequirement::Required`], but [`Flag::user_verified`] was `false`.
    219     UserNotVerified,
    220     /// [`Backup::NotEligible`] was not sent back despite [`BackupReq::NotEligible`].
    221     BackupEligible,
    222     /// [`Backup::NotEligible`] was sent back despite [`BackupReq::Eligible`].
    223     BackupNotEligible,
    224     /// [`Backup::Exists`] was not sent back despite [`BackupReq::Exists`].
    225     BackupDoesNotExist,
    226     /// [`AuthenticatorAttachment`] was not sent back despite being required.
    227     MissingAuthenticatorAttachment,
    228     /// [`AuthenticatorAttachment`] modality changed despite it being forbidden to do so.
    229     AuthenticatorAttachmentMismatch,
    230     /// Variant returned when there is an issue with [`Extension`]s.
    231     Extension(ExtensionErr),
    232     /// [`AuthenticatorData::sign_count`] was not strictly greater than [`DynamicState::sign_count`].
    233     SignatureCounter,
    234     /// [`AuthenticatorAssertion::user_handle`] did not match [`AuthenticatedCredential::user_id`].
    235     UserHandleMismatch,
    236     /// [`Authentication::raw_id`] did not match [`AuthenticatedCredential::id`].
    237     CredentialIdMismatch,
    238     /// [`AllowedCredentials`] did not have a matching [`CredentialId`] as
    239     /// [`Authentication::raw_id`].
    240     NoMatchingAllowedCredential,
    241     /// [`AllowedCredentials`] is empty (i.e., a discoverable request was issued), but
    242     /// [`AuthenticatorAssertion::user_handle`] was [`None`].
    243     MissingUserHandle,
    244     /// Variant returned when [`AuthenticatedCredential`] cannot be updated due to invalid state.
    245     Credential(CredentialErr),
    246 }
    247 impl Display for AuthCeremonyErr {
    248     #[inline]
    249     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
    250         match *self {
    251             Self::Timeout => CeremonyErr::<AuthenticatorDataErr>::Timeout.fmt(f),
    252             Self::CollectedClientData(ref err) => write!(f, "clientDataJSON could not be parsed: {err}"),
    253             #[cfg(feature = "serde_relaxed")]
    254             Self::CollectedClientDataRelaxed(ref err) => write!(f, "clientDataJSON could not be parsed: {err}"),
    255             Self::AuthenticatorData(err) => err.fmt(f),
    256             Self::PubKey(err) => err.fmt(f),
    257             Self::AssertionSignature => AuthRespErr::<AuthenticatorDataErr>::Signature.fmt(f),
    258             Self::OriginMismatch => CeremonyErr::<AuthenticatorDataErr>::OriginMismatch.fmt(f),
    259             Self::CrossOrigin => CeremonyErr::<AuthenticatorDataErr>::CrossOrigin.fmt(f),
    260             Self::TopOriginMismatch => CeremonyErr::<AuthenticatorDataErr>::TopOriginMismatch.fmt(f),
    261             Self::BackupEligible => CeremonyErr::<AuthenticatorDataErr>::BackupEligible.fmt(f),
    262             Self::BackupNotEligible => CeremonyErr::<AuthenticatorDataErr>::BackupNotEligible.fmt(f),
    263             Self::BackupDoesNotExist => CeremonyErr::<AuthenticatorDataErr>::BackupDoesNotExist.fmt(f),
    264             Self::ChallengeMismatch => CeremonyErr::<AuthenticatorDataErr>::ChallengeMismatch.fmt(f),
    265             Self::RpIdHashMismatch => CeremonyErr::<AuthenticatorDataErr>::RpIdHashMismatch.fmt(f),
    266             Self::UserNotVerified => CeremonyErr::<AuthenticatorDataErr>::UserNotVerified.fmt(f),
    267             Self::MissingAuthenticatorAttachment=> f.write_str(
    268                 "authenticator attachment was not sent back despite being required",
    269             ),
    270             Self::AuthenticatorAttachmentMismatch => f.write_str(
    271                 "authenticator attachment modality changed despite not being allowed to",
    272             ),
    273             Self::Extension(err) => err.fmt(f),
    274             Self::SignatureCounter => f.write_str(
    275                 "the signature counter sent back is not strictly greater than the previous counter",
    276             ),
    277             Self::UserHandleMismatch => f.write_str("the user handle does not match"),
    278             Self::CredentialIdMismatch => f.write_str("the credential ID does not match"),
    279             Self::NoMatchingAllowedCredential => f.write_str("none of the credentials used to start the non-discoverable request have the same Credential ID as the credential used to finish the ceremony"),
    280             Self::MissingUserHandle => f.write_str("the credential used to finish the ceremony did not have a user handle despite a discoverable request being issued"),
    281             Self::Credential(err) => err.fmt(f),
    282         }
    283     }
    284 }
    285 impl Error for AuthCeremonyErr {}
    286 /// [`UnknownCredentialOptions`](https://www.w3.org/TR/webauthn-3/#dictdef-unknowncredentialoptions).
    287 ///
    288 /// This can be sent to the client when an authentication ceremony fails due to an unknown [`CredentialId`]. This
    289 /// can be due to the user deleting a credential on the RP's side but not deleting it on the authenticator. This
    290 /// response can be forwarded to the authenticator which can subsequently delete the credential.
    291 #[derive(Debug)]
    292 pub struct UnknownCredentialOptions<'rp, 'cred> {
    293     /// [`rpId`](https://www.w3.org/TR/webauthn-3/#dictdef-unknowncredentialoptions-rpid).
    294     pub rp_id: &'rp RpId,
    295     /// [`credentialId`](https://www.w3.org/TR/webauthn-3/#dictdef-unknowncredentialoptions-credentialid).
    296     pub credential_id: CredentialId<&'cred [u8]>,
    297 }