//! Bindings to the reference (slow, unsafe) implementation of AEZ. //! //! **Warning**: //! //! > This version is slow and susceptible to side-channel attacks. //! > Do not use for any purpose other than to understand AEZ. //! //! We only use it to compare outputs between `zears` and `aezref`. use std::ffi::*; unsafe extern "C" { fn Decrypt( key: *const c_uchar, keybytes: c_uint, nonce: *const c_uchar, noncebytes: c_uint, ad: *const *const c_uchar, adbytes: *const c_uint, veclen: c_uint, abytes: c_uint, ciphertext: *const c_uchar, cipherbytes: c_uint, message: *mut c_uchar, ) -> c_int; fn Encrypt( key: *const c_uchar, keybytes: c_uint, nonce: *const c_uchar, noncebytes: c_uint, ad: *const *const c_uchar, adbytes: *const c_uint, veclen: c_uint, abytes: c_uint, message: *const c_uchar, messagebytes: c_uint, ciphertext: *mut c_uchar, ); } /// Encrypt the given `message` using the given `key`, `nonce` and associated data items `ad`. /// /// The ciphertext will be written to `ciphertext`. /// /// The number of authentication bytes will be inferred from the length difference between /// `message` and `ciphertext` /// /// This function will panic if /// /// * any of the given byte strings is longer than [`c_uint::MAX`] (usually 2^32 - 1) /// * the ciphertext buffer is smaller than the message buffer pub fn encrypt(key: &[u8], nonce: &[u8], ad: &[&[u8]], message: &[u8], ciphertext: &mut [u8]) { assert!(ciphertext.len() >= message.len()); let adlens: Vec = ad .into_iter() .map(|x| x.len().try_into().expect("associated data item too long")) .collect(); let ad = ad.into_iter().map(|x| x.as_ptr()).collect::>(); let abytes = ciphertext.len() - message.len(); unsafe { Encrypt( key.as_ptr(), key.len().try_into().expect("key too long"), nonce.as_ptr(), nonce.len().try_into().expect("nonce too long"), ad.as_ptr(), adlens.as_ptr(), ad.len().try_into().expect("too many associated data items"), abytes.try_into().expect("too many authentication bytes"), message.as_ptr(), message.len().try_into().expect("message too long"), ciphertext.as_mut_ptr(), ) } } /// Decrypt the given `ciphertext` using the given `key`, `nonce` and associated data items `ad`. /// /// The plaintext message will be written to `message`. /// /// The number of authentication bytes will be inferred from the length difference between /// `message` and `ciphertext`. /// /// If the authentication bytes do not match the expected bytes (i.e. the ciphertext has been /// modified, or the wrong key/nonce/ad is provided), `Err(())` is returned. Otherwise, `Ok(())` is /// returned. /// /// This function will panic if /// /// * any of the given byte strings is longer than [`c_uint::MAX`] (usually 2^32 - 1) /// * the ciphertext buffer is smaller than the message buffer pub fn decrypt( key: &[u8], nonce: &[u8], ad: &[&[u8]], ciphertext: &[u8], message: &mut [u8], ) -> Result<(), ()> { assert!(ciphertext.len() >= message.len()); let adlens: Vec = ad .into_iter() .map(|x| x.len().try_into().expect("associated data item too long")) .collect(); let ad = ad.into_iter().map(|x| x.as_ptr()).collect::>(); let abytes = ciphertext.len() - message.len(); let result = unsafe { Decrypt( key.as_ptr(), key.len().try_into().expect("key too long"), nonce.as_ptr(), nonce.len().try_into().expect("nonce too long"), ad.as_ptr(), adlens.as_ptr(), ad.len().try_into().expect("too many associated data items"), abytes.try_into().expect("too many authentication bytes"), ciphertext.as_ptr(), ciphertext.len().try_into().expect("message too long"), message.as_mut_ptr(), ) }; match result { 0 => Ok(()), -1 => Err(()), _ => panic!("unexpected return from Decrypt"), } } #[cfg(test)] mod tests { use super::*; #[test] fn it_works() { let mut ciphertext = [0u8; 10]; encrypt( b"foo", b"bar", &[b"ad 1", b"ad two"], b"hey", &mut ciphertext, ); let mut message = [0u8; 3]; decrypt( b"foo", b"bar", &[b"ad 1", b"ad two"], &ciphertext, &mut message, ) .unwrap(); assert_eq!(&message, b"hey"); } }