philbin 1.0.1

A pure Rust AEGIS library with SIMD and runtime CPU detection
Documentation
// High-level AEGIS APIs built on top of lower-level primitives.

use crate::{auth_tag::*, easy::*, nonce::*};

#[doc = include_str!("rustdoc/easy_encrypt.md")]
#[cfg(feature = "rand")] // Requires `Nonce::generate()`
pub fn encrypt(
  plaintext: Plaintext,
  associated_data: AssociatedData,
  key: &Key256,
) -> Result<Vec<u8>> {
  let pt_len = plaintext.as_bytes().len();
  // SAFETY: Cannot overflow, Rust guarantees slice length <= isize::MAX.
  let payload_len = pt_len + Nonce256::BYTES + AuthTag256::BYTES;

  // SAFETY: Vec panics if number of bytes is > isize::MAX.
  // SECURITY: This IF check is safe WRT timing attacks because:
  // - It is based on the _lengths_ of the plaintext and auth tag
  // and those lengths are not secret.
  if payload_len > isize::MAX as usize {
    return Err(Error::InputBufferWrongSize);
  }
  let mut encrypted_payload = vec![0; payload_len];

  let raw_nonce = Nonce256::generate()?.into_array();
  encrypted_payload[..Nonce256::BYTES].copy_from_slice(&raw_nonce);

  // SAFETY: Addition can't overflow here because:
  //    pt_len + Nonce256::BYTES + AuthTag256::BYTES
  // was successful just a few lines prior.
  let ct_end = pt_len + Nonce256::BYTES;
  let buffer_slice = &mut encrypted_payload[Nonce256::BYTES..ct_end];
  let tag = crate::careful::aegis256x4::encrypt_to_slice_detached::<AuthTag256>(
    plaintext,
    associated_data,
    key,
    Nonce::new(raw_nonce)?,
    CiphertextMut::new(buffer_slice),
  )?;

  encrypted_payload[ct_end..].copy_from_slice(tag.as_ref());
  Ok(encrypted_payload)
}

#[allow(clippy::missing_panics_doc, reason = "Can't actually panic.")]
#[doc = include_str!("rustdoc/easy_decrypt.md")]
pub fn decrypt(
  encrypted_payload: Ciphertext,
  associated_data: AssociatedData,
  key: &Key256,
) -> Result<Vec<u8>> {
  let payload_len = encrypted_payload.as_bytes().len();

  // SECURITY: This IF check is safe WRT timing attacks because:
  // - It is based on the _lengths_ of the ciphertext, nonce and auth tag and
  //   those lengths are not secret.
  let Some(pt_len) =
    payload_len.checked_sub(Nonce256::BYTES + AuthTag256::BYTES)
  else {
    return Err(Error::InputBufferWrongSize);
  };

  let nonce_slice = &encrypted_payload.as_bytes()[..Nonce256::BYTES];
  let nonce = Nonce::new(
    *nonce_slice
      .as_array()
      .expect("slice was created from correct byte range"),
  )?;

  // SAFETY: Subtraction can't underflow here because:
  //    payload_len - Nonce256::BYTES - AuthTag256::BYTES
  // was successful just a few lines prior.
  let auth_tag_start = payload_len - AuthTag256::BYTES;
  let tag_slice = &encrypted_payload.as_bytes()[auth_tag_start..];
  let tag: AuthTag256 = *tag_slice
    .as_array()
    .expect("slice was created from correct byte range");

  let ct_slice = &encrypted_payload.as_bytes()[Nonce256::BYTES..auth_tag_start];

  let mut plaintext = vec![0; pt_len];
  crate::careful::aegis256x4::decrypt_to_slice_detached(
    Ciphertext::new(ct_slice),
    &tag,
    associated_data,
    key,
    nonce,
    PlaintextMut::new(&mut plaintext),
  )?;
  Ok(plaintext)
}

