Skip to main content

seal_open/
seal_open.rs

1//! Demonstrates ChaCha20-Poly1305 authenticated encryption: seal any
2//! `bincode`-encodable struct into an opaque, authenticated byte blob and open
3//! it back with the same key.
4//!
5//! What ChaCha20-Poly1305 (IETF AEAD) guarantees:
6//!   - **Confidentiality** — ciphertext reveals nothing about the plaintext without the key
7//!   - **Integrity & authenticity** — any bit-level modification is detected by the Poly1305 tag
8//!   - **Semantic security** — a fresh random nonce is generated per seal call, so encrypting
9//!     the same value twice produces different ciphertexts
10//!
11//! Format: `nonce (12 B) ‖ ciphertext ‖ Poly1305 tag (16 B)`
12//!
13//! Run with:
14//! ```sh
15//! cargo run --example seal_open --features serialization
16//! ```
17
18use bincode::{Encode, Decode};
19use toolkit_zero::serialization::{seal, open};
20
21// Any struct that derives bincode::Encode + bincode::Decode can be sealed.
22#[derive(Debug, PartialEq, Encode, Decode)]
23struct Payload {
24    user:  String,
25    score: u32,
26    tags:  Vec<String>,
27}
28
29fn main() {
30    let key = "my-secret-key";
31
32    let original = Payload {
33        user:  "alice".into(),
34        score: 9001,
35        tags:  vec!["rust".into(), "crypto".into()],
36    };
37
38    println!("Original : {original:?}");
39
40    // ── Seal ──────────────────────────────────────────────────────────────────
41    // String literals and &str both work; K: AsRef<str> handles the conversion.
42    let blob = seal(&original, Some(key)).expect("seal failed");
43    println!("Sealed   : {} bytes  (nonce ‖ ciphertext ‖ Poly1305 tag)", blob.len());
44
45    // ── Semantic security ─────────────────────────────────────────────────────
46    // A fresh random 12-byte nonce is generated on every seal call, so identical
47    // plaintext + key still produces a different ciphertext each time.
48    let blob2 = seal(&original, Some(key)).expect("seal failed");
49    assert_ne!(blob, blob2, "ciphertexts should differ (different nonces)");
50    println!("Semantic security   : two seals of the same value differ ✓");
51
52    // ── Open ──────────────────────────────────────────────────────────────────
53    // Reconstructs Payload from the opaque blob using the same key.
54    let recovered: Payload = open(&blob, Some(key)).expect("open failed");
55    println!("Recovered: {recovered:?}");
56
57    assert_eq!(original, recovered, "round-trip mismatch!");
58    println!("\nRound-trip successful ✓");
59
60    // ── Wrong key rejects ─────────────────────────────────────────────────────
61    let bad: Result<Payload, _> = open(&blob, Some("wrong-key"));
62    assert!(bad.is_err(), "wrong key should fail to open");
63    println!("Wrong-key rejection ✓");
64
65    // ── Default key ───────────────────────────────────────────────────────────
66    // Pass None::<&str> to use the built-in default key.
67    let blob3 = seal(&original, None::<&str>).expect("seal with default key failed");
68    let back3: Payload = open(&blob3, None::<&str>).expect("open with default key failed");
69    assert_eq!(original, back3);
70    println!("Default-key round-trip ✓");
71}