use crate::{
error::{Error, Result},
keepers::SecretKeeper,
AuthTag, WrappedKey,
};
use async_trait::async_trait;
use bytes::Bytes;
use std::fmt;
use strum_macros::{Display, EnumString};
use tokio::fs::File;
#[async_trait]
pub trait CompressingCipher: Cipher {
async fn seal_compressed(
&self,
src: &[u8],
aad: Option<&[u8]>,
) -> Result<(Bytes, AuthTag), Error>;
async fn open_compressed(
&self,
src: &mut [u8],
tag: &[u8],
size_hint: Option<u64>,
aad: Option<&[u8]>,
) -> Result<Bytes, Error>;
}
#[async_trait]
pub trait Cipher: fmt::Debug + Sync + Send {
fn supports_aad(&self) -> bool;
async fn seal(&self, plaintext: &[u8], aad: Option<&[u8]>) -> Result<Bytes, Error>;
async fn open(&self, ciphertext: &[u8], aad: Option<&[u8]>) -> Result<Bytes, Error>;
async fn seal_file(
&self,
file_path: &str,
aad: Option<&[u8]>,
) -> Result<(Bytes, AuthTag, u64), Error>;
async fn seal_write(
&self,
data: &mut [u8],
file: &mut File,
aad: Option<&[u8]>,
) -> Result<(AuthTag, u64), Error>;
async fn seal_detached(&self, src: &mut [u8], aad: Option<&[u8]>) -> Result<AuthTag, Error>;
async fn open_read(
&self,
file: &mut File,
len: u64,
size_hint: Option<u64>,
tag: &[u8],
aad: Option<&[u8]>,
) -> Result<Bytes, Error>;
async fn open_detached(
&self,
buf: &mut [u8],
tag: &[u8],
aad: Option<&[u8]>,
) -> Result<(), Error>;
async fn export(
&self,
uri: &str,
nonce: &[u8],
keeper: &Box<dyn SecretKeeper>,
) -> Result<WrappedKey, Error>;
fn nonce_len(&self) -> usize;
fn key_len(&self) -> usize;
fn tag_len(&self) -> usize;
fn get_nonce(&self) -> &[u8];
}
#[async_trait]
pub trait Import: Cipher + Sized {
async fn import(
nonce: &[u8],
keeper: &Box<dyn SecretKeeper>,
wrapped: &WrappedKey,
) -> Result<Self, Error>;
}
#[derive(Clone, Debug, Display, PartialEq, EnumString)]
pub enum CipherKind {
#[strum(
to_string = "AesGcm256",
serialize = "aes",
serialize = "aesgcm",
serialize = "aesgcm256"
)]
AesGcm256,
#[strum(
to_string = "XChaCha20Poly1305",
serialize = "xchacha20",
serialize = "xchacha20poly1305"
)]
XChaCha20Poly1305,
#[strum(
to_string = "LZ4XChaCha20Poly1305",
serialize = "lz4",
serialize = "lz4xchacha20",
serialize = "lz4xchacha20poly1305"
)]
LZ4XChaCha20Poly1305,
}
const KIND_AESGCM256: u8 = 1;
const KIND_XCHACHA20POLY1305: u8 = 2;
const KIND_LZ4XCHACHA20POLY1305: u8 = 3;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
impl Serialize for CipherKind {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_u8(match *self {
CipherKind::AesGcm256 => KIND_AESGCM256,
CipherKind::XChaCha20Poly1305 => KIND_XCHACHA20POLY1305,
CipherKind::LZ4XChaCha20Poly1305 => KIND_LZ4XCHACHA20POLY1305,
})
}
}
impl<'de> Deserialize<'de> for CipherKind {
fn deserialize<D>(deserializer: D) -> Result<CipherKind, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_u8(CipherKindVisitor)
}
}
struct CipherKindVisitor;
impl<'de> serde::de::Visitor<'de> for CipherKindVisitor {
type Value = CipherKind;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("u8 from 1 to 3")
}
fn visit_u8<E>(self, n: u8) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
match n {
KIND_AESGCM256 => Ok(CipherKind::AesGcm256),
KIND_XCHACHA20POLY1305 => Ok(CipherKind::XChaCha20Poly1305),
KIND_LZ4XCHACHA20POLY1305 => Ok(CipherKind::LZ4XChaCha20Poly1305),
_ => Err(E::custom(format!("u8 is not a valid CipherKind id: {}", n))),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::{Error, Result};
use bincode;
fn ser_de(k: CipherKind, v: u8) -> Result<(), Error> {
let buf = bincode::serialize(&k)?;
assert_eq!(buf.len(), 1, "serialize {}", k.to_string());
assert_eq!(buf[0], v, "serialize {} to value", k.to_string());
let y: CipherKind = bincode::deserialize(&buf)?;
assert_eq!(y, k, "deserialize {}", k.to_string());
Ok(())
}
#[test]
fn cipher_kind_ser_de() -> Result<(), Error> {
ser_de(CipherKind::AesGcm256, KIND_AESGCM256)?;
ser_de(CipherKind::XChaCha20Poly1305, KIND_XCHACHA20POLY1305)?;
ser_de(CipherKind::LZ4XChaCha20Poly1305, KIND_LZ4XCHACHA20POLY1305)?;
Ok(())
}
}