sthash/
sthash.rs

1use std::sync::Arc;
2
3use byteorder::{ByteOrder, LittleEndian};
4use tiny_keccak::{CShake, Hasher as _, Kmac};
5
6use super::nhpoly1305;
7
8const KMAC_KEY_BYTES: usize = 32;
9const KEY_BYTES: usize = KMAC_KEY_BYTES + nhpoly1305::NHPOLY_KEY_BYTES;
10
11/// Hash output size, in bytes
12pub const OUTPUT_BYTES: usize = 32;
13
14/// Recommended seed size, in bytes
15pub const SEED_BYTES: usize = 32;
16
17/// Minimum seed size, in bytes
18pub const MIN_SEED_BYTES: usize = 16;
19
20/// A large secret key, derived from a secret seed
21#[derive(Clone, Debug, PartialEq, Eq)]
22pub struct Key(Vec<u8>);
23
24struct HashInner {
25    key: Key,
26    st_kmac: Kmac,
27}
28
29/// A `Hasher` can be reused to compute multiple hashes using the same key
30#[derive(Clone)]
31pub struct Hasher {
32    inner: Arc<HashInner>,
33}
34
35impl Hasher {
36    /// Returns an `OUTPUT_BYTES` hash of the message
37    pub fn hash(&self, msg: &[u8]) -> Vec<u8> {
38        let nhpoly_key = &self.inner.key.0[32..];
39        debug_assert_eq!(nhpoly_key.len(), nhpoly1305::NHPOLY_KEY_BYTES);
40        let st_nhpoly = nhpoly1305::Hasher::new(nhpoly_key);
41        let mut poly = [0u8; 16];
42        st_nhpoly.hash(&mut poly, msg);
43
44        let mut msg_len_u8 = [0u8; 8];
45        LittleEndian::write_u64(&mut msg_len_u8, msg.len() as u64);
46
47        let mut st_kmac = self.inner.st_kmac.clone();
48        st_kmac.update(&msg_len_u8);
49        st_kmac.update(&poly);
50        let mut h = vec![0u8; 32];
51        st_kmac.finalize(&mut h);
52        h
53    }
54
55    /// Creates a new `Hasher` object using `key`
56    ///
57    /// `personalization` is an optional context that describes the purpose
58    /// of the hashes this `Hasher` will compute.
59    /// The same key used with the same messages, but in different contexts will
60    /// produce different outputs.
61    pub fn new(key: Key, personalization: Option<&[u8]>) -> Hasher {
62        debug_assert_eq!(key.0.len(), KEY_BYTES);
63        let kmac_key = &key.0[..KMAC_KEY_BYTES];
64        let st_kmac = Kmac::v128(kmac_key, personalization.unwrap_or_default());
65        Hasher {
66            inner: Arc::new(HashInner { key, st_kmac }),
67        }
68    }
69}
70
71impl Key {
72    /// Creates a new key from a secret `seed`
73    ///
74    /// This expands the `seed` into a large secret key.
75    /// `personalization` is an optional context, that can be set to the
76    /// application name. The same `seed` used in different contexts will
77    /// produce different keys, hence different hashes.
78    pub fn from_seed(seed: &[u8], personalization: Option<&[u8]>) -> Key {
79        if seed.len() < MIN_SEED_BYTES {
80            panic!("Seed is too short");
81        }
82        let mut st_cshake = CShake::v128(b"sthash key", personalization.unwrap_or_default());
83        st_cshake.update(seed);
84        let mut key = vec![0; KEY_BYTES];
85        st_cshake.finalize(&mut key);
86        Key(key)
87    }
88}