Vitamin C AEAD
Authenticated Encryption with Associated Data (AEAD) primitives for building secure encryption systems.
This crate is part of the Vitamin C framework to make cryptography code healthy.
What is AEAD?
AEAD (Authenticated Encryption with Associated Data) is a form of encryption that provides both confidentiality and authenticity. It ensures that:
- Confidentiality: The plaintext is encrypted and cannot be read without the key
- Authenticity: The ciphertext cannot be modified without detection
- Associated Data: Additional data can be authenticated (but not encrypted) alongside the ciphertext
This crate provides traits and types for implementing AEAD operations in a safe and ergonomic way.
Key Features
- Composable encryption shape: The [
Cipher] trait is paired with [SeqCipher] and [MapCipher] sub-traits so a single cipher can drive byte, sequence, and map encryption with a consistent API - Visitor-pattern decryption: The [
Decrypt] / [Decipher] / [DecipherVisitor] trio mirrorsserde'sDeserialize/Deserializer/Visitor, letting types describe how they decrypt themselves independently of any specific cipher - Flexible AAD handling: The [
IntoAad] trait lets strings, byte slices, integers, tuples, and()all be used as additional authenticated data - Protected types integration: Works with
vitaminc-protectedso sensitive plaintext stays wrapped through encrypt and decrypt - Side-channel-aware errors: The [
Unspecified] error type reveals no information about the cause of a failure
Usage
Implementing the Cipher trait
A [Cipher] is consumed by the operation it drives — typically you implement it for a reference to your cipher state (&MyCipher) so the same cipher can be reused across many calls. The trait declares the output (Ok) and error types, plus associated types for sequence and map encryption:
use Any;
use ;
use Protected;
;
;
;
For a complete reference implementation see vitaminc_encrypt::Aes256Cipher.
Encrypting data
Once you have a Cipher implementation (typically for &MyCipher), use the [Encrypt] trait. Many built-in types already implement Encrypt:
use Encrypt;
// `cipher: MyCipher` where `Cipher` is implemented for `&MyCipher`.
// Encrypt a string
let encrypted_string = "secret message".encrypt?;
// Encrypt with additional authenticated data
let encrypted_with_aad = "secret".encrypt_with_aad?;
// Encrypt a byte array
let encrypted_bytes = .encrypt?;
Note that Encrypt::encrypt consumes the cipher value. Implementing Cipher for &MyCipher (rather than MyCipher) means you can pass &cipher for each call and reuse the underlying state.
Decrypting data
Decryption uses a visitor pattern modelled on serde::Deserialize:
- A type that knows how to decrypt itself implements [
Decrypt]. - A cipher provides a [
Decipher] (typically wrapping a ciphertext + cipher state) that drives the decryption. - Concrete cipher implementations expose ergonomic decrypt entry points — for example,
Aes256Cipherprovidescipher.decrypt::<T>(ciphertext)andcipher.decrypt_with_aad::<T, _>(ciphertext, aad).
// Using a concrete cipher (see `vitaminc_encrypt::Aes256Cipher`):
let plaintext: String = cipher.decrypt?;
let plaintext: String = cipher.decrypt_with_aad?;
String, Vec<u8>, [u8; N], u32, Vec<T: Decrypt>, HashMap<String, T: Decrypt>, and Protected<T: Decrypt> all implement Decrypt out of the box.
Note on maps:
HashMapdecryption yieldsHashMap<String, T>, but map encryption requires statically known keys — onlyHashMap<&'static str, T>implementsEncrypt. Map keys are passed to [MapCipher::encrypt_key], which takes a&'static str. AHashMap<String, T>with runtime-derived keys can therefore be decrypted but not encrypted directly.
Additional Authenticated Data (AAD)
Many types can be used as AAD through the [IntoAad] trait:
use Encrypt;
// String AAD
"my-secret".encrypt_with_aad?;
// Byte slice AAD
"my-secret".encrypt_with_aad?;
// Integer AAD
"my-secret".encrypt_with_aad?;
// Tuple AAD (PAE-encoded to prevent canonicalisation attacks)
"my-secret".encrypt_with_aad?;
// No AAD
"my-secret".encrypt_with_aad?;
Working with Protected Types
The crate integrates with vitaminc-protected so sensitive plaintext stays wrapped:
use Encrypt;
use Protected;
let sensitive_data = new;
let encrypted = sensitive_data.encrypt?;
The corresponding Decrypt impl for Protected<T> re-wraps the decrypted plaintext, so the value stays inside Protected end-to-end.
Custom Types
Implementing [Encrypt] and [Decrypt] for your own types lets you choose which fields are encrypted and how the ciphertext is structured.
use ;
The visitor pattern keeps the cipher and the type independent: the cipher decides how the ciphertext is laid out and how AAD is enforced, while the type decides how its fields are reassembled.
Nonce Generation
The crate provides nonce generation utilities for AEAD operations:
#
Security Considerations
- Always use unique nonces for each encryption operation with the same key
- Never reuse nonces with the same key, as this can compromise security
- The [
Unspecified] error type is used to prevent side-channel attacks by not revealing information about failures - When decrypting, always verify authentication before processing the plaintext
CipherStash
Vitamin C is brought to you by the team at CipherStash.
License: MIT