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#[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
77impl 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#[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 {}