gimli_crypto/
rustcrypto_aead.rs

1//! # RustCrypto AEAD trait implementation
2//!
3//! This module provides implementations of the RustCrypto `aead` traits for Gimli AEAD.
4
5use crate::{KEY_SIZE, NONCE_SIZE, TAG_SIZE, decrypt_in_place, encrypt_in_place};
6use aead::generic_array::GenericArray;
7use aead::{
8    AeadCore, AeadInPlace, Error, KeyInit, KeySizeUser,
9    consts::{U16, U32},
10};
11use zeroize::{Zeroize, ZeroizeOnDrop};
12
13/// `aead/gimli24v1` cipher implementing RustCrypto traits.
14#[derive(Zeroize, ZeroizeOnDrop)]
15pub struct GimliAead {
16    key: [u8; KEY_SIZE],
17}
18
19impl KeySizeUser for GimliAead {
20    type KeySize = U32;
21}
22
23impl KeyInit for GimliAead {
24    fn new(key: &GenericArray<u8, Self::KeySize>) -> Self {
25        let mut s = Self {
26            key: [0u8; KEY_SIZE],
27        };
28        s.key.copy_from_slice(key.as_slice());
29        s
30    }
31}
32
33impl AeadCore for GimliAead {
34    type NonceSize = U16;
35    type TagSize = U16;
36    type CiphertextOverhead = aead::consts::U0;
37}
38
39/// Helper to convert between `GenericArray` and built-in array types. v0.14 does not make this
40/// conversion easy in any sense.
41#[inline(always)]
42const fn ga_nonce_to_array(
43    nonce: &GenericArray<u8, <GimliAead as AeadCore>::NonceSize>,
44) -> &[u8; NONCE_SIZE] {
45    // SAFETY: `GenericArray<T, N>` is `#[repr(transparent)]` over `[T; N]`,
46    // guaranteeing identical layout. Transmuting `&GenericArray<u8, N>` to
47    // `&[u8; N]` preserves the reference lifetime and validity.
48    //
49    // Preconditions verified at compile-time:
50    // - Size equality: `mem::transmute` will fail to compile if
51    //   `size_of::<GenericArray<T, N>>() != size_of::<[T; N]>()`
52    // - Alignment: Both types have alignment of `T`
53    unsafe { core::mem::transmute(nonce) }
54}
55
56/// Helper to convert between `GenericArray` and built-in array types. v0.14 does not make this
57/// conversion easy in any sense.
58#[inline(always)]
59const fn ga_tag_to_array(
60    tag: &GenericArray<u8, <GimliAead as AeadCore>::TagSize>,
61) -> &[u8; TAG_SIZE] {
62    // SAFETY: `GenericArray<T, N>` is `#[repr(transparent)]` over `[T; N]`,
63    // guaranteeing identical layout. Transmuting `&GenericArray<u8, N>` to
64    // `&[u8; N]` preserves the reference lifetime and validity.
65    //
66    // Preconditions verified at compile-time:
67    // - Size equality: `mem::transmute` will fail to compile if
68    //   `size_of::<GenericArray<T, N>>() != size_of::<[T; N]>()`
69    // - Alignment: Both types have alignment of `T`
70    unsafe { core::mem::transmute(tag) }
71}
72
73/// Helper to convert between `GenericArray` and built-in array types. v0.14 does not make this
74/// conversion easy in any sense.
75#[inline(always)]
76const fn tag_array_to_ga(
77    tag: [u8; TAG_SIZE],
78) -> GenericArray<u8, <GimliAead as AeadCore>::TagSize> {
79    // SAFETY: `GenericArray<T, N>` is `#[repr(transparent)]` over `[T; N]`,
80    // guaranteeing identical layout. Transmuting owned `[u8; N]` to owned
81    // `GenericArray<u8, N>` transfers ownership without copying and preserves
82    // all bit patterns.
83    //
84    // Preconditions verified at compile-time:
85    // - Size equality: `mem::transmute` will fail to compile if
86    //   `size_of::<GenericArray<T, N>>() != size_of::<[T; N]>()`
87    // - Alignment: Both types have alignment of `T`
88    unsafe { core::mem::transmute(tag) }
89}
90
91impl AeadInPlace for GimliAead {
92    #[inline]
93    fn encrypt_in_place_detached(
94        &self,
95        nonce: &GenericArray<u8, Self::NonceSize>,
96        associated_data: &[u8],
97        buffer: &mut [u8],
98    ) -> Result<GenericArray<u8, Self::TagSize>, Error> {
99        let tag = encrypt_in_place(&self.key, ga_nonce_to_array(nonce), associated_data, buffer);
100
101        Ok(tag_array_to_ga(tag))
102    }
103
104    #[inline]
105    fn decrypt_in_place_detached(
106        &self,
107        nonce: &GenericArray<u8, Self::NonceSize>,
108        associated_data: &[u8],
109        buffer: &mut [u8],
110        tag: &GenericArray<u8, Self::TagSize>,
111    ) -> Result<(), Error> {
112        decrypt_in_place(
113            &self.key,
114            ga_nonce_to_array(nonce),
115            associated_data,
116            buffer,
117            ga_tag_to_array(tag),
118        )
119        .map_err(|_| Error)
120    }
121}
122
123#[cfg(test)]
124mod tests {
125    use super::*;
126    use aead::AeadInPlace;
127
128    #[test]
129    fn aead_roundtrip() {
130        let key = GenericArray::from([1u8; 32]);
131        let cipher = GimliAead::new(&key);
132
133        let nonce = GenericArray::from([2u8; 16]);
134        let plaintext = *b"Hello, RustCrypto AEAD!";
135        let aad = b"associated data";
136
137        let mut ciphertext = plaintext;
138        let tag = cipher
139            .encrypt_in_place_detached(&nonce, aad, &mut ciphertext)
140            .expect("encryption failed");
141
142        cipher
143            .decrypt_in_place_detached(&nonce, aad, &mut ciphertext, &tag)
144            .expect("decryption failed");
145
146        assert_eq!(&ciphertext, b"Hello, RustCrypto AEAD!");
147    }
148
149    #[test]
150    fn aead_in_place() {
151        let key = GenericArray::from([42u8; 32]);
152        let cipher = GimliAead::new(&key);
153
154        let nonce = GenericArray::from([99u8; 16]);
155        let aad = b"metadata";
156
157        let mut buffer = *b"In-place test!  ";
158        let original = buffer;
159
160        let tag = cipher
161            .encrypt_in_place_detached(&nonce, aad, &mut buffer)
162            .expect("encryption failed");
163
164        assert_ne!(&buffer, &original);
165
166        cipher
167            .decrypt_in_place_detached(&nonce, aad, &mut buffer, &tag)
168            .expect("decryption failed");
169
170        assert_eq!(&buffer, &original);
171    }
172
173    #[test]
174    fn aead_wrong_tag() {
175        let key = GenericArray::from([1u8; 32]);
176        let cipher = GimliAead::new(&key);
177
178        let nonce = GenericArray::from([2u8; 16]);
179        let mut buffer = *b"Test message";
180
181        let mut tag = cipher
182            .encrypt_in_place_detached(&nonce, b"", &mut buffer)
183            .expect("encryption failed");
184
185        // Corrupt the tag
186        tag[0] ^= 1;
187
188        let result = cipher.decrypt_in_place_detached(&nonce, b"", &mut buffer, &tag);
189        assert!(result.is_err());
190    }
191}