webauthn_rp

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

hash.rs (7192B)


      1 #[cfg(doc)]
      2 use super::{
      3     hash::hash_set::MaxLenHashSet,
      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 /// Hash map with an immutable maximum length that allocates exactly once.
     16 pub mod hash_map;
     17 /// Hash set with an immutable maximum length that allocates exactly once.
     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., [`MaxLenHashSet`]), 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, Default)]
     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     /// Redirects to [`Self::write_u64`] on 64-bit platforms.
    105     /// Sign-extends `i` to a [`u64`] before redirecting to [`Self::write_u64`] on platforms of less than 64 bits.
    106     /// Truncates `i` to a [`u64`] before redirecting to [`Self::write_u64`] on platforms of more than 64 bits.
    107     #[expect(
    108         clippy::as_conversions,
    109         clippy::cast_sign_loss,
    110         reason = "we simply need to convert into a u64 in a deterministic way"
    111     )]
    112     #[inline]
    113     fn write_isize(&mut self, i: isize) {
    114         self.write_u64(i as u64);
    115     }
    116     /// Zero-extends `i` to a [`u64`] before redirecting to [`Self::write_u64`].
    117     #[inline]
    118     fn write_u8(&mut self, i: u8) {
    119         self.write_u64(u64::from(i));
    120     }
    121     /// Zero-extends `i` to a [`u64`] before redirecting to [`Self::write_u64`].
    122     #[inline]
    123     fn write_u16(&mut self, i: u16) {
    124         self.write_u64(u64::from(i));
    125     }
    126     /// Zero-extends `i` to a [`u64`] before redirecting to [`Self::write_u64`].
    127     #[inline]
    128     fn write_u32(&mut self, i: u32) {
    129         self.write_u64(u64::from(i));
    130     }
    131     /// Truncates `i` to a [`u64`] before redirecting to [`Self::write_u64`].
    132     #[expect(
    133         clippy::as_conversions,
    134         clippy::cast_possible_truncation,
    135         reason = "we simply need to convert into a u64 in a deterministic way"
    136     )]
    137     #[inline]
    138     fn write_u128(&mut self, i: u128) {
    139         self.write_u64(i as u64);
    140     }
    141     /// Redirects to [`Self::write_u64`] on 64-bit platforms.
    142     /// Zero-extends `i` to a [`u64`] before redirecting to [`Self::write_u64`] on platforms of less than 64 bits.
    143     /// Truncates `i` to a [`u64`] before redirecting to [`Self::write_u64`] on platforms of more than 64 bits.
    144     #[expect(
    145         clippy::as_conversions,
    146         reason = "we simply need to convert into a u64 in a deterministic way"
    147     )]
    148     #[inline]
    149     fn write_usize(&mut self, i: usize) {
    150         self.write_u64(i as u64);
    151     }
    152     /// This does nothing iff `bytes.len() < 8`; otherwise the first 8 bytes are converted
    153     /// to a [`u64`] that is written via [`Self::write_u64`];
    154     #[expect(clippy::host_endian_bytes, reason = "endianness does not matter")]
    155     #[inline]
    156     fn write(&mut self, bytes: &[u8]) {
    157         if let Some(data) = bytes.get(..8) {
    158             let mut val = [0; 8];
    159             val.copy_from_slice(data);
    160             self.write_u64(u64::from_ne_bytes(val));
    161         }
    162     }
    163 }
    164 /// [`BuildHasher`] of an [`IdentityHasher`].
    165 ///
    166 /// This MUST only be used with hash maps with keys that are randomly generated on the server based on at least 64
    167 /// bits of entropy.
    168 #[derive(Clone, Copy, Debug, Default)]
    169 pub struct BuildIdentityHasher;
    170 impl BuildHasher for BuildIdentityHasher {
    171     type Hasher = IdentityHasher;
    172     #[inline]
    173     fn build_hasher(&self) -> Self::Hasher {
    174         IdentityHasher(0)
    175     }
    176 }