timelock_round_trip/timelock_round_trip.rs
1//! Full encrypt → store-header → decrypt round-trip using the timelock API.
2//!
3//! Demonstrates:
4//! - Building a [`TimelockBuilder`] for the encryption path (`::encrypt()`)
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 with [`TimelockBuilder::decrypt`]
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 TimelockBuilder, 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 = TimelockBuilder::encrypt()
40 .cadence(cadence.clone())
41 .time(lock_time)
42 .precision(TimePrecision::Hour)
43 .format(TimeFormat::Hour24)
44 .salts(salts.clone())
45 .kdf(kdf)
46 .derive()
47 .expect("encryption-side key derivation failed");
48
49 println!("enc_key[:8] = {:02x?}", &enc_key.as_bytes()[..8]);
50
51 // Pack every setting — including salts and KDF params — into a compact header.
52 // This header goes into the ciphertext in plaintext; nothing here is secret.
53 let header = pack(
54 TimePrecision::Hour,
55 TimeFormat::Hour24,
56 &cadence,
57 salts,
58 kdf,
59 );
60
61 // ── Decryption side ───────────────────────────────────────────────────────
62 // Load `header` from the ciphertext and call TimelockBuilder::decrypt at the
63 // matching time slot. All settings are read from the header automatically.
64 println!("Deriving decryption key from system clock…");
65 let dec_key = TimelockBuilder::decrypt(header)
66 .derive()
67 .expect("decryption-side key derivation failed");
68
69 println!("dec_key[:8] = {:02x?}", &dec_key.as_bytes()[..8]);
70
71 // ── Verdict ───────────────────────────────────────────────────────────────
72 if enc_key.as_bytes() == dec_key.as_bytes() {
73 println!("\nKeys match ✓ — running on a Tuesday at 18:xx");
74 } else {
75 println!("\nKeys differ — not running on a Tuesday at 18:xx (expected outside that window)");
76 }
77}