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