A robust, idiomatic Rust wrapper around the rage encryption library (the Rust implementation of age).
librage exposes a safe, simple, and consistent API for encrypting and decrypting data using X25519 keys, scrypt passphrases, SSH keys, tag recipients, and more. Every public function returns a uniform LibrageResponse<T> that can be easily serialized to JSON.
KeyGenData::secret_key) are wrapped in Zeroizing<String> and automatically zeroized on drop.Zeroizing<Vec<u8>>.LibrageError implements std::error::Error.LibrageResponse<T> with success, data, and error fields. Call .to_json() to obtain a JSON string.cargo fmt, cargo clippy -- -D warnings).Add the following to your Cargo.toml:
[dependencies]
librage = { git = "https://github.com/mroczect/librage.git" }
Required dependencies (age, serde, serde_json, thiserror, zeroize, hex) are pulled in automatically.
// examples/simple_tests.rs
use librage::*;
fn main() {
let res = generate_keypair();
assert!(res.success);
let kp = res.data.unwrap();
println!("Public key : {}", kp.public_key);
let plaintext = b"Hello, world!";
let enc = encrypt(plaintext, &kp.public_key);
assert!(enc.success);
let dec = decrypt(&enc.data.unwrap().ciphertext, &kp.secret_key);
assert!(dec.success);
println!(
"Decrypted X25519 : {}",
dec.data.unwrap().as_string()
);
let enc = encrypt_with_passphrase(b"secret data", "my password");
assert!(enc.success);
let dec = decrypt_with_passphrase(&enc.data.unwrap().ciphertext, "my password");
assert!(dec.success);
println!(
"Decrypted passphrase : {}",
dec.data.unwrap().as_string()
);
let ssh_pub = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC7iefA+0Lc9U8bU7NKov97K+I3b5DODVCow//16cpcPpBmUCCeP6f2x1lU+v30irOAy3RQ8BzZG8asG6hK95EI84RzxBPtaR5GpxRDTE3upGxZxa5ZWhjo51DLyoPduYHc35SFtDx9VtMmdk0Rc8o0CvTtsunt8Xg6ZZSoAK1C3PYS8WbkcZRUtZ88lezQ4SyYD2o+0YRWvd9ws8MGNbXAG7NOiPJA2eJDjAqBXSpDqf8t+7ULY17fjeJ9FWy7E8lgR5VbBB6zpv1UEXuG38zcVjUOEreKciKmJ+gRcay+W9sX8iR7CZKXBk6YFO1v4s3tWO1NKydl6LNa49C9AnuoXna80sIDs8Pf8A+Yn7i8I/5LnSuRtIQn5hAAsUkJxkvzAU+jbWyJbNgRJTvL98iJ2FoGVglGMLu6BhAaGCIMrRhl9KeKMOlwufSs+6j0dnyvKGa0J4L4jvyTH8pCGt0bY4zEHUIU6ZpHq22vYP3kP8qRyW8eCVceV4CCMCMZ+Uc=";
let ssh_private_key = include_str!("../tests/common/test_rsa.asc");
let enc = encrypt_with_ssh(b"data", ssh_pub);
assert!(enc.success);
let dec = decrypt_with_ssh(&enc.data.unwrap().ciphertext, ssh_private_key, None);
assert!(dec.success);
println!(
"Decrypted SSH : {}",
dec.data.unwrap().as_string()
);
}
| Function | Signature | Description |
|---|---|---|
generate_keypair |
() -> LibrageResponse<KeyGenData> |
Generates a new X25519 keypair. |
KeyGenData contains:
public_key: Stringsecret_key: Zeroizing<String> (automatically zeroized on drop)| Function | Signature | Description |
|---|---|---|
encrypt |
(plaintext: &[u8], public_key: &str) -> LibrageResponse<EncryptOutput> |
Encrypts data to a single X25519 public key. |
encrypt_armored |
(plaintext: &[u8], public_key: &str) -> LibrageResponse<EncryptOutput> |
Encrypts and returns ASCII‑armoured output (EncryptOutput.armored = true). |
encrypt_multiple |
(plaintext: &[u8], public_keys: &[&str]) -> LibrageResponse<EncryptOutput> |
Encrypts to multiple X25519 public keys. |
encrypt_multiple_armored |
(plaintext: &[u8], public_keys: &[&str]) -> LibrageResponse<EncryptOutput> |
Multiple recipients with armoured output. |
encrypt_stream |
(reader: R, writer: W, public_key: &str) -> LibrageResponse<()> |
Streaming encryption to one recipient. |
encrypt_stream_armored |
(reader: R, writer: W, public_key: &str) -> LibrageResponse<()> |
Streaming encryption with armoured output. |
decrypt |
(ciphertext: &[u8], secret_key: &str) -> LibrageResponse<DecryptOutput> |
Decrypts with an X25519 secret key (auto‑detects armour). |
decrypt_armored |
(ciphertext: &[u8], secret_key: &str) -> LibrageResponse<DecryptOutput> |
Alias for decrypt. |
decrypt_with_identities |
(ciphertext: &[u8], identities: &[Box<dyn age::Identity>]) -> LibrageResponse<DecryptOutput> |
Decrypts using multiple identities (mix of X25519 and SSH). |
decrypt_stream |
(reader: R, writer: W, secret_key: &str) -> LibrageResponse<()> |
Streaming decryption with one identity. |
decrypt_stream_with_identities |
(reader: R, writer: W, identities: &[Box<dyn age::Identity>]) -> LibrageResponse<()> |
Streaming decryption with multiple identities. |
EncryptOutput contains:
ciphertext: Zeroizing<Vec<u8>>armored: boolas_string() -> String – returns PEM if armoured, hex‑encoded otherwise.DecryptOutput contains:
plaintext: Zeroizing<Vec<u8>>as_string() -> Stringas_bytes() -> &[u8]| Function | Signature | Description |
|---|---|---|
encrypt_with_passphrase |
(plaintext: &[u8], passphrase: &str) -> LibrageResponse<EncryptOutput> |
Encrypts with a passphrase. Note: the &str is not zeroized. |
decrypt_with_passphrase |
(ciphertext: &[u8], passphrase: &str) -> LibrageResponse<DecryptOutput> |
Decrypts with a passphrase. The &str is not zeroized. |
| Function | Signature | Description |
|---|---|---|
encrypt_with_ssh |
(plaintext: &[u8], ssh_public_key: &str) -> LibrageResponse<EncryptOutput> |
Encrypts to an SSH public key (RSA or Ed25519). |
decrypt_with_ssh |
(ciphertext: &[u8], ssh_private_key: &str, passphrase: Option<&str>) -> LibrageResponse<DecryptOutput> |
Decrypts with an SSH private key. Supports encrypted keys via passphrase. |
| Function | Signature | Description |
|---|---|---|
encrypt_with_tag |
(plaintext: &[u8], tag: &str) -> LibrageResponse<EncryptOutput> |
Encrypts to a tag recipient. |
encrypt_with_tagpq |
(plaintext: &[u8], tagpq: &str) -> LibrageResponse<EncryptOutput> |
Encrypts to a tagpq recipient. |
| Function | Signature | Description |
|---|---|---|
read_recipients_from_file |
(path: impl AsRef<Path>) -> Result<Vec<String>, LibrageError> |
Reads recipients (one per line, # comments allowed). |
read_identities_from_file |
(path: impl AsRef<Path>) -> Result<Vec<Box<dyn age::Identity>>, LibrageError> |
Reads unencrypted identities (age and SSH) from a file. |
read_identities_from_file_with_passphrase |
(path, passphrase: Option<&str>) -> Result<Vec<Box<dyn age::Identity>>, LibrageError> |
Reads identities, supporting encrypted SSH keys with the given passphrase. |
read_identity_file |
(path: impl AsRef<Path>) -> Result<Vec<Box<dyn age::Identity>>, LibrageError> |
Alias for read_identities_from_file. |
read_identity_file_with_passphrase |
(path, passphrase: Option<&str>) -> Result<Vec<Box<dyn age::Identity>>, LibrageError> |
Alias for read_identities_from_file_with_passphrase. |
LibrageError is an enum with the following variants:
KeyGenFailedSerializationFailedInvalidPublicKeyInvalidSecretKeyInvalidSshKeyEncryptionFailedDecryptionFailedIo (transparently from std::io::Error)InvalidInputArmorErrorExcessiveWork { required: u8, target: u8 }NoMatchingKeysMissingRecipientsMixedRecipientAndPassphraseUnknownFormatLibrageError implements std::error::Error and From<age::EncryptError>, From<age::DecryptError>, From<age::IdentityFileConvertError>.
It also implements Into<ErrorBody> for automatic conversion to a structured error.
ErrorBody contains:
code: String – machine‑readable error code (e.g., INVALID_PUBLIC_KEY)message: String – human‑readable descriptionExample:
let res = encrypt(b"data", "bad_key");
assert!(!res.success);
let err = res.error.unwrap();
println!("Error code: {}, message: {}", err.code, err.message);
KeyGenData::secret_key is wrapped in Zeroizing<String>. Once the value goes out of scope, it is overwritten with zeros.EncryptOutput and DecryptOutput wrap their byte vectors in Zeroizing<Vec<u8>>.&str (e.g., encrypt_with_passphrase) do not zeroize the input. Use them only when the passphrase lifetime is short or the environment is trusted.age::secrecy::SecretString externally and then passing the passphrase as &str (the library will internally process it with SecretString where possible).SecretString internally, but the original &str provided by the caller is not altered. Consider reading passphrases directly into SecretString if possible.cargo fmt --all
cargo clippy -- -D warnings
cargo test
This project is licensed under the MIT License.
Contributions are welcome. Please ensure your code passes the checks above before submitting a pull request.