philbin 1.0.1

A pure Rust AEGIS library with SIMD and runtime CPU detection
Documentation
(_Every part_ of this crate was _written by a human being._)

Philbin is a Rust library implementing the [AEGIS algorithm][aegis][^1] for
[symmetric-key encryption][sym] which was one of the winners of the [CAESAR
Competition][ceasar]. AEGIS is [_significantly_ faster](#benchmarks) and
more secure than other popular [AEAD] algorithms ([AES]-[GCM] and
[ChaCha20-Poly1305][chacha]) on [CPUs with AES hardware
acceleration][easy::is_hw_accelerated].

[^1]: Specifically, Philbin implements AEGIS according to [RFC 10032][aegis].

Philbin uses _runtime_ CPU detection to dispatch to the best [SIMD]
implementation that the CPU supports. There is _no need_ to use
`RUSTFLAGS="-C target-cpu=native"` or similar. CPU feature detection is
automatically performed _only once_ on the first call to Philbin and then
cached in memory.

A **quick API overview** is in the [Get Started with the `easy`
Module](#get-started-with-the-easy-module) section.

# API Organization

All Philbin APIs are in one of two modules:

- [`easy`] module with _simple, high-level_ APIs,
- [`careful`] module with _low-level_ APIs that are _harder to use_.

**We _strongly_ recommend using only [`easy`] APIs** as they are designed to
be maximally misuse-resistant. Using [`easy`] APIs means never having to
think about:

- Which AEGIS algorithm is "the best" for your use-case? Just use
  [`easy::encrypt`] and [`easy::decrypt`]. They're at the "sweet spot" of
  security and performance for almost every use-case and _never_ a bad
  choice.
- Correct _[nonce]_ handling? [`easy`] APIs handle nonces internally. (Using
  a nonce in a subtly wrong way is common and _catastrophic_ to security.)
- Secure random number generation? [`easy`] APIs internally use
  [_cryptographically secure_ random number generators][csrng] provided by
  your operating system and safe even if your app calls [`fork()`][fork] or
  similar.
- Which _authentication tag_ size? [`easy`] APIs use 256 bits; maximally
  secure at effectively no performance cost due to the design of AEGIS.
- Sensible payload framing for the nonce, ciphertext and auth tag?
  [`easy::encrypt`] and [`easy::decrypt`] already handle it. You get a
  [`Vec<u8>`] you can serialize anywhere.
- Etc.

# Get Started with the [`easy`] Module

    use philbin::easy::*;

    let plaintext = b"foo bar zoo";
    let associated_data = b"moo goo";
    let key = Key256::generate()?; // Or: Key256::new(), Key256::from_bytes()
    let encrypted_payload = encrypt(
      Plaintext::new(plaintext),
      AssociatedData::new(associated_data), // Or: AssociatedData::EMPTY
      &key)?;

    let decrypted_plaintext = decrypt(
      Ciphertext::new(&encrypted_payload),
      AssociatedData::new(associated_data),
      &key)?;

    assert_eq!(plaintext.as_slice(), decrypted_plaintext);
    # Ok::<(), Error>(()) // Makes the doctest compile

Notice the usage of [`Plaintext`][easy::Plaintext],
[`Ciphertext`][easy::Ciphertext] and
[`AssociatedData`][easy::AssociatedData] types. These are wrappers around
[`&[u8]`][slice] and [`&mut [u8]`][slice] that help prevent accidental
parameter swaps (e.g. like swapping _associated data_ and _plaintext_).

[`Key256`][easy::Key256] is merely an alias for [`Key<32>`][easy::Key] which
[`encrypt`][easy::encrypt] and [`decrypt`][easy::decrypt] use.

- [`Key::generate()`][easy::Key::generate] will securely create a brand new
  [`Key`][easy::Key].
- [`Key::new()`][easy::Key::new] and
  [`Key::from_bytes()`][easy::Key::from_bytes] can create one from an existing
  byte array and slice, respectively.
- [`Key::expose_secret()`][easy::Key::expose_secret] provides access to the
  key bytes.

**You now know everything you need** to use Philbin effectively. _You can
stop reading._

# The [`careful`] Module

The [`careful`] module provides direct, low-level access to each AEGIS
algorithm and should be used _with great care_.

## AEGIS Variants

[RFC 10032][aegis] defines six AEGIS encryption algorithm _variants_ which are
categorized in the following table:

|             | 128 bit key/nonce | 256 bit key/nonce |
| ----------- | ----------------- | ----------------- |
| **1x rate** | AEGIS-128L        | AEGIS-256         |
| **2x rate** | AEGIS-128X2       | AEGIS-256X2       |
| **4x rate** | AEGIS-128X4       | AEGIS-256X4       |

The AEGIS-128 family uses 128 bit keys and nonces while the AEGIS-256 family
uses 256 bit keys and nonces. The "rate" parameter defines the level of
_data parallelism_; for instance, AEGIS-128X2 processes _two_ AES blocks at a
time while AEGIS-128X4 processes _four_.

This is designed to make use of [SIMD] CPU instructions (which Philbin uses).
Note that **no additional OS threads are used** for this type of parallelism.

In general, the higher rate variants are _significantly_ faster than the lower
rate variants, but it does depend on the CPU. They can also be slower.

**Each AEGIS algorithm variant is independent** and _incompatible_ with the
other variants. You cannot encrypt data with e.g. AEGIS-256 and then decrypt it
with AEGIS-256X2; each variant produces a _different ciphertext_.

Each algorithm is exposed in a separate module: [`careful::aegis128l`],
[`careful::aegis128x2`] etc.

## Module Functions

Each AEGIS variant module contains the same set of functions. The functions
provide different ways to pass or receive data. The encryption functions are:

- `encrypt` returns the _ciphertext_ and _authentication tag_ concatenated in a
  [`Vec<u8>`].
- `encrypt_detached` returns the _ciphertext_ in a [`Vec<u8>`] and the
  _authentication tag_ as a separate ("detached") byte array.
- `encrypt_to_slice_detached` is similar to `encrypt_detached`, but writes the
  _ciphertext_ to a user-provided byte slice. It does not allocate heap memory.
- `encrypt_in_place_detached` is similar to `encrypt_to_slice_detached`, but it
  overwrites the byte slice storing the _plaintext_ with the _ciphertext_. It
  does not allocate heap memory.

Each encryption function has a corresponding decryption function. Thus there's a
`decrypt`, `decrypt_detached`, `decrypt_to_slice_detached` etc.

The desired size of the _authentication tag_ needs to be provided to the
function as a generic parameter. For example:

    use philbin::{easy::*, careful::*};

    let mut plaintext: Vec<u8> = b"foo bar zoo".into();
    let key = Key256::generate()?;

    // AuthTag specified with "turbofish" syntax
    let ciphertext = aegis256x4::encrypt::<AuthTag256>(
      Plaintext::new(&plaintext),
      AssociatedData::EMPTY,
      &key,
      Nonce::generate()?)?;

    // AuthTag specified with explicit type declaration
    let auth_tag: AuthTag256 = aegis256x4::encrypt_in_place_detached(
      PlaintextMut::new(&mut plaintext),
      AssociatedData::EMPTY,
      &key,
      Nonce::generate()?)?;
    # Ok::<(), Error>(()) // Makes the doctest compile

# Correctness and Security

Philbin is built with a heavy emphasis on correctness and security. Philbin:

- Was _entirely written by a human_ and _reviewed_ for security issues by
  fancy AIs (which found nothing of substance).
- Rejects all-zero keys and nonces since they are almost certainly
  _catastrophic_ usage errors that _will_ lead to exploitable
  vulnerabilities.[^2]
- Has full test coverage, including:
  - All [RFC 10032][aegis] test vectors.
  - [Rooterberg] test vectors.[^3] Rooterberg is a continuation of
    [Wycheproof].
  - Custom tests for interesting boundary conditions.
- Is thoroughly [fuzzed][fuzzing] under [`AddressSanitizer`][asan].
- Is [_differentially fuzzed_][diff-fuzz] against [`libaegis`][libaegis] to
  prove identical output.[^3]
- Contains [_two lines of code_ in `unsafe` blocks][unsafe] which are guarded by
  both compile-time and runtime checks.
- Contains a custom _Test Vector Leakage Assessment_ (TVLA)[^4] harness that and
  validates the implementation is constant-time regardless of secret data.
- Exposes _strongly-typed APIs_ designed to prevent common mistakes that lead to
  exploitable vulnerabilities.
- Contains rigorous error handling:
  - _Does not panic_ and is fuzzed to validate this. [`easy::Error`] is returned
    instead.
  - Painstakingly written to avoid all integer overflow and underflow.
  - Prevents inadvertent secret leakage through logs by:
    - Never including _any_ dynamic content in [`easy::Error`] messages.
    - Redacting [`Key`][easy::Key] content in [`Debug`] output.
  - [`easy::Error`] variants are carefully designed to avoid attacks similar to
    [Padding Oracle][oracle].
- Zeroizes [`Key`][easy::Key]s on [`Drop`] and this behavior cannot be disabled.

Philbin has _not_ received a professional, third-party security review.

[^2]:
    This maintainer has seen too many critical security issues that could have
    been prevented had the cryptographic library rejected all-zero keys and
    nonces.

    There are _no valid production use-cases for an all-zero key._ An all-zero
    nonce is sometimes used as the first value of a counter-based nonce; instead
    start your counter from 1 or a random number. Even better, use a randomly
    generated 256-bit nonce which AEGIS supports.
    ([`Nonce::generate()`][careful::Nonce::generate] will securely do this for
    you.)

[^3]:
    Philbin's explicit design choices like rejection of all-zero keys and
    nonces lead to a handful of differences that are fully accounted for.

[^4]:
    See _"Test Vector Leakage Assessment (TVLA) methodology in practice",_
    Becker, G. et al., 2013.

# Benchmarks

![Benchmarks showing Philbin's performance. AEGIS-128X4 is roughly nine times
faster than ChaCha20-Poly1305 and two times faster than AES-128-GCM; similar for
AEGIS-256X4. Philbin is 1% faster overall than the raw C `aegis` crate. Data was
collected across multiple CPU models, vendors and
architectures.][bench-charts]

## Benchmarking details

- The CPUs that benchmarks were run on: `AMD Ryzen 5600`, `Apple M2`, 
  `Intel Core Ultra 7 265K`, `Intel N100`, `Intel Xeon Platinum 8581C`.
- The benchmarks used `encrypt_in_place_detached` and equivalent APIs in other 
  libraries to avoid all heap memory allocation during execution.
- Benchmark execution time was normalized to the performance of either
  `ChaCha20-Poly1305` or the equivalent AEGIS variant in the `aegis` crate 
  (depending on the chart) _on the same CPU_. A _geometric mean_ then produces a
  cross-CPU speedup from many per-CPU speedups.
- Benchmarks use random 16 KiB messages; shorter messages don't accurately
  portray performance because they are affected by static setup overhead. Longer
  messages exhibit similar performance to 16 KiB messages.
- [Raw benchmark data is available][bench-data], as is the [Julia Pluto
  notebook][notebook] used to analyze the data and produce the charts.

# Comparison with [`aegis`][aegis-crate] crate

- **Pure Rust**. Philbin is 100% Rust. The `aegis` crate is just a thin
  wrapper around [`libaegis`][libaegis][^5], a raw C library.
- **Safer implementation**:
  - `libaegis` is 100% `unsafe` (in Rust terms) while Philbin has [_two lines of
    code_ in `unsafe` blocks][unsafe] which are guarded by both compile-time and
    runtime checks.
  - Philbin is [fully fuzzed]#correctness-and-security while
    `aegis`/`libaegis` contain no fuzzing infrastructure and make no
    fuzzing claims.
  - Philbin [includes a TVLA harness]#correctness-and-security that checks
    for constant-time behavior in CI. `aegis`/`libaegis` include no
    constant-time tests.
  - Philbin [_does not panic_]#correctness-and-security.
  - Philbin checks and returns errors for all-zero keys and nonces
    (since they are almost certainly _catastrophic_ usage errors).
  - Philbin is painstakingly written to avoid all integer overflow and
    underflow.
- **Safer API**. Philbin's API is designed to be hard to misuse and to
  prevent catastrophic (and yet very common) mistakes.
  [`Nonce::generate()`][careful::Nonce::generate] creates nonces from
  cryptographically safe RNGs; nonces are completely encapsulated in [`easy`]
  APIs; strong types make it _impossible_ to accidentally swap
  nonce/key/plaintext/associated data parameters; all-zero keys and nonces are
  detected and rejected, etc. `aegis` has none of this _and_ makes it easy to
  re-use a nonce across messages[^6].
- **Higher-level API**. Philbin includes an [`easy`] module with high-level APIs
  that make _many_ mistakes impossible by construction. The [`careful`] module
  exposes low-level access while still providing a significantly less
  error-prone API than `aegis`.
- **Simpler API**. Philbin's [`easy`] module provides [`encrypt`][easy::encrypt]
  and [`decrypt`][easy::decrypt] plain functions. Even [`careful`] APIs don't
  expose any algorithm traits or structs; every algorithm is just a function
  call. Philbin's API "vocabulary" is tiny and obvious.
- **Stable API**. Philbin is `>= 1.0.0`, `aegis` is `< 1.0.0`.
- **Slightly faster**. See the [Benchmarks]#benchmarks section.

Some cons compared to `aegis`/`libaegis`:

- Rust's `std` is required. Supporting `no_std` is explicitly out-of-scope for
  Philbin. If you need `no_std`, use `aegis`.
- Software-only path (no hardware acceleration) for the `powerpc` architecture,
  unlike `aegis`.
- Philbin does not (yet) provide _separate_ implementations of AEGIS [MAC]
  functions. (Philbin of course implements AEGIS MACs as part of AEGIS
  encryption routines.)

[^5]:
    The `aegis` crate includes a `pure-rust` Cargo feature (disabled by
    default), but warns that _"[enabling this feature] will substantially
    degrade performance and some features may not be available"_. The
    `pure-rust` implementation in `aegis` does _not_ use runtime CPU detection.

[^6]:
    See e.g. [struct `Aegis128L` API][aegis-api].
    You are meant to call `new(key, nonce)` and then `encrypt(m, ad)`. Users
    without cryptographic expertise would call `new()` once and `encrypt()` N
    times for N plaintexts, which is _baaaad_.

    Philbin's [`easy`] APIs intentionally don't expose nonces, while [`careful`]
    APIs take a [`Nonce`][careful::Nonce] by-value to prevent nonce reuse.

# Cargo Features

This crate exposes only one Cargo feature to users (`rand`) which is enabled
by default. This feature pulls in the [`rand`][rand] crate as a dependency.
**This feature is required by several APIs in the [`easy`] module,** not
least of which is [`easy::encrypt`].

Other than [`Nonce::generate()`][careful::Nonce::generate], no APIs in
[`careful`] depend on `rand`.

Philbin requires Rust's `std`; support for `no_std` is explicitly out-of-scope.

# MSRV

The crate's Minimum Supported Rust Version (MSRV) is `1.95`. **We do not
consider MSRV increments to be [SemVer][semver] breaking changes.** That being
said, we plan to increase MSRV rarely and for good reasons.

[aegis]: https://www.rfc-editor.org/rfc/rfc10032.html
[sym]: https://en.wikipedia.org/wiki/Symmetric-key_algorithm
[ceasar]: https://en.wikipedia.org/wiki/CAESAR_Competition
[aead]: https://en.wikipedia.org/wiki/Authenticated_encryption
[chacha]: https://en.wikipedia.org/wiki/ChaCha20-Poly1305
[simd]: https://en.wikipedia.org/wiki/Single_instruction,_multiple_data
[fuzzing]: https://en.wikipedia.org/wiki/Fuzzing
[diff-fuzz]: https://en.wikipedia.org/wiki/Differential_testing
[timing]: https://en.wikipedia.org/wiki/Timing_attack
[unsafe]: https://codeberg.org/Valloric/philbin/search/branch/main?path=philbin&q=unsafe+%7B&mode=exact
[nonce]: https://en.wikipedia.org/wiki/Cryptographic_nonce
[rand]: https://crates.io/crates/rand
[csrng]: https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator
[fork]: https://en.wikipedia.org/wiki/Fork_(system_call)
[aes]: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
[gcm]: https://en.wikipedia.org/wiki/Galois/Counter_Mode
[aegis-crate]: https://crates.io/crates/aegis
[aegis-api]: https://docs.rs/aegis/latest/aegis/aegis128l/struct.Aegis128L.html
[libaegis]: https://github.com/aegis-aead/libaegis
[mac]: https://en.wikipedia.org/wiki/Message_authentication_code
[unsafe]: https://codeberg.org/Valloric/philbin/search/branch/main?path=philbin&q=unsafe+%7B&mode=exact
[rooterberg]: https://github.com/bleichenbacher-daniel/Rooterberg
[wycheproof]: https://github.com/C2SP/wycheproof
[oracle]: https://en.wikipedia.org/wiki/Padding_oracle_attack
[asan]: https://github.com/google/sanitizers/wiki/addresssanitizer
[semver]: https://semver.org/
[bench-charts]: https://codeberg.org/Valloric/philbin/media/branch/main/benchmarks/analysis/benchmarks.svg
[bench-data]: https://codeberg.org/Valloric/philbin/src/branch/main/benchmarks/data
[notebook]: https://codeberg.org/Valloric/philbin/src/branch/main/benchmarks/analysis/charts.jl