sponge_hash_aes256/
utilities.rs

1// SPDX-License-Identifier: 0BSD
2// SpongeHash-AES256
3// Copyright (C) 2025-2026 by LoRd_MuldeR <mulder2@gmx.de>
4
5use aes::{
6    cipher::{BlockCipherEncrypt, Key, KeyInit},
7    Aes256Enc,
8};
9use core::{
10    hint::unreachable_unchecked,
11    mem::MaybeUninit,
12    ops::{Index, IndexMut, RangeTo},
13    ptr,
14};
15use wide::u8x16;
16use zeroize::zeroize_flat_type;
17
18pub const BLOCK_SIZE: usize = 16usize;
19pub const ZERO: u8x16 = u8x16::ZERO;
20
21// ---------------------------------------------------------------------------
22// Block type
23// ---------------------------------------------------------------------------
24
25/// Represents an aligned 128-Bit block
26#[derive(Clone, Debug)]
27#[repr(align(16))]
28pub struct BlockType(u8x16);
29
30impl BlockType {
31    /// Create a new block that is initialized entirely from the given `INIT_VALUE`
32    #[inline(always)]
33    pub const fn new<const INIT_VALUE: u8>() -> Self {
34        Self(u8x16::new([INIT_VALUE; BLOCK_SIZE]))
35    }
36
37    /// Create a new block that is initialized from the given array
38    #[cfg(test)]
39    pub const fn from_array(value: [u8; BLOCK_SIZE]) -> Self {
40        Self(u8x16::new(value))
41    }
42
43    /// Create a new block that is initialized to "zero" bytes
44    #[inline(always)]
45    pub const fn zero() -> Self {
46        unsafe { Self(MaybeUninit::zeroed().assume_init()) }
47    }
48
49    /// Create a new block that is *not* initialized to any particular state
50    #[allow(invalid_value)]
51    #[allow(clippy::uninit_assumed_init)]
52    #[inline(always)]
53    pub const fn uninit() -> Self {
54        unsafe { Self(MaybeUninit::uninit().assume_init()) }
55    }
56
57    /// Computes the bit-wise XOR of `other` and *self*, stores the result "in-place" in *self*
58    #[inline(always)]
59    pub fn xor_with(&mut self, other: &Self) {
60        self.0 ^= other.0;
61    }
62
63    /// Computes the bit-wise XOR of `raw_data` and *self*, stores the result "in-place" in *self*
64    #[inline(always)]
65    pub fn xor_with_u8_ptr(&mut self, raw_data: *const u8) {
66        unsafe {
67            self.0 ^= u8x16::new(*raw_data.cast::<[u8; BLOCK_SIZE]>());
68        }
69    }
70
71    /// Get a `&[u8; BLOCK_SIZE]` reference to the contained data
72    #[inline(always)]
73    fn as_array(&self) -> &[u8; BLOCK_SIZE] {
74        self.0.as_array()
75    }
76
77    /// Get a `&mut [u8; BLOCK_SIZE]` reference to the contained data
78    #[inline(always)]
79    fn as_mut_array(&mut self) -> &mut [u8; BLOCK_SIZE] {
80        self.0.as_mut_array()
81    }
82
83    /// Get a "raw" `*const u8` pointer to the contained data
84    #[inline(always)]
85    fn as_ptr(&self) -> *const [u8; BLOCK_SIZE] {
86        self.0.as_array().as_ptr() as *const [u8; BLOCK_SIZE]
87    }
88}
89
90impl Index<usize> for BlockType {
91    type Output = u8;
92
93    #[inline(always)]
94    fn index(&self, _index: usize) -> &Self::Output {
95        unsafe { unreachable_unchecked() }
96    }
97}
98
99impl IndexMut<usize> for BlockType {
100    #[inline(always)]
101    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
102        &mut self.0.as_mut_array()[index]
103    }
104}
105
106impl Index<RangeTo<usize>> for BlockType {
107    type Output = [u8];
108
109    #[inline(always)]
110    fn index(&self, range: RangeTo<usize>) -> &Self::Output {
111        &self.0.as_array()[range]
112    }
113}
114
115impl PartialEq for BlockType {
116    #[inline(always)]
117    fn eq(&self, other: &Self) -> bool {
118        self.0 ^ other.0 == ZERO
119    }
120}
121
122impl Drop for BlockType {
123    #[inline(always)]
124    fn drop(&mut self) {
125        unsafe {
126            zeroize_flat_type(self);
127        }
128    }
129}
130
131// ---------------------------------------------------------------------------
132// Key type
133// ---------------------------------------------------------------------------
134
135/// Represents an aligned 256-Bit key
136#[repr(align(32))]
137pub struct KeyType(Key<Aes256Enc>);
138
139impl KeyType {
140    /// Concatenate the two 128-bit blocks `key0` and `key1` to from a full 256-bit key
141    #[allow(invalid_value)]
142    #[allow(clippy::uninit_assumed_init)]
143    #[inline(always)]
144    pub const fn uninit() -> Self {
145        unsafe { MaybeUninit::uninit().assume_init() }
146    }
147
148    /// Concatenate the two 128-bit blocks `key0` and `key1` to from a full 256-bit key
149    #[inline(always)]
150    pub fn concat(&mut self, key0: &BlockType, key1: &BlockType) -> &Key<Aes256Enc> {
151        unsafe {
152            let write_ptr = self.0.as_mut_ptr() as *mut [u8; BLOCK_SIZE];
153            ptr::copy_nonoverlapping(key0.as_ptr(), write_ptr, 1usize);
154            ptr::copy_nonoverlapping(key1.as_ptr(), write_ptr.add(1usize), 1usize);
155        }
156        &self.0
157    }
158}
159
160impl Drop for KeyType {
161    #[inline(always)]
162    fn drop(&mut self) {
163        unsafe {
164            zeroize_flat_type(self);
165        }
166    }
167}
168
169// ---------------------------------------------------------------------------
170// AES-256 Utility
171// ---------------------------------------------------------------------------
172
173/// Handles encryption with the AES-256 block cipher
174pub struct Aes256Crypto {
175    key: KeyType,
176}
177
178impl Aes256Crypto {
179    /// Encrypes the 128-bit block `src` with AES-256 and stores the result in `dst`.
180    ///
181    /// The 128 key bits from `key0` and the 128 key bits from `key1` are concatenated to form a full 256-bit key.
182    #[inline]
183    pub fn encrypt(&mut self, dst: &mut BlockType, src: &BlockType, key0: &BlockType, key1: &BlockType) {
184        let cipher = Aes256Enc::new(self.key.concat(key0, key1));
185        cipher.encrypt_block_b2b(src.as_array().into(), dst.as_mut_array().into());
186    }
187}
188
189impl Default for Aes256Crypto {
190    /// Creates a new `Aes256Processor` instance
191    #[inline]
192    fn default() -> Self {
193        Self { key: KeyType::uninit() }
194    }
195}
196
197// ---------------------------------------------------------------------------
198// Functions
199// ---------------------------------------------------------------------------
200
201/// Determines the length, in bytes, of the range specified by two "raw" adjacent pointers
202#[inline(always)]
203pub fn length(from: *const u8, to: *const u8) -> usize {
204    debug_assert!(to >= from);
205    unsafe { to.offset_from(from) as usize }
206}
207
208/// Returns the version of the library as a string
209pub const fn version() -> &'static str {
210    static PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
211    PKG_VERSION
212}
213
214// ---------------------------------------------------------------------------
215// Tests
216// ---------------------------------------------------------------------------
217
218#[cfg(test)]
219mod tests {
220    mod aes256_encrypt {
221        use super::super::*;
222        use hex_literal::hex;
223
224        const KEY_0: BlockType = BlockType::from_array(hex!("603deb1015ca71be2b73aef0857d7781"));
225        const KEY_1: BlockType = BlockType::from_array(hex!("1f352c073b6108d72d9810a30914dff4"));
226
227        fn do_aes256_ecb(input: &BlockType, expected: &BlockType, key0: &BlockType, key1: &BlockType) {
228            let mut output = BlockType::zero();
229            Aes256Crypto::default().encrypt(&mut output, input, key0, key1);
230            assert_eq!(&output, expected);
231        }
232
233        #[test]
234        fn test_aes256_ecb_1a() {
235            do_aes256_ecb(
236                &BlockType::from_array(hex!("6bc1bee22e409f96e93d7e117393172a")),
237                &BlockType::from_array(hex!("f3eed1bdb5d2a03c064b5a7e3db181f8")),
238                &KEY_0,
239                &KEY_1,
240            );
241        }
242
243        #[test]
244        fn test_aes256_ecb_1b() {
245            do_aes256_ecb(
246                &BlockType::from_array(hex!("6bc1bee22e409f96e93d7e117393172a")),
247                &BlockType::from_array(hex!("5ba1a80938bf65904c5a406f5651b88c")),
248                &KEY_1,
249                &KEY_0,
250            );
251        }
252
253        #[test]
254        fn test_aes256_ecb_2a() {
255            do_aes256_ecb(
256                &BlockType::from_array(hex!("ae2d8a571e03ac9c9eb76fac45af8e51")),
257                &BlockType::from_array(hex!("591ccb10d410ed26dc5ba74a31362870")),
258                &KEY_0,
259                &KEY_1,
260            );
261        }
262
263        #[test]
264        fn test_aes256_ecb_2b() {
265            do_aes256_ecb(
266                &BlockType::from_array(hex!("ae2d8a571e03ac9c9eb76fac45af8e51")),
267                &BlockType::from_array(hex!("1f38958fe69e4c58d7b0e908000be9b9")),
268                &KEY_1,
269                &KEY_0,
270            );
271        }
272
273        #[test]
274        fn test_aes256_ecb_3a() {
275            do_aes256_ecb(
276                &BlockType::from_array(hex!("30c81c46a35ce411e5fbc1191a0a52ef")),
277                &BlockType::from_array(hex!("b6ed21b99ca6f4f9f153e7b1beafed1d")),
278                &KEY_0,
279                &KEY_1,
280            );
281        }
282
283        #[test]
284        fn test_aes256_ecb_3b() {
285            do_aes256_ecb(
286                &BlockType::from_array(hex!("30c81c46a35ce411e5fbc1191a0a52ef")),
287                &BlockType::from_array(hex!("139a83bda68fe6438220eaa3aa17e849")),
288                &KEY_1,
289                &KEY_0,
290            );
291        }
292
293        #[test]
294        fn test_aes256_ecb_4a() {
295            do_aes256_ecb(
296                &BlockType::from_array(hex!("f69f2445df4f9b17ad2b417be66c3710")),
297                &BlockType::from_array(hex!("23304b7a39f9f3ff067d8d8f9e24ecc7")),
298                &KEY_0,
299                &KEY_1,
300            );
301        }
302
303        #[test]
304        fn test_aes256_ecb_4b() {
305            do_aes256_ecb(
306                &BlockType::from_array(hex!("f69f2445df4f9b17ad2b417be66c3710")),
307                &BlockType::from_array(hex!("5b3fbfb893c88a7252f14f5d9a4a0054")),
308                &KEY_1,
309                &KEY_0,
310            );
311        }
312    }
313
314    mod xor_arrays {
315        use super::super::*;
316        use hex_literal::hex;
317
318        fn do_xor_arrays(input0: &BlockType, input1: &BlockType) {
319            let mut output_xor = input0.clone();
320            let mut output_ptr = input0.clone();
321            let mut output_ref = input0.clone();
322
323            output_xor.xor_with(input1);
324            output_ptr.xor_with_u8_ptr(input1.as_array().as_ptr());
325
326            for (dst, src) in output_ref.as_mut_array().iter_mut().zip(input1.as_array().iter()) {
327                *dst ^= src;
328            }
329
330            assert_eq!(&output_xor, &output_ref);
331            assert_eq!(&output_ptr, &output_ref);
332        }
333
334        #[test]
335        fn test_xor_arrays_1() {
336            do_xor_arrays(&BlockType::from_array(hex!("75863721fe83cf3d6f0500df428126ae")), &BlockType::from_array(hex!("cc39d4653cce685b8de3398eccfe9c48")));
337        }
338
339        #[test]
340        fn test_xor_arrays_2() {
341            do_xor_arrays(&BlockType::from_array(hex!("2381643e0214c832064a0e8fd074055d")), &BlockType::from_array(hex!("ab290a75923b190ed775841e4cca9e25")));
342        }
343
344        #[test]
345        fn test_xor_arrays_3() {
346            do_xor_arrays(&BlockType::from_array(hex!("62f828dce94781e2d31d9ffa786df6e4")), &BlockType::from_array(hex!("ca6bb37d92d3f8a997d561d9e9d7030e")));
347        }
348
349        #[test]
350        fn test_xor_arrays_4() {
351            do_xor_arrays(&BlockType::from_array(hex!("710180b32b5a982ee21d8e76d287e509")), &BlockType::from_array(hex!("389b742402576214410c0633722c593a")));
352        }
353    }
354
355    mod concat_keys {
356        use super::super::*;
357        use hex_literal::hex;
358
359        fn do_concat_keys(input0: &BlockType, input1: &BlockType) {
360            let mut buffer = KeyType::uninit();
361            let key_data = buffer.concat(input0, input1).as_slice();
362            assert_eq!(input0, &BlockType::from_array(key_data[..BLOCK_SIZE].try_into().unwrap()));
363            assert_eq!(input1, &BlockType::from_array(key_data[BLOCK_SIZE..].try_into().unwrap()));
364        }
365
366        #[test]
367        fn test_concat_keys_1a() {
368            do_concat_keys(&BlockType::from_array(hex!("000102030405060708090A0B0C0D0E0F")), &BlockType::from_array(hex!("F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF")));
369        }
370
371        #[test]
372        fn test_concat_keys_1b() {
373            do_concat_keys(&BlockType::from_array(hex!("F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF")), &BlockType::from_array(hex!("000102030405060708090A0B0C0D0E0F")));
374        }
375
376        #[test]
377        fn test_concat_keys_2a() {
378            do_concat_keys(&BlockType::from_array(hex!("00102030405060708090A0B0C0D0E0F0")), &BlockType::from_array(hex!("0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFFF")));
379        }
380
381        #[test]
382        fn test_concat_keys_2b() {
383            do_concat_keys(&BlockType::from_array(hex!("0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFFF")), &BlockType::from_array(hex!("00102030405060708090A0B0C0D0E0F0")));
384        }
385    }
386}