use chacha20::XChaCha20;
use chacha20::cipher::{KeyIvInit, StreamCipher};
use poly1305::Poly1305;
use poly1305::universal_hash::KeyInit;
use subtle::ConstantTimeEq;
pub fn run_hchacha20(key: &[u8; 32], input: &[u8; 16]) -> [u8; 32] {
use chacha20::{R20, hchacha};
let out = hchacha::<R20>(&(*key).into(), &(*input).into());
let mut result = [0u8; 32];
result.copy_from_slice(&out);
result
}
pub fn xchacha20_djb_poly1305_encrypt(
key: &[u8; 32],
nonce: &[u8; 24],
plaintext: &[u8],
) -> Vec<u8> {
#[cfg(feature = "zeroize")]
let mut keystream = zeroize::Zeroizing::new(vec![0u8; 32 + plaintext.len()]);
#[cfg(not(feature = "zeroize"))]
let mut keystream = vec![0u8; 32 + plaintext.len()];
XChaCha20::new(key.into(), nonce.into()).apply_keystream(&mut keystream);
let (poly_key_bytes, keystream_body) = keystream.split_at(32);
#[cfg(feature = "zeroize")]
let mut poly_key = zeroize::Zeroizing::new([0u8; 32]);
#[cfg(not(feature = "zeroize"))]
let mut poly_key = [0u8; 32];
poly_key.copy_from_slice(poly_key_bytes);
let ciphertext: Vec<u8> = plaintext
.iter()
.zip(keystream_body.iter())
.map(|(&p, &k)| p ^ k)
.collect();
#[cfg(feature = "zeroize")]
let tag = Poly1305::new(&(*poly_key).into()).compute_unpadded(&ciphertext);
#[cfg(not(feature = "zeroize"))]
let tag = Poly1305::new(&poly_key.into()).compute_unpadded(&ciphertext);
let mut result = Vec::with_capacity(16 + ciphertext.len());
result.extend_from_slice(&tag[..]);
result.extend_from_slice(&ciphertext);
result
}
pub fn xchacha20_djb_poly1305_decrypt(
key: &[u8; 32],
nonce: &[u8; 24],
ciphertext_with_tag: &[u8],
) -> Result<Vec<u8>, String> {
if ciphertext_with_tag.len() < 16 {
return Err("Ciphertext too short".to_string());
}
let (tag, ciphertext) = ciphertext_with_tag.split_at(16);
#[cfg(feature = "zeroize")]
let mut keystream = zeroize::Zeroizing::new(vec![0u8; 32 + ciphertext.len()]);
#[cfg(not(feature = "zeroize"))]
let mut keystream = vec![0u8; 32 + ciphertext.len()];
XChaCha20::new(key.into(), nonce.into()).apply_keystream(&mut keystream);
let (poly_key_bytes, keystream_body) = keystream.split_at(32);
#[cfg(feature = "zeroize")]
let mut poly_key = zeroize::Zeroizing::new([0u8; 32]);
#[cfg(not(feature = "zeroize"))]
let mut poly_key = [0u8; 32];
poly_key.copy_from_slice(poly_key_bytes);
#[cfg(feature = "zeroize")]
let computed_tag = Poly1305::new(&(*poly_key).into()).compute_unpadded(ciphertext);
#[cfg(not(feature = "zeroize"))]
let computed_tag = Poly1305::new(&poly_key.into()).compute_unpadded(ciphertext);
if computed_tag[..].ct_eq(tag).unwrap_u8() == 0 {
return Err("Poly1305 authentication failed".to_string());
}
let plaintext: Vec<u8> = ciphertext
.iter()
.zip(keystream_body.iter())
.map(|(&c, &k)| c ^ k)
.collect();
Ok(plaintext)
}