use crate::errors::UnknownCryptoError;
pub use crate::hazardous::aead::chacha20poly1305::A_MAX;
pub use crate::hazardous::aead::chacha20poly1305::P_MAX;
use crate::hazardous::aead::chacha20poly1305::{ChaCha20Poly1305, ENC_CTR};
pub use crate::hazardous::cae::chacha20poly1305blake2b::{C_MAX, TAG_SIZE};
use crate::hazardous::hash::blake2::blake2b::Blake2b;
use crate::hazardous::mac::poly1305::POLY1305_OUTSIZE;
use crate::hazardous::stream::chacha20::ChaCha20;
use crate::hazardous::stream::xchacha20::XChaCha20;
pub use crate::hazardous::stream::{chacha20::SecretKey, xchacha20::Nonce};
use crate::util;
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn seal(
secret_key: &SecretKey,
nonce: &Nonce,
plaintext: &[u8],
ad: Option<&[u8]>,
dst_out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
if u64::try_from(plaintext.len()).map_err(|_| UnknownCryptoError)? > P_MAX {
return Err(UnknownCryptoError);
}
let ad = ad.unwrap_or(&[0u8; 0]);
#[allow(clippy::absurd_extreme_comparisons)]
if u64::try_from(ad.len()).map_err(|_| UnknownCryptoError)? > A_MAX {
return Err(UnknownCryptoError);
}
match plaintext.len().checked_add(TAG_SIZE) {
Some(out_min_len) => {
if dst_out.len() < out_min_len {
return Err(UnknownCryptoError);
}
}
None => return Err(UnknownCryptoError),
};
let (subkey, ietf_nonce) = XChaCha20::subkey_and_ietf_nonce(secret_key, nonce);
ChaCha20Poly1305::seal(
&subkey,
&ietf_nonce,
plaintext,
Some(ad),
&mut dst_out[..plaintext.len() + POLY1305_OUTSIZE],
)?;
let mut blake2b = Blake2b::new(32)?;
blake2b.update(secret_key.unprotected_as_ref())?;
blake2b.update(nonce.as_ref())?;
blake2b.update(ad)?;
blake2b.update(&dst_out[plaintext.len()..plaintext.len() + POLY1305_OUTSIZE])?;
let tag = blake2b.finalize()?;
dst_out[plaintext.len()..plaintext.len() + TAG_SIZE].copy_from_slice(tag.as_ref());
Ok(())
}
#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
pub fn open(
secret_key: &SecretKey,
nonce: &Nonce,
ciphertext_with_tag: &[u8],
ad: Option<&[u8]>,
dst_out: &mut [u8],
) -> Result<(), UnknownCryptoError> {
if u64::try_from(ciphertext_with_tag.len()).map_err(|_| UnknownCryptoError)? > C_MAX {
return Err(UnknownCryptoError);
}
let ad = ad.unwrap_or(&[0u8; 0]);
#[allow(clippy::absurd_extreme_comparisons)]
if u64::try_from(ad.len()).map_err(|_| UnknownCryptoError)? > A_MAX {
return Err(UnknownCryptoError);
}
if ciphertext_with_tag.len() < TAG_SIZE {
return Err(UnknownCryptoError);
}
if dst_out.len() < ciphertext_with_tag.len() - TAG_SIZE {
return Err(UnknownCryptoError);
}
let mut blake2b = Blake2b::new(32)?;
blake2b.update(secret_key.unprotected_as_ref())?;
blake2b.update(nonce.as_ref())?;
blake2b.update(ad)?;
let (subkey, ietf_nonce) = XChaCha20::subkey_and_ietf_nonce(secret_key, nonce);
let mut dec_ctx = ChaCha20::new(&subkey, &ietf_nonce);
let mut auth_ctx = ChaCha20Poly1305::poly1305_init(&mut dec_ctx);
let ciphertext_len = ciphertext_with_tag.len() - TAG_SIZE;
ChaCha20Poly1305::process_authentication(
&mut auth_ctx,
ad,
&ciphertext_with_tag[..ciphertext_len],
)?;
blake2b.update(auth_ctx.finalize()?.unprotected_as_ref())?;
util::secure_cmp(
blake2b.finalize()?.as_ref(),
&ciphertext_with_tag[ciphertext_len..],
)?;
debug_assert_eq!(dec_ctx.position(), ENC_CTR);
if ciphertext_len != 0 {
dst_out[..ciphertext_len].copy_from_slice(&ciphertext_with_tag[..ciphertext_len]);
dec_ctx.xor_keystream_into(&mut dst_out[..ciphertext_len])?;
}
Ok(())
}
#[cfg(test)]
#[cfg(feature = "safe_api")]
mod public {
use super::*;
use crate::test_framework::aead_interface::BufferedOnlyAeadTestRunner;
#[quickcheck]
#[cfg(feature = "safe_api")]
fn prop_aead_interface(input: Vec<u8>, ad: Vec<u8>) -> bool {
let secret_key = SecretKey::generate().unwrap();
let nonce = Nonce::generate().unwrap();
BufferedOnlyAeadTestRunner(seal, open, secret_key, nonce, &input, None, TAG_SIZE, &ad);
true
}
}