rind 0.1.10

Pure-Rust cryptography for MoQ, forked from graviola
Documentation
// Written for Graviola by Joe Birr-Pixton, 2024.
// SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT-0

//! ChaCha20-Poly1305 from separate ChaCha20 and Poly1305 passes, where no
//! one-pass routine runs.

use crate::low::chacha20::ChaCha20;
use crate::low::poly1305::Poly1305;

/// Encrypts `inout` in place, returning the tag over `aad` and the ciphertext.
pub(crate) fn seal(key: &[u8; 32], nonce: &[u8; 12], aad: &[u8], inout: &mut [u8]) -> [u8; 16] {
    cipher(key, nonce, aad, inout, true)
}

/// Decrypts `inout` in place, returning the tag over `aad` and the
/// ciphertext, which the caller checks.
// aarch64 tests compare only `seal` with the one-pass routine.
#[cfg(any(target_arch = "arm", target_arch = "x86_64"))]
pub(crate) fn open(key: &[u8; 32], nonce: &[u8; 12], aad: &[u8], inout: &mut [u8]) -> [u8; 16] {
    cipher(key, nonce, aad, inout, false)
}

fn cipher(key: &[u8; 32], nonce: &[u8; 12], aad: &[u8], inout: &mut [u8], seal: bool) -> [u8; 16] {
    let mut counter_nonce = [0u8; 16];
    counter_nonce[4..].copy_from_slice(nonce);

    // Block 0's first half keys Poly1305.
    let mut chacha = ChaCha20::new(key, &counter_nonce);
    let mut poly_key = [0u8; 32];
    chacha.cipher(&mut poly_key);
    let mut poly = Poly1305::new(&poly_key);

    // AAD || pad(AAD) || ciphertext || pad(ciphertext) || len_64(AAD) || len_64(ciphertext)
    let pad = |poly: &mut Poly1305, len: usize| poly.add_bytes(&[0u8; 16][..(16 - len % 16) % 16]);
    poly.add_bytes(aad);
    pad(&mut poly, aad.len());
    if seal {
        chacha.cipher(inout);
        poly.add_bytes(inout);
    } else {
        poly.add_bytes(inout);
        chacha.cipher(inout);
    }
    pad(&mut poly, inout.len());
    poly.add_bytes(&(aad.len() as u64).to_le_bytes());
    poly.add_bytes(&(inout.len() as u64).to_le_bytes());
    poly.finish()
}