timelock_round_trip/timelock_round_trip.rs
1//! Full encrypt → store-header → decrypt round-trip using the timelock API.
2//!
3//! Demonstrates:
4//! - Deriving an encryption key from an explicit time (`params = None` → `_at` path)
5//! - Packing all settings (precision, format, cadence, salts, KDF params) into a
6//! self-contained [`TimeLockParams`] header for plaintext storage in the ciphertext
7//! - Re-deriving the key from the live system clock (`params = Some(header)` → `_now` path)
8//!
9//! Run with:
10//! ```sh
11//! cargo run --example timelock_round_trip --features encryption
12//! ```
13//!
14//! If you run this at exactly the right minute the keys will match. Because
15//! `TimePrecision::Minute` is used the window is 60 seconds wide — any run
16//! within the same minute as the lock-time will succeed.
17
18use toolkit_zero::encryption::timelock::{
19 timelock, pack,
20 TimeLockCadence, TimeLockSalts, TimeLockTime,
21 TimePrecision, TimeFormat, KdfPreset, Weekday,
22};
23
24fn main() {
25 // ── Encryption side ───────────────────────────────────────────────────────
26 // Generate fresh salts. Salts are NOT secret — store them in plaintext
27 // alongside the ciphertext so the decryption side can reproduce the key.
28 let salts = TimeLockSalts::generate();
29
30 // Use a deliberately fast preset so the example finishes quickly.
31 // In production use KdfPreset::Balanced or stronger.
32 let kdf = KdfPreset::Balanced.params();
33
34 // Lock to any Tuesday at 18:00 (hour-precision window = the full 18:00–18:59 block).
35 let cadence = TimeLockCadence::DayOfWeek(Weekday::Tuesday);
36 let lock_time = TimeLockTime::new(18, 0).unwrap();
37
38 println!("Deriving encryption key (this may take a few seconds)…");
39 let enc_key = timelock(
40 Some(cadence.clone()),
41 Some(lock_time),
42 Some(TimePrecision::Hour),
43 Some(TimeFormat::Hour24),
44 Some(salts.clone()),
45 Some(kdf),
46 None, // params = None → _at (encryption) path
47 )
48 .expect("encryption-side key derivation failed");
49
50 println!("enc_key[:8] = {:02x?}", &enc_key.as_bytes()[..8]);
51
52 // Pack every setting — including salts and KDF params — into a compact header.
53 // This header goes into the ciphertext in plaintext; nothing here is secret.
54 let header = pack(
55 TimePrecision::Hour,
56 TimeFormat::Hour24,
57 &cadence,
58 salts,
59 kdf,
60 );
61
62 // ── Decryption side ───────────────────────────────────────────────────────
63 // Load `header` from the ciphertext and call timelock() at the matching
64 // time slot with params = Some(header).
65 println!("Deriving decryption key from system clock…");
66 let dec_key = timelock(
67 None, None, None, None, None, None,
68 Some(header), // params = Some → _now (decryption) path
69 )
70 .expect("decryption-side key derivation failed");
71
72 println!("dec_key[:8] = {:02x?}", &dec_key.as_bytes()[..8]);
73
74 // ── Verdict ───────────────────────────────────────────────────────────────
75 if enc_key.as_bytes() == dec_key.as_bytes() {
76 println!("\nKeys match ✓ — running on a Tuesday at 18:xx");
77 } else {
78 println!("\nKeys differ — not running on a Tuesday at 18:xx (expected outside that window)");
79 }
80}