Crate anubis_age

Crate anubis_age 

Source
Expand description

§Anubis Rage - Post-Quantum Secure File Encryption

Quantum-resistant file encryption using ML-KEM-1024 (NIST FIPS 203)

Anubis Rage is a modern, simple, and secure file encryption tool and library that implements post-quantum cryptography to protect your files against both current and future threats, including attacks from quantum computers.

§Table of Contents

§Quick Start

§Library Usage

use std::io::{Read, Write};

// Generate Hybrid keypair (X25519 + ML-KEM-1024) - RECOMMENDED
let identity = age::pqc::hybrid::Identity::generate();
let recipient = identity.to_public();

// Encrypt (uses both X25519 and ML-KEM-1024)
let plaintext = b"Secret message with defense-in-depth security!";
let encryptor = age::Encryptor::with_recipients(vec![&recipient as _])?;
let mut ciphertext = vec![];
let mut writer = encryptor.wrap_output(&mut ciphertext)?;
writer.write_all(plaintext)?;
writer.finish()?;

// Decrypt (both X25519 and ML-KEM-1024 must succeed)
let decryptor = age::Decryptor::new(&ciphertext[..])?;
let mut decrypted = vec![];
let mut reader = decryptor.decrypt(vec![&identity as _])?;
reader.read_to_end(&mut decrypted)?;

assert_eq!(decrypted, plaintext);

§CLI Tool

# Install
cargo install anubis-rage

# Generate a key
anubis-rage-keygen -o key.txt

# Encrypt a file
anubis-rage -r $(grep -o 'anubis1[^"]*' key.txt) -o secret.txt.anubis secret.txt

# Decrypt a file
anubis-rage -d -i key.txt -o decrypted.txt secret.txt.anubis

§What is Anubis Rage?

Anubis Rage is a post-quantum secure file encryption tool based on ML-KEM-1024 (Module-Lattice-Based Key-Encapsulation Mechanism), standardized as NIST FIPS 203.

§Key Features

  • Post-Quantum Security: NIST Category 5 (maximum security level)
  • Simple & Modern: Small explicit keys, no config files, UNIX-style composability
  • Streaming Encryption: Handles files of any size with constant memory usage
  • Authenticated Encryption: AES-256-GCM-SIV or ChaCha20-Poly1305 AEAD
  • Forward Secrecy: Ephemeral key encapsulation per recipient
  • Standards Compliant: NIST FIPS 203, FIPS 198-1, SP 800-56C

§Why Post-Quantum Cryptography?

Quantum computers (when built at sufficient scale) will break current public-key cryptography:

  • Shor’s Algorithm: Breaks RSA, ECDSA, ECDH in polynomial time
  • Grover’s Algorithm: Halves symmetric key security (256-bit → 128-bit effective)

Anubis Rage uses lattice-based cryptography (ML-KEM-1024) which resists both classical and quantum attacks, ensuring your encrypted files remain secure for decades to come.

§Security Guarantees

§Cryptographic Security

PropertyStatus
Confidentiality✅ IND-CCA2 secure (ML-KEM-1024)
Integrity✅ Authenticated encryption (AEAD)
Forward Secrecy✅ Ephemeral key wrapping
Post-Quantum✅ NIST Category 5 (256-bit quantum security)
Classical Security✅ 256-bit equivalent (AES-256)

§NIST Category 5 Security Level

Anubis Rage achieves NIST Category 5 - the highest security classification:

  • Classical Attack Cost: 2^256 operations (equivalent to AES-256)
  • Quantum Attack Cost: > 2^170 quantum gates (exceeds NIST requirement)
  • Public Key Size: 2592 bytes
  • Secret Key Size: 4736 bytes
  • Ciphertext Overhead: 1568 bytes + 64-byte SHA-512 MAC

§Installation

§As a Library

Add to your Cargo.toml:

[dependencies]
anubis-rage = "1.0"

§As a CLI Tool

cargo install anubis-rage

This installs three binaries:

  • anubis-rage: Encryption/decryption tool
  • anubis-rage-keygen: Key generation utility
  • anubis-rage-sign: Digital signature tool (ML-DSA-87)

§Library Usage

§Basic Encryption/Decryption

use anubis_rage::{pqc::mlkem, Encryptor, Decryptor};
use std::io::{Read, Write};

// Generate keypair
let identity = mlkem::Identity::generate();
let recipient = identity.to_public();

// Encrypt data
let plaintext = b"Top secret quantum-safe data";
let encryptor = Encryptor::with_recipients(vec![&recipient as _])?;
let mut ciphertext = vec![];
let mut writer = encryptor.wrap_output(&mut ciphertext)?;
writer.write_all(plaintext)?;
writer.finish()?;

// Decrypt data
let decryptor = Decryptor::new(&ciphertext[..])?;
let mut decrypted = vec![];
let mut reader = decryptor.decrypt(vec![&identity as _])?;
reader.read_to_end(&mut decrypted)?;

