anubis-age 2.0.0

Post-quantum secure encryption library with hybrid X25519+ML-KEM-1024 mode (internal dependency for anubis-rage)
Documentation
# age Rust library (Anubis Rage Edition)

**Post-quantum secure file encryption library with hybrid X25519+ML-KEM-1024 support**

---

age is a simple, modern, and secure file encryption library. This is the **Anubis Rage edition**, which extends the original age library with **defense-in-depth post-quantum cryptography** through hybrid mode combining X25519 and ML-KEM-1024.

## Features

- **🛡️ Hybrid Mode (RECOMMENDED)**: X25519 + ML-KEM-1024 defense-in-depth
- **🔐 Quantum-Resistant**: ML-KEM-1024 (NIST FIPS 203) for post-quantum security
- **🎯 Classical Algorithms**: X25519, scrypt, and SSH key support remain available
- **🚀 Simple API**: Small explicit keys, no config options, UNIX-style composability
- **⚡ High Performance**: Efficient implementations via liboqs and Rust crypto ecosystem
- **🔒 NIST Level-5**: Highest standardized post-quantum security level (256-bit equivalent)

## What's New in Anubis Rage v2.0

This crate provides a set of Rust APIs that can be used to build tools based on the age format, with **defense-in-depth post-quantum cryptography**:

### Hybrid Mode (NEW in v2.0)
- **Defense-in-Depth**: Combines X25519 ECDH with ML-KEM-1024 KEM
- **Dual-Algorithm Security**: Attacker must break BOTH algorithms
- **Industry Standard**: Same approach as Signal Protocol, TLS 1.3
- **NIST Compliant**: Follows NIST SP 800-56C Rev. 2 for hybrid KDF

### Pure Post-Quantum Mode
- **ML-KEM-1024 Recipients**: Encrypt to quantum-resistant public keys
- **ML-KEM-1024 Identities**: Decrypt with quantum-resistant private keys
- **NIST Standardized**: Implements FIPS 203 approved post-quantum KEM

### Classical Mode
- **Backward Compatible**: Still supports X25519, scrypt, and SSH keys
- **Battle-Tested**: Proven security against classical attacks

