use crate::gimli::{State, gimli};
use crate::{KEY_SIZE, NONCE_SIZE, RATE, STATE_LAST_BYTE, TAG_SIZE};
use subtle::ConstantTimeEq;
pub type Tag = [u8; TAG_SIZE];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AuthenticationFailed;
fn initialize(key: &[u8; KEY_SIZE], nonce: &[u8; NONCE_SIZE]) -> State {
let mut state = State::new();
let state_bytes = state.as_bytes_mut();
state_bytes[..16].copy_from_slice(nonce);
state_bytes[16..].copy_from_slice(key);
gimli(&mut state);
state
}
fn process_aad(state: &mut State, associated_data: &[u8]) {
let mut iter = associated_data.chunks_exact(RATE);
for chunk in iter.by_ref() {
let state_bytes = state.as_bytes_mut();
for i in 0..RATE {
state_bytes[i] ^= chunk[i];
}
gimli(state);
}
let remainder = iter.remainder();
let state_bytes = state.as_bytes_mut();
for i in 0..remainder.len() {
state_bytes[i] ^= remainder[i];
}
state_bytes[remainder.len()] ^= 1;
state_bytes[STATE_LAST_BYTE] ^= 1;
gimli(state);
}
#[must_use]
pub fn encrypt_in_place(
key: &[u8; KEY_SIZE],
nonce: &[u8; NONCE_SIZE],
associated_data: &[u8],
buffer: &mut [u8],
) -> Tag {
let mut state = initialize(key, nonce);
process_aad(&mut state, associated_data);
let mut iter = buffer.chunks_exact_mut(RATE);
for chunk in &mut iter {
let state_bytes = state.as_bytes_mut();
for i in 0..RATE {
state_bytes[i] ^= chunk[i];
}
chunk.copy_from_slice(&state_bytes[..16]);
gimli(&mut state);
}
let remainder = iter.into_remainder();
let state_bytes = state.as_bytes_mut();
for i in 0..remainder.len() {
state_bytes[i] ^= remainder[i];
}
remainder.copy_from_slice(&state_bytes[..remainder.len()]);
state_bytes[remainder.len()] ^= 1;
state_bytes[STATE_LAST_BYTE] ^= 1;
gimli(&mut state);
let mut tag = [0u8; TAG_SIZE];
tag.copy_from_slice(&state.as_bytes()[..TAG_SIZE]);
tag
}
pub fn decrypt_in_place(
key: &[u8; KEY_SIZE],
nonce: &[u8; NONCE_SIZE],
associated_data: &[u8],
buffer: &mut [u8],
tag: &Tag,
) -> Result<(), AuthenticationFailed> {
let mut state = initialize(key, nonce);
process_aad(&mut state, associated_data);
let mut iter = buffer.chunks_exact_mut(RATE);
for chunk in &mut iter {
let state_bytes = state.as_bytes_mut();
for i in 0..RATE {
let ciphertext_byte = chunk[i];
chunk[i] = state_bytes[i] ^ ciphertext_byte;
state_bytes[i] = ciphertext_byte;
}
gimli(&mut state);
}
let state_bytes = state.as_bytes_mut();
let remainder = iter.into_remainder();
for i in 0..remainder.len() {
let ciphertext_byte = remainder[i];
remainder[i] = state_bytes[i] ^ ciphertext_byte;
state_bytes[i] = ciphertext_byte;
}
state_bytes[remainder.len()] ^= 1;
state_bytes[STATE_LAST_BYTE] ^= 1;
gimli(&mut state);
let computed_tag = &state.as_bytes()[..TAG_SIZE];
if computed_tag.ct_eq(tag).into() {
Ok(())
} else {
Err(AuthenticationFailed)
}
}
#[cfg(test)]
mod tests;