assert_eq!(decrypted, plaintext);

§Multi-Recipient Encryption

use anubis_rage::{pqc::mlkem, Encryptor};

// Generate keys for multiple recipients
let alice = mlkem::Identity::generate();
let bob = mlkem::Identity::generate();
let carol = mlkem::Identity::generate();

let recipients = vec![
    &alice.to_public() as &dyn anubis_rage::Recipient,
    &bob.to_public() as &dyn anubis_rage::Recipient,
    &carol.to_public() as &dyn anubis_rage::Recipient,
];

// Encrypt to all recipients (any can decrypt)
let encryptor = Encryptor::with_recipients(recipients)?;
// ... encrypt data ...

§Async I/O Support

use anubis_rage::{pqc::mlkem, Encryptor};
use futures::io::AsyncWriteExt;

let identity = mlkem::Identity::generate();
let recipient = identity.to_public();

let encryptor = Encryptor::with_recipients(vec![&recipient as _])?;
let mut encrypted = vec![];
let mut writer = encryptor.wrap_async_output(&mut encrypted).await?;
writer.write_all(b"Async quantum-safe encryption!").await?;
writer.close().await?;

§Command-Line Usage

§Key Generation

# Generate new ML-KEM-1024 keypair
anubis-rage-keygen -o identity.txt
chmod 600 identity.txt

# Extract public key
grep "public key:" identity.txt

§Encryption

# Encrypt to a recipient
anubis-rage -r RECIPIENT_PUBLIC_KEY -o file.anubis file.txt

# Encrypt to multiple recipients
anubis-rage -r alice.pub -r bob.pub -o file.anubis file.txt

# Encrypt with ASCII armor
anubis-rage -r RECIPIENT --armor -o file.anubis file.txt

# Pipe encryption
cat file.txt | anubis-rage -r RECIPIENT > file.anubis

§Decryption

# Decrypt with identity file
anubis-rage -d -i identity.txt -o output.txt file.anubis

# Pipe decryption
cat file.anubis | anubis-rage -d -i identity.txt > output.txt

§File Format

Anubis Rage uses the anubis-encryption.org/v1 file format:

anubis-encryption.org/v1
-> MLKEM-1024
[2144-char base64: ML-KEM-1024 encapsulated key (1568 bytes)]
[76-char base64: wrapped file key with ChaCha20-Poly1305 (44 bytes)]
--- [86-char base64: SHA-512 HMAC header MAC (64 bytes)]
[encrypted payload with AES-256-GCM-SIV or ChaCha20-Poly1305]

§Size Overhead

  • Fixed header: ~2.4 KB
  • Per recipient: ~2.1 KB
  • Total: ~2.4 KB + (num_recipients × 2.1 KB)

For files >100 KB, overhead is <2%. For files >1 MB, overhead is <0.2%.

§Cryptographic Stack

Anubis Rage uses NIST Category 5 (maximum strength) cryptography throughout:

ComponentAlgorithmSecurity LevelStandard
Key EncapsulationML-KEM-1024Cat. 5 (256-bit)NIST FIPS 203
Key DerivationHKDF-SHA512256-bitSP 800-56C Rev. 2
Message AuthHMAC-SHA512256-bitFIPS 198-1
AEAD (Default)AES-256-GCM-SIV256-bitRFC 8452
AEAD (Alt)ChaCha20-Poly1305256-bitRFC 8439
Random GenerationOS CSPRNGSystem/dev/urandom

§Key Derivation Process

1. ML-KEM-1024.Encapsulate(recipient_pk) → (ciphertext, shared_secret)
2. wrap_key = HKDF-SHA512-Expand(
     HKDF-SHA512-Extract(
       salt: recipient_pk || ciphertext,
       IKM: shared_secret
     ),
     info: "anubis-encryption.org/v1/MLKEM-1024",
     L: 32 bytes
   )
3. encrypted_file_key = ChaCha20-Poly1305.Encrypt(wrap_key, file_key)
4. payload = AES-256-GCM-SIV.Encrypt(file_key, plaintext)

§Performance

Benchmarks on Apple M1 with 2.0 GB video file:

OperationThroughputTime
Encryption~187 MB/s10.97s
Decryption~159 MB/s12.89s
Key GenerationN/A~2ms

§Cryptographic Operation Timing

  • ML-KEM-1024 KeyGen: ~2ms
  • ML-KEM-1024 Encapsulate: ~0.5ms
  • ML-KEM-1024 Decapsulate: ~0.6ms
  • HKDF-SHA512 Derive: <0.1ms
  • File Encryption: I/O-bound (~170 MB/s)

§Memory Usage

  • Encryption: ~64 KB constant (streaming)
  • Decryption: ~64 KB constant (streaming)
  • Key Storage: ~5 KB per identity

Files of any size can be encrypted with constant memory usage.

§Examples

§File Encryption

