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 (chunks, remainder) = data.as_chunks::<8>();
89        for chunk in chunks {
90            self.state.absorb_block(chunk);
91            p12(&mut self.state);
92        }
93
94        if !remainder.is_empty() {
95            self.buf[..remainder.len()].copy_from_slice(remainder);
96            self.buf_len = remainder.len();
97        }
98    }
99
100    fn sum(mut self) -> Hash {
101        self.pad_and_finalize();
102
103        let mut hash = Bytes::<64>::with_length(32);
104        let out = hash.as_mut();
105        // Unrolled: squeeze 4 blocks of 8 bytes each
106        out[0..8].copy_from_slice(&self.state.squeeze_rate_u64().to_le_bytes());
107        p12(&mut self.state);
108        out[8..16].copy_from_slice(&self.state.squeeze_rate_u64().to_le_bytes());
109        p12(&mut self.state);
110        out[16..24].copy_from_slice(&self.state.squeeze_rate_u64().to_le_bytes());
111        p12(&mut self.state);
112        out[24..32].copy_from_slice(&self.state.squeeze_rate_u64().to_le_bytes());
113
114        Hash(hash)
115    }
116}
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121    use crate::Hasher;
122
123    #[test]
124    fn empty_input() {
125        let h = AsconHash256::hash(b"");
126        let expected = hex::decode("0B3BE5850F2F6B98CAF29F8FDEA89B64A1FA70AA249B8F839BD53BAA304D92B2").unwrap();
127        assert_eq!(h.as_ref(), expected.as_slice());
128    }
129
130    #[test]
131    fn one_byte() {
132        let h = AsconHash256::hash(b"\x00");
133        let expected = hex::decode("0728621035AF3ED2BCA03BF6FDE900F9456F5330E4B5EE23E7F6A1E70291BC80").unwrap();
134        assert_eq!(h.as_ref(), expected.as_slice());
135    }
136
137    #[test]
138    fn two_bytes() {
139        let h = AsconHash256::hash(b"\x00\x01");
140        let expected = hex::decode("6115E7C9C4081C2797FC8FE1BC57A836AFA1C5381E556DD583860CA2DFB48DD2").unwrap();
141        assert_eq!(h.as_ref(), expected.as_slice());
142    }
143
144    #[test]
145    fn exactly_one_block() {
146        let h = AsconHash256::hash(b"\x00\x01\x02\x03\x04\x05\x06\x07");
147        let expected = hex::decode("B88E497AE8E6FB641B87EF622EB8F2FCA0ED95383F7FFEBE167ACF1099BA764F").unwrap();
148        assert_eq!(h.as_ref(), expected.as_slice());
149    }
150
151    #[test]
152    fn incremental() {
153        let msg = b"hello world";
154        let one_shot = AsconHash256::hash(msg);
155        let mut h = AsconHash256::new();
156        for byte in msg {
157            h.update(&[*byte]);
158        }
159        assert_eq!(one_shot.as_ref(), h.sum().as_ref());
160    }
161
162    #[test]
163    fn block_boundaries() {
164        for len in [1usize, 7, 8, 9, 15, 16, 17, 63, 64, 65] {
165            let input = vec![b'a'; len];
166            let one_shot = AsconHash256::hash(&input);
167            let mut h = AsconHash256::new();
168            for chunk in input.chunks(3) {
169                h.update(chunk);
170            }
171            assert_eq!(one_shot.as_ref(), h.sum().as_ref(), "len={len}");
172        }
173    }
174
175    #[test]
176    fn kat_vectors() {
177        let data = include_str!("../../testdata/ascon/LWC_HASH_KAT_128_256.txt");
178        let mut count = 0u64;
179        let mut msg_hex = String::new();
180
181        for line in data.lines() {
182            let line = line.trim();
183            if line.is_empty() {
184                continue;
185            }
186            if line.starts_with("Count = ") {
187                count = line["Count = ".len()..].parse().unwrap();
188                msg_hex.clear();
189                continue;
190            }
191            if line.starts_with("Msg = ") {
192                msg_hex = line[6..].to_string();
193                continue;
194            }
195            if line.starts_with("MD = ") {
196                let expected_md: &str = &line[5..];
197                let msg = hex::decode(&msg_hex).unwrap();
198                let expected = hex::decode(expected_md).unwrap();
199                let hash = AsconHash256::hash(&msg);
200                assert_eq!(hash.as_ref(), expected.as_slice(), "KAT Hash Count={count} mismatch");
201                let mut h = AsconHash256::new();
202                for chunk in msg.chunks(3) {
203                    h.update(chunk);
204                }
205                assert_eq!(
206                    h.sum().as_ref(),
207                    expected.as_slice(),
208                    "KAT Hash Count={count} incremental mismatch"
209                );
210                msg_hex.clear();
211                continue;
212            }
213        }
214    }
215}