gimli_crypto/
aead_impl.rs1use crate::gimli::{State, gimli};
31use crate::{KEY_SIZE, NONCE_SIZE, RATE, STATE_LAST_BYTE, TAG_SIZE};
32use subtle::ConstantTimeEq;
33
34pub type Tag = [u8; TAG_SIZE];
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub struct AuthenticationFailed;
40
41fn initialize(key: &[u8; KEY_SIZE], nonce: &[u8; NONCE_SIZE]) -> State {
43 let mut state = State::new();
44 let state_bytes = state.as_bytes_mut();
45
46 state_bytes[..16].copy_from_slice(nonce);
48
49 state_bytes[16..].copy_from_slice(key);
51
52 gimli(&mut state);
53
54 state
55}
56
57fn process_aad(state: &mut State, associated_data: &[u8]) {
59 let mut iter = associated_data.chunks_exact(RATE);
60
61 for chunk in iter.by_ref() {
63 let state_bytes = state.as_bytes_mut();
64 for i in 0..RATE {
65 state_bytes[i] ^= chunk[i];
66 }
67 gimli(state);
68 }
69
70 let remainder = iter.remainder();
72 let state_bytes = state.as_bytes_mut();
73 for i in 0..remainder.len() {
74 state_bytes[i] ^= remainder[i];
75 }
76
77 state_bytes[remainder.len()] ^= 1;
78 state_bytes[STATE_LAST_BYTE] ^= 1;
79
80 gimli(state);
81}
82
83#[must_use]
88pub fn encrypt_in_place(
89 key: &[u8; KEY_SIZE],
90 nonce: &[u8; NONCE_SIZE],
91 associated_data: &[u8],
92 buffer: &mut [u8],
93) -> Tag {
94 let mut state = initialize(key, nonce);
95
96 process_aad(&mut state, associated_data);
98
99 let mut iter = buffer.chunks_exact_mut(RATE);
101
102 for chunk in &mut iter {
104 let state_bytes = state.as_bytes_mut();
105
106 for i in 0..RATE {
107 state_bytes[i] ^= chunk[i];
108 }
109 chunk.copy_from_slice(&state_bytes[..16]);
110
111 gimli(&mut state);
112 }
113
114 let remainder = iter.into_remainder();
116 let state_bytes = state.as_bytes_mut();
117 for i in 0..remainder.len() {
118 state_bytes[i] ^= remainder[i];
119 }
120 remainder.copy_from_slice(&state_bytes[..remainder.len()]);
121
122 state_bytes[remainder.len()] ^= 1;
123 state_bytes[STATE_LAST_BYTE] ^= 1;
124
125 gimli(&mut state);
126
127 let mut tag = [0u8; TAG_SIZE];
129 tag.copy_from_slice(&state.as_bytes()[..TAG_SIZE]);
130 tag
131}
132
133pub fn decrypt_in_place(
138 key: &[u8; KEY_SIZE],
139 nonce: &[u8; NONCE_SIZE],
140 associated_data: &[u8],
141 buffer: &mut [u8],
142 tag: &Tag,
143) -> Result<(), AuthenticationFailed> {
144 let mut state = initialize(key, nonce);
145
146 process_aad(&mut state, associated_data);
148
149 let mut iter = buffer.chunks_exact_mut(RATE);
151 for chunk in &mut iter {
152 let state_bytes = state.as_bytes_mut();
153
154 for i in 0..RATE {
155 let ciphertext_byte = chunk[i];
156 chunk[i] = state_bytes[i] ^ ciphertext_byte;
157 state_bytes[i] = ciphertext_byte;
158 }
159
160 gimli(&mut state);
161 }
162
163 let state_bytes = state.as_bytes_mut();
165 let remainder = iter.into_remainder();
166 for i in 0..remainder.len() {
167 let ciphertext_byte = remainder[i];
168 remainder[i] = state_bytes[i] ^ ciphertext_byte;
169 state_bytes[i] = ciphertext_byte;
170 }
171
172 state_bytes[remainder.len()] ^= 1;
173 state_bytes[STATE_LAST_BYTE] ^= 1;
174
175 gimli(&mut state);
176
177 let computed_tag = &state.as_bytes()[..TAG_SIZE];
179 if computed_tag.ct_eq(tag).into() {
180 Ok(())
181 } else {
182 Err(AuthenticationFailed)
183 }
184}
185
186#[cfg(test)]
187mod tests;