age-setup
Simple, secure X25519 keypair generation for age encryption — with validation and automatic memory zeroization.
Table of Contents
- Features
- Installation
- Quick Start
- Usage Examples
- API Reference
- Security Features
- Development
- Contributing
- License
- Credits
Features
- Simple API — One function call generates a complete keypair
- Secure by Default — Automatic memory zeroization on drop
- Validated Keys — Public keys guaranteed to start with
"age1" - Privacy-First — Secret keys redacted in
Displayoutput - Zero Config — Works out of the box with sensible defaults
- Well-Tested — Comprehensive test coverage across all modules
- Fast — Built on the battle-tested
agecrate - Documented — Full API documentation with examples
Installation
Add age-setup to your Cargo.toml:
[]
= "0.1"
Or use cargo add:
Minimum Supported Rust Version (MSRV): 1.70.0
Quick Start
Generate an age keypair in just two lines:
use build_keypair;
That's it! You now have a cryptographically secure X25519 keypair ready for age encryption.
Usage Examples
Basic Key Generation
The simplest way to generate a keypair:
use build_keypair;
let keypair = build_keypair.expect;
// Public key is validated and guaranteed to start with "age1"
assert!;
println!;
Error Handling
Handle different error types explicitly:
use ;
match build_keypair
Accessing Keys
use build_keypair;
let keypair = build_keypair?;
// Public key (safe to display)
let public_str: &str = keypair.public.expose;
let public_owned: String = keypair.public.to_string;
// Secret key (handle with care!)
let secret_str: &str = keypair.secret.expose_secret;
// Convert to AsRef<str> for compatibility
print_key;
// Secret is automatically zeroized when dropped
// <-- Memory is securely wiped here
Integration with age Encryption
use build_keypair;
use Write;
let keypair = build_keypair?;
// age::encrypt expects a recipient; you'll need to convert the public key
// with age::x25519::Recipient::from_str (but that's outside this crate)
// This example is illustrative.
let encrypted_data = "pretend_encrypted"; // real usage would call age::encrypt
// Save the secret key securely
let key_path = home_dir.unwrap.join;
write?;
// Set restrictive permissions (Unix only)
API Reference
Functions
build_keypair()
Generates a new age X25519 keypair.
Returns:
Ok(KeyPair)— A new keypair with validated public keyErr(Error)— If generation or validation fails
Example:
let keypair = build_keypair?;
Types
KeyPair
A keypair consisting of an age public key and its corresponding secret key.
Fields:
public: PublicKey— The public key (starts with "age1")secret: SecretKey— The secret key (zeroized on drop)
Example:
let keypair = build_keypair?;
println!;
println!; // Prints: [REDACTED]
PublicKey
An age public key, guaranteed to start with "age1".
;
Methods:
expose(&self) -> &str
Returns the raw string representation of the public key.
let public_str = keypair.public.expose;
assert!;
Traits Implemented:
Display— Prints the public keyDebug— Debug representationClone— Can be cloned safelyAsRef<str>— Convert to string reference
SecretKey
An age secret key that securely wipes its memory when dropped.
;
Methods:
expose_secret(&self) -> &str
Exposes the raw secret key as a string.
⚠️ Security Warning: Only use this when absolutely necessary, as it exposes the secret.
let secret_str = keypair.secret.expose_secret;
// Use secret_str with caution!
Traits Implemented:
Display— Prints[REDACTED]instead of the actual secretDebug— Debug representation (does not expose secret)Clone— Can be cloned (new copy is also zeroized on drop)Drop— Automatically zeroizes memory when dropped
Error Types
Error
Main error type for the crate.
Variants:
Error::Generation
Error during key generation.
Error::Validation
Error validating public/secret key format.
Example:
use PublicKey;
let result = new;
assert!; // Does not start with "age1"
All errors implement:
std::error::ErrorDisplay— Human-readable error messagesDebug— Detailed debug information
Security Features
Memory Zeroization
The SecretKey type automatically zeroes its memory when dropped, preventing secrets from lingering in RAM.
// <-- Memory is securely overwritten with zeros here
Implementation:
- Uses the
zeroizecrate - Compiler optimizations cannot remove the zeroing operation
- Applies to both the original and cloned instances
Display Redaction
Secret keys are automatically redacted when printed:
let keypair = build_keypair?;
println!; // Prints: [REDACTED]
println!; // Prints: SecretKey { ... }
// Only expose_secret() shows the actual secret
println!; // Prints actual key
This prevents accidental logging or display of sensitive material.
Public Key Validation
All public keys are validated to ensure they start with the "age1" prefix:
use PublicKey;
// Valid key
let valid = new?;
// Invalid key (will return error)
let invalid = new;
assert!;
Validation Rules:
- Must not be empty
- Must start with "age1"
- Automatically applied during keypair generation
Note: The validation function
validate_age_prefixis internal (pub(crate)) and not exposed to end users. It is used only insidePublicKey::new.
Development
Project Structure
age-setup/
├── src/
│ ├── errors.rs # Error types (Error, GenerationError, ValidationError)
│ ├── generator.rs # Keypair generation (build_keypair)
│ ├── keypair.rs # KeyPair struct
│ ├── lib.rs # Public API re-exports
│ ├── public_key.rs # PublicKey type
│ ├── secret_key.rs # SecretKey type (with zeroization)
│ ├── security.rs # wipe_memory utility
│ └── validation.rs # Internal validate_age_prefix (pub(crate))
├── tests/
│ └── integration_test.rs # Integration tests
├── benches/
│ └── keygen.rs # Benchmarks using Criterion
├── Cargo.toml
└── README.md
Building
# Clone the repository
# Build the library
# Build with optimizations
# Build documentation
Testing
The library has comprehensive test coverage across all modules.
# Run all tests
# Run tests with output
# Run tests for a specific module
# Run tests with coverage (requires cargo-tarpaulin)
Test Coverage:
- Keypair generation
- Public key validation
- Secret key zeroization
- Display implementations
- Error handling
- Type conversions
Benchmarking
Performance benchmarks are available using Criterion:
# Run benchmarks
# Run specific benchmark (if multiple)
# Generate benchmark report
Example Benchmark Results (Apple M1):
keygen/build_keypair time: [45.2 µs 45.8 µs 46.5 µs]
Contributing
Contributions are welcome! Here's how you can help:
-
Fork the repository
-
Create a feature branch
-
Make your changes
- Write tests for new functionality
- Ensure all tests pass:
cargo test - Format code:
cargo fmt - Run clippy:
cargo clippy
-
Commit your changes
Use Conventional Commits format:
feat:— New featurefix:— Bug fixdocs:— Documentation changestest:— Test additions/changesrefactor:— Code refactoring
-
Push and create a Pull Request
Code of Conduct
Please be respectful and constructive in all interactions. We're here to build great software together. See CODE_OF_CONDUCT.md.
License
MIT License — see LICENSE for details.
This library is free to use in both open-source and commercial projects.
Copyright (c) 2026 neuxdotdev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
Credits
- Built on the excellent
agecrate by @str4d - Uses
zeroizefor secure memory handling - Inspired by the simplicity of the age specification
Related Projects
- age — A simple, modern, and secure file encryption tool
- rage — A Rust implementation of age
- age-plugin — Framework for age plugins
FAQ
Why use this instead of calling age directly?
age-setup provides:
- Validation — Ensures public keys always have the correct format
- Security — Automatic memory zeroization of secrets
- Simplicity — Single function call instead of multiple steps
- Safety — Type-safe wrappers prevent misuse
Is this production-ready?
Yes! The library:
- Uses battle-tested dependencies (
age,zeroize) - Has comprehensive test coverage
- Follows Rust security best practices
- Is actively maintained
How do I save keys to disk?
use build_keypair;
use fs;
let keypair = build_keypair?;
// Save public key
write?;
// Save secret key (with proper permissions!)
let secret_content = format!;
write?;
Can I use this with async code?
Yes! Key generation is CPU-bound and very fast (~45µs), so you can call it directly or use spawn_blocking:
spawn_blocking.await??;
Repository: https://github.com/neuxdotdev/age-setup
Issues: https://github.com/neuxdotdev/age-setup/issues
Crates.io: https://crates.io/crates/age-setup
Documentation: https://docs.rs/age-setup
Made with ❤️ by neuxdotdev