#![no_std]
extern crate alloc;
pub use aead;
use aead::generic_array::{
typenum::{U0, U16, U24, U32},
GenericArray,
};
use aead::{Aead, Error, NewAead, Payload};
use alloc::vec::Vec;
use poly1305::{universal_hash::UniversalHash, Poly1305};
use salsa20::stream_cipher::{NewStreamCipher, SyncStreamCipher, SyncStreamCipherSeek};
use salsa20::XSalsa20;
use zeroize::{Zeroize, Zeroizing};
pub type Tag = GenericArray<u8, U16>;
#[derive(Clone)]
pub struct XSalsa20Poly1305 {
key: GenericArray<u8, U32>,
}
impl NewAead for XSalsa20Poly1305 {
type KeySize = U32;
fn new(key: GenericArray<u8, U32>) -> Self {
XSalsa20Poly1305 { key }
}
}
impl Aead for XSalsa20Poly1305 {
type NonceSize = U24;
type TagSize = U16;
type CiphertextOverhead = U0;
fn encrypt<'msg, 'aad>(
&self,
nonce: &GenericArray<u8, Self::NonceSize>,
plaintext: impl Into<Payload<'msg, 'aad>>,
) -> Result<Vec<u8>, Error> {
let payload = plaintext.into();
let mut buffer = Vec::with_capacity(payload.msg.len() + poly1305::BLOCK_SIZE);
buffer.extend_from_slice(&[0u8; poly1305::BLOCK_SIZE]);
buffer.extend_from_slice(payload.msg);
let tag = self.encrypt_in_place_detached(
nonce,
payload.aad,
&mut buffer[poly1305::BLOCK_SIZE..],
)?;
buffer[..poly1305::BLOCK_SIZE].copy_from_slice(tag.as_slice());
Ok(buffer)
}
fn decrypt<'msg, 'aad>(
&self,
nonce: &GenericArray<u8, Self::NonceSize>,
ciphertext: impl Into<Payload<'msg, 'aad>>,
) -> Result<Vec<u8>, Error> {
let payload = ciphertext.into();
if payload.msg.len() < poly1305::BLOCK_SIZE {
return Err(Error);
}
let mut buffer = Vec::from(&payload.msg[poly1305::BLOCK_SIZE..]);
let tag = Tag::from_slice(&payload.msg[..poly1305::BLOCK_SIZE]);
self.decrypt_in_place_detached(nonce, payload.aad, &mut buffer, &tag)?;
Ok(buffer)
}
}
impl XSalsa20Poly1305 {
pub fn encrypt_in_place_detached(
&self,
nonce: &GenericArray<u8, <Self as Aead>::NonceSize>,
associated_data: &[u8],
buffer: &mut [u8],
) -> Result<Tag, Error> {
Cipher::new(XSalsa20::new(&self.key, nonce))
.encrypt_in_place_detached(associated_data, buffer)
}
pub fn decrypt_in_place_detached(
&self,
nonce: &GenericArray<u8, <Self as Aead>::NonceSize>,
associated_data: &[u8],
buffer: &mut [u8],
tag: &Tag,
) -> Result<(), Error> {
Cipher::new(XSalsa20::new(&self.key, nonce)).decrypt_in_place_detached(
associated_data,
buffer,
tag,
)
}
}
impl Drop for XSalsa20Poly1305 {
fn drop(&mut self) {
self.key.as_mut_slice().zeroize();
}
}
pub(crate) struct Cipher<C>
where
C: SyncStreamCipher + SyncStreamCipherSeek,
{
cipher: C,
mac: Poly1305,
}
impl<C> Cipher<C>
where
C: SyncStreamCipher + SyncStreamCipherSeek,
{
pub(crate) fn new(mut cipher: C) -> Self {
let mut mac_key = Zeroizing::new(poly1305::Key::default());
cipher.apply_keystream(&mut *mac_key);
let mac = Poly1305::new(GenericArray::from_slice(&*mac_key));
Self { cipher, mac }
}
pub(crate) fn encrypt_in_place_detached(
mut self,
associated_data: &[u8],
buffer: &mut [u8],
) -> Result<Tag, Error> {
if !associated_data.is_empty() {
return Err(Error);
}
self.cipher.apply_keystream(buffer);
self.mac.update(buffer);
Ok(self.mac.result().into_bytes())
}
pub(crate) fn decrypt_in_place_detached(
mut self,
associated_data: &[u8],
buffer: &mut [u8],
tag: &Tag,
) -> Result<(), Error> {
if !associated_data.is_empty() {
return Err(Error);
}
self.mac.update(buffer);
if self.mac.verify(tag).is_ok() {
self.cipher.apply_keystream(buffer);
Ok(())
} else {
Err(Error)
}
}
}