use crate::errors::PortalError::*;
use serde::{Deserialize, Serialize};
use std::convert::TryInto;
use std::error::Error;
use rand::Rng;
#[cfg(not(feature = "ring-backend"))]
use chacha20poly1305::{aead::AeadInPlace, aead::NewAead, ChaCha20Poly1305, Key, Nonce, Tag};
#[cfg(feature = "ring-backend")]
use ring::aead::{Aad, LessSafeKey, Nonce, Tag, UnboundKey, CHACHA20_POLY1305};
const NONCE_SIZE: usize = 12;
const TAG_SIZE: usize = 16;
#[derive(PartialEq, Eq, Debug)]
pub struct NonceSequence([u8; TAG_SIZE]);
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Default)]
pub struct EncryptedMessage {
pub nonce: [u8; NONCE_SIZE],
pub tag: [u8; TAG_SIZE],
pub len: usize,
}
#[cfg(not(feature = "ring-backend"))]
impl EncryptedMessage {
pub fn encrypt(
key: &[u8],
nseq: &mut NonceSequence,
data: &mut [u8],
) -> Result<Self, Box<dyn Error>> {
let mut state = Self {
nonce: nseq.next_unique()?,
..Default::default()
};
let nonce = Nonce::from_slice(&state.nonce);
let cha_key = Key::from_slice(key);
let cipher = ChaCha20Poly1305::new(cha_key);
state.len = data.len();
let tag = cipher
.encrypt_in_place_detached(nonce, b"", data)
.or(Err(EncryptError))?;
state.tag = tag.into();
Ok(state)
}
pub fn decrypt(&mut self, key: &[u8], data: &mut [u8]) -> Result<usize, Box<dyn Error>> {
let cha_key = Key::from_slice(key);
let cipher = ChaCha20Poly1305::new(cha_key);
let nonce = Nonce::from_slice(&self.nonce);
let tag = Tag::from_slice(&self.tag);
cipher
.decrypt_in_place_detached(nonce, b"", data, tag)
.or(Err(DecryptError))?;
Ok(data.len())
}
}
#[cfg(feature = "ring-backend")]
impl EncryptedMessage {
pub fn encrypt(
key: &[u8],
nseq: &mut NonceSequence,
data: &mut [u8],
) -> Result<Self, Box<dyn Error>> {
let mut state = Self::default();
let ring_key_chacha20 =
LessSafeKey::new(UnboundKey::new(&CHACHA20_POLY1305, key).or(Err(CryptoError))?);
state.nonce = nseq.next()?;
let ring_nonce = Nonce::assume_unique_for_key(state.nonce);
state.len = data.len();
let tag = ring_key_chacha20
.seal_in_place_separate_tag(ring_nonce, Aad::empty(), data)
.or(Err(EncryptError))?;
state.tag = tag.as_ref().try_into().or(Err(EncryptError))?;
Ok(state)
}
pub fn decrypt(&mut self, key: &[u8], data: &mut [u8]) -> Result<usize, Box<dyn Error>> {
let ring_key_chacha20 =
LessSafeKey::new(UnboundKey::new(&CHACHA20_POLY1305, key).or(Err(CryptoError))?);
let ring_tag: Tag = self.tag.try_into().or(Err(DecryptError))?;
let ring_nonce = Nonce::assume_unique_for_key(self.nonce);
ring_key_chacha20
.open_in_place_separate_tag(ring_nonce, Aad::empty(), ring_tag, data, 0..)
.or(Err(DecryptError))?;
Ok(data.len())
}
}
impl Default for NonceSequence {
fn default() -> Self {
Self::new()
}
}
impl NonceSequence {
pub fn new() -> Self {
let mut rng = rand::thread_rng();
Self(rng.gen::<[u8; 16]>())
}
pub fn next_unique(&mut self) -> Result<[u8; NONCE_SIZE], Box<dyn Error>> {
let old = self.0;
let new = u128::from_be_bytes(self.0).wrapping_shr(32);
self.0 = new.wrapping_add(1).wrapping_shl(32).to_be_bytes();
Ok(old[..NONCE_SIZE].try_into().or(Err(CryptoError))?)
}
}