conundrum 0.1.0

Hard-to-misuse crypto primitives with purpose scoping.
Documentation
use chacha20poly1305::aead::{Aead, NewAead};
use litl::{serde::DeserializeError, Litl, impl_debug_as_litl};
use rand07::{rngs::OsRng, RngCore};
use serde::{Deserialize, Serialize};
use serde_derive::{Deserialize, Serialize};
use std::{fmt::Debug, marker::PhantomData, ops::Deref};
use thiserror::Error;
use zeroize::Zeroizing;

#[derive(Serialize, Deserialize)]
#[serde(rename = "Conundrum/EncrKey:Chacha20Poly1305")]
pub struct RawEncrKeyChacha20Poly1305(chacha20poly1305::Key);

use crate::serde_bytes_array;

#[derive(Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct EncrKeyID(
    #[serde(with = "serde_bytes_array")]
    [u8; 16]
);

impl_debug_as_litl!(EncrKeyID);

#[derive(Serialize, Deserialize)]
pub struct RawEncrKey {
    pub id: EncrKeyID,
    key: RawEncrKeyChacha20Poly1305,
}

impl RawEncrKey {
    pub fn new_random() -> Self {
        let mut key = [0u8; 32];
        OsRng {}.fill_bytes(&mut key);
        let mut id = [0u8; 16];
        OsRng {}.fill_bytes(&mut id);
        RawEncrKey {
            id: EncrKeyID(id),
            key: RawEncrKeyChacha20Poly1305(*chacha20poly1305::Key::from_slice(&key)),
        }
    }
}

impl Deref for RawEncrKey {
    type Target = chacha20poly1305::Key;

    fn deref(&self) -> &Self::Target {
        &self.key.0
    }
}

impl Debug for RawEncrKey {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str("(Secret RawEncrKey)")
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct EncrKey<S> {
    key: RawEncrKey,
    purpose: S,
}

impl<S: Default> EncrKey<S> {
    pub fn new_random() -> Self {
        EncrKey {
            key: RawEncrKey::new_random(),
            purpose: S::default(),
        }
    }
}

impl<S> Deref for EncrKey<S> {
    type Target = RawEncrKey;
    fn deref(&self) -> &Self::Target {
        &self.key
    }
}

impl<S: Clone> EncrKey<S> {
    pub fn encrypt<T: Serialize>(&self, value: &T) -> Encrypted<T, S> {
        let cipher = chacha20poly1305::ChaCha20Poly1305::new(self);
        let mut nonce_bytes = [0; 12];
        OsRng {}.fill_bytes(&mut nonce_bytes);
        let nonce = chacha20poly1305::Nonce::from(nonce_bytes);

        Encrypted {
            encrypted: RawEncrypted {
                encrypted: RawEncryptedChacha20Poly1305(RawEncryptedChacha20Poly1305Inner {
                    ciphertext: cipher
                        .encrypt(&nonce, Zeroizing::new(Litl::write_from(value)).as_slice())
                        .expect("Failed to encrypt"),
                    nonce: nonce_bytes,
                }),
                for_key: self.id,
            },
            purpose: self.purpose.clone(),
            _marker: PhantomData,
        }
    }

    pub fn decrypt<'de, T: Deserialize<'de>>(
        self,
        encrypted: &Encrypted<T, S>,
    ) -> Result<T, DecryptionError> {
        let cipher = chacha20poly1305::ChaCha20Poly1305::new(&self);
        let plaintext = cipher
            .decrypt(
                chacha20poly1305::Nonce::from_slice(&encrypted.encrypted.encrypted.0.nonce),
                encrypted.encrypted.encrypted.0.ciphertext.as_slice(),
            )
            .map(Zeroizing::new)
            .map_err(DecryptionError::DecryptionError)?;
        Litl::read_as::<T>(&plaintext).map_err(DecryptionError::DeserializeError)
    }
}

#[derive(Error, Debug)]
pub enum DecryptionError {
    #[error("Decryption error.")]
    DecryptionError(chacha20poly1305::aead::Error),
    #[error("Error converting from decrypted bytes.")]
    DeserializeError(DeserializeError),
}

#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct RawEncryptedChacha20Poly1305Inner {
    #[serde(with = "serde_bytes")]
    pub ciphertext: Vec<u8>,
    pub nonce: [u8; 12],
}

#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename = "Conundrum/Encrypted:Chacha20Poly1305")]
pub struct RawEncryptedChacha20Poly1305(RawEncryptedChacha20Poly1305Inner);

#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct RawEncrypted {
    for_key: EncrKeyID,
    encrypted: RawEncryptedChacha20Poly1305,
}

#[derive(Serialize, Deserialize)]
pub struct Encrypted<T, S> {
    encrypted: RawEncrypted,
    purpose: S,
    _marker: PhantomData<T>,
}

impl<T, S: Serialize> Debug for Encrypted<T, S> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        Litl::from_se(self).fmt(f)
    }
}

impl<T, S: Clone> Clone for Encrypted<T, S> {
    fn clone(&self) -> Self {
        Encrypted {
            encrypted: self.encrypted.clone(),
            purpose: self.purpose.clone(),
            _marker: PhantomData,
        }
    }
}

impl <T, S: PartialEq> PartialEq for Encrypted<T, S> {
    fn eq(&self, other: &Self) -> bool {
        self.encrypted == other.encrypted && self.purpose == other.purpose
    }
}

impl <T, S: Eq> Eq for Encrypted<T, S> {}