🔮 signed age encryption
examples | ||
src | ||
.gitignore | ||
Cargo.lock | ||
Cargo.toml | ||
README.md |
🔮 Sage
Sage is a lightweight cryptographic library built with Rust that layers age encryption and minisign signatures to enable signed encryption — ensuring both confidentiality and authenticity.
Sage signs a message before encryption and again after encryption, verifying both layers on decryption. This offers end-to-end message integrity, trust, and non-repudiation.
🚀 Features
- 🔐 Encryption with age
- ✍️ Dual minisign signatures (before and after encryption)
- ✅ Signature verification on both layers during decryption
- 📦 Easy key serialization and identity management
🌱 Getting Started
Generate a New Identity
let id = sage::Identity::new();
let persona = id.public();
Identity
: holds your private encryption and signing keys.Persona
: a tuple of(age_public_key, minisign_public_key)
for sharing public keys.
Encrypt and Sign
let message = b"Hello, secure world!";
let recipient = persona.enc_key().unwrap(); // Recipient's age public key
let ciphertext = id.encrypt(message, &recipient);
Verify and Decrypt
let sender_pk = persona.sign_key().unwrap(); // Sender's minisign public key
match id.decrypt(&ciphertext, &sender_pk) {
Ok(msg) => {
println!("Decrypted: {}", String::from_utf8_lossy(&msg.payload));
println!("Signed at timestamp: {}", msg.timestamp);
}
Err(err) => eprintln!("Failed to decrypt: {:?}", err),
}
📁 Saving & Loading Identities
id.save("my_keys/");
let loaded = sage::Identity::try_load("my_keys/").unwrap();
- Saves two files:
age.key
andsign.key
🔍 How It Works
Encryption Flow
- Sign the plaintext with minisign and add meta information (timestamp)
- Encrypt the signed payload using age
- Sign the encrypted blob with minisign again
Decryption Flow
- Verify the outer signature (authenticity of ciphertext)
- Decrypt using the age identity
- Verify the inner signature (authenticity of plaintext)
- Extract message and timestamp