use crate::crypto_aead::aes256gcm::{Key, Nonce, ABYTES, NPUBBYTES};
use crate::{Result, SodiumError};
use std::convert::TryInto;
#[derive(Debug)]
pub struct State {
inner: Box<libsodium_sys::crypto_aead_aes256gcm_state>,
}
impl State {
pub fn from_key(key: &Key) -> Result<Self> {
if !is_available() {
return Err(SodiumError::UnsupportedOperation(
"AES-256-GCM is not supported on this CPU".into(),
));
}
let mut state = Box::new(unsafe { std::mem::zeroed() });
let result = unsafe {
libsodium_sys::crypto_aead_aes256gcm_beforenm(
&mut *state,
key.as_bytes().as_ptr(),
)
};
if result != 0 {
return Err(SodiumError::OperationError(
"precomputation failed".into(),
));
}
Ok(Self { inner: state })
}
}
pub fn encrypt_afternm(
message: &[u8],
additional_data: Option<&[u8]>,
nonce: &Nonce,
state: &State,
) -> Result<Vec<u8>> {
let ad = additional_data.unwrap_or(&[]);
let mut ciphertext = vec![0u8; message.len() + ABYTES];
let mut ciphertext_len = 0u64;
let result = unsafe {
libsodium_sys::crypto_aead_aes256gcm_encrypt_afternm(
ciphertext.as_mut_ptr(),
&mut ciphertext_len,
message.as_ptr(),
message.len().try_into().unwrap(),
ad.as_ptr(),
ad.len().try_into().unwrap(),
std::ptr::null(),
nonce.as_ref().as_ptr(),
&*state.inner,
)
};
if result != 0 {
return Err(SodiumError::OperationError("encryption failed".into()));
}
ciphertext.truncate(ciphertext_len as usize);
Ok(ciphertext)
}
pub fn decrypt_afternm(
ciphertext: &[u8],
additional_data: Option<&[u8]>,
nonce: &Nonce,
state: &State,
) -> Result<Vec<u8>> {
if ciphertext.len() < ABYTES {
return Err(SodiumError::InvalidInput("ciphertext too short".into()));
}
let ad = additional_data.unwrap_or(&[]);
let mut message = vec![0u8; ciphertext.len() - ABYTES];
let mut message_len = 0u64;
let result = unsafe {
libsodium_sys::crypto_aead_aes256gcm_decrypt_afternm(
message.as_mut_ptr(),
&mut message_len,
std::ptr::null_mut(),
ciphertext.as_ptr(),
ciphertext.len().try_into().unwrap(),
ad.as_ptr(),
ad.len().try_into().unwrap(),
nonce.as_ref().as_ptr(),
&*state.inner,
)
};
if result != 0 {
return Err(SodiumError::OperationError("decryption failed".into()));
}
message.truncate(message_len as usize);
Ok(message)
}
pub fn encrypt_detached_afternm(
message: &[u8],
additional_data: Option<&[u8]>,
nonce: &Nonce,
state: &State,
) -> Result<(Vec<u8>, Vec<u8>)> {
let ad = additional_data.unwrap_or(&[]);
let mut ciphertext = vec![0u8; message.len()];
let mut tag = vec![0u8; ABYTES];
let mut tag_len = 0u64;
let result = unsafe {
libsodium_sys::crypto_aead_aes256gcm_encrypt_detached_afternm(
ciphertext.as_mut_ptr(),
tag.as_mut_ptr(),
&mut tag_len,
message.as_ptr(),
message.len().try_into().unwrap(),
ad.as_ptr(),
ad.len().try_into().unwrap(),
std::ptr::null(),
nonce.as_ref().as_ptr(),
&*state.inner,
)
};
if result != 0 {
return Err(SodiumError::OperationError("encryption failed".into()));
}
tag.truncate(tag_len as usize);
Ok((ciphertext, tag))
}
pub fn decrypt_detached_afternm(
ciphertext: &[u8],
tag: &[u8],
additional_data: Option<&[u8]>,
nonce: &Nonce,
state: &State,
) -> Result<Vec<u8>> {
if tag.len() != ABYTES {
return Err(SodiumError::InvalidInput(format!(
"tag must be exactly {} bytes",
ABYTES
)));
}
let ad = additional_data.unwrap_or(&[]);
let mut message = vec![0u8; ciphertext.len()];
let result = unsafe {
libsodium_sys::crypto_aead_aes256gcm_decrypt_detached_afternm(
message.as_mut_ptr(),
std::ptr::null_mut(),
ciphertext.as_ptr(),
ciphertext.len().try_into().unwrap(),
tag.as_ptr(),
ad.as_ptr(),
ad.len().try_into().unwrap(),
nonce.as_ref().as_ptr(),
&*state.inner,
)
};
if result != 0 {
return Err(SodiumError::OperationError("decryption failed".into()));
}
Ok(message)
}