lib.rs (73804B)
1 //! [![git]](https://git.philomathiclife.com/webauthn_rp/log.html) [![crates-io]](https://crates.io/crates/webauthn_rp) [![docs-rs]](crate) 2 //! 3 //! [git]: https://git.philomathiclife.com/git_badge.svg 4 //! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust 5 //! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs 6 //! 7 //! `webauthn_rp` is a library for _server-side_ 8 //! [Web Authentication (WebAuthn)](https://www.w3.org/TR/webauthn-3/#sctn-rp-operations) Relying Party 9 //! (RP) operations. 10 //! 11 //! The purpose of a server-side RP library is to be modular so that any client can be used with it as a backend 12 //! _including_ native applications—WebAuthn technically only covers web applications; however it's relatively easy 13 //! to adapt to native applications as well. It achieves this by not assuming how data is sent to/from the client; 14 //! having said that, there are pre-defined serialization formats for "common" deployments which can be used when 15 //! [`serde`](#serde) is enabled. 16 //! 17 //! ## `webauthn_rp` in action 18 //! 19 //! ```no_run 20 //! use core::convert; 21 //! use webauthn_rp::{ 22 //! AuthenticatedCredential64, DiscoverableAuthentication64, DiscoverableAuthenticationServerState, 23 //! DiscoverableCredentialRequestOptions, CredentialCreationOptions64, RegisteredCredential64, 24 //! Registration, RegistrationServerState64, 25 //! hash::hash_set::FixedCapHashSet, 26 //! request::{ 27 //! AsciiDomain, PublicKeyCredentialDescriptor, RpId, 28 //! auth::AuthenticationVerificationOptions, 29 //! register::{ 30 //! Nickname, PublicKeyCredentialUserEntity64, RegistrationVerificationOptions, 31 //! UserHandle64, Username, 32 //! }, 33 //! }, 34 //! response::{ 35 //! CredentialId, 36 //! auth::error::AuthCeremonyErr, 37 //! register::{CompressedPubKey, DynamicState, error::RegCeremonyErr}, 38 //! }, 39 //! }; 40 //! # #[cfg(feature = "serde")] 41 //! use serde::de::{Deserialize, Deserializer}; 42 //! # #[cfg(feature = "serde_relaxed")] 43 //! use serde_json::Error as JsonErr; 44 //! /// The RP ID our application uses. 45 //! const RP_ID: &str = "example.com"; 46 //! /// Error we return in our application when a function fails. 47 //! enum AppErr { 48 //! /// WebAuthn registration ceremony failed. 49 //! RegCeremony(RegCeremonyErr), 50 //! /// WebAuthn authentication ceremony failed. 51 //! AuthCeremony(AuthCeremonyErr), 52 //! /// Unable to insert a WebAuthn ceremony. 53 //! WebAuthnCeremonyCreation, 54 //! /// WebAuthn ceremony does not exist; thus the ceremony could not be completed. 55 //! MissingWebAuthnCeremony, 56 //! /// General error related to JSON deserialization. 57 //! # #[cfg(feature = "serde_relaxed")] 58 //! Json(JsonErr), 59 //! /// No account exists associated with a particular `UserHandle64`. 60 //! NoAccount, 61 //! /// No credential exists associated with a particular `CredentialId`. 62 //! NoCredential, 63 //! /// `CredentialId` exists but the associated `UserHandle64` does not match. 64 //! CredentialUserIdMismatch, 65 //! } 66 //! # #[cfg(feature = "serde_relaxed")] 67 //! impl From<JsonErr> for AppErr { 68 //! fn from(value: JsonErr) -> Self { 69 //! Self::Json(value) 70 //! } 71 //! } 72 //! impl From<RegCeremonyErr> for AppErr { 73 //! fn from(value: RegCeremonyErr) -> Self { 74 //! Self::RegCeremony(value) 75 //! } 76 //! } 77 //! impl From<AuthCeremonyErr> for AppErr { 78 //! fn from(value: AuthCeremonyErr) -> Self { 79 //! Self::AuthCeremony(value) 80 //! } 81 //! } 82 //! /// First-time account creation. 83 //! /// 84 //! /// This gets sent from the user after an account is created on their side. The registration ceremony 85 //! /// still has to be successfully completed for the account to be created server side. In the event of an error, 86 //! /// the user should delete the created passkey since it won't be usable. 87 //! struct AccountReg<'a, 'b> { 88 //! registration: Registration, 89 //! user_name: Username<'a>, 90 //! user_display_name: Nickname<'b>, 91 //! } 92 //! # #[cfg(feature = "serde")] 93 //! impl<'de: 'a + 'b, 'a, 'b> Deserialize<'de> for AccountReg<'a, 'b> { 94 //! fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 95 //! where 96 //! D: Deserializer<'de>, 97 //! { 98 //! // ⋮ 99 //! # panic!(""); 100 //! } 101 //! } 102 //! /// Starts account creation. 103 //! /// 104 //! /// This only makes sense for greenfield deployments since account information (e.g., user name) would likely 105 //! /// already exist otherwise. This is similar to credential creation except a random `UserHandle64` is generated and 106 //! /// will be used for subsequent credential registrations. 107 //! # #[cfg(feature = "serde_relaxed")] 108 //! fn start_account_creation( 109 //! reg_ceremonies: &mut FixedCapHashSet<RegistrationServerState64>, 110 //! ) -> Result<Vec<u8>, AppErr> { 111 //! let rp_id = RpId::Domain( 112 //! AsciiDomain::try_from(RP_ID.to_owned()) 113 //! .unwrap_or_else(|_e| unreachable!("example.com is a valid domain")), 114 //! ); 115 //! let user_id = UserHandle64::new(); 116 //! let (server, client) = 117 //! CredentialCreationOptions64::first_passkey_with_blank_user_info( 118 //! &rp_id, &user_id, 119 //! ) 120 //! .start_ceremony() 121 //! .unwrap_or_else(|_e| { 122 //! unreachable!("we don't manually mutate the options and we assume the server clock is functioning; thus this won't error") 123 //! }); 124 //! if reg_ceremonies.insert_remove_all_expired(server).is_some_and(convert::identity) 125 //! { 126 //! Ok(serde_json::to_vec(&client) 127 //! .unwrap_or_else(|_e| unreachable!("bug in RegistrationClientState64::serialize"))) 128 //! } else { 129 //! Err(AppErr::WebAuthnCeremonyCreation) 130 //! } 131 //! } 132 //! /// Finishes account creation. 133 //! /// 134 //! /// Pending a successful registration ceremony, a new account associated with the randomly generated 135 //! /// `UserHandle64` will be created with a corresponding passkey entry. This passkey will be used to 136 //! /// log into the application. 137 //! /// 138 //! /// Note if this errors, then the user should be notified to delete the passkey created on their 139 //! /// authenticator. 140 //! # #[cfg(feature = "serde_relaxed")] 141 //! fn finish_account_creation( 142 //! reg_ceremonies: &mut FixedCapHashSet<RegistrationServerState64>, 143 //! client_data: Vec<u8>, 144 //! ) -> Result<(), AppErr> { 145 //! let account = serde_json::from_slice::<AccountReg<'_, '_>>(client_data.as_slice())?; 146 //! insert_account( 147 //! &account, 148 //! reg_ceremonies 149 //! // `Registration::challenge_relaxed` is available iff `serde_relaxed` is enabled. 150 //! .take(&account.registration.challenge_relaxed()?) 151 //! .ok_or(AppErr::MissingWebAuthnCeremony)? 152 //! .verify( 153 //! &RpId::Domain( 154 //! AsciiDomain::try_from(RP_ID.to_owned()) 155 //! .unwrap_or_else(|_e| unreachable!("example.com is a valid domain")), 156 //! ), 157 //! &account.registration, 158 //! &RegistrationVerificationOptions::<&str, &str>::default(), 159 //! )?, 160 //! ) 161 //! } 162 //! /// Starts passkey registration. 163 //! /// 164 //! /// This is used for _existing_ accounts where the user is already logged in and wants to register another 165 //! /// passkey. This is similar to account creation except we already have the user entity info and we need to 166 //! /// fetch the registered `PublicKeyCredentialDescriptor`s to avoid accidentally overwriting a passkey on 167 //! /// the authenticator. 168 //! # #[cfg(feature = "serde_relaxed")] 169 //! fn start_cred_registration( 170 //! user_id: &UserHandle64, 171 //! reg_ceremonies: &mut FixedCapHashSet<RegistrationServerState64>, 172 //! ) -> Result<Vec<u8>, AppErr> { 173 //! let rp_id = RpId::Domain( 174 //! AsciiDomain::try_from(RP_ID.to_owned()) 175 //! .unwrap_or_else(|_e| unreachable!("example.com is a valid domain")), 176 //! ); 177 //! let (entity, creds) = select_user_info(user_id)?.ok_or_else(|| AppErr::NoAccount)?; 178 //! let (server, client) = CredentialCreationOptions64::passkey(&rp_id, entity, creds) 179 //! .start_ceremony() 180 //! .unwrap_or_else(|_e| { 181 //! unreachable!("we don't manually mutate the options and we assume the server clock is functioning; thus this won't error") 182 //! }); 183 //! if reg_ceremonies.insert_remove_all_expired(server).is_some_and(convert::identity) 184 //! { 185 //! Ok(serde_json::to_vec(&client) 186 //! .unwrap_or_else(|_e| unreachable!("bug in RegistrationClientState64::serialize"))) 187 //! } else { 188 //! Err(AppErr::WebAuthnCeremonyCreation) 189 //! } 190 //! } 191 //! /// Finishes passkey registration. 192 //! /// 193 //! /// Pending a successful registration ceremony, a new credential associated with the `UserHandle64` 194 //! /// will be created. This passkey can then be used to log into the application just like any other registered 195 //! /// passkey. 196 //! /// 197 //! /// Note if this errors, then the user should be notified to delete the passkey created on their 198 //! /// authenticator. 199 //! # #[cfg(feature = "serde_relaxed")] 200 //! fn finish_cred_registration( 201 //! reg_ceremonies: &mut FixedCapHashSet<RegistrationServerState64>, 202 //! client_data: Vec<u8>, 203 //! ) -> Result<(), AppErr> { 204 //! // `Registration::from_json_custom` is available iff `serde_relaxed` is enabled. 205 //! let registration = Registration::from_json_custom(client_data.as_slice())?; 206 //! insert_credential( 207 //! reg_ceremonies 208 //! // `Registration::challenge_relaxed` is available iff `serde_relaxed` is enabled. 209 //! .take(®istration.challenge_relaxed()?) 210 //! .ok_or(AppErr::MissingWebAuthnCeremony)? 211 //! .verify( 212 //! &RpId::Domain( 213 //! AsciiDomain::try_from(RP_ID.to_owned()) 214 //! .unwrap_or_else(|_e| unreachable!("example.com is a valid domain")), 215 //! ), 216 //! ®istration, 217 //! &RegistrationVerificationOptions::<&str, &str>::default(), 218 //! )?, 219 //! ) 220 //! } 221 //! /// Starts the passkey authentication ceremony. 222 //! # #[cfg(feature = "serde_relaxed")] 223 //! fn start_auth( 224 //! auth_ceremonies: &mut FixedCapHashSet<DiscoverableAuthenticationServerState>, 225 //! ) -> Result<Vec<u8>, AppErr> { 226 //! let rp_id = RpId::Domain( 227 //! AsciiDomain::try_from(RP_ID.to_owned()) 228 //! .unwrap_or_else(|_e| unreachable!("example.com is a valid domain")), 229 //! ); 230 //! let (server, client) = DiscoverableCredentialRequestOptions::passkey(&rp_id) 231 //! .start_ceremony() 232 //! .unwrap_or_else(|_e| { 233 //! unreachable!("we don't manually mutate the options and we assume the server clock is functioning; thus this won't error") 234 //! }); 235 //! if auth_ceremonies.insert_remove_all_expired(server).is_some_and(convert::identity) 236 //! { 237 //! Ok(serde_json::to_vec(&client).unwrap_or_else(|_e| { 238 //! unreachable!("bug in DiscoverableAuthenticationClientState::serialize") 239 //! })) 240 //! } else { 241 //! Err(AppErr::WebAuthnCeremonyCreation) 242 //! } 243 //! } 244 //! /// Finishes the passkey authentication ceremony. 245 //! # #[cfg(feature = "serde_relaxed")] 246 //! fn finish_auth( 247 //! auth_ceremonies: &mut FixedCapHashSet<DiscoverableAuthenticationServerState>, 248 //! client_data: Vec<u8>, 249 //! ) -> Result<(), AppErr> { 250 //! // `DiscoverableAuthentication64::from_json_custom` is available iff `serde_relaxed` is enabled. 251 //! let authentication = 252 //! DiscoverableAuthentication64::from_json_custom(client_data.as_slice())?; 253 //! let mut cred = select_credential( 254 //! authentication.raw_id(), 255 //! authentication.response().user_handle(), 256 //! )? 257 //! .ok_or_else(|| AppErr::NoCredential)?; 258 //! if auth_ceremonies 259 //! // `DiscoverableAuthentication64::challenge_relaxed` is available iff `serde_relaxed` is enabled. 260 //! .take(&authentication.challenge_relaxed()?) 261 //! .ok_or(AppErr::MissingWebAuthnCeremony)? 262 //! .verify( 263 //! &RpId::Domain( 264 //! AsciiDomain::try_from(RP_ID.to_owned()) 265 //! .unwrap_or_else(|_e| unreachable!("example.com is a valid domain")), 266 //! ), 267 //! &authentication, 268 //! &mut cred, 269 //! &AuthenticationVerificationOptions::<&str, &str>::default(), 270 //! )? 271 //! { 272 //! update_credential(cred.id(), cred.dynamic_state()) 273 //! } else { 274 //! Ok(()) 275 //! } 276 //! } 277 //! /// Writes `account` and `cred` to storage. 278 //! /// 279 //! /// # Errors 280 //! /// 281 //! /// Errors iff writing `account` or `cred` errors, there already exists a credential using the same 282 //! /// `CredentialId`, or there already exists an account using the same `UserHandle64`. 283 //! fn insert_account( 284 //! account: &AccountReg<'_, '_>, 285 //! cred: RegisteredCredential64<'_>, 286 //! ) -> Result<(), AppErr> { 287 //! // ⋮ 288 //! # Ok(()) 289 //! } 290 //! /// Fetches the user info and registered credentials associated with `user_id`. 291 //! /// 292 //! /// # Errors 293 //! /// 294 //! /// Errors iff fetching the data errors. 295 //! fn select_user_info<'a>( 296 //! user_id: &'a UserHandle64, 297 //! ) -> Result< 298 //! Option<( 299 //! PublicKeyCredentialUserEntity64<'static, 'static, 'a>, 300 //! Vec<PublicKeyCredentialDescriptor<Vec<u8>>>, 301 //! )>, 302 //! AppErr, 303 //! > { 304 //! // ⋮ 305 //! # Ok(None) 306 //! } 307 //! /// Writes `cred` to storage. 308 //! /// 309 //! /// # Errors 310 //! /// 311 //! /// Errors iff writing `cred` errors, there already exists a credential using the same `CredentialId`, 312 //! /// or there does not exist an account under the `UserHandle64`. 313 //! fn insert_credential( 314 //! cred: RegisteredCredential64<'_>, 315 //! ) -> Result<(), AppErr> { 316 //! // ⋮ 317 //! # Ok(()) 318 //! } 319 //! /// Fetches the `AuthenticatedCredential` associated with `cred_id` ensuring `user_id` matches the 320 //! /// `UserHandle64` associated with the account. 321 //! /// 322 //! /// # Errors 323 //! /// 324 //! /// Errors iff fetching the data errors or the `user_id` does not match the stored `UserHandle64`. 325 //! fn select_credential<'cred, 'user>( 326 //! cred_id: CredentialId<&'cred [u8]>, 327 //! user_id: &'user UserHandle64, 328 //! ) -> Result< 329 //! Option< 330 //! AuthenticatedCredential64< 331 //! 'cred, 332 //! 'user, 333 //! CompressedPubKey<[u8; 32], [u8; 32], [u8; 48], Vec<u8>>, 334 //! >, 335 //! >, 336 //! AppErr, 337 //! > { 338 //! // ⋮ 339 //! # Ok(None) 340 //! } 341 //! /// Overwrites the current `DynamicState` associated with `cred_id` with `dynamic_state`. 342 //! /// 343 //! /// # Errors 344 //! /// 345 //! /// Errors iff writing errors or `cred_id` does not exist. 346 //! fn update_credential( 347 //! cred_id: CredentialId<&[u8]>, 348 //! dynamic_state: DynamicState, 349 //! ) -> Result<(), AppErr> { 350 //! // ⋮ 351 //! # Ok(()) 352 //! } 353 //! ``` 354 //! 355 //! ## Cargo "features" 356 //! 357 //! [`custom`](#custom) or both [`bin`](#bin) and [`serde`](#serde) must be enabled; otherwise a [`compile_error`] 358 //! will occur. 359 //! 360 //! ### `bin` 361 //! 362 //! Enables binary (de)serialization via [`Encode`] and [`Decode`]. Since registered credentials will almost always 363 //! have to be saved to persistent storage, _some_ form of (de)serialization is necessary. In the event `bin` is 364 //! unsuitable or only partially suitable (e.g., human-readable output is desired), one will need to enable 365 //! [`custom`](#custom) to allow construction of certain types (e.g., [`AuthenticatedCredential`]). 366 //! 367 //! If possible and desired, one may wish to save the data "directly" to avoid any potential temporary allocations. 368 //! For example [`StaticState::encode`] will return a [`Vec`] containing hundreds (and possibly thousands in the 369 //! extreme case) of bytes if the underlying public key is an RSA key. This additional allocation and copy of data 370 //! is obviously avoided if [`StaticState`] is stored as a 371 //! [composite type](https://www.postgresql.org/docs/current/rowtypes.html) or its fields are stored in separate 372 //! columns when written to a relational database (RDB). 373 //! 374 //! ### `custom` 375 //! 376 //! Exposes functions (e.g., [`AuthenticatedCredential::new`]) that allows one to construct instances of types that 377 //! cannot be constructed when [`bin`](#bin) or [`serde`](#serde) is not enabled. 378 //! 379 //! ### `serde` 380 //! 381 //! This feature _strictly_ adheres to the JSON-motivated definitions. You _will_ encounter clients that send data 382 //! that cannot be deserialized using this feature. For many [`serde_relaxed`](#serde_relaxed) should be used 383 //! instead. 384 //! 385 //! Enables (de)serialization of data sent to/from the client via [`serde`](https://docs.rs/serde/latest/serde/) 386 //! based on the JSON-motivated definitions (e.g., 387 //! [`RegistrationResponseJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-registrationresponsejson)). Since 388 //! data has to be sent to/from the client, _some_ form of (de)serialization is necessary. In the event `serde` 389 //! is unsuitable or only partially suitable, one will need to enable [`custom`](#custom) to allow construction 390 //! of certain types (e.g., [`Registration`]). 391 //! 392 //! Code is _strongly_ encouraged to rely on the [`Deserialize`] implementations as much as possible to reduce the 393 //! chances of improperly deserializing the client data. 394 //! 395 //! Note that clients are free to send data in whatever form works best, so there is no requirement the 396 //! JSON-motivated definitions are used even when JSON is sent. This is especially relevant since the JSON-motivated 397 //! definitions were only added in [WebAuthn Level 3](https://www.w3.org/TR/webauthn-3/); thus many deployments only 398 //! partially conform. Some specific deviations that may require partial customization of deserialization are the 399 //! following: 400 //! 401 //! * [`ArrayBuffer`](https://webidl.spec.whatwg.org/#idl-ArrayBuffer)s encoded using something other than 402 //! base64url. 403 //! * `ArrayBuffer`s that are encoded multiple times (including the use of different encodings each time). 404 //! * Missing fields (e.g., 405 //! [`transports`](https://www.w3.org/TR/webauthn-3/#dom-authenticatorattestationresponsejson-transports)). 406 //! * Different field names (e.g., `extensions` instead of 407 //! [`clientExtensionResults`](https://www.w3.org/TR/webauthn-3/#dom-registrationresponsejson-clientextensionresults)). 408 //! 409 //! ### `serde_relaxed` 410 //! 411 //! Automatically enables [`serde`](#serde) in addition to "relaxed" [`Deserialize`] implementations 412 //! (e.g., [`RegistrationRelaxed`]). Roughly "relaxed" translates to unknown fields being ignored and only 413 //! the fields necessary for construction of the type are required. Case still matters, duplicate fields are still 414 //! forbidden, and interrelated data validation is still performed when applicable. This can be useful when one 415 //! wants to accommodate non-conforming clients or clients that implement older versions of the spec. 416 //! 417 //! ### `serializable_server_state` 418 //! 419 //! Automatically enables [`bin`](#bin) in addition to [`Encode`] and [`Decode`] implementations for 420 //! [`RegistrationServerState`], [`DiscoverableAuthenticationServerState`], and 421 //! [`NonDiscoverableAuthenticationServerState`]. Less accurate [`SystemTime`] is used instead of [`Instant`] for 422 //! timeout enforcement. This should be enabled if you don't desire to use in-memory collections to store the instances 423 //! of those types. 424 //! 425 //! Note even when written to persistent storage, an application should still periodically remove expired ceremonies. 426 //! If one is using a relational database (RDB); then one can achieve this by storing [`SentChallenge`], 427 //! the `Vec` returned from [`Encode::encode`], and [`TimedCeremony::expiration`] and periodically remove all rows 428 //! whose expiration exceeds the current date and time. 429 //! 430 //! ## Registration and authentication 431 //! 432 //! Both [registration](https://www.w3.org/TR/webauthn-3/#registration-ceremony) and 433 //! [authentication](https://www.w3.org/TR/webauthn-3/#authentication-ceremony) ceremonies rely on "challenges", and 434 //! these challenges are inherently temporary. For this reason the data associated with challenge completion can 435 //! often be stored in memory without concern for out-of-memory (OOM) conditions. There are several benefits to 436 //! storing such data in memory: 437 //! 438 //! * No data manipulation 439 //! * By leveraging move semantics, the data sent to the client cannot be mutated once the ceremony begins. 440 //! * Improved timeout enforcement 441 //! * By ensuring the same machine that started the ceremony is also used to finish the ceremony, deviation of 442 //! system clocks is not a concern. Additionally, allowing serialization requires the use of some form of 443 //! cross-platform "timestamp" (e.g., [Unix time](https://en.wikipedia.org/wiki/Unix_time)) which differ in 444 //! implementation (e.g., platforms implement leap seconds in different ways) and are often not monotonically 445 //! increasing. If data resides in memory, a monotonic [`Instant`] can be used instead. 446 //! 447 //! It is for those reasons data like [`RegistrationServerState`] are not serializable by default and require the 448 //! use of in-memory collections (e.g., [`FixedCapHashSet`]). To better ensure OOM is not a concern, RPs should set 449 //! reasonable timeouts. Since ceremonies can only be completed by moving data (e.g., 450 //! [`RegistrationServerState::verify`]), ceremony completion is guaranteed to free up the memory used— 451 //! `RegistrationServerState` instances are as small as 48 bytes on `x86_64-unknown-linux-gnu` platforms. To avoid 452 //! issues related to incomplete ceremonies, RPs can periodically iterate the collection for expired ceremonies and 453 //! remove such data. Other techniques can be employed as well to mitigate OOM, but they are application specific 454 //! and out-of-scope. If this is undesirable, one can enable [`serializable_server_state`](#serializable_server_state) 455 //! so that `RegistrationServerState`, [`DiscoverableAuthenticationServerState`], and 456 //! [`NonDiscoverableAuthenticationServerState`] implement [`Encode`] and [`Decode`]. Another reason one may need to 457 //! store this information persistently is for load-balancing purposes where the server that started the ceremony is 458 //! not guaranteed to be the server that finishes the ceremony. 459 //! 460 //! ## Supported signature algorithms 461 //! 462 //! The only supported signature algorithms are the following: 463 //! 464 //! * Ed25519 as defined in [RFC 8032 § 5.1](https://www.rfc-editor.org/rfc/rfc8032#section-5.1). This corresponds 465 //! to [`CoseAlgorithmIdentifier::Eddsa`]. 466 //! * ECDSA as defined in [SEC 1 Version 2.0 § 4.1](https://www.secg.org/sec1-v2.pdf#subsection.4.1) using SHA-256 467 //! as the hash function and NIST P-256 as defined in 468 //! [NIST SP 800-186 § 3.2.1.3](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-186.pdf#%5B%7B%22num%22%3A229%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C70%2C275%2C0%5D) 469 //! for the underlying elliptic curve. This corresponds to [`CoseAlgorithmIdentifier::Es256`]. 470 //! * ECDSA as defined in SEC 1 Version 2.0 § 4.1 using SHA-384 as the hash function and NIST P-384 as defined in 471 //! [NIST SP 800-186 § 3.2.1.4](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-186.pdf#%5B%7B%22num%22%3A232%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C70%2C264%2C0%5D) 472 //! for the underlying elliptic curve. This corresponds to [`CoseAlgorithmIdentifier::Es384`]. 473 //! * RSASSA-PKCS1-v1_5 as defined in [RFC 8017 § 8.2](https://www.rfc-editor.org/rfc/rfc8017#section-8.2) using 474 //! SHA-256 as the hash function. This corresponds to [`CoseAlgorithmIdentifier::Rs256`]. 475 //! 476 //! ## Correctness of code 477 //! 478 //! This library more strictly adheres to the spec than many other similar libraries including but not limited to 479 //! the following ways: 480 //! 481 //! * [CTAP2 canonical CBOR encoding form](https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#ctap2-canonical-cbor-encoding-form). 482 //! * `Deserialize` implementations requiring _exact_ conformance (e.g., not allowing unknown data). 483 //! * More thorough interrelated data validation (e.g., all places a Credential ID exists must match). 484 //! * Implement a lot of recommended (i.e., SHOULD) criteria (e.g., 485 //! [User display names conforming to the Nickname Profile as defined in RFC 8266](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialentity-name)). 486 //! 487 //! Unfortunately like almost all software, this library has not been formally verified; however great care is 488 //! employed in the following ways: 489 //! 490 //! * Leverage move semantics to prevent mutation of data once in a static state. 491 //! * Ensure a great many invariants via types. 492 //! * Reduce code duplication. 493 //! * Reduce variable mutation allowing for simpler algebraic reasoning. 494 //! * `panic`-free code[^note] (i.e., define true/total functions). 495 //! * Ensure arithmetic "side effects" don't occur (e.g., overflow). 496 //! * Aggressive use of compiler and [Clippy](https://doc.rust-lang.org/stable/clippy/lints.html) lints. 497 //! * Unit tests for common cases, edge cases, and error cases. 498 //! 499 //! ## Cryptographic libraries 500 //! 501 //! This library does not rely on _any_ sensitive data (e.g., private keys) as only signature verification is 502 //! ever performed. This means that the only thing that matters with the libraries used is their algorithmic 503 //! correctness and not other normally essential aspects like susceptibility to side-channel attacks. While I 504 //! personally believe the libraries that are used are at least as "secure" as alternatives even when dealing with 505 //! sensitive data, one only needs to audit the correctness of the libraries to be confident in their use. In fact 506 //! [`curve25519_dalek`](https://docs.rs/curve25519-dalek/latest/curve25519_dalek/#backends) has been formally 507 //! verified when the [`fiat`](https://github.com/mit-plv/fiat-crypto) backend is used making it _objectively_ 508 //! better than many other libraries whose correctness has not been proven. Two additional benefits of the library 509 //! choices are simpler APIs making it more likely their use is correct and better cross-platform compatibility. 510 //! 511 //! [^note]: `panic`s related to memory allocations or stack overflow are possible since such issues are not 512 //! formally guarded against. 513 #![expect( 514 clippy::multiple_crate_versions, 515 reason = "RustCrypto hasn't updated rand yet" 516 )] 517 #![cfg_attr(docsrs, feature(doc_cfg))] 518 #[cfg(not(any(feature = "custom", all(feature = "bin", feature = "serde"))))] 519 compile_error!("'custom' must be enabled or both 'bin' and 'serde' must be enabled"); 520 #[cfg(feature = "serializable_server_state")] 521 use crate::request::{ 522 auth::ser_server_state::{ 523 DecodeDiscoverableAuthenticationServerStateErr, 524 DecodeNonDiscoverableAuthenticationServerStateErr, 525 EncodeNonDiscoverableAuthenticationServerStateErr, 526 }, 527 register::ser_server_state::DecodeRegistrationServerStateErr, 528 }; 529 #[cfg(any(feature = "bin", feature = "custom"))] 530 use crate::response::error::CredentialIdErr; 531 #[cfg(feature = "serde_relaxed")] 532 use crate::response::ser_relaxed::SerdeJsonErr; 533 #[cfg(doc)] 534 use crate::{ 535 hash::hash_set::FixedCapHashSet, 536 request::{ 537 AsciiDomain, DomainOrigin, Port, PublicKeyCredentialDescriptor, RpId, Scheme, 538 TimedCeremony, Url, 539 auth::{AllowedCredential, AllowedCredentials, PublicKeyCredentialRequestOptions}, 540 register::{ 541 CoseAlgorithmIdentifier, Nickname, PublicKeyCredentialCreationOptions, 542 PublicKeyCredentialUserEntity, UserHandle16, UserHandle64, Username, 543 }, 544 }, 545 response::{ 546 CollectedClientData, Flag, SentChallenge, 547 auth::{self, Authentication, DiscoverableAuthenticatorAssertion}, 548 register::{ 549 self, Aaguid, Attestation, AttestationObject, AttestedCredentialData, 550 AuthenticatorExtensionOutput, ClientExtensionsOutputs, CompressedPubKey, 551 CredentialPropertiesOutput, 552 }, 553 }, 554 }; 555 #[cfg(feature = "bin")] 556 use crate::{ 557 request::register::bin::{DecodeNicknameErr, DecodeUsernameErr}, 558 response::{ 559 bin::DecodeAuthTransportsErr, 560 register::bin::{DecodeDynamicStateErr, DecodeStaticStateErr}, 561 }, 562 }; 563 use crate::{ 564 request::{ 565 auth::error::{InvalidTimeout, SecondFactorErr}, 566 error::{AsciiDomainErr, DomainOriginParseErr, PortParseErr, SchemeParseErr, UrlErr}, 567 register::{ 568 ResidentKeyRequirement, USER_HANDLE_MAX_LEN, UserHandle, 569 error::{CreationOptionsErr, NicknameErr, UsernameErr}, 570 }, 571 }, 572 response::{ 573 AuthTransports, CredentialId, 574 auth::error::{AuthCeremonyErr, AuthenticatorDataErr as AuthAuthDataErr}, 575 error::CollectedClientDataErr, 576 register::{ 577 CredentialProtectionPolicy, DynamicState, Metadata, StaticState, UncompressedPubKey, 578 error::{ 579 AaguidErr, AttestationObjectErr, AuthenticatorDataErr as RegAuthDataErr, 580 RegCeremonyErr, 581 }, 582 }, 583 }, 584 }; 585 #[cfg(all(doc, feature = "bin"))] 586 use bin::{Decode, Encode}; 587 #[cfg(doc)] 588 use core::str::FromStr; 589 use core::{ 590 convert, 591 error::Error, 592 fmt::{self, Display, Formatter}, 593 ops::Not, 594 }; 595 #[cfg(all(doc, feature = "serde_relaxed"))] 596 use response::register::ser_relaxed::RegistrationRelaxed; 597 #[cfg(all(doc, feature = "serde"))] 598 use serde::Deserialize; 599 #[cfg(all(doc, feature = "serde_relaxed"))] 600 use serde_json::de::{Deserializer, StreamDeserializer}; 601 #[cfg(feature = "serializable_server_state")] 602 use std::time::SystemTimeError; 603 #[cfg(doc)] 604 use std::time::{Instant, SystemTime}; 605 /// Contains functionality to (de)serialize data to a data store. 606 #[cfg_attr(docsrs, doc(cfg(feature = "bin")))] 607 #[cfg(feature = "bin")] 608 pub mod bin; 609 /// Contains functionality for fixed-capacity hash maps and sets. 610 pub mod hash; 611 /// Functionality for starting ceremonies. 612 /// 613 /// # What kind of credential should I create? 614 /// 615 /// Without partitioning the possibilities _too_ much, the following are possible authentication flows: 616 /// 617 /// | Label | Username | Password | Client-side credential | Authenticator-side user verification | Recommended | 618 /// |-------|----------|----------|------------------------|--------------------------------------|:-----------:| 619 /// | 1 | Yes | Yes | Required | Yes | ❌ | 620 /// | 2 | Yes | Yes | Required | No | ❌ | 621 /// | 3 | Yes | Yes | Optional | Yes | ❌ | 622 /// | <a name="label4">4</a> | Yes | Yes | Optional | No | ✅ | 623 /// | 5 | Yes | No | Required | Yes | ❌ | 624 /// | 6 | Yes | No | Required | No | ❌ | 625 /// | <a name="label7">7</a> | Yes | No | Optional | Yes | ❔ | 626 /// | 8 | Yes | No | Optional | No | ❌ | 627 /// | 9 | No | Yes | Required | Yes | ❌ | 628 /// | 10 | No | Yes | Required | No | ❌ | 629 /// | 11 | No | Yes | Optional | Yes | ❌ | 630 /// | 12 | No | Yes | Optional | No | ❌ | 631 /// | <a name="label13">13</a> | No | No | Required | Yes | ✅ | 632 /// | 14 | No | No | Required | No | ❌ | 633 /// | 15 | No | No | Optional | Yes | ❌ | 634 /// | 16 | No | No | Optional | No | ❌ | 635 /// 636 /// * All `Label`s with both `Password` and `Authenticator-side user verification` set to `Yes` are not recommended 637 /// since the verification done on the authenticator is likely the same "factor" as a password; thus it does not 638 /// add benefit but only serves as an annoyance to users. 639 /// * All `Label`s with `Username` or `Password` set to `Yes` and `Client-side credential` set to `Required` are not 640 /// recommended since you may preclude authenticators that are storage constrained (e.g., security keys). 641 /// * All `Label`s with `Username` set to `No` and `Client-side credential` set to `Optional` are not possible since 642 /// RPs would not have a way to identify the set of encrypted credentials to pass to the unknown user. 643 /// * All `Label`s with `Password` and `Authenticator-side user verification` set to `No` are not recommended since 644 /// those are single-factor authentication schemes; thus anyone possessing the credential without also passing 645 /// some form of user verification (e.g., password) would authenticate. 646 /// * [`Label 7`](#label7) is possible for RPs that are comfortable passing an encrypted credential to a potential user 647 /// without having that user first pass another form of authentication. For many RPs passing such information even 648 /// if encrypted is not desirable though. 649 /// * [`Label 4`](#label4) is ideal as a single-factor flow incorporated within a wider multi-factor authentication (MFA) 650 /// setup. The easiest way to register such a credential is with 651 /// [`CredentialCreationOptions::second_factor`]. 652 /// * [`Label 13`](#label13) is ideal for passkey setups as it allows for pleasant UX where a user does not have to type a 653 /// username nor password while still being secured with MFA with one of the factors being based on public-key 654 /// cryptography which for many is the most secure form of single-factor authentication. The easiest way to register 655 /// such a credential is with [`CredentialCreationOptions::passkey`]. 656 /// 657 /// Two other reasons one may prefer to construct client-side credentials is richer support for extensions (e.g., 658 /// [`largeBlobKey`](https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#sctn-largeBlobKey-extension) 659 /// for CTAP 2.2 authenticators) and the ability to use both discoverable and nondiscoverable requests. The former is not 660 /// relevant for this library—at least currently—since the only extensions supported are applicable for both 661 /// client-side and server-side credentials. The latter can be important especially if an RP wants the ability to 662 /// seamlessly transition from a username and password scheme to a userless and passwordless one in the future. 663 /// 664 /// Note the table is purely informative. While helper functions 665 /// (e.g., [`CredentialCreationOptions::passkey`]) only exist for [`Label 4`](#label4) and 666 /// [`Label 13`](#label13), one can create any credential since all fields in [`CredentialCreationOptions`] 667 /// and [`PublicKeyCredentialRequestOptions`] are accessible. 668 pub mod request; 669 /// Functionality for completing ceremonies. 670 /// 671 /// Read [`request`] for more information about what credentials one should create. 672 pub mod response; 673 #[doc(inline)] 674 pub use crate::{ 675 request::{ 676 auth::{ 677 DiscoverableAuthenticationClientState, DiscoverableAuthenticationServerState, 678 DiscoverableCredentialRequestOptions, NonDiscoverableAuthenticationClientState, 679 NonDiscoverableAuthenticationServerState, NonDiscoverableCredentialRequestOptions, 680 }, 681 register::{ 682 CredentialCreationOptions, CredentialCreationOptions16, CredentialCreationOptions64, 683 RegistrationClientState, RegistrationClientState16, RegistrationClientState64, 684 RegistrationServerState, RegistrationServerState16, RegistrationServerState64, 685 }, 686 }, 687 response::{ 688 auth::{ 689 DiscoverableAuthentication, DiscoverableAuthentication16, DiscoverableAuthentication64, 690 NonDiscoverableAuthentication, NonDiscoverableAuthentication16, 691 NonDiscoverableAuthentication64, 692 }, 693 register::Registration, 694 }, 695 }; 696 /// Error returned in [`RegCeremonyErr::Credential`] and [`AuthenticatedCredential::new`]. 697 #[derive(Clone, Copy, Debug)] 698 pub enum CredentialErr { 699 /// Variant when [`CredentialProtectionPolicy::UserVerificationRequired`], but 700 /// [`DynamicState::user_verified`] is `false`. 701 CredProtectUserVerificationRequiredWithoutUserVerified, 702 /// Variant when [`AuthenticatorExtensionOutput::hmac_secret`] is `Some(true)` and 703 /// [`DynamicState::user_verified`] is `false`. 704 HmacSecretWithoutUserVerified, 705 /// Variant when [`AuthenticatorExtensionOutput::hmac_secret`] is `Some(true)`, but 706 /// [`ClientExtensionsOutputs::prf`] is `Some(AuthenticationExtensionsPRFOutputs { enabled: false })` 707 /// or `AuthenticatorExtensionOutput::hmac_secret` is `Some`, but 708 /// `ClientExtensionsOutputs::prf` is `None`. 709 HmacSecretWithoutPrf, 710 /// Variant when [`ClientExtensionsOutputs::prf`] is 711 /// `Some(AuthenticationExtensionsPRFOutputs { enabled: true })`, but 712 /// [`AuthenticatorExtensionOutput::hmac_secret`] is `Some(false)`. 713 PrfWithoutHmacSecret, 714 /// Variant when [`ResidentKeyRequirement::Required`] was sent, but 715 /// [`CredentialPropertiesOutput::rk`] is `Some(false)`. 716 ResidentKeyRequiredServerCredentialCreated, 717 } 718 impl Display for CredentialErr { 719 #[inline] 720 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 721 f.write_str(match *self { 722 Self::CredProtectUserVerificationRequiredWithoutUserVerified => { 723 "credProtect requires user verification, but the user is not verified" 724 } 725 Self::HmacSecretWithoutUserVerified => { 726 "hmac-secret was enabled, but the user is not verified" 727 } 728 Self::HmacSecretWithoutPrf => "hmac-secret was enabled but prf was not", 729 Self::PrfWithoutHmacSecret => "prf was enabled, but hmac-secret was not", 730 Self::ResidentKeyRequiredServerCredentialCreated => { 731 "server-side credential was created, but a client-side credential is required" 732 } 733 }) 734 } 735 } 736 impl Error for CredentialErr {} 737 /// Checks if the `static_state` and `dynamic_state` are valid. 738 /// 739 /// # Errors 740 /// 741 /// Errors iff `static_state` or `dynamc_state` are invalid. 742 fn verify_static_and_dynamic_state<T>( 743 static_state: &StaticState<T>, 744 dynamic_state: DynamicState, 745 ) -> Result<(), CredentialErr> { 746 if dynamic_state.user_verified { 747 Ok(()) 748 } else if matches!( 749 static_state.extensions.cred_protect, 750 CredentialProtectionPolicy::UserVerificationRequired 751 ) { 752 Err(CredentialErr::CredProtectUserVerificationRequiredWithoutUserVerified) 753 } else if static_state 754 .extensions 755 .hmac_secret 756 .is_some_and(convert::identity) 757 { 758 Err(CredentialErr::HmacSecretWithoutUserVerified) 759 } else { 760 Ok(()) 761 } 762 .and_then(|()| { 763 static_state.client_extension_results.prf.map_or_else( 764 || { 765 if static_state.extensions.hmac_secret.is_none() { 766 Ok(()) 767 } else { 768 Err(CredentialErr::HmacSecretWithoutPrf) 769 } 770 }, 771 |prf| { 772 if prf.enabled { 773 if static_state.extensions.hmac_secret.is_some_and(Not::not) { 774 Err(CredentialErr::PrfWithoutHmacSecret) 775 } else { 776 Ok(()) 777 } 778 } else if static_state 779 .extensions 780 .hmac_secret 781 .is_some_and(convert::identity) 782 { 783 Err(CredentialErr::HmacSecretWithoutPrf) 784 } else { 785 Ok(()) 786 } 787 }, 788 ) 789 }) 790 } 791 /// Registered credential that needs to be saved server-side to perform future 792 /// [authentication ceremonies](https://www.w3.org/TR/webauthn-3/#authentication-ceremony) with 793 /// [`AuthenticatedCredential`]. 794 /// 795 /// When saving `RegisteredCredential` to persistent storage, one will almost always want to save the contained data 796 /// separately. The reasons for this are the following: 797 /// 798 /// * [`CredentialId`] 799 /// * MUST be globally unique, and it will likely be easier to enforce such uniqueness when it's separate. 800 /// * Fetching the [`AuthenticatedCredential`] by [`Authentication::raw_id`] when completing the 801 /// authentication ceremony via [`DiscoverableAuthenticationServerState::verify`] or 802 /// [`NonDiscoverableAuthenticationServerState::verify`] will likely be easier than alternatives. 803 /// * [`AuthTransports`] 804 /// * Fetching [`CredentialId`]s and associated `AuthTransports` by [`UserHandle`] will likely make credential 805 /// registration easier since one should set [`PublicKeyCredentialCreationOptions::exclude_credentials`] to 806 /// the [`PublicKeyCredentialDescriptor`]s belonging to a `UserHandle` in order to avoid accidentally 807 /// overwriting an existing credential on the authenticator. 808 /// * Fetching `CredentialId`s and associated `AuthTransports` by `UserHandle` will likely make starting 809 /// authentication ceremonies easier for [`NonDiscoverableCredentialRequestOptions`]. 810 /// * [`UserHandle`] 811 /// * Fetching the [`AuthenticatedCredential`] by [`DiscoverableAuthentication::raw_id`] must also coincide with 812 /// verifying the associated `UserHandle` matches [`DiscoverableAuthenticatorAssertion::user_handle`]. 813 /// * Fetching [`CredentialId`]s and associated [`AuthTransports`] by `UserHandle` will likely make credential 814 /// registration easier since one should set [`PublicKeyCredentialCreationOptions::exclude_credentials`] to 815 /// the [`PublicKeyCredentialDescriptor`]s belonging to a `UserHandle` in order to avoid accidentally 816 /// overwriting an existing credential on the authenticator. 817 /// * Fetching `CredentialId`s and associated `AuthTransports` by `UserHandle` will likely make starting 818 /// authentication ceremonies easier for [`NonDiscoverableCredentialRequestOptions`]. 819 /// * [`DynamicState`] 820 /// * `DynamicState` is the only part that is ever updated after a successful authentication ceremony 821 /// via [`DiscoverableAuthenticationServerState::verify`] or 822 /// [`NonDiscoverableAuthenticationServerState::verify`]. It being separate allows for smaller and quicker 823 /// updates. 824 /// * [`Metadata`] 825 /// * Informative data that is never used during authentication ceremonies; consequently, one may wish to 826 /// not even save this information. 827 /// * [`StaticState`] 828 /// * All other data exists as part of `StaticState`. 829 /// 830 /// It is for those reasons that `RegisteredCredential` does not implement [`Encode`] or [`Decode`]; instead its parts 831 /// do. 832 /// 833 /// Note that [`RpId`] and user information other than the `UserHandle` are not stored in `RegisteredCredential`. 834 /// RPs that wish to store such information must do so on their own. Since user information is likely the same 835 /// for a given `UserHandle` and `RpId` is likely static, it makes little sense to store such information 836 /// automatically. Types like [`Username`] implement `Encode` and `Decode` to assist such a thing. 837 /// 838 /// When registering a credential, [`AttestedCredentialData::aaguid`], [`AttestedCredentialData::credential_id`], 839 /// and [`AttestedCredentialData::credential_public_key`] will be the sources for [`Metadata::aaguid`], 840 /// [`Self::id`], and [`StaticState::credential_public_key`] respectively. The [`PublicKeyCredentialUserEntity::id`] 841 /// associated with the [`CredentialCreationOptions`] used to create the `RegisteredCredential` via 842 /// [`RegistrationServerState::verify`] will be the source for [`Self::user_id`]. 843 /// 844 /// The only way to create this is via `RegistrationServerState::verify`. 845 #[derive(Debug)] 846 pub struct RegisteredCredential<'reg, const USER_LEN: usize> { 847 /// The credential ID. 848 /// 849 /// For client-side credentials, this is a unique identifier; but for server-side 850 /// credentials, this _is_ the credential (i.e., the encrypted private key and necessary information). 851 id: CredentialId<&'reg [u8]>, 852 /// Hints for how the client might communicate with the authenticator containing the credential. 853 transports: AuthTransports, 854 /// The identifier for the user. 855 /// 856 /// Unlike [`Self::id`] which is globally unique for an RP, this is unique up to "user" (i.e., 857 /// multiple [`CredentialId`]s will often exist for the same `UserHandle`). 858 user_id: UserHandle<USER_LEN>, 859 /// Immutable state returned during registration. 860 static_state: StaticState<UncompressedPubKey<'reg>>, 861 /// State that can change during authentication ceremonies. 862 dynamic_state: DynamicState, 863 /// Metadata. 864 metadata: Metadata<'reg>, 865 } 866 impl<'reg, const USER_LEN: usize> RegisteredCredential<'reg, USER_LEN> { 867 /// The credential ID. 868 /// 869 /// For client-side credentials, this is a unique identifier; but for server-side 870 /// credentials, this _is_ the credential (i.e., the encrypted private key and necessary information). 871 #[inline] 872 #[must_use] 873 pub const fn id(&self) -> CredentialId<&'reg [u8]> { 874 self.id 875 } 876 /// Hints for how the client might communicate with the authenticator containing the credential. 877 #[inline] 878 #[must_use] 879 pub const fn transports(&self) -> AuthTransports { 880 self.transports 881 } 882 /// The identifier for the user. 883 /// 884 /// Unlike [`Self::id`] which is globally unique for an RP, this is unique up to "user" (i.e., 885 /// multiple [`CredentialId`]s will often exist for the same `UserHandle`). 886 #[inline] 887 #[must_use] 888 pub const fn user_id(&self) -> &UserHandle<USER_LEN> { 889 &self.user_id 890 } 891 /// Immutable state returned during registration. 892 #[inline] 893 #[must_use] 894 pub const fn static_state(&self) -> StaticState<UncompressedPubKey<'reg>> { 895 self.static_state 896 } 897 /// State that can change during authentication ceremonies. 898 #[inline] 899 #[must_use] 900 pub const fn dynamic_state(&self) -> DynamicState { 901 self.dynamic_state 902 } 903 /// Metadata. 904 #[inline] 905 #[must_use] 906 pub const fn metadata(&self) -> Metadata<'reg> { 907 self.metadata 908 } 909 /// Constructs a `RegisteredCredential` based on the passed arguments. 910 /// 911 /// # Errors 912 /// 913 /// Errors iff the passed arguments are invalid. Read [`CredentialErr`] 914 /// for more information. 915 #[inline] 916 fn new<'a: 'reg>( 917 id: CredentialId<&'a [u8]>, 918 transports: AuthTransports, 919 user_id: UserHandle<USER_LEN>, 920 static_state: StaticState<UncompressedPubKey<'a>>, 921 dynamic_state: DynamicState, 922 metadata: Metadata<'a>, 923 ) -> Result<Self, CredentialErr> { 924 verify_static_and_dynamic_state(&static_state, dynamic_state).and_then(|()| { 925 if !matches!(metadata.resident_key, ResidentKeyRequirement::Required) 926 || metadata 927 .client_extension_results 928 .cred_props 929 .as_ref() 930 .is_none_or(|props| props.rk.is_none_or(convert::identity)) 931 { 932 Ok(Self { 933 id, 934 transports, 935 user_id, 936 static_state, 937 dynamic_state, 938 metadata, 939 }) 940 } else { 941 Err(CredentialErr::ResidentKeyRequiredServerCredentialCreated) 942 } 943 }) 944 } 945 /// Returns the contained data consuming `self`. 946 #[inline] 947 #[must_use] 948 pub const fn into_parts( 949 self, 950 ) -> ( 951 CredentialId<&'reg [u8]>, 952 AuthTransports, 953 UserHandle<USER_LEN>, 954 StaticState<UncompressedPubKey<'reg>>, 955 DynamicState, 956 Metadata<'reg>, 957 ) { 958 ( 959 self.id, 960 self.transports, 961 self.user_id, 962 self.static_state, 963 self.dynamic_state, 964 self.metadata, 965 ) 966 } 967 /// Returns the contained data. 968 #[inline] 969 #[must_use] 970 pub const fn as_parts( 971 &self, 972 ) -> ( 973 CredentialId<&'reg [u8]>, 974 AuthTransports, 975 &UserHandle<USER_LEN>, 976 StaticState<UncompressedPubKey<'reg>>, 977 DynamicState, 978 Metadata<'reg>, 979 ) { 980 ( 981 self.id, 982 self.transports, 983 &self.user_id, 984 self.static_state, 985 self.dynamic_state, 986 self.metadata, 987 ) 988 } 989 } 990 /// `RegisteredCredential` based on a [`UserHandle64`]. 991 pub type RegisteredCredential64<'reg> = RegisteredCredential<'reg, USER_HANDLE_MAX_LEN>; 992 /// `RegisteredCredential` based on a [`UserHandle16`]. 993 pub type RegisteredCredential16<'reg> = RegisteredCredential<'reg, 16>; 994 /// Credential used in authentication ceremonies. 995 /// 996 /// Similar to [`RegisteredCredential`] except designed to only contain the necessary data to complete 997 /// authentication ceremonies. In particular there is no [`AuthTransports`] or [`Metadata`], 998 /// [`StaticState::credential_public_key`] is [`CompressedPubKey`] that can own or borrow its data, [`Self::id`] is 999 /// based on the [`CredentialId`] passed to [`Self::new`] which itself must be from [`Authentication::raw_id`], and 1000 /// [`Self::user_id`] is based on the [`UserHandle`] passed to [`Self::new`] which itself must be the value in 1001 /// persistent storage associated with the `CredentialId`. 1002 /// 1003 /// When [`DiscoverableAuthentication`] is used, one can use [`DiscoverableAuthenticatorAssertion::user_handle`] 1004 /// for `Self::user_id` so long as it matches the value in persistent storage. 1005 /// 1006 /// Note `PublicKey` should be `CompressedPubKey` for this to be useful. 1007 /// 1008 /// The only way to create this is via `Self::new`. 1009 #[derive(Debug)] 1010 pub struct AuthenticatedCredential<'cred, 'user, const USER_LEN: usize, PublicKey> { 1011 /// The credential ID. 1012 /// 1013 /// For client-side credentials, this is a unique identifier; but for server-side 1014 /// credentials, this _is_ the credential (i.e., the encrypted private key and necessary information). 1015 id: CredentialId<&'cred [u8]>, 1016 /// The identifier for the user. 1017 /// 1018 /// Unlike [`Self::id`] which is globally unique for an RP, this is unique up to "user" (i.e., 1019 /// multiple [`CredentialId`]s will often exist for the same `UserHandle`). 1020 user_id: &'user UserHandle<USER_LEN>, 1021 /// Immutable state returned during registration. 1022 static_state: StaticState<PublicKey>, 1023 /// State that can change during authentication ceremonies. 1024 dynamic_state: DynamicState, 1025 } 1026 impl<'cred, 'user, const USER_LEN: usize, PublicKey> 1027 AuthenticatedCredential<'cred, 'user, USER_LEN, PublicKey> 1028 { 1029 /// The credential ID. 1030 /// 1031 /// For client-side credentials, this is a unique identifier; but for server-side 1032 /// credentials, this _is_ the credential (i.e., the encrypted private key and necessary information). 1033 #[inline] 1034 #[must_use] 1035 pub const fn id(&self) -> CredentialId<&'cred [u8]> { 1036 self.id 1037 } 1038 /// The identifier for the user. 1039 /// 1040 /// Unlike [`Self::id`] which is globally unique for an RP, this is unique up to "user" (i.e., 1041 /// multiple [`CredentialId`]s will often exist for the same `UserHandle`). 1042 #[inline] 1043 #[must_use] 1044 pub const fn user_id(&self) -> &'user UserHandle<USER_LEN> { 1045 self.user_id 1046 } 1047 /// Immutable state returned during registration. 1048 #[inline] 1049 #[must_use] 1050 pub const fn static_state(&self) -> &StaticState<PublicKey> { 1051 &self.static_state 1052 } 1053 /// State that can change during authentication ceremonies. 1054 #[inline] 1055 #[must_use] 1056 pub const fn dynamic_state(&self) -> DynamicState { 1057 self.dynamic_state 1058 } 1059 /// Constructs an `AuthenticatedCredential` based on the passed arguments. 1060 /// 1061 /// # Errors 1062 /// 1063 /// Errors iff the passed arguments are invalid. Read [`CredentialErr`] 1064 /// for more information. 1065 #[expect(single_use_lifetimes, reason = "false positive")] 1066 #[cfg_attr(docsrs, doc(cfg(any(feature = "bin", feature = "custom"))))] 1067 #[cfg(any(feature = "bin", feature = "custom"))] 1068 #[inline] 1069 pub fn new<'a: 'cred, 'b: 'user>( 1070 id: CredentialId<&'a [u8]>, 1071 user_id: &'b UserHandle<USER_LEN>, 1072 static_state: StaticState<PublicKey>, 1073 dynamic_state: DynamicState, 1074 ) -> Result<Self, CredentialErr> { 1075 verify_static_and_dynamic_state(&static_state, dynamic_state).map(|()| Self { 1076 id, 1077 user_id, 1078 static_state, 1079 dynamic_state, 1080 }) 1081 } 1082 /// Returns the contained data consuming `self`. 1083 #[inline] 1084 #[must_use] 1085 pub fn into_parts( 1086 self, 1087 ) -> ( 1088 CredentialId<&'cred [u8]>, 1089 &'user UserHandle<USER_LEN>, 1090 StaticState<PublicKey>, 1091 DynamicState, 1092 ) { 1093 (self.id, self.user_id, self.static_state, self.dynamic_state) 1094 } 1095 /// Returns the contained data. 1096 #[inline] 1097 #[must_use] 1098 pub const fn as_parts( 1099 &self, 1100 ) -> ( 1101 CredentialId<&'cred [u8]>, 1102 &'user UserHandle<USER_LEN>, 1103 &StaticState<PublicKey>, 1104 DynamicState, 1105 ) { 1106 ( 1107 self.id, 1108 self.user_id, 1109 self.static_state(), 1110 self.dynamic_state, 1111 ) 1112 } 1113 } 1114 /// `AuthenticatedCredential` based on a [`UserHandle64`]. 1115 pub type AuthenticatedCredential64<'cred, 'user, PublicKey> = 1116 AuthenticatedCredential<'cred, 'user, USER_HANDLE_MAX_LEN, PublicKey>; 1117 /// `AuthenticatedCredential` based on a [`UserHandle16`]. 1118 pub type AuthenticatedCredential16<'cred, 'user, PublicKey> = 1119 AuthenticatedCredential<'cred, 'user, 16, PublicKey>; 1120 /// Convenience aggregate error that rolls up all errors into one. 1121 #[derive(Debug)] 1122 pub enum AggErr { 1123 /// Variant when [`AsciiDomain::try_from`] errors. 1124 AsciiDomain(AsciiDomainErr), 1125 /// Variant when [`Url::from_str`] errors. 1126 Url(UrlErr), 1127 /// Variant when [`Scheme::try_from`] errors. 1128 Scheme(SchemeParseErr), 1129 /// Variant when [`DomainOrigin::try_from`] errors. 1130 DomainOrigin(DomainOriginParseErr), 1131 /// Variant when [`Port::from_str`] errors. 1132 Port(PortParseErr), 1133 /// Variant when [`DiscoverableCredentialRequestOptions::start_ceremony`] or 1134 /// [`NonDiscoverableCredentialRequestOptions::start_ceremony`] 1135 /// error. 1136 InvalidTimeout(InvalidTimeout), 1137 /// Variant when [`NonDiscoverableCredentialRequestOptions::second_factor`] errors. 1138 SecondFactor(SecondFactorErr), 1139 /// Variant when [`CredentialCreationOptions::start_ceremony`] errors. 1140 CreationOptions(CreationOptionsErr), 1141 /// Variant when [`Nickname::try_from`] errors. 1142 Nickname(NicknameErr), 1143 /// Variant when [`Username::try_from`] errors. 1144 Username(UsernameErr), 1145 /// Variant when [`RegistrationServerState::verify`] errors. 1146 RegCeremony(RegCeremonyErr), 1147 /// Variant when [`DiscoverableAuthenticationServerState::verify`] or. 1148 /// [`NonDiscoverableAuthenticationServerState::verify`] error. 1149 AuthCeremony(AuthCeremonyErr), 1150 /// Variant when [`AttestationObject::try_from`] errors. 1151 AttestationObject(AttestationObjectErr), 1152 /// Variant when [`register::AuthenticatorData::try_from`] errors. 1153 RegAuthenticatorData(RegAuthDataErr), 1154 /// Variant when [`auth::AuthenticatorData::try_from`] errors. 1155 AuthAuthenticatorData(AuthAuthDataErr), 1156 /// Variant when [`CollectedClientData::from_client_data_json`] errors. 1157 CollectedClientData(CollectedClientDataErr), 1158 /// Variant when [`CollectedClientData::from_client_data_json_relaxed`] errors or any of the [`Deserialize`] 1159 /// implementations error when relying on [`Deserializer`] or [`StreamDeserializer`]. 1160 #[cfg_attr(docsrs, doc(cfg(feature = "serde_relaxed")))] 1161 #[cfg(feature = "serde_relaxed")] 1162 SerdeJson(SerdeJsonErr), 1163 /// Variant when [`Aaguid::try_from`] errors. 1164 Aaguid(AaguidErr), 1165 /// Variant when [`AuthTransports::decode`] errors. 1166 #[cfg_attr(docsrs, doc(cfg(feature = "bin")))] 1167 #[cfg(feature = "bin")] 1168 DecodeAuthTransports(DecodeAuthTransportsErr), 1169 /// Variant when [`StaticState::decode`] errors. 1170 #[cfg_attr(docsrs, doc(cfg(feature = "bin")))] 1171 #[cfg(feature = "bin")] 1172 DecodeStaticState(DecodeStaticStateErr), 1173 /// Variant when [`DynamicState::decode`] errors. 1174 #[cfg_attr(docsrs, doc(cfg(feature = "bin")))] 1175 #[cfg(feature = "bin")] 1176 DecodeDynamicState(DecodeDynamicStateErr), 1177 /// Variant when [`Nickname::decode`] errors. 1178 #[cfg_attr(docsrs, doc(cfg(feature = "bin")))] 1179 #[cfg(feature = "bin")] 1180 DecodeNickname(DecodeNicknameErr), 1181 /// Variant when [`Username::decode`] errors. 1182 #[cfg_attr(docsrs, doc(cfg(feature = "bin")))] 1183 #[cfg(feature = "bin")] 1184 DecodeUsername(DecodeUsernameErr), 1185 /// Variant when [`RegistrationServerState::decode`] errors. 1186 #[cfg_attr(docsrs, doc(cfg(feature = "serializable_server_state")))] 1187 #[cfg(feature = "serializable_server_state")] 1188 DecodeRegistrationServerState(DecodeRegistrationServerStateErr), 1189 /// Variant when [`DiscoverableAuthenticationServerState::decode`] errors. 1190 #[cfg_attr(docsrs, doc(cfg(feature = "serializable_server_state")))] 1191 #[cfg(feature = "serializable_server_state")] 1192 DecodeDiscoverableAuthenticationServerState(DecodeDiscoverableAuthenticationServerStateErr), 1193 /// Variant when [`NonDiscoverableAuthenticationServerState::decode`] errors. 1194 #[cfg_attr(docsrs, doc(cfg(feature = "serializable_server_state")))] 1195 #[cfg(feature = "serializable_server_state")] 1196 DecodeNonDiscoverableAuthenticationServerState( 1197 DecodeNonDiscoverableAuthenticationServerStateErr, 1198 ), 1199 /// Variant when [`RegistrationServerState::encode`] errors. 1200 #[cfg_attr(docsrs, doc(cfg(feature = "serializable_server_state")))] 1201 #[cfg(feature = "serializable_server_state")] 1202 EncodeRegistrationServerState(SystemTimeError), 1203 /// Variant when [`DiscoverableAuthenticationServerState::encode`] errors. 1204 #[cfg_attr(docsrs, doc(cfg(feature = "serializable_server_state")))] 1205 #[cfg(feature = "serializable_server_state")] 1206 EncodeDiscoverableAuthenticationServerState(SystemTimeError), 1207 /// Variant when [`NonDiscoverableAuthenticationServerState::encode`] errors. 1208 #[cfg_attr(docsrs, doc(cfg(feature = "serializable_server_state")))] 1209 #[cfg(feature = "serializable_server_state")] 1210 EncodeNonDiscoverableAuthenticationServerState( 1211 EncodeNonDiscoverableAuthenticationServerStateErr, 1212 ), 1213 /// Variant when [`AuthenticatedCredential::new`] errors. 1214 #[cfg_attr(docsrs, doc(cfg(any(feature = "bin", feature = "custom"))))] 1215 #[cfg(any(feature = "bin", feature = "custom"))] 1216 Credential(CredentialErr), 1217 /// Variant when [`CredentialId::try_from`] or [`CredentialId::decode`] errors. 1218 #[cfg_attr(docsrs, doc(cfg(any(feature = "bin", feature = "custom"))))] 1219 #[cfg(any(feature = "bin", feature = "custom"))] 1220 CredentialId(CredentialIdErr), 1221 } 1222 impl From<AsciiDomainErr> for AggErr { 1223 #[inline] 1224 fn from(value: AsciiDomainErr) -> Self { 1225 Self::AsciiDomain(value) 1226 } 1227 } 1228 impl From<UrlErr> for AggErr { 1229 #[inline] 1230 fn from(value: UrlErr) -> Self { 1231 Self::Url(value) 1232 } 1233 } 1234 impl From<SchemeParseErr> for AggErr { 1235 #[inline] 1236 fn from(value: SchemeParseErr) -> Self { 1237 Self::Scheme(value) 1238 } 1239 } 1240 impl From<DomainOriginParseErr> for AggErr { 1241 #[inline] 1242 fn from(value: DomainOriginParseErr) -> Self { 1243 Self::DomainOrigin(value) 1244 } 1245 } 1246 impl From<PortParseErr> for AggErr { 1247 #[inline] 1248 fn from(value: PortParseErr) -> Self { 1249 Self::Port(value) 1250 } 1251 } 1252 impl From<InvalidTimeout> for AggErr { 1253 #[inline] 1254 fn from(value: InvalidTimeout) -> Self { 1255 Self::InvalidTimeout(value) 1256 } 1257 } 1258 impl From<SecondFactorErr> for AggErr { 1259 #[inline] 1260 fn from(value: SecondFactorErr) -> Self { 1261 Self::SecondFactor(value) 1262 } 1263 } 1264 impl From<CreationOptionsErr> for AggErr { 1265 #[inline] 1266 fn from(value: CreationOptionsErr) -> Self { 1267 Self::CreationOptions(value) 1268 } 1269 } 1270 impl From<NicknameErr> for AggErr { 1271 #[inline] 1272 fn from(value: NicknameErr) -> Self { 1273 Self::Nickname(value) 1274 } 1275 } 1276 impl From<UsernameErr> for AggErr { 1277 #[inline] 1278 fn from(value: UsernameErr) -> Self { 1279 Self::Username(value) 1280 } 1281 } 1282 impl From<RegCeremonyErr> for AggErr { 1283 #[inline] 1284 fn from(value: RegCeremonyErr) -> Self { 1285 Self::RegCeremony(value) 1286 } 1287 } 1288 impl From<AuthCeremonyErr> for AggErr { 1289 #[inline] 1290 fn from(value: AuthCeremonyErr) -> Self { 1291 Self::AuthCeremony(value) 1292 } 1293 } 1294 impl From<AttestationObjectErr> for AggErr { 1295 #[inline] 1296 fn from(value: AttestationObjectErr) -> Self { 1297 Self::AttestationObject(value) 1298 } 1299 } 1300 impl From<RegAuthDataErr> for AggErr { 1301 #[inline] 1302 fn from(value: RegAuthDataErr) -> Self { 1303 Self::RegAuthenticatorData(value) 1304 } 1305 } 1306 impl From<AuthAuthDataErr> for AggErr { 1307 #[inline] 1308 fn from(value: AuthAuthDataErr) -> Self { 1309 Self::AuthAuthenticatorData(value) 1310 } 1311 } 1312 impl From<CollectedClientDataErr> for AggErr { 1313 #[inline] 1314 fn from(value: CollectedClientDataErr) -> Self { 1315 Self::CollectedClientData(value) 1316 } 1317 } 1318 #[cfg_attr(docsrs, doc(cfg(feature = "serde_relaxed")))] 1319 #[cfg(feature = "serde_relaxed")] 1320 impl From<SerdeJsonErr> for AggErr { 1321 #[inline] 1322 fn from(value: SerdeJsonErr) -> Self { 1323 Self::SerdeJson(value) 1324 } 1325 } 1326 impl From<AaguidErr> for AggErr { 1327 #[inline] 1328 fn from(value: AaguidErr) -> Self { 1329 Self::Aaguid(value) 1330 } 1331 } 1332 #[cfg_attr(docsrs, doc(cfg(feature = "bin")))] 1333 #[cfg(feature = "bin")] 1334 impl From<DecodeAuthTransportsErr> for AggErr { 1335 #[inline] 1336 fn from(value: DecodeAuthTransportsErr) -> Self { 1337 Self::DecodeAuthTransports(value) 1338 } 1339 } 1340 #[cfg_attr(docsrs, doc(cfg(feature = "bin")))] 1341 #[cfg(feature = "bin")] 1342 impl From<DecodeStaticStateErr> for AggErr { 1343 #[inline] 1344 fn from(value: DecodeStaticStateErr) -> Self { 1345 Self::DecodeStaticState(value) 1346 } 1347 } 1348 #[cfg_attr(docsrs, doc(cfg(feature = "bin")))] 1349 #[cfg(feature = "bin")] 1350 impl From<DecodeDynamicStateErr> for AggErr { 1351 #[inline] 1352 fn from(value: DecodeDynamicStateErr) -> Self { 1353 Self::DecodeDynamicState(value) 1354 } 1355 } 1356 #[cfg_attr(docsrs, doc(cfg(feature = "bin")))] 1357 #[cfg(feature = "bin")] 1358 impl From<DecodeNicknameErr> for AggErr { 1359 #[inline] 1360 fn from(value: DecodeNicknameErr) -> Self { 1361 Self::DecodeNickname(value) 1362 } 1363 } 1364 #[cfg_attr(docsrs, doc(cfg(feature = "bin")))] 1365 #[cfg(feature = "bin")] 1366 impl From<DecodeUsernameErr> for AggErr { 1367 #[inline] 1368 fn from(value: DecodeUsernameErr) -> Self { 1369 Self::DecodeUsername(value) 1370 } 1371 } 1372 #[cfg_attr(docsrs, doc(cfg(feature = "serializable_server_state")))] 1373 #[cfg(feature = "serializable_server_state")] 1374 impl From<DecodeRegistrationServerStateErr> for AggErr { 1375 #[inline] 1376 fn from(value: DecodeRegistrationServerStateErr) -> Self { 1377 Self::DecodeRegistrationServerState(value) 1378 } 1379 } 1380 #[cfg_attr(docsrs, doc(cfg(feature = "serializable_server_state")))] 1381 #[cfg(feature = "serializable_server_state")] 1382 impl From<DecodeDiscoverableAuthenticationServerStateErr> for AggErr { 1383 #[inline] 1384 fn from(value: DecodeDiscoverableAuthenticationServerStateErr) -> Self { 1385 Self::DecodeDiscoverableAuthenticationServerState(value) 1386 } 1387 } 1388 #[cfg_attr(docsrs, doc(cfg(feature = "serializable_server_state")))] 1389 #[cfg(feature = "serializable_server_state")] 1390 impl From<DecodeNonDiscoverableAuthenticationServerStateErr> for AggErr { 1391 #[inline] 1392 fn from(value: DecodeNonDiscoverableAuthenticationServerStateErr) -> Self { 1393 Self::DecodeNonDiscoverableAuthenticationServerState(value) 1394 } 1395 } 1396 #[cfg_attr(docsrs, doc(cfg(feature = "serializable_server_state")))] 1397 #[cfg(feature = "serializable_server_state")] 1398 impl From<EncodeNonDiscoverableAuthenticationServerStateErr> for AggErr { 1399 #[inline] 1400 fn from(value: EncodeNonDiscoverableAuthenticationServerStateErr) -> Self { 1401 Self::EncodeNonDiscoverableAuthenticationServerState(value) 1402 } 1403 } 1404 #[cfg_attr(docsrs, doc(cfg(any(feature = "bin", feature = "custom"))))] 1405 #[cfg(any(feature = "bin", feature = "custom"))] 1406 impl From<CredentialErr> for AggErr { 1407 #[inline] 1408 fn from(value: CredentialErr) -> Self { 1409 Self::Credential(value) 1410 } 1411 } 1412 #[cfg_attr(docsrs, doc(cfg(any(feature = "bin", feature = "custom"))))] 1413 #[cfg(any(feature = "bin", feature = "custom"))] 1414 impl From<CredentialIdErr> for AggErr { 1415 #[inline] 1416 fn from(value: CredentialIdErr) -> Self { 1417 Self::CredentialId(value) 1418 } 1419 } 1420 impl Display for AggErr { 1421 #[inline] 1422 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 1423 match *self { 1424 Self::AsciiDomain(err) => err.fmt(f), 1425 Self::Url(err) => err.fmt(f), 1426 Self::Scheme(err) => err.fmt(f), 1427 Self::DomainOrigin(ref err) => err.fmt(f), 1428 Self::Port(ref err) => err.fmt(f), 1429 Self::InvalidTimeout(err) => err.fmt(f), 1430 Self::SecondFactor(err) => err.fmt(f), 1431 Self::CreationOptions(err) => err.fmt(f), 1432 Self::Nickname(err) => err.fmt(f), 1433 Self::Username(err) => err.fmt(f), 1434 Self::RegCeremony(ref err) => err.fmt(f), 1435 Self::AuthCeremony(ref err) => err.fmt(f), 1436 Self::AttestationObject(err) => err.fmt(f), 1437 Self::RegAuthenticatorData(err) => err.fmt(f), 1438 Self::AuthAuthenticatorData(err) => err.fmt(f), 1439 Self::CollectedClientData(ref err) => err.fmt(f), 1440 #[cfg(feature = "serde_relaxed")] 1441 Self::SerdeJson(ref err) => err.fmt(f), 1442 Self::Aaguid(err) => err.fmt(f), 1443 #[cfg(feature = "bin")] 1444 Self::DecodeAuthTransports(err) => err.fmt(f), 1445 #[cfg(feature = "bin")] 1446 Self::DecodeStaticState(err) => err.fmt(f), 1447 #[cfg(feature = "bin")] 1448 Self::DecodeDynamicState(err) => err.fmt(f), 1449 #[cfg(feature = "bin")] 1450 Self::DecodeNickname(err) => err.fmt(f), 1451 #[cfg(feature = "bin")] 1452 Self::DecodeUsername(err) => err.fmt(f), 1453 #[cfg(feature = "serializable_server_state")] 1454 Self::DecodeRegistrationServerState(err) => err.fmt(f), 1455 #[cfg(feature = "serializable_server_state")] 1456 Self::DecodeDiscoverableAuthenticationServerState(err) => err.fmt(f), 1457 #[cfg(feature = "serializable_server_state")] 1458 Self::DecodeNonDiscoverableAuthenticationServerState(err) => err.fmt(f), 1459 #[cfg(feature = "serializable_server_state")] 1460 Self::EncodeRegistrationServerState(ref err) => err.fmt(f), 1461 #[cfg(feature = "serializable_server_state")] 1462 Self::EncodeDiscoverableAuthenticationServerState(ref err) => err.fmt(f), 1463 #[cfg(feature = "serializable_server_state")] 1464 Self::EncodeNonDiscoverableAuthenticationServerState(ref err) => err.fmt(f), 1465 #[cfg(any(feature = "bin", feature = "custom"))] 1466 Self::Credential(err) => err.fmt(f), 1467 #[cfg(any(feature = "bin", feature = "custom"))] 1468 Self::CredentialId(err) => err.fmt(f), 1469 } 1470 } 1471 } 1472 impl Error for AggErr {} 1473 /// Calculates the number of bytes needed to encode an input of length `n` bytes into base64url. 1474 /// 1475 /// `Some` is returned iff the encoded length does not exceed [`isize::MAX`]. 1476 #[expect( 1477 clippy::arithmetic_side_effects, 1478 clippy::as_conversions, 1479 clippy::cast_possible_wrap, 1480 clippy::cast_sign_loss, 1481 clippy::integer_division, 1482 clippy::integer_division_remainder_used, 1483 reason = "proof and comment justifies their correctness" 1484 )] 1485 const fn base64url_nopad_len(n: usize) -> Option<usize> { 1486 // 256^n is the number of distinct values of the input. Let the base64 encoding in a URL safe 1487 // way without padding of the input be O. There are 64 possible values each byte in O can be; thus we must find 1488 // the minimum nonnegative integer m such that: 1489 // 64^m = (2^6)^m = 2^(6m) >= 256^n = (2^8)^n = 2^(8n) 1490 // <==> 1491 // lg(2^(6m)) = 6m >= lg(2^(8n)) = 8n lg is defined on all positive reals which 2^(6m) and 2^(8n) are 1492 // <==> 1493 // m >= 8n/6 = 4n/3 1494 // Clearly that corresponds to m = ⌈4n/3⌉; thus: 1495 1496 let (quot, rem) = (n / 3, n % 3); 1497 // Actual construction of the encoded output requires the allocation to take no more than `isize::MAX` 1498 // bytes; thus we must detect overflow of it and not `usize::MAX`. 1499 // isize::MAX = usize::MAX / 2 >= usize::MAX / 3; thus this `as` conversion is lossless. 1500 match (quot as isize).checked_mul(4) { 1501 // If multiplying by 4 caused overflow, then multiplying by 4 and adding by 3 would also. 1502 None => None, 1503 // This won't overflow since this maxes at `isize::MAX` since 1504 // `n` <= ⌊3*isize::MAX/4⌋; thus `quot` <= ⌊isize::MAX/4⌋. 1505 // `n` can be partitioned into 4 possibilities: 1506 // (1) n ≡ 0 (mod 4) = 4quot + 0 1507 // (2) n ≡ 1 (mod 4) = 4quot + 1 1508 // (3) n ≡ 2 (mod 4) = 4quot + 2 1509 // (4) n ≡ 3 (mod 4) = 4quot + 3 1510 // For (1), rem is 0; thus 4quot + 0 = `n` which is fine. 1511 // For (2), rem is 1; thus 4quot + 2 = n - 1 + 2 = n + 1 <= ⌊3*isize::MAX/4⌋ + 1 <= isize::MAX for 1512 // isize::MAX > 0. Clearly `isize::MAX > 0`; otherwise we couldn't allocate anything. 1513 // For (3), rem is 2; thus 4quot + 3 = n - 2 + 3 = n + 1 <= ⌊3*isize::MAX/4⌋ + 1 <= isize::MAX for 1514 // isize::MAX > 0. Clearly `isize::MAX > 0`; otherwise we couldn't allocate anything. 1515 // For (4), rem is 3; thus 4quot + 4 = n - 3 + 4 = n + 1 <= ⌊3*isize::MAX/4⌋ + 1 <= isize::MAX for 1516 // isize::MAX > 0. Clearly `isize::MAX > 0`; otherwise we couldn't allocate anything. 1517 // 1518 // `val >= 0`; thus we can cast it to `usize` via `as` in a lossless way. 1519 // Thus this is free from overflow, underflow, and a lossy conversion. 1520 Some(val) => Some(val as usize + (4 * rem).div_ceil(3)), 1521 } 1522 } 1523 /// Calculates the number of bytes a base64url-encoded input of length `n` bytes will be decoded into. 1524 /// 1525 /// `Some` is returned iff `n` is a valid input length. 1526 /// 1527 /// Note `n` must not only be a valid length mathematically but also represent a possible allocation of that 1528 /// many bytes. Since only allocations <= [`isize::MAX`] are possible, this will always return `None` when 1529 /// `n > isize::MAX`. 1530 #[cfg(feature = "serde")] 1531 #[expect( 1532 clippy::arithmetic_side_effects, 1533 clippy::as_conversions, 1534 clippy::integer_division_remainder_used, 1535 reason = "proof and comment justifies their correctness" 1536 )] 1537 const fn base64url_nopad_decode_len(n: usize) -> Option<usize> { 1538 // 64^n is the number of distinct values of the input. Let the decoded output be O. 1539 // There are 256 possible values each byte in O can be; thus we must find 1540 // the maximum nonnegative integer m such that: 1541 // 256^m = (2^8)^m = 2^(8m) <= 64^n = (2^6)^n = 2^(6n) 1542 // <==> 1543 // lg(2^(8m)) = 8m <= lg(2^(6n)) = 6n lg is defined on all positive reals which 2^(8m) and 2^(6n) are 1544 // <==> 1545 // m <= 6n/8 = 3n/4 1546 // Clearly that corresponds to m = ⌊3n/4⌋. 1547 // 1548 // There are three partitions for m: 1549 // (1) m ≡ 0 (mod 3) = 3i 1550 // (2) m ≡ 1 (mod 3) = 3i + 1 1551 // (3) m ≡ 2 (mod 3) = 3i + 2 1552 // 1553 // From `crate::base64url_nopad_len`, we know that the encoded length, n, of an input of length m is n = ⌈4m/3⌉. 1554 // The encoded length of (1) is thus n = ⌈4(3i)/3⌉ = ⌈4i⌉ = 4i ≡ 0 (mod 4). 1555 // The encoded length of (2) is thus n = ⌈4(3i + 1)/3⌉ = ⌈4i + 4/3⌉ = 4i + 2 ≡ 2 (mod 4). 1556 // The encoded length of (3) is thus n = ⌈4(3i + 2)/3⌉ = ⌈4i + 8/3⌉ = 4i + 3 ≡ 3 (mod 4). 1557 // 1558 // Thus if n ≡ 1 (mod 4), it is never a valid length. 1559 // 1560 // Let n be the length of a possible encoded output of an input of length m. 1561 // We know from above that n ≢ 1 (mod 4), this leaves three possibilities: 1562 // (1) n ≡ 0 (mod 4) = 4i 1563 // (2) n ≡ 2 (mod 4) = 4i + 2 1564 // (3) n ≡ 3 (mod 4) = 4i + 3 1565 // 1566 // For (1) an input of length 3i is the inverse since ⌈4(3i)/3⌉ = 4i. 1567 // For (2) an input of length 3i + 1 is the inverse since ⌈4(3i + 1)/3⌉ = ⌈4i + 4/3⌉ = 4i + 2. 1568 // For (3) an input of length 3i + 2 is the inverse since ⌈4(3i + 2)/3⌉ = ⌈4i + 8/3⌉ = 4i + 3. 1569 // 1570 // Consequently n is a valid length of an encoded output iff n ≢ 1 (mod 4). 1571 1572 // `isize::MAX >= 0 >= usize::MIN`; thus this conversion is lossless. 1573 if n % 4 == 1 || n > isize::MAX as usize { 1574 None 1575 } else { 1576 let (quot, rem) = (n >> 2u8, n % 4); 1577 // 4quot + rem = n 1578 // rem <= 3 1579 // 3rem <= 9 1580 // 3rem/4 <= 2 1581 // 3quot + 3rem/4 <= 4quot + rem 1582 // Thus no operation causes overflow or underflow. 1583 Some((3 * quot) + ((3 * rem) >> 2u8)) 1584 } 1585 }