rapidhash/v3/seed.rs
1//! Reliable seeding and secrets generation for the hash functions.
2
3use crate::util::mix::rapid_mix;
4
5/// The default seed used in the C++ implementation.
6pub(crate) const DEFAULT_SEED: u64 = 0;
7
8/// Used only for generating random secrets.
9const DEFAULT_SECRETS: [u64; 7] = [
10 0x2d358dccaa6c78a5,
11 0x8bb84b93962eacc9,
12 0x4b33a62ed433d4a3,
13 0x4d5a2da51de1aa47,
14 0xa0761d6478bd642f,
15 0xe7037ed1a0b428db,
16 0x90ed1765281c388c,
17];
18
19/// The default rapidhash secrets used in the C++ implementation.
20///
21/// We recommend generating your own secrets using the [`RapidSecrets::seed`] method to avoid
22/// trivial collision attacks if you need minimal HashDoS protection.
23pub const DEFAULT_RAPID_SECRETS: RapidSecrets = RapidSecrets::seed_cpp(DEFAULT_SEED);
24
25/// Hold the seed and secrets to be used by rapidhash.
26///
27/// RapidSecrets premix the seed and generate a set of other secrets based on the seed that are all
28/// used in the hashing process. There are some quality checks on the random values to ensure a
29/// reasonable distribution of entropy in the generated secrets.
30///
31/// Constructing this struct is fairly cheap, but unnecessary in the critical path. We therefore
32/// recommend instantiating it once and re-using the same instance for any persistent hashing. The
33/// `seed` method is marked `const` to also do so at compile time.
34///
35/// # Minimal HashDoS Protection
36/// We recommend changing the default seed and secrets to avoid
37/// [trivial collision attacks](https://liams.website/articles/seed-independent-collisions-on-wyhash-and-rapidhash).
38/// For persistent hashing, you can hard code your own randomized seed at compile time.
39///
40/// ```rust
41/// use rapidhash::v3::RapidSecrets;
42/// const DEFAULT_SECRETS: RapidSecrets = RapidSecrets::seed(0x123456); // <-- change this value!
43///
44/// /// Export your chosen rapidhash version and secrets for use throughout your project.
45/// pub fn rapidhash(data: &[u8]) -> u64 {
46/// rapidhash::v3::rapidhash_v3_seeded(data, &DEFAULT_SECRETS)
47/// }
48/// ```
49///
50/// TODO: serde or serialization support.
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
52pub struct RapidSecrets {
53 /// The core rapidhash seed.
54 pub seed: u64,
55
56 /// The secrets, effectively other seeds used in the hashing process.
57 pub secrets: [u64; 7],
58}
59
60impl RapidSecrets {
61 /// Generate secrets from a given randomized seed.
62 ///
63 /// Note the chosen seed will be pre-mixed to further randomized it, and the secrets will be
64 /// computed based on the seed.
65 ///
66 /// If compatibility with the C++ implementation is required, use the `seed_cpp` method instead.
67 #[inline]
68 pub const fn seed(seed: u64) -> Self {
69 let seed = premix_seed(seed, 0);
70 let mut secrets = [0; 7];
71 secrets[0] = premix_seed(seed, 0);
72 secrets[1] = premix_seed(secrets[0], 1);
73 secrets[2] = premix_seed(secrets[1], 2);
74 secrets[3] = premix_seed(secrets[2], 3);
75 secrets[4] = premix_seed(secrets[3], 4);
76 secrets[5] = premix_seed(secrets[4], 5);
77 secrets[6] = premix_seed(secrets[5], 6);
78 Self { seed, secrets }
79 }
80
81 /// Creates a new `RapidSecrets` instance with a different seed and the same secrets.
82 ///
83 /// This is useful for in-memory hashing, so we can quickly use a different seed for other
84 /// HashMaps.
85 #[inline(always)]
86 pub const fn reseed(&self) -> Self {
87 Self {
88 seed: premix_seed(self.seed, 6),
89 secrets: self.secrets,
90 }
91 }
92
93 /// Creates a new `RapidSecrets` instance using a seed and secrets that are compatible with the
94 /// C++ implementation.
95 ///
96 /// Note that these **use the default secrets** and therefore are liable to some trivial
97 /// collision attacks, as randomising both the seed and secrets is necessary to provide minimal
98 /// HashDoS resistance.
99 #[inline(always)]
100 pub const fn seed_cpp(seed: u64) -> Self {
101 Self {
102 seed: rapidhash_seed(seed),
103 secrets: DEFAULT_SECRETS,
104 }
105 }
106
107 /// Creates a new `RapidSecrets` instance with a randomized seed and secrets.
108 ///
109 /// The quality of the randomness will be better with the `rand` feature enabled.
110 #[inline]
111 pub fn random() -> Self {
112 let seed = crate::inner::seeding::seed::get_seed();
113 let secrets = crate::inner::seeding::secrets::get_secrets();
114
115 Self {
116 seed,
117 secrets: *secrets,
118 }
119 }
120}
121
122#[inline(always)]
123const fn rapidhash_seed(seed: u64) -> u64 {
124 seed ^ rapid_mix::<false>(seed ^ DEFAULT_SECRETS[2], DEFAULT_SECRETS[1])
125}
126
127#[inline]
128const fn premix_seed(mut seed: u64, i: usize) -> u64 {
129 seed ^= rapid_mix::<false>(seed ^ DEFAULT_SECRETS[2], DEFAULT_SECRETS[i]);
130
131 // ensure the seeds are of reasonable non-zero quality
132 const HI: u64 = 0xFFFF << 48;
133 const MI: u64 = 0xFFFF << 24;
134 const LO: u64 = 0xFFFF;
135
136 if (seed & HI) == 0 {
137 seed |= 1u64 << 63;
138 }
139
140 if (seed & MI) == 0 {
141 seed |= 1u64 << 31;
142 }
143
144 if (seed & LO) == 0 {
145 seed |= 1u64;
146 }
147
148 seed
149}