The primary consumer of these APIs is the [`anubis-rage`](https://crates.io/crates/anubis-rage) CLI tool, which provides straightforward quantum-resistant encryption and decryption of files or streams.

## Format Specification

The age format specification is at [age-encryption.org/v1](https://age-encryption.org/v1).

Anubis Rage extends this with two post-quantum recipient stanza formats:

### Hybrid Mode (RECOMMENDED)
```
-> hybrid [base64-x25519-epk] [base64-mlkem-ciphertext]
[base64-encoded-wrapped-file-key]
```

### Pure ML-KEM-1024
```
-> mlkem1024 [base64-encoded-ciphertext]
[base64-encoded-wrapped-file-key]
```

The age format was designed by [@Benjojo](https://benjojo.co.uk/) and [@FiloSottile](https://bsky.app/profile/did:plc:x2nsupeeo52oznrmplwapppl).

The reference interoperable Go implementation is available at [filippo.io/age](https://filippo.io/age).

## Installation

Add this line to your `Cargo.toml`:

```toml
anubis-age = "2.0"
```

For post-quantum features, ensure you have **liboqs** installed:

**macOS:**
```bash
brew install liboqs
```

**Ubuntu/Debian:**
```bash
sudo apt-get install cmake ninja-build
git clone https://github.com/open-quantum-safe/liboqs.git
cd liboqs && mkdir build && cd build
cmake -GNinja -DCMAKE_INSTALL_PREFIX=/usr/local ..
ninja && sudo ninja install
```

## Usage

### Hybrid Mode (RECOMMENDED - Defense-in-Depth)

```rust
use age::pqc::hybrid::{Identity, Recipient};
use age::{Encryptor, Decryptor};
use std::io::{Read, Write};

// Generate a new hybrid identity (X25519 + ML-KEM-1024)
let identity = Identity::generate();
let recipient = identity.to_public();

// Encrypt
let encryptor = Encryptor::with_recipients(vec![Box::new(recipient)])
    .expect("we provided a recipient");

let mut encrypted = vec![];
let mut writer = encryptor.wrap_output(&mut encrypted)?;
writer.write_all(b"Secret message with defense-in-depth security")?;
writer.finish()?;

// Decrypt
let decryptor = match Decryptor::new(&encrypted[..])? {
    Decryptor::Recipients(d) => d,
    _ => unreachable!(),
};

let mut decrypted = vec![];
let mut reader = decryptor.decrypt(std::iter::once(&identity as &dyn age::Identity))?;
reader.read_to_end(&mut decrypted)?;

assert_eq!(decrypted, b"Secret message with defense-in-depth security");
```

### Pure ML-KEM-1024 (Post-Quantum Only)

```rust
use age::pqc::mlkem::{Identity, Recipient};

// Generate a new ML-KEM-1024 identity (quantum-resistant only)
let identity = Identity::generate();
let recipient = identity.to_public();

// Use same Encryptor/Decryptor API as above
```

### Using X25519 (Classical)

```rust
use age::x25519;

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

// Use same Encryptor/Decryptor API as above
```

### Using Passphrase Encryption

```rust
use age::scrypt;

let identity = scrypt::Identity::new("correct horse battery staple");

// Encrypt
let encryptor = Encryptor::with_user_passphrase(
    secrecy::SecretString::new("correct horse battery staple".to_string())
);

// Decrypt using scrypt::Identity
```

## API Documentation

See the [documentation](https://docs.rs/anubis-age) for complete API details and examples.

## Feature Flags

- **`pqc-mlkem`** - Enables ML-KEM-1024 and hybrid mode support (enabled by default)
- **`armor`** - Enables the `age::armor` module for ASCII-armored age files
- **`async`** - Enables asynchronous APIs for encryption and decryption
- **`cli-common`** - Common helper functions for building age CLI tools
- **`ssh`** - Enables the `age::ssh` module for reusing SSH key files
- **`web-sys`** - WebAssembly support for passphrase work factor calculation
- **`unstable`** - In-development functionality (no stability guarantees)

## Security Considerations

### Hybrid Mode (RECOMMENDED)

Hybrid mode provides defense-in-depth by requiring an attacker to break **BOTH**:

- **X25519 ECDH**: ~128-bit classical security (discrete log problem)
- **ML-KEM-1024**: ~256-bit quantum security (Module-LWE problem)

**Key Properties**:
- **No Single Point of Failure**: If either algorithm is broken, the other still protects data
- **Future-Proof**: Protected against both classical and quantum attacks
- **Industry Standard**: Same approach used in Signal Protocol, TLS 1.3, SSH
- **NIST Compliant**: Follows NIST SP 800-56C Rev. 2 hybrid key derivation

### Pure Post-Quantum Security

ML-KEM-1024 provides:
- **IND-CCA2 security**: Indistinguishability under adaptive chosen-ciphertext attack
- **NIST Level-5**: Equivalent to AES-256 classical security
- **Quantum resistance**: Secure against Shor's and Grover's algorithms
- **Standardized**: NIST FIPS 203 compliant

### Classical Security

X25519, scrypt, and SSH support remain available for:
- Backward compatibility with existing age files
- Integration with existing SSH infrastructure
- Scenarios where post-quantum security is not required

### Recommendations

| Use Case | Recommended Mode | Rationale |
|----------|------------------|-----------|
| Long-term data protection | **Hybrid** | Defense-in-depth, no single point of failure |
| High-security scenarios | **Hybrid** | Industry best practice |
| General file encryption | **Hybrid** | Future-proof with minimal overhead |
| Legacy compatibility | X25519 | Interoperability with original age |
| Passphrase-based | scrypt | Simple, password-based encryption |

## Comparison with Original rage

| Feature | Anubis Rage v2.0 | Original rage |
|---------|------------------|---------------|
| Hybrid Mode | ✅ X25519 + ML-KEM-1024 | ❌ No |
| Post-Quantum Security | ✅ ML-KEM-1024 | ❌ No |
| Defense-in-Depth | ✅ Dual-algorithm | ❌ Single algorithm |
| NIST Standardized PQC | ✅ FIPS 203 ||
| X25519 Support | ✅ Yes | ✅ Yes |
| SSH Key Support | ✅ Yes | ✅ Yes |
| Passphrase Encryption | ✅ Yes | ✅ Yes |
| File Compatibility | ✅ Full (with age v1) | ✅ Standard age |
| Quantum Resistant | ✅ With hybrid/ML-KEM | ❌ No |

## Examples

### Multiple Recipients (Defense-in-Depth)

```rust
use age::pqc::hybrid;

let hybrid_identity = hybrid::Identity::generate();

// Encrypt to hybrid recipient (recommended)
let recipients: Vec<Box<dyn age::Recipient>> = vec![
    Box::new(hybrid_identity.to_public()),
];

let encryptor = Encryptor::with_recipients(recipients)
    .expect("we provided recipients");
```

### Streaming Encryption

```rust
use age::Encryptor;
use age::pqc::hybrid;
use std::io::Write;

let recipient = hybrid::Identity::generate().to_public();
let encryptor = Encryptor::with_recipients(vec![Box::new(recipient)])?;

let output = std::fs::File::create("encrypted.age")?;
let mut writer = encryptor.wrap_output(output)?;

// Stream data in chunks
for chunk in data_chunks {
    writer.write_all(chunk)?;
}

writer.finish()?;
```

### ASCII Armoring

```rust
use age::armor::ArmoredWriter;
use age::pqc::hybrid;

let recipient = hybrid::Identity::generate().to_public();
let encryptor = Encryptor::with_recipients(vec![Box::new(recipient)])?;

let output = Vec::new();
let armored = ArmoredWriter::wrap_output(output, age::armor::Format::AsciiArmor)?;
let mut writer = encryptor.wrap_output(armored)?;

writer.write_all(b"Secret data")?;
let armored_output = writer.finish()?.into_inner()?;

// armored_output contains ASCII-armored ciphertext
```

## Migration from v1.x

Files encrypted with v1.x (pure ML-KEM-1024) can still be decrypted by v2.0:

```rust
// Old v1.x files using pure ML-KEM-1024
let old_mlkem_identity = age::pqc::mlkem::Identity::from_string("mlkem1024-sk-...");

// Works fine in v2.0
let decryptor = Decryptor::new(old_file_data)?;
let reader = decryptor.decrypt(std::iter::once(&old_mlkem_identity))?;
```

For new encryptions, we recommend migrating to hybrid mode:

```rust
// New v2.0 hybrid mode (recommended)
let new_hybrid_identity = age::pqc::hybrid::Identity::generate();
let recipient = new_hybrid_identity.to_public();

// Encrypt with defense-in-depth
let encryptor = Encryptor::with_recipients(vec![Box::new(recipient)])?;
```

## Library Development

### Building

```bash
# Build the library
cargo build --release

# Run tests
cargo test

# Build with all features
cargo build --all-features
```

### Testing

```bash
# Run all tests
cargo test

# Test hybrid mode specifically
cargo test hybrid

# Test with sanitizers (requires nightly)
RUSTFLAGS="-Z sanitizer=address" cargo +nightly test
```

### Contributing

See [CONTRIBUTING.md](../CONTRIBUTING.md) for guidelines on:
- Code style and conventions
- Adding new features
- Localization support
- Testing requirements

## License

Licensed under either of:

 * Apache License, Version 2.0 ([LICENSE-APACHE]../LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
 * MIT license ([LICENSE-MIT]../LICENSE-MIT or http://opensource.org/licenses/MIT)

at your option.

### Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

## Acknowledgments

- **NIST** - For post-quantum cryptography standardization (FIPS 203)
- **Open Quantum Safe** - For the liboqs ML-KEM-1024 implementation
- **Signal Foundation** - For pioneering hybrid post-quantum cryptography
- **Filippo Valsorda & Ben Cox** - For designing the age format
- **Original rage contributors** - For the excellent foundation
- **Rust crypto community** - For high-quality cryptography crates

## Further Reading

- [NIST Post-Quantum Cryptography]https://csrc.nist.gov/projects/post-quantum-cryptography
- [FIPS 203: ML-KEM Standard]https://csrc.nist.gov/pubs/fips/203/final
- [NIST SP 800-56C Rev. 2: Hybrid Key Derivation]https://csrc.nist.gov/publications/detail/sp/800-56c/rev-2/final
- [Signal Protocol: PQXDH]https://signal.org/docs/specifications/pqxdh/
- [age specification v1]https://age-encryption.org/v1
- [Open Quantum Safe Project]https://openquantumsafe.org/
- [Anubis Rage GitHub]https://github.com/AnubisQuantumCipher/anubis-rage

---

**Anubis Rage v2.0** - Defense-in-depth protection for the quantum era.