74 lines
2.3 KiB
Markdown
74 lines
2.3 KiB
Markdown
# 🔮 Sage
|
|
**Sage** is a lightweight cryptographic library built with Rust that layers [age](https://github.com/FiloSottile/age) encryption and [minisign](https://jedisct1.github.io/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
|
|
|
|
```rust
|
|
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
|
|
|
|
```rust
|
|
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
|
|
|
|
```rust
|
|
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
|
|
|
|
```rust
|
|
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](https://github.com/FiloSottile/age) by Filippo Valsorda
|
|
- [minisign](https://jedisct1.github.io/minisign/) by Frank Denis (jedisct1)
|