Skip to main content

ascon_hash/
lib.rs

1// Copyright 2022-2025 Sebastian Ramacher
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4#![no_std]
5#![cfg_attr(docsrs, feature(doc_cfg))]
6#![doc = include_str!("../README.md")]
7#![warn(missing_docs)]
8
9//! ## Usage (Hashing)
10//!
11//! ```
12//! use ascon_hash::{AsconHash256, Digest};
13//!
14//! let mut hasher = AsconHash256::new();
15//! hasher.update(b"some bytes");
16//! let digest = hasher.finalize();
17//! assert_eq!(&digest[..], b"\xe9\x09\xc2\xf6\xda\x9c\xb3\x02\x84\x23\x26\x5c\x8f\x23\xfc\x2d\x26\xbf\xc0\xf3\xdb\x70\x46\x83\xef\x16\xb7\x87\xa9\x45\xed\x68");
18//! ```
19//!
20//! ## Usage (XOF)
21//!
22//! ```
23//! use ascon_hash::{AsconXof128, ExtendableOutput, Update, XofReader};
24//!
25//! let mut xof = AsconXof128::default();
26//! xof.update(b"some bytes");
27//! let mut reader = xof.finalize_xof();
28//! let mut dst = [0u8; 5];
29//! reader.read(&mut dst);
30//! assert_eq!(&dst, b"\x8c\x7d\xd1\x14\xa0");
31//! ```
32
33use core::{fmt, marker::PhantomData};
34
35use ascon_core::State;
36#[cfg(feature = "zeroize")]
37use digest::zeroize::ZeroizeOnDrop;
38pub use digest::{self, Digest, ExtendableOutput, Reset, Update, XofReader};
39use digest::{
40    CollisionResistance, HashMarker, Output, OutputSizeUser,
41    block_api::{
42        AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, Eager, ExtendableOutputCore,
43        FixedOutputCore, UpdateCore, XofReaderCore,
44    },
45    common::hazmat::{DeserializeStateError, SerializableState, SerializedState},
46    consts::{U8, U16, U32, U40},
47};
48
49/// Produce mask for padding.
50#[inline(always)]
51const fn pad(n: usize) -> u64 {
52    0x01_u64 << (8 * n)
53}
54
55/// Parameters for Ascon hash instances.
56///
57/// These parameters represent the permutation applied to the zero-extended IV.
58trait HashParameters {
59    /// Part of the IV.
60    const IV0: u64;
61    /// Part of the IV.
62    const IV1: u64;
63    /// Part of the IV.
64    const IV2: u64;
65    /// Part of the IV.
66    const IV3: u64;
67    /// Part of the IV.
68    const IV4: u64;
69}
70
71/// Parameters for Ascon-Hash256.
72#[derive(Clone, Debug)]
73struct Parameters;
74
75impl HashParameters for Parameters {
76    const IV0: u64 = 0x9b1e5494e934d681;
77    const IV1: u64 = 0x4bc3a01e333751d2;
78    const IV2: u64 = 0xae65396c6b34b81a;
79    const IV3: u64 = 0x3c7fd4a4d56a4db3;
80    const IV4: u64 = 0x1a5c464906c5976d;
81}
82
83/// Parameters for Ascon-XOF128
84#[derive(Clone, Debug)]
85struct ParametersXof;
86
87impl HashParameters for ParametersXof {
88    const IV0: u64 = 0xda82ce768d9447eb;
89    const IV1: u64 = 0xcc7ce6c75f1ef969;
90    const IV2: u64 = 0xe7508fd780085631;
91    const IV3: u64 = 0x0ee0ea53416b58cc;
92    const IV4: u64 = 0xe0547524db6f0bde;
93}
94
95#[derive(Clone, Debug)]
96struct HashCore<P: HashParameters> {
97    state: State,
98    phantom: PhantomData<P>,
99}
100
101#[cfg(feature = "zeroize")]
102impl<P: HashParameters> ZeroizeOnDrop for HashCore<P> {}
103
104impl<P: HashParameters> HashCore<P> {
105    fn absorb_block(&mut self, block: &[u8; 8]) {
106        self.state[0] ^= u64::from_le_bytes(*block);
107        self.permute_state();
108    }
109
110    fn absorb_last_block(&mut self, block: &[u8]) {
111        debug_assert!(block.len() < 8);
112
113        let len = block.len();
114        if len > 0 {
115            let mut tmp = [0u8; 8];
116            tmp[0..len].copy_from_slice(block);
117            self.state[0] ^= u64::from_le_bytes(tmp);
118        }
119        self.state[0] ^= pad(len);
120        self.state.permute_12();
121    }
122
123    // for fixed-sized output
124    fn squeeze(&mut self, mut block: &mut [u8]) {
125        debug_assert_eq!(block.len() % 8, 0);
126
127        while block.len() > 8 {
128            block[..8].copy_from_slice(&u64::to_le_bytes(self.state[0]));
129            self.permute_state();
130            block = &mut block[8..];
131        }
132        block[..8].copy_from_slice(&u64::to_le_bytes(self.state[0]));
133    }
134
135    // for XOF output
136    fn squeeze_block(&mut self) -> [u8; 8] {
137        let ret = u64::to_le_bytes(self.state[0]);
138        self.permute_state();
139        ret
140    }
141
142    #[inline(always)]
143    fn permute_state(&mut self) {
144        self.state.permute_12();
145    }
146}
147
148impl<P: HashParameters> Default for HashCore<P> {
149    fn default() -> Self {
150        Self {
151            state: State::new(P::IV0, P::IV1, P::IV2, P::IV3, P::IV4),
152            phantom: PhantomData,
153        }
154    }
155}
156
157/// Ascon hash implementation
158#[derive(Clone, Debug, Default)]
159pub struct AsconCore {
160    state: HashCore<Parameters>,
161}
162
163#[cfg(feature = "zeroize")]
164impl ZeroizeOnDrop for AsconCore {}
165
166impl HashMarker for AsconCore {}
167
168impl BlockSizeUser for AsconCore {
169    type BlockSize = U8;
170}
171
172impl BufferKindUser for AsconCore {
173    type BufferKind = Eager;
174}
175
176impl OutputSizeUser for AsconCore {
177    type OutputSize = U32;
178}
179
180impl UpdateCore for AsconCore {
181    fn update_blocks(&mut self, blocks: &[Block<Self>]) {
182        for block in blocks {
183            self.state.absorb_block(block.as_ref());
184        }
185    }
186}
187
188impl FixedOutputCore for AsconCore {
189    fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
190        debug_assert!(buffer.get_pos() < 8);
191        self.state
192            .absorb_last_block(&buffer.get_data()[..buffer.get_pos()]);
193        self.state.squeeze(out);
194    }
195}
196
197impl Reset for AsconCore {
198    fn reset(&mut self) {
199        *self = Default::default();
200    }
201}
202
203impl AlgorithmName for AsconCore {
204    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
205        f.write_str("Ascon-Hash256")
206    }
207}
208
209impl SerializableState for AsconCore {
210    type SerializedStateSize = U40;
211
212    fn serialize(&self) -> SerializedState<Self> {
213        self.state.state.as_bytes().into()
214    }
215
216    fn deserialize(
217        serialized_state: &SerializedState<Self>,
218    ) -> Result<Self, DeserializeStateError> {
219        let state = State::from(&serialized_state.0);
220        Ok(Self {
221            state: HashCore {
222                state,
223                phantom: PhantomData,
224            },
225        })
226    }
227}
228
229/// Ascon XOF
230#[derive(Clone, Debug, Default)]
231pub struct AsconXofCore {
232    state: HashCore<ParametersXof>,
233}
234
235#[cfg(feature = "zeroize")]
236impl ZeroizeOnDrop for AsconXofCore {}
237
238impl HashMarker for AsconXofCore {}
239
240impl BlockSizeUser for AsconXofCore {
241    type BlockSize = U8;
242}
243
244impl BufferKindUser for AsconXofCore {
245    type BufferKind = Eager;
246}
247
248impl UpdateCore for AsconXofCore {
249    fn update_blocks(&mut self, blocks: &[Block<Self>]) {
250        for block in blocks {
251            self.state.absorb_block(block.as_ref());
252        }
253    }
254}
255
256/// Reader for XOF output
257#[derive(Clone, Debug)]
258pub struct AsconXofReaderCore {
259    hasher: HashCore<ParametersXof>,
260}
261
262#[cfg(feature = "zeroize")]
263impl ZeroizeOnDrop for AsconXofReaderCore {}
264
265impl BlockSizeUser for AsconXofReaderCore {
266    type BlockSize = U8;
267}
268
269impl XofReaderCore for AsconXofReaderCore {
270    fn read_block(&mut self) -> Block<Self> {
271        self.hasher.squeeze_block().into()
272    }
273}
274
275impl ExtendableOutputCore for AsconXofCore {
276    type ReaderCore = AsconXofReaderCore;
277
278    fn finalize_xof_core(&mut self, buffer: &mut Buffer<Self>) -> Self::ReaderCore {
279        debug_assert!(buffer.get_pos() < 8);
280        self.state
281            .absorb_last_block(&buffer.get_data()[..buffer.get_pos()]);
282        Self::ReaderCore {
283            hasher: self.state.clone(),
284        }
285    }
286}
287
288impl Reset for AsconXofCore {
289    fn reset(&mut self) {
290        *self = Default::default();
291    }
292}
293
294impl AlgorithmName for AsconXofCore {
295    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
296        f.write_str("Ascon-XOF128")
297    }
298}
299
300impl SerializableState for AsconXofCore {
301    type SerializedStateSize = U40;
302
303    fn serialize(&self) -> SerializedState<Self> {
304        self.state.state.as_bytes().into()
305    }
306
307    fn deserialize(
308        serialized_state: &SerializedState<Self>,
309    ) -> Result<Self, DeserializeStateError> {
310        let state = State::from(&serialized_state.0);
311        Ok(Self {
312            state: HashCore {
313                state,
314                phantom: PhantomData,
315            },
316        })
317    }
318}
319
320digest::buffer_fixed!(
321    /// Ascon-Hash256
322    pub struct AsconHash256(AsconCore);
323    impl: FixedHashTraits;
324);
325
326digest::buffer_xof!(
327    /// Ascon-XOF128 hasher.
328    pub struct AsconXof128(AsconXofCore);
329    impl: XofHasherTraits;
330    /// Ascon-XOF128 reader.
331    pub struct AsconXof128Reader(AsconXofReaderCore);
332    impl: XofReaderTraits;
333);
334
335impl CollisionResistance for AsconXof128 {
336    // https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-232.pdf#table.caption.24
337    type CollisionResistance = U16;
338}