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 }