Skip to main content

crypto/ascon/
ascon_hash256.rs

1use super::*;
2use crate::{Bytes, Hash, Hasher};
3
4/// ASCHB implicit helper const: Ascon-Hash256 initialization vector (NIST SP 800-232).
5const IV: u64 = 0x0000_0801_00cc_0002;
6
7/// Ascon-Hash256 cryptographic hash function (NIST SP 800-232 ยง5.1).
8///
9/// Produces a 256-bit (32-byte) digest. Rate = 64 bits (8 bytes), capacity = 256 bits.
10///
11/// Implements the [`Hasher`] trait.
12///
13/// # One-shot API
14///
15/// ```ignore
16/// use crypto::{Hasher, ascon::AsconHash256};
17///
18/// let hash = AsconHash256::hash(b"hello world");
19/// ```
20///
21/// # Incremental API
22///
23/// ```ignore
24/// use crypto::{Hasher, ascon::AsconHash256};
25///
26/// let mut hasher = AsconHash256::new();
27/// hasher.update(b"hello ");
28/// hasher.update(b"world");
29/// let hash = hasher.sum();
30/// ```
31#[derive(Clone)]
32#[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop))]
33pub struct AsconHash256 {
34    state: State,
35    buf: [u8; 8],
36    buf_len: usize,
37}
38
39impl AsconHash256 {
40    #[inline]
41    pub fn new() -> Self {
42        let mut state = State::init_hash(IV);
43        p12(&mut state);
44        AsconHash256 {
45            state,
46            buf: [0u8; 8],
47            buf_len: 0,
48        }
49    }
50
51    fn process_buffer(&mut self) {
52        debug_assert_eq!(self.buf_len, 8);
53        self.state.absorb_block(&self.buf);
54        p12(&mut self.state);
55        self.buf_len = 0;
56    }
57
58    fn pad_and_finalize(&mut self) {
59        let mut padded = [0u8; 8];
60        padded[..self.buf_len].copy_from_slice(&self.buf[..self.buf_len]);
61        padded[self.buf_len] = 0x01;
62        self.state.absorb_block(&padded);
63        p12(&mut self.state);
64    }
65}
66
67impl Hasher for AsconHash256 {
68    const BLOCK_SIZE: usize = 8;
69    const OUTPUT_SIZE: usize = 32;
70
71    #[inline]
72    fn new() -> Self {
73        AsconHash256::new()
74    }
75
76    fn update(&mut self, mut data: &[u8]) {
77        if self.buf_len > 0 {
78            let to_fill = (8 - self.buf_len).min(data.len());
79            self.buf[self.buf_len..self.buf_len + to_fill].copy_from_slice(&data[..to_fill]);
80            self.buf_len += to_fill;
81            data = &data[to_fill..];
82
83            if self.buf_len == 8 {
84                self.process_buffer();
85            }
86        }
87
88        let mut chunks = data.chunks_exact(8);
89        for chunk in &mut chunks {
90            self.state.absorb_block(chunk);
91            p12(&mut self.state);
92        }
93
94        let remainder = chunks.remainder();
95        if !remainder.is_empty() {
96            self.buf[..remainder.len()].copy_from_slice(remainder);
97            self.buf_len = remainder.len();
98        }
99    }
100
101    fn sum(mut self) -> Hash {
102        self.pad_and_finalize();
103
104        let mut hash = Bytes::<64>::with_length(32);
105        let out = hash.as_mut();
106        // Unrolled: squeeze 4 blocks of 8 bytes each
107        out[0..8].copy_from_slice(&self.state.squeeze_rate_u64().to_le_bytes());
108        p12(&mut self.state);
109        out[8..16].copy_from_slice(&self.state.squeeze_rate_u64().to_le_bytes());
110        p12(&mut self.state);
111        out[16..24].copy_from_slice(&self.state.squeeze_rate_u64().to_le_bytes());
112        p12(&mut self.state);
113        out[24..32].copy_from_slice(&self.state.squeeze_rate_u64().to_le_bytes());
114
115        Hash(hash)
116    }
117}
118
119#[cfg(test)]
120mod tests {
121    use super::*;
122    use crate::Hasher;
123
124    #[test]
125    fn empty_input() {
126        let h = AsconHash256::hash(b"");
127        let expected = hex::decode("0B3BE5850F2F6B98CAF29F8FDEA89B64A1FA70AA249B8F839BD53BAA304D92B2").unwrap();
128        assert_eq!(h.as_ref(), expected.as_slice());
129    }
130
131    #[test]
132    fn one_byte() {
133        let h = AsconHash256::hash(b"\x00");
134        let expected = hex::decode("0728621035AF3ED2BCA03BF6FDE900F9456F5330E4B5EE23E7F6A1E70291BC80").unwrap();
135        assert_eq!(h.as_ref(), expected.as_slice());
136    }
137
138    #[test]
139    fn two_bytes() {
140        let h = AsconHash256::hash(b"\x00\x01");
141        let expected = hex::decode("6115E7C9C4081C2797FC8FE1BC57A836AFA1C5381E556DD583860CA2DFB48DD2").unwrap();
142        assert_eq!(h.as_ref(), expected.as_slice());
143    }
144
145    #[test]
146    fn exactly_one_block() {
147        let h = AsconHash256::hash(b"\x00\x01\x02\x03\x04\x05\x06\x07");
148        let expected = hex::decode("B88E497AE8E6FB641B87EF622EB8F2FCA0ED95383F7FFEBE167ACF1099BA764F").unwrap();
149        assert_eq!(h.as_ref(), expected.as_slice());
150    }
151
152    #[test]
153    fn incremental() {
154        let msg = b"hello world";
155        let one_shot = AsconHash256::hash(msg);
156        let mut h = AsconHash256::new();
157        for byte in msg {
158            h.update(&[*byte]);
159        }
160        assert_eq!(one_shot.as_ref(), h.sum().as_ref());
161    }
162
163    #[test]
164    fn block_boundaries() {
165        for len in [1usize, 7, 8, 9, 15, 16, 17, 63, 64, 65] {
166            let input = vec![b'a'; len];
167            let one_shot = AsconHash256::hash(&input);
168            let mut h = AsconHash256::new();
169            for chunk in input.chunks(3) {
170                h.update(chunk);
171            }
172            assert_eq!(one_shot.as_ref(), h.sum().as_ref(), "len={len}");
173        }
174    }
175
176    #[test]
177    fn kat_vectors() {
178        let data = include_str!("../../testdata/ascon/LWC_HASH_KAT_128_256.txt");
179        let mut count = 0u64;
180        let mut msg_hex = String::new();
181
182        for line in data.lines() {
183            let line = line.trim();
184            if line.is_empty() {
185                continue;
186            }
187            if line.starts_with("Count = ") {
188                count = line["Count = ".len()..].parse().unwrap();
189                msg_hex.clear();
190                continue;
191            }
192            if line.starts_with("Msg = ") {
193                msg_hex = line[6..].to_string();
194                continue;
195            }
196            if line.starts_with("MD = ") {
197                let expected_md: &str = &line[5..];
198                let msg = hex::decode(&msg_hex).unwrap();
199                let expected = hex::decode(expected_md).unwrap();
200                let hash = AsconHash256::hash(&msg);
201                assert_eq!(hash.as_ref(), expected.as_slice(), "KAT Hash Count={count} mismatch");
202                let mut h = AsconHash256::new();
203                for chunk in msg.chunks(3) {
204                    h.update(chunk);
205                }
206                assert_eq!(
207                    h.sum().as_ref(),
208                    expected.as_slice(),
209                    "KAT Hash Count={count} incremental mismatch"
210                );
211                msg_hex.clear();
212                continue;
213            }
214        }
215    }
216}