Skip to main content

rapidhash/v2/
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 [`crate::v3::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 must be changed to avoid trivial collision
37/// attacks. For persistent hashing, you can hard code your own randomized seed at compile time.
38///
39/// ```rust
40/// use rapidhash::v2::RapidSecrets;
41/// const DEFAULT_SECRETS: RapidSecrets = RapidSecrets::seed(0x123456);  // <-- change this value!
42///
43/// /// Export your chosen rapidhash version and secrets for use throughout your project.
44/// pub fn rapidhash(data: &[u8]) -> u64 {
45///     rapidhash::v2::rapidhash_v2_2_seeded(data, &DEFAULT_SECRETS)
46/// }
47/// ```
48///
49/// TODO: serde or serialization support.
50#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
51pub struct RapidSecrets {
52    /// The core rapidhash seed.
53    pub seed: u64,
54
55    /// The secrets, effectively other seeds used in the hashing process.
56    pub secrets: [u64; 7],
57}
58
59impl RapidSecrets {
60    /// Generate secrets from a given randomized seed.
61    ///
62    /// Note the chosen seed will be pre-mixed to further randomized it, and the secrets will be
63    /// computed based on the seed.
64    ///
65    /// If compatibility with the C++ implementation is required, use the `seed_cpp` method instead.
66    #[inline]
67    pub const fn seed(seed: u64) -> Self {
68        let seed = premix_seed(seed, 0);
69        let mut secrets = [0; 7];
70        secrets[0] = premix_seed(seed, 0);
71        secrets[1] = premix_seed(secrets[0], 1);
72        secrets[2] = premix_seed(secrets[1], 2);
73        secrets[3] = premix_seed(secrets[2], 3);
74        secrets[4] = premix_seed(secrets[3], 4);
75        secrets[5] = premix_seed(secrets[4], 5);
76        secrets[6] = premix_seed(secrets[5], 6);
77        Self { seed, secrets }
78    }
79
80    /// Creates a new `RapidSecrets` instance with a different seed and the same secrets.
81    ///
82    /// This is useful for in-memory hashing, so we can quickly use a different seed for other
83    /// HashMaps.
84    #[inline(always)]
85    pub const fn reseed(&self) -> Self {
86        Self {
87            seed: premix_seed(self.seed, 6),
88            secrets: self.secrets,
89        }
90    }
91
92    /// Creates a new `RapidSecrets` instance using a seed and secrets that are compatible with the
93    /// C++ implementation.
94    ///
95    /// Note that these **use the default secrets** and therefore are liable to some trivial
96    /// collision attacks, as randomising both the seed and secrets is necessary to provide minimal
97    /// HashDoS resistance.
98    #[inline(always)]
99    pub const fn seed_cpp(seed: u64) -> Self {
100        Self {
101            seed: rapidhash_seed(seed),
102            secrets: DEFAULT_SECRETS,
103        }
104    }
105}
106
107const fn rapidhash_seed(seed: u64) -> u64 {
108    seed ^ rapid_mix::<false>(seed ^ DEFAULT_SECRETS[2], DEFAULT_SECRETS[1])
109}
110
111#[inline]
112const fn premix_seed(mut seed: u64, i: usize) -> u64 {
113    seed ^= rapid_mix::<false>(seed ^ DEFAULT_SECRETS[2], DEFAULT_SECRETS[i]);
114
115    // ensure the seeds are of reasonable non-zero quality
116    const HI: u64 = 0xFFFF << 48;
117    const MI: u64 = 0xFFFF << 24;
118    const LO: u64 = 0xFFFF;
119
120    if (seed & HI) == 0 {
121        seed |= 1u64 << 63;
122    }
123
124    if (seed & MI) == 0 {
125        seed |= 1u64 << 31;
126    }
127
128    if (seed & LO) == 0 {
129        seed |= 1u64;
130    }
131
132    seed
133}