webauthn_rp

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

hash.rs (6020B)


      1 #[cfg(doc)]
      2 use super::{
      3     hash::hash_set::FixedCapHashSet,
      4     request::{
      5         Challenge,
      6         auth::{
      7             DiscoverableAuthenticationServerState, DiscoverableCredentialRequestOptions,
      8             NonDiscoverableAuthenticationServerState, NonDiscoverableCredentialRequestOptions,
      9         },
     10         register::{CredentialCreationOptions, RegistrationServerState},
     11     },
     12 };
     13 use core::hash::{BuildHasher, Hasher};
     14 pub use hashbrown;
     15 /// Fixed-capacity hash map.
     16 pub mod hash_map;
     17 /// Fixed-capacity hash set.
     18 pub mod hash_set;
     19 /// [`Hasher`] whose `write_*` methods simply store up to 64 bits of the passed argument _as is_ overwriting
     20 /// any previous state.
     21 ///
     22 /// This is designed to only be used indirectly via a hash map whose keys are randomly generated on the server
     23 /// based on at least 64 bits—the size of the integer returned from [`Self::finish`]—of entropy.
     24 /// This makes this `Hasher` usable (and ideal) in only the most niche circumstances.
     25 ///
     26 /// [`RegistrationServerState`], [`DiscoverableAuthenticationServerState`], and
     27 /// [`NonDiscoverableAuthenticationServerState`] implement [`Hash`] by simply writing the
     28 /// contained [`Challenge`]; thus when they are stored in a hashed collection (e.g., [`FixedCapHashSet`]), one can
     29 /// optimize without fear by using this `Hasher` since `Challenge`s are immutable and can only ever be created on
     30 /// the server via [`Challenge::new`] (and equivalently [`Challenge::default`]). `RegistrationServerState`,
     31 /// `DiscoverableAuthenticationServerState`, and `NonDiscoverableAuthenticationServerState` are also immutable and
     32 /// only constructable via [`CredentialCreationOptions::start_ceremony`],
     33 /// [`DiscoverableCredentialRequestOptions::start_ceremony`], and
     34 /// [`NonDiscoverableCredentialRequestOptions::start_ceremony`] respectively. Since `Challenge` is already based on
     35 /// a random `u128`, other `Hasher`s will be slower and likely produce lower-quality hashes (and never
     36 /// higher quality).
     37 #[derive(Clone, Copy, Debug)]
     38 pub struct IdentityHasher(u64);
     39 // Note it is _not_ required for `write_*` methods to do the same thing as other `write_*` methods
     40 // (e.g., `Self::write_u64` may not be the same thing as 8 calls to `Self::write_u8`).
     41 impl Hasher for IdentityHasher {
     42     /// Returns `0` if no `write_*` calls have been made; otherwise returns the result of the most recent
     43     /// `write_*` call.
     44     #[inline]
     45     fn finish(&self) -> u64 {
     46         self.0
     47     }
     48     /// Writes `i` to `self`.
     49     #[inline]
     50     fn write_u64(&mut self, i: u64) {
     51         self.0 = i;
     52     }
     53     /// Sign-extends `i` to a [`u64`] before redirecting to [`Self::write_u64`].
     54     #[expect(
     55         clippy::as_conversions,
     56         clippy::cast_sign_loss,
     57         reason = "we simply need to convert into a u64 in a deterministic way"
     58     )]
     59     #[inline]
     60     fn write_i8(&mut self, i: i8) {
     61         self.write_u64(i as u64);
     62     }
     63     /// Sign-extends `i` to a [`u64`] before redirecting to [`Self::write_u64`].
     64     #[expect(
     65         clippy::as_conversions,
     66         clippy::cast_sign_loss,
     67         reason = "we simply need to convert into a u64 in a deterministic way"
     68     )]
     69     #[inline]
     70     fn write_i16(&mut self, i: i16) {
     71         self.write_u64(i as u64);
     72     }
     73     /// Sign-extends `i` to a [`u64`] before redirecting to [`Self::write_u64`].
     74     #[expect(
     75         clippy::as_conversions,
     76         clippy::cast_sign_loss,
     77         reason = "we simply need to convert into a u64 in a deterministic way"
     78     )]
     79     #[inline]
     80     fn write_i32(&mut self, i: i32) {
     81         self.write_u64(i as u64);
     82     }
     83     /// Redirects to [`Self::write_u64`].
     84     #[expect(
     85         clippy::as_conversions,
     86         clippy::cast_sign_loss,
     87         reason = "we simply need to convert into a u64 in a deterministic way"
     88     )]
     89     #[inline]
     90     fn write_i64(&mut self, i: i64) {
     91         self.write_u64(i as u64);
     92     }
     93     /// Truncates `i` to a [`u64`] before redirecting to [`Self::write_u64`].
     94     #[expect(
     95         clippy::as_conversions,
     96         clippy::cast_possible_truncation,
     97         clippy::cast_sign_loss,
     98         reason = "we simply need to convert into a u64 in a deterministic way"
     99     )]
    100     #[inline]
    101     fn write_i128(&mut self, i: i128) {
    102         self.write_u64(i as u64);
    103     }
    104     /// Zero-extends `i` to a [`u64`] before redirecting to [`Self::write_u64`].
    105     #[inline]
    106     fn write_u8(&mut self, i: u8) {
    107         self.write_u64(u64::from(i));
    108     }
    109     /// Zero-extends `i` to a [`u64`] before redirecting to [`Self::write_u64`].
    110     #[inline]
    111     fn write_u16(&mut self, i: u16) {
    112         self.write_u64(u64::from(i));
    113     }
    114     /// Zero-extends `i` to a [`u64`] before redirecting to [`Self::write_u64`].
    115     #[inline]
    116     fn write_u32(&mut self, i: u32) {
    117         self.write_u64(u64::from(i));
    118     }
    119     /// Truncates `i` to a [`u64`] before redirecting to [`Self::write_u64`].
    120     #[expect(
    121         clippy::as_conversions,
    122         clippy::cast_possible_truncation,
    123         reason = "we simply need to convert into a u64 in a deterministic way"
    124     )]
    125     #[inline]
    126     fn write_u128(&mut self, i: u128) {
    127         self.write_u64(i as u64);
    128     }
    129     /// This does nothing iff `bytes.len() < 8`; otherwise the first 8 bytes are converted
    130     /// to a [`u64`] that is written via [`Self::write_u64`];
    131     #[expect(clippy::host_endian_bytes, reason = "endianness does not matter")]
    132     #[inline]
    133     fn write(&mut self, bytes: &[u8]) {
    134         if let Some(data) = bytes.get(..8) {
    135             let mut val = [0; 8];
    136             val.copy_from_slice(data);
    137             self.write_u64(u64::from_ne_bytes(val));
    138         }
    139     }
    140 }
    141 /// [`BuildHasher`] of an [`IdentityHasher`].
    142 ///
    143 /// This MUST only be used with hash maps with keys that are randomly generated on the server based on at least 64
    144 /// bits of entropy.
    145 #[derive(Clone, Copy, Debug)]
    146 pub struct BuildIdentityHasher;
    147 impl BuildHasher for BuildIdentityHasher {
    148     type Hasher = IdentityHasher;
    149     #[inline]
    150     fn build_hasher(&self) -> Self::Hasher {
    151         IdentityHasher(0)
    152     }
    153 }