// Takes the name of an Aegis algo variant and expands to functions implementing
// all the high-level "facade" functions for it.
//
// Valid algo names: aegis128l, aegis128x2, aegis128x4
//                   aegis256,  aegis256x2, aegis256x4
//
// Call example:
//   gen_aegis_facade_fns!(aegis128l);
macro_rules! gen_aegis_facade_fns {
  ($algo:ident) => {
    use crate::auth_tag::*;
    use crate::easy::*;

    // AEGIS-128 variants -> Key128 / Nonce128
    // AEGIS-256 variants -> Key256 / Nonce256
    type Key = crate::facade::aegis_key_type!($algo);
    type Nonce = crate::facade::aegis_nonce_type!($algo);

    #[doc = include_str!("rustdoc/encrypt.md")]
    pub fn encrypt<Tag: AuthTag>(
      plaintext: Plaintext,
      associated_data: AssociatedData,
      key: &Key,
      nonce: Nonce,
    ) -> Result<Vec<u8>> {
      let pt_len = plaintext.as_bytes().len();
      // SAFETY: Cannot overflow, Rust guarantees slice length <= isize::MAX.
      let ct_with_tag_len = pt_len + size_of::<Tag>();

      // SAFETY: Vec panics if number of bytes is > isize::MAX.
      // SECURITY: This IF check is safe WRT timing attacks because:
      // - It is based on the _lengths_ of the plaintext and auth tag
      // and those lengths are not secret.
      if ct_with_tag_len > isize::MAX as usize {
        return Err(Error::InputBufferWrongSize);
      }

      let mut ciphertext_with_tag = vec![0; ct_with_tag_len];
      let ciphertext_slice = &mut ciphertext_with_tag[..pt_len];

      let tag = encrypt_to_slice_detached::<Tag>(
        plaintext,
        associated_data,
        key,
        nonce,
        CiphertextMut::new(ciphertext_slice),
      )?;

      // Can't panic, ciphertext_with_tag.len() was calculated above to include
      // `size_of::<Tag>()`.
      ciphertext_with_tag[pt_len..].copy_from_slice(tag.as_ref());
      Ok(ciphertext_with_tag)
    }

    #[doc = include_str!("rustdoc/decrypt.md")]
    pub fn decrypt<Tag: AuthTag>(
      ciphertext_with_tag: Ciphertext,
      associated_data: AssociatedData,
      key: &Key,
      nonce: Nonce,
    ) -> Result<Vec<u8>> {
      let ct_len = ciphertext_with_tag.as_bytes().len();

      // SECURITY: This IF check is safe WRT timing attacks because:
      // - It is based on the _lengths_ of the ciphertext, nonce and auth tag
      // (see `pt_len` calculation above) and those lengths are not secret.
      let Some(pt_len) = ct_len.checked_sub(Tag::BYTES) else {
        return Err(Error::InputBufferWrongSize);
      };

      let ciphertext_slice = &ciphertext_with_tag.as_bytes()[..pt_len];
      let tag_slice = &ciphertext_with_tag.as_bytes()[pt_len..];
      let mut tag = Tag::default();
      // `tag` len is guaranteed to equal `tag_slice` len because `pt_len` is
      // calculated by subtracting `Tag::BYTES`. The copy will most likely
      // be optimized out and if not it's 8-16 bytes, no big deal.
      tag.as_mut().copy_from_slice(tag_slice);

      decrypt_detached(
        Ciphertext::new(ciphertext_slice),
        &tag,
        associated_data,
        key,
        nonce,
      )
    }

    #[doc = include_str!("rustdoc/encrypt_detached.md")]
    pub fn encrypt_detached<Tag: AuthTag>(
      plaintext: Plaintext,
      associated_data: AssociatedData,
      key: &Key,
      nonce: Nonce,
    ) -> Result<(Vec<u8>, Tag)> {
      let mut ciphertext = vec![0; plaintext.as_bytes().len()];
      let tag = encrypt_to_slice_detached(
        plaintext,
        associated_data,
        key,
        nonce,
        CiphertextMut::new(&mut ciphertext),
      )?;
      Ok((ciphertext, tag))
    }

    #[doc = include_str!("rustdoc/decrypt_detached.md")]
    pub fn decrypt_detached<Tag: AuthTag>(
      ciphertext: Ciphertext,
      auth_tag: &Tag,
      associated_data: AssociatedData,
      key: &Key,
      nonce: Nonce,
    ) -> Result<Vec<u8>> {
      let mut plaintext = vec![0; ciphertext.as_bytes().len()];
      decrypt_to_slice_detached(
        ciphertext,
        auth_tag,
        associated_data,
        key,
        nonce,
        PlaintextMut::new(&mut plaintext),
      )?;
      Ok(plaintext)
    }
  };
}
pub(crate) use gen_aegis_facade_fns;

// Takes an AEGIS algo literal and returns the Key type it uses.
//
// Call example:
//
//   aegis_key_type(aegis128l)
macro_rules! aegis_key_type {
  (aegis128l) => {
    crate::easy::Key128
  };
  (aegis128x2) => {
    crate::easy::Key128
  };
  (aegis128x4) => {
    crate::easy::Key128
  };

  (aegis256) => {
    crate::easy::Key256
  };
  (aegis256x2) => {
    crate::easy::Key256
  };
  (aegis256x4) => {
    crate::easy::Key256
  };
}
pub(crate) use aegis_key_type;

// Takes an AEGIS algo literal and returns the Nonce type it uses.
//
// Call example:
//
//   aegis_nonce_type(aegis128l)
macro_rules! aegis_nonce_type {
  (aegis128l) => {
    crate::careful::Nonce128
  };
  (aegis128x2) => {
    crate::careful::Nonce128
  };
  (aegis128x4) => {
    crate::careful::Nonce128
  };

  (aegis256) => {
    crate::careful::Nonce256
  };
  (aegis256x2) => {
    crate::careful::Nonce256
  };
  (aegis256x4) => {
    crate::careful::Nonce256
  };
}
pub(crate) use aegis_nonce_type;