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 }