cryprot_core/
aes_hash.rs

1//! Correlation robust AES hash.
2//!
3//! This implementation of a correlation robust AES hash function
4//! is based on the findings of <https://eprint.iacr.org/2019/074>.
5use std::sync::LazyLock;
6
7use aes::{
8    Aes128,
9    cipher::{BlockCipherEncrypt, Key, KeyInit},
10};
11use bytemuck::Pod;
12
13use crate::{AES_PAR_BLOCKS, Block, utils::xor_inplace};
14
15/// AES accelerated hashing of [`Block`]s.
16pub struct AesHash {
17    aes: Aes128,
18}
19
20impl AesHash {
21    /// Create a new `AesHash` with the given key.
22    pub fn new(key: &Key<Aes128>) -> Self {
23        Self {
24            aes: Aes128::new(key),
25        }
26    }
27
28    /// Compute the correlation robust hash of a block.
29    ///
30    /// # Warning: only secure in semi-honest setting!
31    /// See <https://eprint.iacr.org/2019/074> for details.
32    pub fn cr_hash_block(&self, x: Block) -> Block {
33        let mut x_enc = x.into();
34        self.aes.encrypt_block(&mut x_enc);
35        x ^ x_enc.into()
36    }
37
38    /// Compute the correlation robust hashes of multiple blocks.
39    ///
40    /// Warning: only secure in semi-honest setting!
41    /// See <https://eprint.iacr.org/2019/074> for details.
42    pub fn cr_hash_blocks<const N: usize>(&self, x: &[Block; N]) -> [Block; N]
43    where
44        [Block; N]: Pod,
45        [aes::Block; N]: Pod,
46    {
47        let mut blocks: [aes::Block; N] = bytemuck::cast(*x);
48        self.aes.encrypt_blocks(&mut blocks);
49        let mut blocks: [Block; N] = bytemuck::cast(blocks);
50        xor_inplace(&mut blocks, x);
51        blocks
52    }
53
54    /// Compute the correlation robust hashes of multiple blocks.
55    ///
56    /// Warning: only secure in semi-honest setting!
57    /// See <https://eprint.iacr.org/2019/074> for details.
58    ///
59    /// # Panics
60    /// If N != out.len()
61    pub fn cr_hash_blocks_b2b<const N: usize>(&self, inp: &[Block; N], out: &mut [Block])
62    where
63        [Block; N]: Pod,
64        [aes::Block; N]: Pod,
65    {
66        assert_eq!(N, out.len());
67        let inp_aes: &[aes::Block; N] = bytemuck::cast_ref(inp);
68        let out_aes: &mut [aes::Block] = bytemuck::cast_slice_mut(out);
69        self.aes
70            .encrypt_blocks_b2b(inp_aes, out_aes)
71            .expect("buffer have equal size");
72        xor_inplace(out, inp);
73    }
74
75    /// Correlation robust hash of a slice of blocks.
76    ///
77    /// Warning: only secure in semi-honest setting!
78    /// See <https://eprint.iacr.org/2019/074> for details.
79    ///
80    /// In most cases, this method will be the most performant, as it can make
81    /// use of AES instruction level parallelism.
82    pub fn cr_hash_slice_mut(&self, x: &mut [Block]) {
83        let mut tmp = [aes::Block::default(); AES_PAR_BLOCKS];
84
85        for chunk in x.chunks_mut(AES_PAR_BLOCKS) {
86            self.aes
87                .encrypt_blocks_b2b(bytemuck::cast_slice(chunk), &mut tmp[..chunk.len()])
88                .expect("in and out always have same length");
89            chunk
90                .iter_mut()
91                .zip(tmp)
92                .for_each(|(x, x_enc)| *x ^= x_enc.into());
93        }
94    }
95
96    /// Tweakable circular correlation robust hash function.
97    ///
98    /// See <https://eprint.iacr.org/2019/074> for details. This is the TMMO function.
99    pub fn tccr_hash_slice_mut(&self, x: &mut [Block], mut tweak_fn: impl FnMut(usize) -> Block) {
100        let mut tmp = [aes::Block::default(); AES_PAR_BLOCKS];
101        for (chunk_idx, chunk) in x.chunks_mut(AES_PAR_BLOCKS).enumerate() {
102            self.aes
103                .encrypt_blocks_b2b(bytemuck::cast_slice(chunk), &mut tmp[..chunk.len()])
104                .expect("in and out always have same length");
105            chunk
106                .iter_mut()
107                .zip(&mut tmp)
108                .enumerate()
109                .for_each(|(idx, (dest, x_enc))| {
110                    *dest = Block::from(*x_enc) ^ tweak_fn(chunk_idx * AES_PAR_BLOCKS + idx);
111                });
112            self.aes.encrypt_blocks(bytemuck::cast_slice_mut(chunk));
113            chunk
114                .iter_mut()
115                .zip(tmp)
116                .for_each(|(x, x_enc)| *x ^= x_enc.into());
117        }
118    }
119}
120
121/// An `AesHash` with a fixed key.
122pub static FIXED_KEY_HASH: LazyLock<AesHash> = LazyLock::new(|| {
123    let key = 193502124791825095790518994062991136444_u128
124        .to_le_bytes()
125        .into();
126    AesHash::new(&key)
127});