use anubis_rage::{pqc::mlkem, Encryptor};
use std::fs::File;
use std::io::{copy, Write};

fn encrypt_file(
    input_path: &str,
    output_path: &str,
    recipient: &mlkem::Recipient
) -> Result<(), Box<dyn std::error::Error>> {
    let encryptor = Encryptor::with_recipients(vec![recipient as _])?;

    let mut input = File::open(input_path)?;
    let output = File::create(output_path)?;
    let mut writer = encryptor.wrap_output(output)?;

    copy(&mut input, &mut writer)?;
    writer.finish()?;

    Ok(())
}

§Streaming Large Files

use anubis_rage::{pqc::mlkem, Encryptor, Decryptor};
use std::io::{Read, Write, copy};

let identity = mlkem::Identity::generate();
let recipient = identity.to_public();

// Encrypt a large file with constant memory
let encryptor = Encryptor::with_recipients(vec![&recipient as _])?;
let input = std::fs::File::open("large-file.bin")?;
let output = std::fs::File::create("large-file.bin.anubis")?;
let mut writer = encryptor.wrap_output(output)?;
std::io::copy(&mut input.take(u64::MAX), &mut writer)?;
writer.finish()?;

// Decrypt with constant memory
let encrypted = std::fs::File::open("large-file.bin.anubis")?;
let decryptor = Decryptor::new(encrypted)?;
let mut reader = decryptor.decrypt(vec![&identity as _])?;
let mut output = std::fs::File::create("large-file-decrypted.bin")?;
std::io::copy(&mut reader, &mut output)?;

§Identity File Management

use anubis_rage::{IdentityFile, pqc::mlkem};
use std::fs::File;
use std::io::Write;

// Generate and save identity
let identity = mlkem::Identity::generate();
let recipient = identity.to_public();

let identity_file = format!(
    "# Anubis Rage ML-KEM-1024 identity\n\
     {}",
    recipient.to_string(),
    identity.to_string()
);

let mut file = File::create("identity.txt")?;
file.write_all(identity_file.as_bytes())?;

§Module Organization

  • pqc: Post-quantum cryptography (ML-KEM-1024, ML-DSA-87)
  • Encryptor: Encryption API
  • Decryptor: Decryption API
  • armor: ASCII armoring support
  • stream: Low-level streaming encryption primitives
  • fips: FIPS 140-3 compliance and self-tests

§Platform Support

Anubis Rage runs on all platforms supported by Rust and liboqs:

  • ✅ Linux (x86_64, aarch64)
  • ✅ macOS (Intel, Apple Silicon)
  • ✅ Windows (x86_64)
  • ✅ BSD (FreeBSD, OpenBSD, NetBSD)
  • ✅ Android / iOS (via FFI)

§Standards Compliance

  • NIST FIPS 203: ML-KEM (Module-Lattice-Based KEM)
  • NIST FIPS 204: ML-DSA (Module-Lattice-Based Digital Signature Algorithm)
  • NIST FIPS 198-1: HMAC (Keyed-Hash Message Authentication Code)
  • NIST SP 800-56C Rev. 2: Key Derivation (HKDF)
  • RFC 8452: AES-GCM-SIV (Authenticated Encryption)
  • RFC 8439: ChaCha20-Poly1305 (Authenticated Encryption)
  • RFC 4648: Base64 Encoding

§License

Licensed under either of:

at your option.

§Contributing

Contributions are welcome! Please see CONTRIBUTING.md.

For security issues, see SECURITY.md or email security@anubis-rage.org.

Re-exports§

pub use anubis_core::secrecy;

Modules§

armorarmor
I/O helper structs for the age ASCII armor format.
cli_commoncli-common
Common helpers for CLI binaries.
fips
FIPS 140-3 compliance module
pqcpqc-mlkem
Post-quantum cryptography (PQC) using ML-KEM (formerly Kyber).
stream
I/O helper structs for age file encryption and decryption.

Structs§

Decryptor
Decryptor for an age file.
Encryptor
Encryptor for creating an age file.
IdentityFile
A list of identities that has been parsed from some input file.
NoCallbacks
An implementation of Callbacks that does not allow callbacks.

Enums§

DecryptError
The various errors that can be returned during the decryption process.
EncryptError
The various errors that can be returned during the encryption process.
IdentityFileConvertError
Errors returned when converting an identity file to a recipients file.

Traits§

Callbacks
Callbacks that might be triggered during encryption or decryption.
Identity
A private key or other value that can unwrap an opaque file key from a recipient stanza.
Recipient
A public key or other value that can wrap an opaque file key to a recipient stanza.

Functions§

decrypt
Decrypts the given ciphertext with the given identity.
encrypt
Encrypts the given plaintext to the given recipient.
encrypt_and_armorarmor
Encrypts the given plaintext to the given recipient, and wraps the ciphertext in ASCII armor.
localizer
Returns the Localizer to be used for localizing this library.