use super::{poly1305_key_gen, poly1305_mac_padded16, poly1305_tags_equal, ChaCha20};
use crate::internal_alloc::Vec;
use noxtls_core::{Error, Result};
fn pad16_len(x: usize) -> usize {
let rem = x % 16;
if rem == 0 {
0
} else {
16 - rem
}
}
fn build_mac_data(aad: &[u8], ciphertext: &[u8]) -> Vec<u8> {
let pad_a = pad16_len(aad.len());
let pad_c = pad16_len(ciphertext.len());
let mut out = Vec::with_capacity(aad.len() + pad_a + ciphertext.len() + pad_c + 16);
out.extend_from_slice(aad);
out.resize(out.len() + pad_a, 0);
out.extend_from_slice(ciphertext);
out.resize(out.len() + pad_c, 0);
let aad_len = aad.len() as u64;
let ct_len = ciphertext.len() as u64;
out.extend_from_slice(&aad_len.to_le_bytes());
out.extend_from_slice(&ct_len.to_le_bytes());
out
}
pub fn chacha20_poly1305_encrypt(
key: &[u8; 32],
nonce: &[u8; 12],
aad: &[u8],
plaintext: &[u8],
) -> Result<(Vec<u8>, [u8; 16])> {
const MAX_PLAINTEXT: usize = (1_usize << 32).saturating_mul(64).saturating_sub(64);
if plaintext.len() > MAX_PLAINTEXT {
return Err(Error::InvalidLength(
"chacha20-poly1305 plaintext exceeds RFC 8439 counter range",
));
}
let otk = poly1305_key_gen(key, nonce);
let mut cipher = ChaCha20::new(key, nonce, 1);
let mut ciphertext = vec![0_u8; plaintext.len()];
cipher.apply_keystream(plaintext, &mut ciphertext)?;
let mac_data = build_mac_data(aad, &ciphertext);
let tag = poly1305_mac_padded16(&otk, &mac_data);
Ok((ciphertext, tag))
}
pub fn chacha20_poly1305_decrypt(
key: &[u8; 32],
nonce: &[u8; 12],
aad: &[u8],
ciphertext: &[u8],
tag: &[u8; 16],
) -> Result<Vec<u8>> {
const MAX_CIPHERTEXT: usize = (1_usize << 32).saturating_mul(64).saturating_sub(64);
if ciphertext.len() > MAX_CIPHERTEXT {
return Err(Error::InvalidLength(
"chacha20-poly1305 ciphertext exceeds RFC 8439 counter range",
));
}
let otk = poly1305_key_gen(key, nonce);
let mac_data = build_mac_data(aad, ciphertext);
let expected = poly1305_mac_padded16(&otk, &mac_data);
if !poly1305_tags_equal(tag, &expected) {
return Err(Error::CryptoFailure(
"chacha20-poly1305 authentication failed",
));
}
let mut cipher = ChaCha20::new(key, nonce, 1);
let mut plaintext = vec![0_u8; ciphertext.len()];
cipher.apply_keystream(ciphertext, &mut plaintext)?;
Ok(plaintext)
}