ascon_core/
lib.rs

1// Copyright 2021-2025 Sebastian Ramacher
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4#![no_std]
5#![cfg_attr(docsrs, feature(doc_auto_cfg))]
6#![doc = include_str!("../README.md")]
7#![forbid(unsafe_code)]
8#![warn(missing_docs)]
9
10use core::mem::size_of;
11
12#[cfg(feature = "zeroize")]
13use zeroize::{Zeroize, ZeroizeOnDrop};
14
15/// Compute round constant
16#[inline(always)]
17const fn round_constant(round: u64) -> u64 {
18    ((0xfu64 - round) << 4) | round
19}
20
21/// The state of Ascon's permutation.
22///
23/// The permutation operates on a state of 320 bits represented as 5 64 bit words.
24#[derive(Clone, Debug, Default)]
25pub struct State {
26    x: [u64; 5],
27}
28
29/// Ascon's round function
30const fn round(x: [u64; 5], c: u64) -> [u64; 5] {
31    // S-box layer
32    let x0 = x[0] ^ x[4];
33    let x2 = x[2] ^ x[1] ^ c; // with round constant
34    let x4 = x[4] ^ x[3];
35
36    let tx0 = x0 ^ (!x[1] & x2);
37    let tx1 = x[1] ^ (!x2 & x[3]);
38    let tx2 = x2 ^ (!x[3] & x4);
39    let tx3 = x[3] ^ (!x4 & x0);
40    let tx4 = x4 ^ (!x0 & x[1]);
41    let tx1 = tx1 ^ tx0;
42    let tx3 = tx3 ^ tx2;
43    let tx0 = tx0 ^ tx4;
44
45    // linear layer
46    let x0 = tx0 ^ tx0.rotate_right(9);
47    let x1 = tx1 ^ tx1.rotate_right(22);
48    let x2 = tx2 ^ tx2.rotate_right(5);
49    let x3 = tx3 ^ tx3.rotate_right(7);
50    let x4 = tx4 ^ tx4.rotate_right(34);
51    [
52        tx0 ^ x0.rotate_right(19),
53        tx1 ^ x1.rotate_right(39),
54        !(tx2 ^ x2.rotate_right(1)),
55        tx3 ^ x3.rotate_right(10),
56        tx4 ^ x4.rotate_right(7),
57    ]
58}
59
60impl State {
61    /// Instantiate new state from the given values.
62    pub fn new(x0: u64, x1: u64, x2: u64, x3: u64, x4: u64) -> Self {
63        State {
64            x: [x0, x1, x2, x3, x4],
65        }
66    }
67
68    #[cfg(not(feature = "no_unroll"))]
69    /// Perform permutation with 12 rounds.
70    pub fn permute_12(&mut self) {
71        // We could in theory iter().fold() over an array of round constants,
72        // but the compiler produces better results when optimizing this chain
73        // of round function calls.
74        self.x = round(
75            round(
76                round(
77                    round(
78                        round(
79                            round(
80                                round(
81                                    round(
82                                        round(round(round(round(self.x, 0xf0), 0xe1), 0xd2), 0xc3),
83                                        0xb4,
84                                    ),
85                                    0xa5,
86                                ),
87                                0x96,
88                            ),
89                            0x87,
90                        ),
91                        0x78,
92                    ),
93                    0x69,
94                ),
95                0x5a,
96            ),
97            0x4b,
98        );
99    }
100
101    #[cfg(feature = "no_unroll")]
102    /// Perform permutation with 12 rounds.
103    pub fn permute_12(&mut self) {
104        self.x = [
105            0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b,
106        ]
107        .into_iter()
108        .fold(self.x, round);
109    }
110
111    #[cfg(not(feature = "no_unroll"))]
112    /// Perform permutation with 8 rounds.
113    pub fn permute_8(&mut self) {
114        self.x = round(
115            round(
116                round(
117                    round(
118                        round(round(round(round(self.x, 0xb4), 0xa5), 0x96), 0x87),
119                        0x78,
120                    ),
121                    0x69,
122                ),
123                0x5a,
124            ),
125            0x4b,
126        );
127    }
128
129    #[cfg(feature = "no_unroll")]
130    /// Perform permutation with 8 rounds.
131    pub fn permute_8(&mut self) {
132        self.x = [0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b]
133            .into_iter()
134            .fold(self.x, round);
135    }
136
137    /// Perform permutation with 1 round
138    pub fn permute_1(&mut self) {
139        self.x = round(self.x, 0x4b);
140    }
141
142    /// Perform a given number (up to 12) of permutations
143    ///
144    /// Panics (in debug mode) if `rounds` is larger than 12.
145    pub fn permute_n(&mut self, rounds: usize) {
146        debug_assert!(rounds <= 12);
147
148        let start = 12 - rounds;
149        self.x = (start..12).fold(self.x, |x, round_index| {
150            round(x, round_constant(round_index as u64))
151        });
152    }
153
154    /// Convert state to bytes.
155    pub fn as_bytes(&self) -> [u8; 40] {
156        let mut bytes = [0u8; size_of::<u64>() * 5];
157        for (dst, src) in bytes
158            .chunks_exact_mut(size_of::<u64>())
159            .zip(self.x.into_iter())
160        {
161            dst.copy_from_slice(&u64::to_be_bytes(src));
162        }
163        bytes
164    }
165}
166
167impl core::ops::Index<usize> for State {
168    type Output = u64;
169
170    #[inline(always)]
171    fn index(&self, index: usize) -> &Self::Output {
172        &self.x[index]
173    }
174}
175
176impl core::ops::IndexMut<usize> for State {
177    #[inline(always)]
178    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
179        &mut self.x[index]
180    }
181}
182
183impl TryFrom<&[u64]> for State {
184    type Error = ();
185
186    fn try_from(value: &[u64]) -> Result<Self, Self::Error> {
187        match value.len() {
188            5 => Ok(Self::new(value[0], value[1], value[2], value[3], value[4])),
189            _ => Err(()),
190        }
191    }
192}
193
194impl From<&[u64; 5]> for State {
195    fn from(value: &[u64; 5]) -> Self {
196        Self { x: *value }
197    }
198}
199
200impl TryFrom<&[u8]> for State {
201    type Error = ();
202
203    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
204        if value.len() != core::mem::size_of::<u64>() * 5 {
205            return Err(());
206        }
207
208        let mut state = Self::default();
209        for (src, dst) in value
210            .chunks_exact(core::mem::size_of::<u64>())
211            .zip(state.x.iter_mut())
212        {
213            *dst = u64::from_be_bytes(src.try_into().unwrap());
214        }
215        Ok(state)
216    }
217}
218
219impl From<&[u8; size_of::<u64>() * 5]> for State {
220    fn from(value: &[u8; size_of::<u64>() * 5]) -> Self {
221        let mut state = Self::default();
222        for (src, dst) in value
223            .chunks_exact(core::mem::size_of::<u64>())
224            .zip(state.x.iter_mut())
225        {
226            *dst = u64::from_be_bytes(src.try_into().unwrap());
227        }
228        state
229    }
230}
231
232impl AsRef<[u64]> for State {
233    fn as_ref(&self) -> &[u64] {
234        &self.x
235    }
236}
237
238#[cfg(feature = "zeroize")]
239impl Drop for State {
240    fn drop(&mut self) {
241        self.x.zeroize();
242    }
243}
244
245#[cfg(feature = "zeroize")]
246impl ZeroizeOnDrop for State {}
247
248#[cfg(test)]
249mod tests {
250    use super::*;
251
252    #[test]
253    fn round_constants() {
254        assert_eq!(round_constant(0), 0xf0);
255        assert_eq!(round_constant(1), 0xe1);
256        assert_eq!(round_constant(2), 0xd2);
257        assert_eq!(round_constant(3), 0xc3);
258        assert_eq!(round_constant(4), 0xb4);
259        assert_eq!(round_constant(5), 0xa5);
260        assert_eq!(round_constant(6), 0x96);
261        assert_eq!(round_constant(7), 0x87);
262        assert_eq!(round_constant(8), 0x78);
263        assert_eq!(round_constant(9), 0x69);
264        assert_eq!(round_constant(10), 0x5a);
265        assert_eq!(round_constant(11), 0x4b);
266    }
267
268    #[test]
269    fn one_round() {
270        let state = round(
271            [
272                0x0123456789abcdef,
273                0x23456789abcdef01,
274                0x456789abcdef0123,
275                0x6789abcdef012345,
276                0x89abcde01234567f,
277            ],
278            0x1f,
279        );
280        assert_eq!(
281            state,
282            [
283                0x3c1748c9be2892ce,
284                0x5eafb305cd26164f,
285                0xf9470254bb3a4213,
286                0xf0428daf0c5d3948,
287                0x281375af0b294899
288            ]
289        );
290    }
291
292    #[test]
293    fn state_permute_12() {
294        let mut state = State::new(
295            0x0123456789abcdef,
296            0xef0123456789abcd,
297            0xcdef0123456789ab,
298            0xabcdef0123456789,
299            0x89abcdef01234567,
300        );
301        state.permute_12();
302        assert_eq!(state[0], 0x206416dfc624bb14);
303        assert_eq!(state[1], 0x1b0c47a601058aab);
304        assert_eq!(state[2], 0x8934cfc93814cddd);
305        assert_eq!(state[3], 0xa9738d287a748e4b);
306        assert_eq!(state[4], 0xddd934f058afc7e1);
307    }
308
309    #[test]
310    fn state_permute_8() {
311        let mut state = State::new(
312            0x0123456789abcdef,
313            0xef0123456789abcd,
314            0xcdef0123456789ab,
315            0xabcdef0123456789,
316            0x89abcdef01234567,
317        );
318        state.permute_8();
319        assert_eq!(state[0], 0x67ed228272f46eee);
320        assert_eq!(state[1], 0x80bc0b097aad7944);
321        assert_eq!(state[2], 0x2fa599382c6db215);
322        assert_eq!(state[3], 0x368133fae2f7667a);
323        assert_eq!(state[4], 0x28cefb195a7c651c);
324    }
325
326    #[test]
327    fn state_permute_n() {
328        let mut state = State::new(
329            0x0123456789abcdef,
330            0xef0123456789abcd,
331            0xcdef0123456789ab,
332            0xabcdef0123456789,
333            0x89abcdef01234567,
334        );
335        let mut state2 = state.clone();
336
337        state.permute_8();
338        state2.permute_n(8);
339        assert_eq!(state.x, state2.x);
340
341        state.permute_12();
342        state2.permute_n(12);
343        assert_eq!(state.x, state2.x);
344    }
345
346    #[test]
347    fn state_convert_bytes() {
348        let state = State::new(
349            0x0123456789abcdef,
350            0xef0123456789abcd,
351            0xcdef0123456789ab,
352            0xabcdef0123456789,
353            0x89abcdef01234567,
354        );
355        let bytes = state.as_bytes();
356
357        // test TryFrom<&[u8]>
358        let state2 = State::try_from(&bytes[..]);
359        assert_eq!(state2.expect("try_from bytes").x, state.x);
360
361        let state2 = State::from(&bytes);
362        assert_eq!(state2.x, state.x);
363    }
364}