philbin 1.0.1

A pure Rust AEGIS library with SIMD and runtime CPU detection
Documentation
use crate::{error::Result, utils::validate_non_zero};
use std::fmt::Debug;

// Only needed by rustdoc
#[cfg(doc)]
use crate::error::Error;

/// Stores the nonce bytes used in AEGIS encryption and decryption.
///
/// <div class="warning">
///
/// **Nonce misuse is a common source of vulnerabilities.**
/// Use the APIs in the [`easy`][crate::easy] module since they handle
/// nonces internally. You'll never have to touch this type and you'll eliminate
/// a whole category of vulnerabilities.
///
/// </div>
///
/// This type takes a const generic parameter (`BYTES`) specifying the
/// number of nonce bytes. `AEGIS-128[L|X]` ciphers use 128 bit nonces while
/// `AEGIS-256[X]` ciphers use 256 bit nonces. (All ciphers validate the
/// provided `Nonce` size at compile time making it impossible to use an
/// incorrect `Nonce` size.)
///
/// Since only 128 or 256 bit nonces are valid for AEGIS ciphers, only values
/// `16` and `32` are supported for the `Nonce`'s `BYTES` const generic
/// parameter. This too is validated at compile time.
///
/// [`Nonce128`] (for `Nonce<16>`) and [`Nonce256`] (for `Nonce<32>`) type
/// aliases are provided for convenience and should be preferred over raw
/// `Nonce` usage.
///
/// **Use [`Nonce::generate()`] to securely create random `Nonce`s** instead of
/// generating nonce bytes yourself and passing them to [`Nonce::new()`]. The
/// [`Nonce::generate()`] method will use an appropriate cryptographically
/// secure random number generator (CSRNG).
///
/// `Nonce` can be converted into an owned array with [`Nonce::into_array()`].
/// There is also a [`From<Nonce>`][From] impl for the appropriate [`[u8;
/// N]`][array] type.
///
/// Convenience methods and impls to create a `Nonce` from a _borrowed_ slice
/// are intentionally omitted to make it difficult to accidentally re-use the
/// same nonce to encrypt _multiple_ plaintexts with the _same_ key since that
/// would lead to system compromise.
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct Nonce<const BYTES: usize> {
  data: [u8; BYTES],
}

impl<const BYTES: usize> Nonce<BYTES> {
  /// The length of `Nonce` in bytes.
  pub const BYTES: usize = BYTES;

  /// Create a new, random `Nonce` from a cryptographically secure random
  /// number generator (CSRNG).
  ///
  /// <div class="warning">
  ///
  /// 128 bit random nonces need to be used with care. They can safely encrypt
  /// up to 2<sup>48</sup> plaintexts using the same key with negligible
  /// (~2<sup>-33</sup>, to align with NIST guidelines) collision probability.
  ///
  /// 256 bit random nonces can be used without practical limits.
  ///
  /// </div>
  ///
  /// # Errors
  ///
  /// - Returns `Error::RandError` if there's an error in fetching random
  ///   bytes.
  #[cfg(feature = "rand")]
  pub fn generate() -> Result<Self> {
    use crate::utils::random_bytes;
    Self::new(random_bytes()?)
  }

  /// Create a new `Nonce` from a correctly sized byte array.
  ///
  /// **Prefer [`Nonce::generate()`] over this method**.
  ///
  /// # Errors
  ///
  /// - Returns [`Error::AllZeroNotAllowed`][Error] if the
  ///   provided byte slice contains only zeros. While an all-zero nonce is not
  ///   _technically_ invalid as long as it's used only once, philbin rejects it
  ///   anyway as a misuse-detection measure.
  pub fn new(data: [u8; BYTES]) -> Result<Self> {
    // Compile-time validation for correct size
    const {
      assert!(
        BYTES == 16 || BYTES == 32,
        "Only 16 or 32 byte (128 or 256 bit) nonces are supported!"
      );
    }

    // The user is almost certainly making a (catastrophic) mistake if they
    // provide an all-zero nonce, so we return an error in that case.
    validate_non_zero(data).map(|x| Self { data: x })
  }

  // Strictly for philbin-internal benchmarking so we can make apple-to-apple
  // comparisons with other implementations. NEVER USE THIS IN PROD.
  #[cfg(feature = "internal_test_only_eats_babies")]
  #[doc(hidden)]
  #[must_use]
  pub fn new_unvalidated(data: [u8; BYTES]) -> Self {
    Self { data }
  }

  /// Consumes `Nonce` and returns the internal byte array.
  #[must_use]
  pub fn into_array(self) -> [u8; BYTES] {
    self.data
  }
}

impl<const BYTES: usize> From<Nonce<BYTES>> for [u8; BYTES] {
  fn from(value: Nonce<BYTES>) -> Self {
    value.data
  }
}

/// A type alias for a 128 bit (16 byte) [`Nonce`].
pub type Nonce128 = Nonce<16>;

/// A type alias for a 256 bit (32 byte) [`Nonce`].
pub type Nonce256 = Nonce<32>;