Skip to main content

redoubt_aead_aegis_x86/
lib.rs

1// Copyright (c) 2025-2026 Federico Hoerth <memparanoid@gmail.com>
2// SPDX-License-Identifier: GPL-3.0-only
3// See LICENSE in the repository root for full license text.
4
5//! x86_64 assembly AEGIS-128L AEAD implementation.
6//!
7//! ## License
8//!
9//! GPL-3.0-only
10
11#![no_std]
12#![warn(missing_docs)]
13
14#[cfg(all(target_arch = "x86_64", not(target_os = "windows")))]
15extern crate alloc;
16
17#[cfg(all(target_arch = "x86_64", not(target_os = "windows")))]
18use alloc::vec;
19#[cfg(all(target_arch = "x86_64", not(target_os = "windows")))]
20use alloc::vec::Vec;
21
22#[cfg(all(target_arch = "x86_64", not(target_os = "windows")))]
23use redoubt_aead_core::{AeadApi, AeadError, EntropyError};
24
25/// Key size: 128 bits (16 bytes).
26pub const KEY_SIZE: usize = 16;
27/// Nonce size: 128 bits (16 bytes).
28pub const NONCE_SIZE: usize = 16;
29/// Tag size: 128 bits (16 bytes).
30pub const TAG_SIZE: usize = 16;
31
32#[cfg(all(target_arch = "x86_64", not(target_os = "windows")))]
33unsafe extern "C" {
34    fn aegis128l_encrypt(
35        key: *const [u8; 16],
36        nonce: *const [u8; 16],
37        aad: *const u8,
38        aad_len: usize,
39        plaintext: *mut u8,
40        plaintext_len: usize,
41        tag: *mut [u8; 16],
42    );
43
44    fn aegis128l_decrypt(
45        key: *const [u8; 16],
46        nonce: *const [u8; 16],
47        aad: *const u8,
48        aad_len: usize,
49        ciphertext: *mut u8,
50        ciphertext_len: usize,
51        expected_tag: *const [u8; 16],
52        computed_tag: *mut [u8; 16],
53    );
54}
55
56/// x86_64 assembly AEGIS-128L backend.
57pub struct Aegis128LX86Backend;
58
59#[cfg(all(target_arch = "x86_64", not(target_os = "windows")))]
60impl AeadApi for Aegis128LX86Backend {
61    fn api_encrypt(
62        &mut self,
63        key: &[u8],
64        nonce: &[u8],
65        aad: &[u8],
66        data: &mut [u8],
67        tag: &mut [u8],
68    ) -> Result<(), AeadError> {
69        let key: &[u8; KEY_SIZE] = key.try_into().map_err(|_| AeadError::InvalidKeySize)?;
70        let nonce: &[u8; NONCE_SIZE] = nonce.try_into().map_err(|_| AeadError::InvalidNonceSize)?;
71        let tag: &mut [u8; TAG_SIZE] = tag.try_into().map_err(|_| AeadError::InvalidTagSize)?;
72
73        unsafe {
74            aegis128l_encrypt(
75                key,
76                nonce,
77                aad.as_ptr(),
78                aad.len(),
79                data.as_mut_ptr(),
80                data.len(),
81                tag,
82            );
83        }
84
85        Ok(())
86    }
87
88    fn api_decrypt(
89        &mut self,
90        key: &[u8],
91        nonce: &[u8],
92        aad: &[u8],
93        data: &mut [u8],
94        tag: &[u8],
95    ) -> Result<(), AeadError> {
96        let key: &[u8; KEY_SIZE] = key.try_into().map_err(|_| AeadError::InvalidKeySize)?;
97        let nonce: &[u8; NONCE_SIZE] = nonce.try_into().map_err(|_| AeadError::InvalidNonceSize)?;
98        let tag: &[u8; TAG_SIZE] = tag.try_into().map_err(|_| AeadError::InvalidTagSize)?;
99
100        let mut computed_tag = [0u8; TAG_SIZE];
101
102        unsafe {
103            aegis128l_decrypt(
104                key,
105                nonce,
106                aad.as_ptr(),
107                aad.len(),
108                data.as_mut_ptr(),
109                data.len(),
110                tag,
111                &mut computed_tag,
112            );
113        }
114
115        if redoubt_util::constant_time_eq(&computed_tag, tag) {
116            Ok(())
117        } else {
118            Err(AeadError::AuthenticationFailed)
119        }
120    }
121
122    fn api_generate_nonce(&mut self) -> Result<Vec<u8>, EntropyError> {
123        let mut nonce = vec![0u8; NONCE_SIZE];
124        redoubt_rand::fill_with_random_bytes(&mut nonce)?;
125
126        Ok(nonce)
127    }
128
129    fn api_key_size(&self) -> usize {
130        KEY_SIZE
131    }
132
133    fn api_nonce_size(&self) -> usize {
134        NONCE_SIZE
135    }
136
137    fn api_tag_size(&self) -> usize {
138        TAG_SIZE
139    }
140}
141
142#[cfg(test)]
143mod tests {
144    #[cfg(all(target_arch = "x86_64", not(target_os = "windows")))]
145    use super::Aegis128LX86Backend;
146
147    #[test]
148    fn instrumentation() {
149        let _ = super::Aegis128LX86Backend;
150    }
151
152    #[cfg(all(target_arch = "x86_64", not(target_os = "windows")))]
153    #[test]
154    fn test_aegis128l_wycheproof() {
155        redoubt_aead_aegis_wycheproof::run_aegis128l_wycheproof_tests(&mut Aegis128LX86Backend);
156    }
157
158    #[cfg(all(target_arch = "x86_64", not(target_os = "windows")))]
159    #[test]
160    fn test_aegis128l_roundtrip() {
161        redoubt_aead_aegis_wycheproof::run_aegis128l_roundtrip_tests(&mut Aegis128LX86Backend);
162    }
163
164    #[cfg(all(target_arch = "x86_64", not(target_os = "windows")))]
165    #[test]
166    fn test_aegis128l_flipped_tag() {
167        redoubt_aead_aegis_wycheproof::run_aegis128l_flipped_tag_tests(&mut Aegis128LX86Backend);
168    }
169
170    #[cfg(all(target_arch = "x86_64", not(target_os = "windows")))]
171    #[test]
172    fn test_aegis128l_invalid_size_encrypt() {
173        redoubt_aead_aegis_wycheproof::run_aegis128l_invalid_size_encrypt_tests(
174            &mut Aegis128LX86Backend,
175        );
176    }
177
178    #[cfg(all(target_arch = "x86_64", not(target_os = "windows")))]
179    #[test]
180    fn test_aegis128l_invalid_size_decrypt() {
181        redoubt_aead_aegis_wycheproof::run_aegis128l_invalid_size_decrypt_tests(
182            &mut Aegis128LX86Backend,
183        );
184    }
185
186    #[cfg(all(target_arch = "x86_64", not(target_os = "windows")))]
187    #[test]
188    fn test_aegis128l_generate_nonce() {
189        redoubt_aead_aegis_wycheproof::run_aegis128l_generate_nonce_test(&mut Aegis128LX86Backend);
190    }
191}