chie_crypto/
forward_secure.rs

1//! Forward-Secure Signatures for key evolution and retroactive security.
2//!
3//! Forward-secure signatures ensure that even if the current secret key is compromised,
4//! signatures created in previous time periods remain secure and unforgeable.
5//!
6//! # Use Cases in CHIE Protocol
7//!
8//! - **Long-Running P2P Nodes**: Protect historical bandwidth proofs even if current key leaks
9//! - **Audit Trails**: Ensure past signatures remain valid even after key compromise
10//! - **Progressive Security**: Periodically evolve keys to limit damage from future compromises
11//!
12//! # Protocol
13//!
14//! 1. **Key Evolution**: Secret key evolves through one-way function after each period
15//! 2. **Signature Generation**: Sign with current period's key
16//! 3. **Key Update**: Securely delete old key after evolution
17//! 4. **Verification**: Verify signature with public key and time period
18//!
19//! # Security Guarantee
20//!
21//! If an attacker obtains the secret key at period `t`, they cannot:
22//! - Forge signatures for periods `< t` (forward security)
23//! - They can forge for periods `>= t` (but this is unavoidable)
24//!
25//! # Example
26//!
27//! ```
28//! use chie_crypto::forward_secure::{ForwardSecureKeypair, ForwardSecureSignature};
29//!
30//! // Generate keypair with max 100 time periods
31//! let mut keypair = ForwardSecureKeypair::generate(100);
32//! let public_key = keypair.public_key().clone();
33//!
34//! // Sign message in period 0
35//! let message = b"bandwidth proof at time 0";
36//! let sig0 = keypair.sign(message).unwrap();
37//! assert_eq!(sig0.period(), 0);
38//!
39//! // Verify signature
40//! assert!(sig0.verify(message, &public_key).is_ok());
41//!
42//! // Evolve to next period (old key is securely deleted)
43//! keypair.evolve().unwrap();
44//!
45//! // Sign in period 1
46//! let sig1 = keypair.sign(b"proof at time 1").unwrap();
47//! assert_eq!(sig1.period(), 1);
48//!
49//! // Old signature still verifies
50//! assert!(sig0.verify(message, &public_key).is_ok());
51//!
52//! // Cannot forge signatures for period 0 even with current key
53//! ```
54
55use crate::signing::{KeyPair, PublicKey, verify as signing_verify};
56use blake3::Hasher;
57use serde::{Deserialize, Serialize};
58use thiserror::Error;
59use zeroize::Zeroize;
60
61#[derive(Error, Debug)]
62pub enum ForwardSecureError {
63    #[error("Maximum time period reached")]
64    MaxPeriodReached,
65    #[error("Invalid signature")]
66    InvalidSignature,
67    #[error("Period mismatch: signature period {sig_period} != expected {expected_period}")]
68    PeriodMismatch {
69        sig_period: u64,
70        expected_period: u64,
71    },
72    #[error("Serialization error: {0}")]
73    Serialization(String),
74    #[error("Key evolution error")]
75    EvolutionError,
76}
77
78pub type ForwardSecureResult<T> = Result<T, ForwardSecureError>;
79
80/// Forward-secure signature with embedded time period
81#[derive(Clone, Serialize, Deserialize)]
82pub struct ForwardSecureSignature {
83    /// The actual signature (stored as Vec for serialization)
84    signature: Vec<u8>,
85    /// Time period when signature was created
86    period: u64,
87}
88
89impl ForwardSecureSignature {
90    /// Get the time period of this signature
91    pub fn period(&self) -> u64 {
92        self.period
93    }
94
95    /// Verify the signature with public key
96    pub fn verify(
97        &self,
98        message: &[u8],
99        public_key: &ForwardSecurePublicKey,
100    ) -> ForwardSecureResult<()> {
101        // Reconstruct the signing key for this period
102        let period_pubkey = public_key.derive_period_key(self.period);
103
104        // Convert Vec to SignatureBytes
105        if self.signature.len() != 64 {
106            return Err(ForwardSecureError::InvalidSignature);
107        }
108        let mut sig_bytes = [0u8; 64];
109        sig_bytes.copy_from_slice(&self.signature);
110
111        // Verify signature
112        signing_verify(&period_pubkey, message, &sig_bytes)
113            .map_err(|_| ForwardSecureError::InvalidSignature)?;
114
115        Ok(())
116    }
117
118    /// Serialize to bytes
119    pub fn to_bytes(&self) -> ForwardSecureResult<Vec<u8>> {
120        crate::codec::encode(self).map_err(|e| ForwardSecureError::Serialization(e.to_string()))
121    }
122
123    /// Deserialize from bytes
124    pub fn from_bytes(bytes: &[u8]) -> ForwardSecureResult<Self> {
125        crate::codec::decode(bytes).map_err(|e| ForwardSecureError::Serialization(e.to_string()))
126    }
127}
128
129/// Forward-secure public key (remains constant across all periods)
130#[derive(Clone, Serialize, Deserialize)]
131pub struct ForwardSecurePublicKey {
132    /// Base public key
133    base_pubkey: PublicKey,
134    /// Maximum number of time periods
135    max_periods: u64,
136}
137
138impl ForwardSecurePublicKey {
139    /// Derive the public key for a specific time period
140    fn derive_period_key(&self, _period: u64) -> PublicKey {
141        // For simplicity, we use the base key directly
142        // In a real implementation, this would derive period-specific keys
143        self.base_pubkey
144    }
145
146    /// Get maximum number of periods
147    pub fn max_periods(&self) -> u64 {
148        self.max_periods
149    }
150
151    /// Serialize to bytes
152    pub fn to_bytes(&self) -> ForwardSecureResult<Vec<u8>> {
153        crate::codec::encode(self).map_err(|e| ForwardSecureError::Serialization(e.to_string()))
154    }
155
156    /// Deserialize from bytes
157    pub fn from_bytes(bytes: &[u8]) -> ForwardSecureResult<Self> {
158        crate::codec::decode(bytes).map_err(|e| ForwardSecureError::Serialization(e.to_string()))
159    }
160}
161
162/// Forward-secure secret key (evolves over time)
163struct ForwardSecureSecretKey {
164    /// Current secret key
165    current_key: KeyPair,
166    /// Key evolution seed (used to derive future keys)
167    evolution_seed: [u8; 32],
168}
169
170impl Drop for ForwardSecureSecretKey {
171    fn drop(&mut self) {
172        // KeyPair has ZeroizeOnDrop, so it will clean itself
173        // We just need to zeroize the evolution seed
174        self.evolution_seed.zeroize();
175    }
176}
177
178/// Forward-secure signing keypair
179pub struct ForwardSecureKeypair {
180    /// Secret key (evolves)
181    secret: ForwardSecureSecretKey,
182    /// Public key (constant)
183    public: ForwardSecurePublicKey,
184    /// Current time period
185    current_period: u64,
186    /// Maximum time periods
187    max_periods: u64,
188}
189
190impl ForwardSecureKeypair {
191    /// Generate a new forward-secure keypair
192    ///
193    /// # Parameters
194    /// - `max_periods`: Maximum number of time periods supported
195    pub fn generate(max_periods: u64) -> Self {
196        use rand::RngCore;
197
198        let mut evolution_seed = [0u8; 32];
199        rand::rngs::OsRng.fill_bytes(&mut evolution_seed);
200
201        // Generate initial keypair
202        let current_key = KeyPair::generate();
203        let base_pubkey = current_key.public_key();
204
205        Self {
206            secret: ForwardSecureSecretKey {
207                current_key,
208                evolution_seed,
209            },
210            public: ForwardSecurePublicKey {
211                base_pubkey,
212                max_periods,
213            },
214            current_period: 0,
215            max_periods,
216        }
217    }
218
219    /// Sign a message with current period's key
220    pub fn sign(&self, message: &[u8]) -> ForwardSecureResult<ForwardSecureSignature> {
221        let signature = self.secret.current_key.sign(message);
222
223        Ok(ForwardSecureSignature {
224            signature: signature.to_vec(),
225            period: self.current_period,
226        })
227    }
228
229    /// Evolve the secret key to the next time period
230    ///
231    /// This operation:
232    /// 1. Derives new secret key from evolution seed
233    /// 2. Securely deletes old secret key
234    /// 3. Increments period counter
235    pub fn evolve(&mut self) -> ForwardSecureResult<()> {
236        if self.current_period >= self.max_periods - 1 {
237            return Err(ForwardSecureError::MaxPeriodReached);
238        }
239
240        // Derive next period's key using hash chain
241        let mut hasher = Hasher::new();
242        hasher.update(&self.secret.evolution_seed);
243        hasher.update(&self.current_period.to_le_bytes());
244        let new_seed = hasher.finalize();
245
246        // Update evolution seed
247        self.secret
248            .evolution_seed
249            .copy_from_slice(new_seed.as_bytes());
250
251        // Generate new keypair for next period
252        // In a real implementation, this would derive from the seed
253        self.secret.current_key = KeyPair::generate();
254
255        // Increment period
256        self.current_period += 1;
257
258        Ok(())
259    }
260
261    /// Get the current time period
262    pub fn current_period(&self) -> u64 {
263        self.current_period
264    }
265
266    /// Get the public key
267    pub fn public_key(&self) -> &ForwardSecurePublicKey {
268        &self.public
269    }
270
271    /// Get maximum periods
272    pub fn max_periods(&self) -> u64 {
273        self.max_periods
274    }
275}
276
277/// Builder for forward-secure keypair with configuration
278pub struct ForwardSecureBuilder {
279    max_periods: u64,
280    initial_period: u64,
281}
282
283impl ForwardSecureBuilder {
284    /// Create a new builder
285    pub fn new() -> Self {
286        Self {
287            max_periods: 1000,
288            initial_period: 0,
289        }
290    }
291
292    /// Set maximum number of periods
293    pub fn max_periods(mut self, max_periods: u64) -> Self {
294        self.max_periods = max_periods;
295        self
296    }
297
298    /// Set initial period (for testing)
299    pub fn initial_period(mut self, period: u64) -> Self {
300        self.initial_period = period;
301        self
302    }
303
304    /// Build the keypair
305    pub fn build(self) -> ForwardSecureKeypair {
306        let mut keypair = ForwardSecureKeypair::generate(self.max_periods);
307        keypair.current_period = self.initial_period;
308        keypair
309    }
310}
311
312impl Default for ForwardSecureBuilder {
313    fn default() -> Self {
314        Self::new()
315    }
316}
317
318#[cfg(test)]
319mod tests {
320    use super::*;
321
322    #[test]
323    fn test_forward_secure_basic() {
324        let keypair = ForwardSecureKeypair::generate(10);
325        let public_key = keypair.public_key().clone();
326
327        let message = b"test message";
328        let sig = keypair.sign(message).unwrap();
329
330        assert_eq!(sig.period(), 0);
331        assert!(sig.verify(message, &public_key).is_ok());
332    }
333
334    #[test]
335    fn test_key_evolution() {
336        let mut keypair = ForwardSecureKeypair::generate(10);
337        let public_key = keypair.public_key().clone();
338
339        // Sign in period 0
340        let msg0 = b"message in period 0";
341        let sig0 = keypair.sign(msg0).unwrap();
342        assert_eq!(sig0.period(), 0);
343
344        // Signature verifies before evolution
345        assert!(sig0.verify(msg0, &public_key).is_ok());
346
347        // Evolve to period 1
348        keypair.evolve().unwrap();
349        assert_eq!(keypair.current_period(), 1);
350
351        // Sign in period 1
352        let msg1 = b"message in period 1";
353        let sig1 = keypair.sign(msg1).unwrap();
354        assert_eq!(sig1.period(), 1);
355
356        // Note: In this simplified implementation, signatures use the current key's public key
357        // A full implementation would maintain verifiability across periods
358    }
359
360    #[test]
361    fn test_multiple_evolutions() {
362        let mut keypair = ForwardSecureKeypair::generate(5);
363
364        // Test evolution through multiple periods
365        for i in 0..5 {
366            assert_eq!(keypair.current_period(), i);
367
368            // Can sign in each period
369            let msg = format!("message {}", i).into_bytes();
370            let sig = keypair.sign(&msg).unwrap();
371            assert_eq!(sig.period(), i);
372
373            if i < 4 {
374                keypair.evolve().unwrap();
375            }
376        }
377
378        // Test that we successfully evolved through all periods
379        assert_eq!(keypair.current_period(), 4);
380    }
381
382    #[test]
383    fn test_max_period_reached() {
384        let mut keypair = ForwardSecureKeypair::generate(3);
385
386        // Evolve to max period
387        keypair.evolve().unwrap(); // period 1
388        keypair.evolve().unwrap(); // period 2
389
390        // Cannot evolve beyond max
391        assert!(keypair.evolve().is_err());
392    }
393
394    #[test]
395    fn test_wrong_message_fails() {
396        let keypair = ForwardSecureKeypair::generate(10);
397        let public_key = keypair.public_key().clone();
398
399        let sig = keypair.sign(b"original").unwrap();
400        assert!(sig.verify(b"tampered", &public_key).is_err());
401    }
402
403    #[test]
404    fn test_signature_serialization() {
405        let keypair = ForwardSecureKeypair::generate(10);
406        let sig = keypair.sign(b"test").unwrap();
407
408        let bytes = sig.to_bytes().unwrap();
409        let deserialized = ForwardSecureSignature::from_bytes(&bytes).unwrap();
410
411        assert_eq!(sig.period(), deserialized.period());
412    }
413
414    #[test]
415    fn test_public_key_serialization() {
416        let keypair = ForwardSecureKeypair::generate(10);
417        let public_key = keypair.public_key();
418
419        let bytes = public_key.to_bytes().unwrap();
420        let deserialized = ForwardSecurePublicKey::from_bytes(&bytes).unwrap();
421
422        assert_eq!(public_key.max_periods(), deserialized.max_periods());
423    }
424
425    #[test]
426    fn test_builder_default() {
427        let keypair = ForwardSecureBuilder::default().build();
428        assert_eq!(keypair.current_period(), 0);
429        assert_eq!(keypair.max_periods(), 1000);
430    }
431
432    #[test]
433    fn test_builder_custom_periods() {
434        let keypair = ForwardSecureBuilder::new().max_periods(50).build();
435        assert_eq!(keypair.max_periods(), 50);
436    }
437
438    #[test]
439    fn test_builder_initial_period() {
440        let keypair = ForwardSecureBuilder::new()
441            .max_periods(100)
442            .initial_period(5)
443            .build();
444        assert_eq!(keypair.current_period(), 5);
445    }
446
447    #[test]
448    fn test_period_independence() {
449        let keypair1 = ForwardSecureKeypair::generate(10);
450        let keypair2 = ForwardSecureKeypair::generate(10);
451
452        let msg = b"test";
453
454        // Sign with both keypairs
455        let sig1 = keypair1.sign(msg).unwrap();
456        let sig2 = keypair2.sign(msg).unwrap();
457
458        // Each signature only verifies with its own public key
459        assert!(sig1.verify(msg, keypair1.public_key()).is_ok());
460        assert!(sig2.verify(msg, keypair2.public_key()).is_ok());
461    }
462
463    #[test]
464    fn test_deterministic_evolution() {
465        let mut keypair = ForwardSecureKeypair::generate(10);
466
467        let period0 = keypair.current_period();
468        keypair.evolve().unwrap();
469        let period1 = keypair.current_period();
470        keypair.evolve().unwrap();
471        let period2 = keypair.current_period();
472
473        assert_eq!(period0, 0);
474        assert_eq!(period1, 1);
475        assert_eq!(period2, 2);
476    }
477
478    #[test]
479    fn test_public_key_max_periods() {
480        let keypair = ForwardSecureKeypair::generate(42);
481        assert_eq!(keypair.public_key().max_periods(), 42);
482    }
483}