use crate::crypto_aead::chacha20poly1305_ietf::{Key, Nonce, ABYTES};
use crate::{Result, SodiumError};
use std::convert::TryInto;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct State {
key: Key,
}
impl State {
pub fn from_key(key: impl AsRef<Key>) -> Result<Self> {
Ok(Self {
key: key.as_ref().clone(),
})
}
pub fn key(&self) -> &Key {
&self.key
}
}
pub fn encrypt_afternm(
message: &[u8],
additional_data: Option<&[u8]>,
nonce: impl AsRef<Nonce>,
state: &State,
) -> Result<Vec<u8>> {
let nonce = nonce.as_ref();
let mut ciphertext = vec![0u8; message.len() + ABYTES];
let mut ciphertext_len = 0u64;
let ad = additional_data.unwrap_or(&[]);
let result = unsafe {
libsodium_sys::crypto_aead_chacha20poly1305_ietf_encrypt(
ciphertext.as_mut_ptr(),
&mut ciphertext_len,
message.as_ptr(),
message
.len()
.try_into()
.map_err(|_| SodiumError::InvalidInput("message too large".into()))?,
ad.as_ptr(),
ad.len()
.try_into()
.map_err(|_| SodiumError::InvalidInput("additional data too large".into()))?,
std::ptr::null(),
nonce.as_bytes().as_ptr(),
state.key.as_bytes().as_ptr(),
)
};
if result != 0 {
return Err(SodiumError::EncryptionError(
"ChaCha20-Poly1305-IETF encryption failed".into(),
));
}
ciphertext.truncate(ciphertext_len as usize);
Ok(ciphertext)
}
pub fn decrypt_afternm(
ciphertext: &[u8],
additional_data: Option<&[u8]>,
nonce: impl AsRef<Nonce>,
state: &State,
) -> Result<Vec<u8>> {
if ciphertext.len() < ABYTES {
return Err(SodiumError::InvalidInput(format!(
"ciphertext must be at least {ABYTES} bytes"
)));
}
let nonce = nonce.as_ref();
let mut message = vec![0u8; ciphertext.len() - ABYTES];
let mut message_len = 0u64;
let ad = additional_data.unwrap_or(&[]);
let result = unsafe {
libsodium_sys::crypto_aead_chacha20poly1305_ietf_decrypt(
message.as_mut_ptr(),
&mut message_len,
std::ptr::null_mut(),
ciphertext.as_ptr(),
ciphertext
.len()
.try_into()
.map_err(|_| SodiumError::InvalidInput("ciphertext too large".into()))?,
ad.as_ptr(),
ad.len()
.try_into()
.map_err(|_| SodiumError::InvalidInput("additional data too large".into()))?,
nonce.as_bytes().as_ptr(),
state.key.as_bytes().as_ptr(),
)
};
if result != 0 {
return Err(SodiumError::DecryptionError(
"ChaCha20-Poly1305-IETF authentication failed".into(),
));
}
message.truncate(message_len as usize);
Ok(message)
}
pub fn encrypt_detached_afternm(
message: &[u8],
additional_data: Option<&[u8]>,
nonce: impl AsRef<Nonce>,
state: &State,
) -> Result<(Vec<u8>, Vec<u8>)> {
let nonce = nonce.as_ref();
let mut ciphertext = vec![0u8; message.len()];
let mut tag = vec![0u8; ABYTES];
let mut tag_len = 0u64;
let ad = additional_data.unwrap_or(&[]);
let result = unsafe {
libsodium_sys::crypto_aead_chacha20poly1305_ietf_encrypt_detached(
ciphertext.as_mut_ptr(),
tag.as_mut_ptr(),
&mut tag_len,
message.as_ptr(),
message
.len()
.try_into()
.map_err(|_| SodiumError::InvalidInput("message too large".into()))?,
ad.as_ptr(),
ad.len()
.try_into()
.map_err(|_| SodiumError::InvalidInput("additional data too large".into()))?,
std::ptr::null(),
nonce.as_bytes().as_ptr(),
state.key.as_bytes().as_ptr(),
)
};
if result != 0 {
return Err(SodiumError::EncryptionError(
"ChaCha20-Poly1305-IETF 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: impl AsRef<Nonce>,
state: &State,
) -> Result<Vec<u8>> {
if tag.len() != ABYTES {
return Err(SodiumError::InvalidInput(format!(
"tag must be exactly {ABYTES} bytes"
)));
}
let nonce = nonce.as_ref();
let mut message = vec![0u8; ciphertext.len()];
let ad = additional_data.unwrap_or(&[]);
let result = unsafe {
libsodium_sys::crypto_aead_chacha20poly1305_ietf_decrypt_detached(
message.as_mut_ptr(),
std::ptr::null_mut(),
ciphertext.as_ptr(),
ciphertext
.len()
.try_into()
.map_err(|_| SodiumError::InvalidInput("ciphertext too large".into()))?,
tag.as_ptr(),
ad.as_ptr(),
ad.len()
.try_into()
.map_err(|_| SodiumError::InvalidInput("additional data too large".into()))?,
nonce.as_bytes().as_ptr(),
state.key.as_bytes().as_ptr(),
)
};
if result != 0 {
return Err(SodiumError::DecryptionError(
"ChaCha20-Poly1305-IETF authentication failed".into(),
));
}
Ok(message)
}