Skip to main content

dryoc/classic/
crypto_shorthash.rs

1//! # Short-input hashing
2//!
3//! Implements libsodium's SipHash-2-4 function for short inputs. It produces a
4//! compact, keyed hash that can protect hash tables against attacker-chosen
5//! collision patterns.
6//!
7//! This function is intended for short, keyed inputs. It is not a
8//! general-purpose hash or an encryption primitive. Use
9//! [`crate::classic::crypto_auth`] for message authentication, or
10//! [`crate::classic::crypto_generichash`] for general-purpose hashing.
11//!
12//! Treat the key as secret and generate it randomly. See the
13//! [libsodium documentation](https://doc.libsodium.org/hashing/short-input_hashing)
14//! for details.
15//!
16//! ## Classic API example
17//!
18//! ```
19//! use dryoc::classic::crypto_shorthash::*;
20//! use dryoc::rng::copy_randombytes;
21//!
22//! // Generate a random key
23//! let key = crypto_shorthash_keygen();
24//!
25//! // Generate some random input data
26//! let mut input = vec![0u8; 69];
27//! copy_randombytes(&mut input);
28//!
29//! // Compute the hash, put result into `output`
30//! let mut output = Hash::default();
31//! crypto_shorthash(&mut output, &input, &key);
32//! ```
33use crate::constants::{CRYPTO_SHORTHASH_BYTES, CRYPTO_SHORTHASH_KEYBYTES};
34use crate::rng::copy_randombytes;
35use crate::siphash24::siphash24;
36
37/// Hash type alias for short input hashing.
38pub type Hash = [u8; CRYPTO_SHORTHASH_BYTES];
39/// Key type alias for short input hashing.
40pub type Key = [u8; CRYPTO_SHORTHASH_KEYBYTES];
41
42/// Generates a random key for short input hashing.
43#[must_use]
44pub fn crypto_shorthash_keygen() -> Key {
45    let mut key = Key::default();
46    copy_randombytes(&mut key);
47    key
48}
49
50/// Computes a short input hash for `input` and `key`, placing the result into
51/// `output`, using SipHash-2-4.
52pub fn crypto_shorthash(output: &mut Hash, input: &[u8], key: &Key) {
53    siphash24(output, input, key)
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59    use crate::test_prelude::*;
60
61    /// The SipHash-2-4 reference vectors (key `00..0f`, message `00..n-1`)
62    /// at the word boundaries: 0, 7, 8, 9, 15 and 16 bytes.
63    #[test]
64    fn test_shorthash_reference_vectors() {
65        let key: Key = core::array::from_fn(|i| i as u8);
66        for (len, expected) in [
67            (0usize, [0x31, 0x0e, 0x0e, 0xdd, 0x47, 0xdb, 0x6f, 0x72]),
68            (7, [0x37, 0xd1, 0x01, 0x8b, 0xf5, 0x00, 0x02, 0xab]),
69            (8, [0x62, 0x24, 0x93, 0x9a, 0x79, 0xf5, 0xf5, 0x93]),
70            (9, [0xb0, 0xe4, 0xa9, 0x0b, 0xdf, 0x82, 0x00, 0x9e]),
71            (15, [0xe5, 0x45, 0xbe, 0x49, 0x61, 0xca, 0x29, 0xa1]),
72            (16, [0xdb, 0x9b, 0xc2, 0x57, 0x7f, 0xcc, 0x2a, 0x3f]),
73        ] {
74            let input: Vec<u8> = (0..len as u8).collect();
75            let mut output = Hash::default();
76            crypto_shorthash(&mut output, &input, &key);
77            assert_eq!(output, expected, "len {len}");
78        }
79    }
80
81    #[cfg(dryoc_native_tests)]
82    #[test]
83    fn test_shorthash_matches_libsodium() {
84        use crate::native_test_util::shorthash_siphash24;
85
86        let key: Key = core::array::from_fn(|i| (i as u8).wrapping_mul(37).wrapping_add(11));
87        for len in [0usize, 1, 7, 8, 9, 63, 64, 65] {
88            let input: Vec<u8> = (0..len as u32).map(|i| (i * 31 % 251) as u8).collect();
89            let mut output = Hash::default();
90            crypto_shorthash(&mut output, &input, &key);
91            let so_output = shorthash_siphash24(&input, &key);
92            assert_eq!(output, so_output, "len {len}");
93        }
94    }
95}