dave 0.2.0

Rust DAVE primitives with Discord Opus audio and video frame transform support.
Documentation
use aead::{Error, Key, KeyInit, KeySizeUser};
use aes::Aes128;
use cipher::{BlockCipherEncrypt, InnerIvInit, StreamCipherCore, array::Array, consts::U16};
use ctr::{CtrCore, flavors::Ctr32BE};
use ghash::{GHash, universal_hash::UniversalHash};
use subtle::ConstantTimeEq;

type Block = Array<u8, U16>;

#[derive(Clone)]
pub(crate) struct Aes128Gcm8 {
    cipher: Aes128,
    ghash: GHash,
}

impl KeySizeUser for Aes128Gcm8 {
    type KeySize = <Aes128 as cipher::KeySizeUser>::KeySize;
}

impl KeyInit for Aes128Gcm8 {
    fn new(key: &Key<Self>) -> Self {
        let cipher = <Aes128 as cipher::KeyInit>::new(key);
        let mut ghash_key = ghash::Key::default();
        cipher.encrypt_block(&mut ghash_key);
        Self {
            cipher,
            ghash: GHash::new(&ghash_key),
        }
    }
}

impl Aes128Gcm8 {
    pub(crate) fn encrypt_in_place_detached(
        &self,
        nonce: &[u8; 12],
        associated_data: &[u8],
        buffer: &mut [u8],
    ) -> Result<[u8; 8], Error> {
        let (ctr, mask) = self.init_ctr(nonce);
        ctr.apply_keystream_partial(buffer.into());
        let tag = self.compute_tag(mask, associated_data, buffer);
        let mut truncated = [0_u8; 8];
        truncated.copy_from_slice(&tag[..8]);
        Ok(truncated)
    }

    pub(crate) fn decrypt_in_place_detached(
        &self,
        nonce: &[u8; 12],
        associated_data: &[u8],
        buffer: &mut [u8],
        tag: &[u8],
    ) -> Result<(), Error> {
        let (ctr, mask) = self.init_ctr(nonce);
        let expected = self.compute_tag(mask, associated_data, buffer);
        if expected[..8].ct_eq(tag).into() {
            ctr.apply_keystream_partial(buffer.into());
            Ok(())
        } else {
            Err(Error)
        }
    }

    fn init_ctr(&self, nonce: &[u8; 12]) -> (CtrCore<&Aes128, Ctr32BE>, Block) {
        let mut j0 = ghash::Block::default();
        j0[..12].copy_from_slice(nonce);
        j0[15] = 1;

        let mut ctr = CtrCore::<_, Ctr32BE>::inner_iv_init(&self.cipher, &j0);
        let mut mask = Block::default();
        ctr.write_keystream_block(&mut mask);
        (ctr, mask)
    }

    fn compute_tag(&self, mask: Block, associated_data: &[u8], buffer: &[u8]) -> Block {
        let mut ghash = self.ghash.clone();
        ghash.update_padded(associated_data);
        ghash.update_padded(buffer);

        let mut lengths = ghash::Block::default();
        lengths[..8].copy_from_slice(&((associated_data.len() as u64) * 8).to_be_bytes());
        lengths[8..].copy_from_slice(&((buffer.len() as u64) * 8).to_be_bytes());
        ghash.update(&[lengths]);

        let mut tag = ghash.finalize();
        for (tag, mask) in tag.iter_mut().zip(mask.iter()) {
            *tag ^= *mask;
        }
        tag
    }
}