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