navajo 0.0.4

cryptographic APIs
Documentation
use crate::{
    aead::nonce::SingleNonce,
    buffer::RcBuffer,
    error::{DecryptError, EncryptError},
    Buffer,
};

use super::Algorithm;

#[allow(clippy::large_enum_variant)]
pub(super) enum Backend {
    #[cfg(feature = "ring")]
    Ring(RingBackend),
    RustCrypto(RustCryptoCipher),
}
impl core::fmt::Debug for Backend {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.debug_tuple("Cipher").field(&self.algorithm()).finish()
    }
}
impl Backend {
    pub(super) fn algorithm(&self) -> Algorithm {
        match self {
            Self::RustCrypto(rc) => rc.algorithm(),
            #[cfg(feature = "ring")]
            Self::Ring(ring) => ring.algorithm(),
        }
    }
    pub(super) fn new(algorithm: Algorithm, key: &[u8]) -> Self {
        match algorithm {
            Algorithm::ChaCha20Poly1305 => {
                #[cfg(feature = "ring")]
                {
                    Self::Ring(RingBackend::new(
                        algorithm,
                        &ring::aead::CHACHA20_POLY1305,
                        key,
                    ))
                }
                #[cfg(not(feature = "ring"))]
                {
                    Self::RustCrypto(RustCryptoCipher::new_chacha20_poly1305(key))
                }
            }
            Algorithm::Aes128Gcm => {
                #[cfg(feature = "ring")]
                {
                    Self::Ring(RingBackend::new(algorithm, &ring::aead::AES_128_GCM, key))
                }
                #[cfg(not(feature = "ring"))]
                {
                    Self::RustCrypto(RustCryptoCipher::new_aes_128_gcm(key))
                }
            }
            Algorithm::Aes256Gcm => {
                #[cfg(feature = "ring")]
                {
                    Self::Ring(RingBackend::new(algorithm, &ring::aead::AES_256_GCM, key))
                }
                #[cfg(not(feature = "ring"))]
                {
                    Self::RustCrypto(RustCryptoCipher::new_aes_256_gcm(key))
                }
            }
            Algorithm::XChaCha20Poly1305 => {
                Self::RustCrypto(RustCryptoCipher::new_x_chacha20_poly1305(key))
            }
        }
    }
    pub(super) fn decrypt_in_place<B>(
        &self,
        nonce: SingleNonce,
        aad: &[u8],
        data: &mut B,
    ) -> Result<(), DecryptError>
    where
        B: Buffer,
    {
        match self {
            Self::RustCrypto(rc) => rc.decrypt_in_place(nonce, aad, data),
            #[cfg(feature = "ring")]
            Self::Ring(ring) => ring.decrypt_in_place(nonce, aad, data),
        }
    }
    pub(super) fn encrypt_in_place<B>(
        &self,
        nonce: SingleNonce,
        aad: &[u8],
        data: &mut B,
    ) -> Result<(), EncryptError>
    where
        B: Buffer,
    {
        match self {
            Self::RustCrypto(rc) => rc.encrypt_in_place(nonce, aad, data),
            #[cfg(feature = "ring")]
            Self::Ring(ring) => ring.encrypt_in_place(nonce, aad, data),
        }
    }
}
#[cfg(feature = "ring")]
pub(super) struct RingBackend {
    key: ring::aead::LessSafeKey,
    algorithm: Algorithm,
}

#[cfg(feature = "ring")]
impl RingBackend {
    pub(super) fn new(
        algorithm: Algorithm,
        ring_algorithm: &'static ring::aead::Algorithm,
        key: &[u8],
    ) -> RingBackend {
        let unbounded = ring::aead::UnboundKey::new(ring_algorithm, key).unwrap(); // safe, keys are only generated and are always the correct size
        let key = ring::aead::LessSafeKey::new(unbounded);
        RingBackend { key, algorithm }
    }
    fn algorithm(&self) -> Algorithm {
        self.algorithm
    }
    pub(super) fn encrypt_in_place<B>(
        &self,
        nonce: SingleNonce,
        aad: &[u8],
        data: &mut B,
    ) -> Result<(), EncryptError>
    where
        B: Buffer,
    {
        let aad = ring::aead::Aad::from(aad);
        self.key.seal_in_place_append_tag(nonce.into(), aad, data)?;
        Ok(())
    }
    pub(super) fn decrypt_in_place<B>(
        &self,
        nonce: SingleNonce,
        aad: &[u8],
        data: &mut B,
    ) -> Result<(), DecryptError>
    where
        B: Buffer,
    {
        let aad = ring::aead::Aad::from(aad);
        self.key.open_in_place(nonce.into(), aad, data.as_mut())?;
        data.truncate(data.len() - self.key.algorithm().tag_len());
        Ok(())
    }
}

