aboutsummaryrefslogtreecommitdiff
path: root/aezref/src/lib.rs
diff options
context:
space:
mode:
authorDaniel Schadt <kingdread@gmx.de>2025-04-17 12:56:44 +0200
committerDaniel Schadt <kingdread@gmx.de>2025-04-17 12:56:44 +0200
commit9287a6cdc37c7c37e744f8418a13a74bb0e629ef (patch)
treef4d349e0d4ece886fda31f755b08163485a7a02e /aezref/src/lib.rs
parent66814768f8c172d6996d037064924c908245a951 (diff)
downloadzears-9287a6cdc37c7c37e744f8418a13a74bb0e629ef.tar.gz
zears-9287a6cdc37c7c37e744f8418a13a74bb0e629ef.tar.bz2
zears-9287a6cdc37c7c37e744f8418a13a74bb0e629ef.zip
fuzz against slow aez-ref, not fast aez-ni
Two reasons: First, this allows us to test more of the algorithm, as the (slow) reference implementation supports multiple associated data items, large values for tau, ... Second, this avoids the segfault crash, which is a limit of the fast implementation (the assumption there is that data is aligned properly, and even a read out-of-bounds will not cause a segfault).
Diffstat (limited to 'aezref/src/lib.rs')
-rw-r--r--aezref/src/lib.rs155
1 files changed, 155 insertions, 0 deletions
diff --git a/aezref/src/lib.rs b/aezref/src/lib.rs
new file mode 100644
index 0000000..8fc902c
--- /dev/null
+++ b/aezref/src/lib.rs
@@ -0,0 +1,155 @@
+//! 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<c_uint> = 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::<Vec<_>>();
+ 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<c_uint> = 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::<Vec<_>>();
+ 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");
+ }
+}