Skip to main content

ascon_xof128/
cxof.rs

1use ascon::State;
2use core::fmt;
3use digest::{
4    CollisionResistance, ExtendableOutput, HashMarker, OutputSizeUser, TryCustomizedInit, Update,
5    common::{
6        AlgorithmName,
7        hazmat::{DeserializeStateError, SerializableState, SerializedState},
8    },
9    consts::{U16, U32, U41},
10};
11use sponge_cursor::SpongeCursor;
12
13use crate::{AsconXof128Reader, consts::CXOF_INIT_STATE};
14
15const MAX_CUSTOMIZATION_LEN: usize = 256;
16
17/// Ascon-CXOF128 hasher.
18///
19/// Note that NIST SP 800-232 specifies the following:
20///
21/// >The length of the customization string **shall** be at most 2048 bits (i.e., 256 bytes).
22///
23/// Implementation of the [`TryCustomizedInit`] trait for this type returns
24/// [`InvalidCustomizationError`] for longer customization strings.
25#[derive(Clone, Debug)]
26pub struct AsconCxof128 {
27    state: State,
28    cursor: SpongeCursor<8>,
29}
30
31impl TryCustomizedInit for AsconCxof128 {
32    type Error = InvalidCustomizationError;
33
34    #[inline]
35    #[allow(clippy::unwrap_in_result)]
36    fn try_new_customized(customization: &[u8]) -> Result<Self, InvalidCustomizationError> {
37        if customization.len() > MAX_CUSTOMIZATION_LEN {
38            return Err(InvalidCustomizationError);
39        }
40
41        let bit_len = 8 * customization.len();
42        let mut state = CXOF_INIT_STATE;
43
44        state[0] ^= u64::try_from(bit_len).expect("`bit_len` can not be greater than 2048");
45
46        ascon::permute12(&mut state);
47
48        let mut blocks = customization.chunks_exact(size_of::<u64>());
49        for block in &mut blocks {
50            let block = block.try_into().expect("block has correct length");
51            state[0] ^= u64::from_le_bytes(block);
52            ascon::permute12(&mut state);
53        }
54
55        let last_block = blocks.remainder();
56        let len = last_block.len();
57
58        let mut buf = [0u8; 8];
59        buf[..len].copy_from_slice(last_block);
60
61        let pad = 1u64 << (8 * len);
62        state[0] ^= u64::from_le_bytes(buf) ^ pad;
63
64        ascon::permute12(&mut state);
65
66        let cursor = Default::default();
67        Ok(Self { state, cursor })
68    }
69}
70
71impl HashMarker for AsconCxof128 {}
72
73impl OutputSizeUser for AsconCxof128 {
74    type OutputSize = U32;
75}
76
77// https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-232.ipd.pdf#table.caption.25
78impl CollisionResistance for AsconCxof128 {
79    type CollisionResistance = U16;
80}
81
82impl Update for AsconCxof128 {
83    #[inline]
84    fn update(&mut self, data: &[u8]) {
85        self.cursor
86            .absorb_u64_le(&mut self.state, ascon::permute12, data);
87    }
88}
89
90impl ExtendableOutput for AsconCxof128 {
91    type Reader = AsconXof128Reader;
92
93    fn finalize_xof(mut self) -> Self::Reader {
94        let pos = self.cursor.pos();
95        self.state[0] ^= 1u64 << (8 * pos);
96        AsconXof128Reader::new(&self.state)
97    }
98}
99
100impl AlgorithmName for AsconCxof128 {
101    #[inline]
102    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
103        f.write_str("Ascon-CXOF128")
104    }
105}
106
107impl SerializableState for AsconCxof128 {
108    type SerializedStateSize = U41;
109
110    #[inline]
111    fn serialize(&self) -> SerializedState<Self> {
112        let mut res = SerializedState::<Self>::default();
113        let (state_dst, cursor_dst) = res.split_at_mut(size_of::<State>());
114        let mut chunks = state_dst.chunks_exact_mut(size_of::<u64>());
115        for (src, dst) in self.state.iter().zip(&mut chunks) {
116            dst.copy_from_slice(&src.to_le_bytes());
117        }
118        assert!(chunks.into_remainder().is_empty());
119        assert_eq!(cursor_dst.len(), 1);
120        cursor_dst[0] = self.cursor.raw_pos();
121        res
122    }
123
124    #[inline]
125    fn deserialize(
126        serialized_state: &SerializedState<Self>,
127    ) -> Result<Self, DeserializeStateError> {
128        let (state_src, cursor_src) = serialized_state.split_at(size_of::<State>());
129        let state = core::array::from_fn(|i| {
130            let n = size_of::<u64>();
131            let chunk = &state_src[n * i..][..n];
132            u64::from_le_bytes(chunk.try_into().expect("chunk has correct length"))
133        });
134        assert_eq!(cursor_src.len(), 1);
135        SpongeCursor::new(cursor_src[0])
136            .ok_or(DeserializeStateError)
137            .map(|cursor| Self { state, cursor })
138    }
139}
140
141impl Drop for AsconCxof128 {
142    #[inline]
143    fn drop(&mut self) {
144        #[cfg(feature = "zeroize")]
145        {
146            use digest::zeroize::Zeroize;
147            self.state.zeroize();
148            self.cursor.zeroize();
149        }
150    }
151}
152
153#[cfg(feature = "zeroize")]
154impl digest::zeroize::ZeroizeOnDrop for AsconCxof128 {}
155
156/// Invalid Ascon-CXOF128 customization string error.
157#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
158pub struct InvalidCustomizationError;
159
160impl fmt::Display for InvalidCustomizationError {
161    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162        f.write_str(
163            "Invalid Ascon-CXOF128 customization string. \
164            The length of the customization string shall be at most 256 bytes.",
165        )
166    }
167}
168
169impl core::error::Error for InvalidCustomizationError {}