// todo: should some(all?) of these be boxed?
#[allow(clippy::large_enum_variant)]
pub(super) enum RustCryptoCipher {
    #[cfg(not(feature = "ring"))]
    Aes128Gcm(aes_gcm::Aes128Gcm),
    #[cfg(not(feature = "ring"))]
    Aes256Gcm(aes_gcm::Aes256Gcm),
    #[cfg(not(feature = "ring"))]
    ChaCha20Poly1305(chacha20poly1305::ChaCha20Poly1305),
    XChaCha20Poly1305(chacha20poly1305::XChaCha20Poly1305),
}
impl RustCryptoCipher {
    #[cfg(not(feature = "ring"))]
    fn new_chacha20_poly1305(key: &[u8]) -> Self {
        use chacha20poly1305::KeyInit;
        let key = chacha20poly1305::ChaCha20Poly1305::new_from_slice(key).unwrap(); // safe: keys are always generated and the correct size
        Self::ChaCha20Poly1305(key)
    }
    fn new_x_chacha20_poly1305(key: &[u8]) -> Self {
        use chacha20poly1305::KeyInit;
        let key = chacha20poly1305::XChaCha20Poly1305::new_from_slice(key).unwrap(); // safe: keys are always generated and the correct size
        Self::XChaCha20Poly1305(key)
    }
    #[cfg(not(feature = "ring"))]
    fn new_aes_128_gcm(key: &[u8]) -> Self {
        use aes_gcm::KeyInit;
        let key = aes_gcm::Aes128Gcm::new_from_slice(key).unwrap(); // safe: keys are always generated and the correct size
        Self::Aes128Gcm(key)
    }
    #[cfg(not(feature = "ring"))]
    fn new_aes_256_gcm(key: &[u8]) -> Self {
        use aes_gcm::KeyInit;
        let key = aes_gcm::Aes256Gcm::new_from_slice(key).unwrap(); // safe: keys are always generated and the correct size
        Self::Aes256Gcm(key)
    }
    pub(super) fn encrypt_in_place<B>(
        &self,
        nonce: SingleNonce,
        aad: &[u8],
        data: &mut B,
    ) -> Result<(), EncryptError>
    where
        B: Buffer,
    {
        let mut buffer = RcBuffer(data);
        match self {
            #[cfg(not(feature = "ring"))]
            Self::Aes128Gcm(aes) => {
                use aes_gcm::aead::AeadInPlace;
                aes.encrypt_in_place(&nonce.into(), aad, &mut buffer)
            }
            #[cfg(not(feature = "ring"))]
            Self::Aes256Gcm(aes) => {
                use aes_gcm::aead::AeadInPlace;
                aes.encrypt_in_place(&nonce.into(), aad, &mut buffer)
            }
            #[cfg(not(feature = "ring"))]
            Self::ChaCha20Poly1305(chacha) => {
                use chacha20poly1305::aead::AeadInPlace;
                chacha.encrypt_in_place(&nonce.into(), aad, &mut buffer)
            }
            Self::XChaCha20Poly1305(cipher) => {
                use chacha20poly1305::aead::AeadInPlace;
                cipher.encrypt_in_place(&nonce.into(), aad, &mut buffer)
            }
        }?;
        Ok(())
    }
    pub(super) fn decrypt_in_place<B>(
        &self,
        nonce: SingleNonce,
        aad: &[u8],
        data: &mut B,
    ) -> Result<(), DecryptError>
    where
        B: Buffer,
    {
        let mut buffer = RcBuffer(data);
        match self {
            #[cfg(not(feature = "ring"))]
            Self::Aes128Gcm(aes) => {
                use aes_gcm::aead::AeadInPlace;
                aes.decrypt_in_place(&nonce.into(), aad, &mut buffer)
            }
            #[cfg(not(feature = "ring"))]
            Self::Aes256Gcm(aes) => {
                use aes_gcm::aead::AeadInPlace;
                aes.decrypt_in_place(&nonce.into(), aad, &mut buffer)
            }
            #[cfg(not(feature = "ring"))]
            Self::ChaCha20Poly1305(chacha) => {
                use chacha20poly1305::aead::AeadInPlace;
                chacha.decrypt_in_place(&nonce.into(), aad, &mut buffer)
            }
            Self::XChaCha20Poly1305(cipher) => {
                use chacha20poly1305::aead::AeadInPlace;
                cipher.decrypt_in_place(&nonce.into(), aad, &mut buffer)
            }
        }?;
        Ok(())
    }

    fn algorithm(&self) -> Algorithm {
        match self {
            #[cfg(not(feature = "ring"))]
            Self::Aes128Gcm(_) => Algorithm::Aes128Gcm,
            #[cfg(not(feature = "ring"))]
            Self::Aes256Gcm(_) => Algorithm::Aes256Gcm,
            #[cfg(not(feature = "ring"))]
            Self::ChaCha20Poly1305(_) => Algorithm::ChaCha20Poly1305,
            Self::XChaCha20Poly1305(_) => Algorithm::XChaCha20Poly1305,
        }
    }
}