clatter 0.1.3-alpha

no_std compatible implementation of Noise protocol framework with Post-Quantum extensions
Documentation
use core::ops::Deref;

use chacha20poly1305::{AeadInPlace, ChaCha20Poly1305, KeyInit};

use crate::bytearray::SensitiveByteArray;
use crate::error::CipherError;
use crate::traits::{Cipher, CryptoComponent};

/// ChaCha20-Poly1305 cipher implementation
pub struct ChaChaPoly;

impl CryptoComponent for ChaChaPoly {
    fn name() -> &'static str {
        "ChaChaPoly"
    }
}

impl Cipher for ChaChaPoly {
    type Key = SensitiveByteArray<[u8; 32]>;

    fn encrypt(k: &Self::Key, nonce: u64, ad: &[u8], plaintext: &[u8], out: &mut [u8]) {
        assert!(plaintext.len().checked_add(Self::tag_len()) == Some(out.len()));
        out[..plaintext.len()].copy_from_slice(plaintext);
        Self::encrypt_in_place(k, nonce, ad, out, plaintext.len());
    }

    fn encrypt_in_place(
        k: &Self::Key,
        nonce: u64,
        ad: &[u8],
        in_out: &mut [u8],
        plaintext_len: usize,
    ) -> usize {
        assert!(plaintext_len
            .checked_add(Self::tag_len())
            .map_or(false, |len| len <= in_out.len()));

        let mut full_nonce = [0u8; 12];
        full_nonce[4..].copy_from_slice(&nonce.to_le_bytes());

        let out_len = plaintext_len + Self::tag_len();
        let (buffer, tag_out) = in_out[..out_len].split_at_mut(plaintext_len);

        let tag = ChaCha20Poly1305::new(k.deref().into())
            .encrypt_in_place_detached(&full_nonce.into(), ad, buffer)
            .unwrap();

        tag_out.copy_from_slice(&tag);
        out_len
    }

    fn decrypt(
        k: &Self::Key,
        nonce: u64,
        ad: &[u8],
        ciphertext: &[u8],
        out: &mut [u8],
    ) -> crate::error::CipherResult<()> {
        assert!(ciphertext.len().checked_sub(Self::tag_len()) == Some(out.len()));

        let mut full_nonce = [0u8; 12];
        full_nonce[4..].copy_from_slice(&nonce.to_le_bytes());

        out.copy_from_slice(&ciphertext[..out.len()]);
        let tag = &ciphertext[out.len()..];

        ChaCha20Poly1305::new(k.deref().into())
            .decrypt_in_place_detached(&full_nonce.into(), ad, out, tag.into())
            .map_err(|_| CipherError::Decrypt)?;

        Ok(())
    }

    fn decrypt_in_place(
        k: &Self::Key,
        nonce: u64,
        ad: &[u8],
        in_out: &mut [u8],
        ciphertext_len: usize,
    ) -> crate::error::CipherResult<usize> {
        assert!(ciphertext_len <= in_out.len());
        assert!(ciphertext_len >= Self::tag_len());

        let mut full_nonce = [0u8; 12];
        full_nonce[4..].copy_from_slice(&nonce.to_le_bytes());

        let (buffer, tag) = in_out[..ciphertext_len].split_at_mut(ciphertext_len - 16);

        ChaCha20Poly1305::new(k.deref().into())
            .decrypt_in_place_detached(&full_nonce.into(), ad, buffer, tag.as_ref().into())
            .map_err(|_| CipherError::Decrypt)?;

        Ok(buffer.len())
    }

    fn tag_len() -> usize {
        16
    }
}