🔮 signed age encryption
Find a file
2025-04-29 20:43:42 +02:00
examples init 2025-04-29 20:43:42 +02:00
src init 2025-04-29 20:43:42 +02:00
.gitignore init 2025-04-29 20:43:42 +02:00
Cargo.lock init 2025-04-29 20:43:42 +02:00
Cargo.toml init 2025-04-29 20:43:42 +02:00
README.md init 2025-04-29 20:43:42 +02:00

🔮 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 and sign.key

🔍 How It Works

Encryption Flow

  1. Sign the plaintext with minisign and add meta information (timestamp)
  2. Encrypt the signed payload using age
  3. Sign the encrypted blob with minisign again

Decryption Flow

  1. Verify the outer signature (authenticity of ciphertext)
  2. Decrypt using the age identity
  3. Verify the inner signature (authenticity of plaintext)
  4. Extract message and timestamp

🙏 Credits

  • age by Filippo Valsorda
  • minisign by Frank Denis (jedisct1)