philbin 1.0.1

A pure Rust AEGIS library with SIMD and runtime CPU detection
Documentation
// We call the following APIs "facade" functions:
//  - encrypt
//  - encrypt_detached
//  - decrypt
//  - decrypt_detached
// since they merely adapt input parameters for caller convenience and delegate
// their work to "(encrypt|decrypt)_to_slice_detached" or
// "(encrypt|decrypt)_in_place" versions.
//
// Since the non-facade functions are extensively tested for correctness (see
// file_tests), the facade functions only need to be tested to verify they are
// correctly passing along their parameters.

macro_rules! gen_facade_tests {
  ($algo:ident) => {
    gen_facade_tests!($algo, 128);
    gen_facade_tests!($algo, 256);
  };
  ($algo:ident, $tag_bits:expr) => {
    pastey::paste! {
      // The `mod` is here only to prevent `use` statement pollution.
      mod [<facade_tag $tag_bits>] {
        use philbin::{
          careful::*,
          easy::*,
        };
        use test_utils::AegisTestCase;
        use rstest::rstest;
        use similar_asserts::assert_eq;
        use std::assert_matches;

        const KEY_BYTES: usize = crate::file_tests::aegis_key_bytes!($algo);
        type FullAuthTag = [u8; $tag_bits / 8];

        #[rstest]
        fn encrypt_decrypt(
          #[from(test_utils::$algo::example)] expected: AegisTestCase,
        ) -> anyhow::Result<()> {
          let ciphertext_with_tag = $algo::encrypt::<FullAuthTag>(
            Plaintext::new(&expected.plaintext),
            AssociatedData::new(&expected.associated_data),
            &Key::<KEY_BYTES>::from_bytes(expected.key.clone())?,
            Nonce::<KEY_BYTES>::new(*expected.nonce.as_array().unwrap())?,
          )?;

          let expected_ciphertext_with_tag =
            [expected.ciphertext.as_ref(),
             expected.[<tag $tag_bits>].as_ref()].concat();

          assert_eq!(
            expected: expected_ciphertext_with_tag,
            actual: ciphertext_with_tag);

          let plaintext = $algo::decrypt::<FullAuthTag>(
            Ciphertext::new(&ciphertext_with_tag), // Round-trip must work
            AssociatedData::new(&expected.associated_data),
            &Key::<KEY_BYTES>::from_bytes(expected.key.clone())?,
            Nonce::<KEY_BYTES>::new(*expected.nonce.as_array().unwrap())?,
          )?;
          assert_eq!(expected: expected.plaintext, actual: plaintext);

          Ok(())
        }

        #[rstest]
        fn encrypt_decrypt_detached(
          #[from(test_utils::$algo::example)] expected: AegisTestCase,
        ) -> anyhow::Result<()> {
          let (ciphertext, tag) = $algo::encrypt_detached::<FullAuthTag>(
            Plaintext::new(&expected.plaintext),
            AssociatedData::new(&expected.associated_data),
            &Key::<KEY_BYTES>::from_bytes(expected.key.clone())?,
            Nonce::<KEY_BYTES>::new(*expected.nonce.as_array().unwrap())?,
          )?;

          assert_eq!(expected: expected.ciphertext, actual: ciphertext);
          assert_eq!(expected: expected.[<tag $tag_bits>], actual: tag);

          let plaintext = $algo::decrypt_detached::<FullAuthTag>(
            Ciphertext::new(&ciphertext), // Round-trip must work
            &tag,                         // Round-trip must work
            AssociatedData::new(&expected.associated_data),
            &Key::<KEY_BYTES>::from_bytes(expected.key.clone())?,
            Nonce::<KEY_BYTES>::new(*expected.nonce.as_array().unwrap())?,
          )?;
          assert_eq!(expected: expected.plaintext, actual: plaintext);

          Ok(())
        }

        #[rstest]
        fn decrypt_ct_too_short(
          #[from(test_utils::$algo::example)] expected: AegisTestCase,
        ) -> anyhow::Result<()> {
          let result = $algo::decrypt::<FullAuthTag>(
            Ciphertext::new(b""),
            AssociatedData::new(&expected.associated_data),
            &Key::<KEY_BYTES>::from_bytes(expected.key.clone())?,
            Nonce::<KEY_BYTES>::new(*expected.nonce.as_array().unwrap())?,
          );
          assert_matches!(result, Err(Error::InputBufferWrongSize));
          Ok(())
        }
      }
    }
  };
}
pub(crate) use gen_facade_tests;