chie_crypto/
ibe.rs

1//! Identity-Based Encryption (IBE) for simplified key management.
2//!
3//! IBE allows deriving public keys directly from arbitrary identities (email, node ID, etc.)
4//! without requiring a certificate infrastructure. This is particularly useful for P2P systems
5//! where nodes join and leave dynamically.
6//!
7//! This implementation uses a simplified hash-based IBE scheme suitable for the CHIE protocol:
8//! - Master key authority generates public parameters
9//! - User secret keys are derived from identity strings using HKDF
10//! - Encryption uses hybrid encryption (X25519 + ChaCha20-Poly1305)
11//! - Identity-based key derivation simplifies key distribution
12//!
13//! # Example
14//!
15//! ```
16//! use chie_crypto::ibe::{IbeMaster, IbeParams};
17//!
18//! // Setup: Master authority generates public parameters
19//! let master = IbeMaster::generate();
20//! let params = master.public_params();
21//!
22//! // Extract user secret key for an identity
23//! let alice_id = "alice@example.com";
24//! let alice_sk = master.extract_secret_key(alice_id);
25//!
26//! // Encrypt to Alice using only her identity
27//! let plaintext = b"Secret message for Alice";
28//! let ciphertext = params.encrypt(alice_id, plaintext).unwrap();
29//!
30//! // Alice decrypts using her secret key
31//! let decrypted = alice_sk.decrypt(&ciphertext).unwrap();
32//! assert_eq!(plaintext.as_slice(), decrypted.as_bytes());
33//! ```
34
35use crate::zeroizing::SecureBuffer;
36use blake3::Hasher;
37use chacha20poly1305::{ChaCha20Poly1305, KeyInit, aead::Aead};
38use curve25519_dalek::{RistrettoPoint, Scalar, constants::RISTRETTO_BASEPOINT_POINT};
39use rand::RngCore;
40use serde::{Deserialize, Serialize};
41use std::fmt;
42use zeroize::{Zeroize, ZeroizeOnDrop};
43
44/// Result type for IBE operations.
45pub type IbeResult<T> = Result<T, IbeError>;
46
47/// Errors that can occur during IBE operations.
48#[derive(Debug, Clone, PartialEq, Eq)]
49pub enum IbeError {
50    /// Invalid identity string
51    InvalidIdentity,
52    /// Decryption failed
53    DecryptionFailed,
54    /// Serialization failed
55    SerializationFailed,
56    /// Deserialization failed
57    DeserializationFailed,
58    /// Invalid ciphertext
59    InvalidCiphertext,
60}
61
62impl fmt::Display for IbeError {
63    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64        match self {
65            IbeError::InvalidIdentity => write!(f, "Invalid identity string"),
66            IbeError::DecryptionFailed => write!(f, "Decryption failed"),
67            IbeError::SerializationFailed => write!(f, "Serialization failed"),
68            IbeError::DeserializationFailed => write!(f, "Deserialization failed"),
69            IbeError::InvalidCiphertext => write!(f, "Invalid ciphertext"),
70        }
71    }
72}
73
74impl std::error::Error for IbeError {}
75
76/// Master secret key for IBE system.
77///
78/// The master authority holds this key and uses it to extract user secret keys.
79#[derive(Clone, Zeroize, ZeroizeOnDrop)]
80pub struct IbeMasterKey {
81    /// Master secret scalar
82    #[zeroize(skip)]
83    master_secret: Scalar,
84}
85
86/// Public parameters for IBE system.
87///
88/// These parameters are public and used by anyone to encrypt messages to identities.
89#[derive(Clone, Debug, Serialize, Deserialize)]
90pub struct IbeParams {
91    /// Master public point = master_secret * G
92    #[serde(with = "serde_ristretto_point")]
93    master_public: RistrettoPoint,
94}
95
96/// IBE master authority.
97///
98/// Holds the master secret key and can extract user secret keys for any identity.
99pub struct IbeMaster {
100    /// Master secret key
101    master_key: IbeMasterKey,
102    /// Public parameters
103    params: IbeParams,
104}
105
106/// User secret key for a specific identity.
107///
108/// Derived by the master authority from the user's identity string.
109#[derive(Clone, Zeroize, ZeroizeOnDrop)]
110pub struct IbeSecretKey {
111    /// Identity string
112    identity: String,
113    /// Derived secret scalar
114    #[zeroize(skip)]
115    secret: Scalar,
116    /// Public parameters (needed for decryption)
117    #[zeroize(skip)]
118    params: IbeParams,
119}
120
121/// IBE ciphertext.
122#[derive(Clone, Serialize, Deserialize)]
123pub struct IbeCiphertext {
124    /// Ephemeral public point
125    #[serde(with = "serde_ristretto_point")]
126    ephemeral: RistrettoPoint,
127    /// Encrypted data
128    ciphertext: Vec<u8>,
129    /// Nonce for ChaCha20-Poly1305
130    nonce: [u8; 12],
131}
132
133impl IbeMaster {
134    /// Generate a new IBE master authority.
135    pub fn generate() -> Self {
136        let mut rng = rand::thread_rng();
137        let mut master_secret_bytes = [0u8; 32];
138        rng.fill_bytes(&mut master_secret_bytes);
139        let master_secret = Scalar::from_bytes_mod_order(master_secret_bytes);
140        let master_public = master_secret * RISTRETTO_BASEPOINT_POINT;
141
142        let master_key = IbeMasterKey { master_secret };
143        let params = IbeParams { master_public };
144
145        Self { master_key, params }
146    }
147
148    /// Get the public parameters.
149    pub fn public_params(&self) -> IbeParams {
150        self.params.clone()
151    }
152
153    /// Extract a secret key for a given identity.
154    ///
155    /// The identity can be any string (email, node ID, etc.).
156    pub fn extract_secret_key(&self, identity: &str) -> IbeSecretKey {
157        // Hash the identity to a scalar
158        let identity_hash = hash_identity_to_scalar(identity);
159
160        // Secret key = master_secret * H(identity)
161        let secret = self.master_key.master_secret * identity_hash;
162
163        IbeSecretKey {
164            identity: identity.to_string(),
165            secret,
166            params: self.params.clone(),
167        }
168    }
169
170    /// Get the master secret key (for serialization/backup).
171    pub fn master_key(&self) -> &IbeMasterKey {
172        &self.master_key
173    }
174}
175
176impl IbeParams {
177    /// Encrypt a message to a specific identity.
178    pub fn encrypt(&self, identity: &str, plaintext: &[u8]) -> IbeResult<IbeCiphertext> {
179        let mut rng = rand::thread_rng();
180
181        // Generate ephemeral key pair
182        let mut ephemeral_secret_bytes = [0u8; 32];
183        rng.fill_bytes(&mut ephemeral_secret_bytes);
184        let ephemeral_secret = Scalar::from_bytes_mod_order(ephemeral_secret_bytes);
185        let ephemeral = ephemeral_secret * RISTRETTO_BASEPOINT_POINT;
186
187        // Compute identity point
188        let identity_hash = hash_identity_to_scalar(identity);
189        let identity_point = identity_hash * self.master_public;
190
191        // Shared secret = ephemeral_secret * identity_point
192        let shared_point = ephemeral_secret * identity_point;
193
194        // Derive encryption key from shared point
195        let encryption_key = derive_encryption_key(&shared_point);
196
197        // Encrypt with ChaCha20-Poly1305
198        let cipher = ChaCha20Poly1305::new(&encryption_key.into());
199        let mut nonce_bytes = [0u8; 12];
200        rng.fill_bytes(&mut nonce_bytes);
201        let nonce = chacha20poly1305::Nonce::from(nonce_bytes);
202
203        let ciphertext = cipher
204            .encrypt(&nonce, plaintext)
205            .map_err(|_| IbeError::DecryptionFailed)?;
206
207        Ok(IbeCiphertext {
208            ephemeral,
209            ciphertext,
210            nonce: nonce_bytes,
211        })
212    }
213}
214
215impl IbeSecretKey {
216    /// Decrypt a ciphertext using this secret key.
217    pub fn decrypt(&self, ciphertext: &IbeCiphertext) -> IbeResult<SecureBuffer> {
218        // Shared secret = secret * ephemeral
219        let shared_point = self.secret * ciphertext.ephemeral;
220
221        // Derive decryption key from shared point
222        let decryption_key = derive_encryption_key(&shared_point);
223
224        // Decrypt with ChaCha20-Poly1305
225        let cipher = ChaCha20Poly1305::new(&decryption_key.into());
226        let nonce = chacha20poly1305::Nonce::from(ciphertext.nonce);
227
228        let plaintext = cipher
229            .decrypt(&nonce, ciphertext.ciphertext.as_ref())
230            .map_err(|_| IbeError::DecryptionFailed)?;
231
232        Ok(SecureBuffer::from(plaintext))
233    }
234
235    /// Get the identity associated with this secret key.
236    pub fn identity(&self) -> &str {
237        &self.identity
238    }
239
240    /// Serialize the secret key to bytes.
241    pub fn to_bytes(&self) -> Vec<u8> {
242        crate::codec::encode(&(&self.identity, self.secret.to_bytes(), &self.params))
243            .unwrap_or_default()
244    }
245
246    /// Deserialize a secret key from bytes.
247    pub fn from_bytes(bytes: &[u8]) -> IbeResult<Self> {
248        let (identity, secret_bytes, params): (String, [u8; 32], IbeParams) =
249            crate::codec::decode(bytes).map_err(|_| IbeError::DeserializationFailed)?;
250
251        let secret = Scalar::from_bytes_mod_order(secret_bytes);
252
253        Ok(Self {
254            identity,
255            secret,
256            params,
257        })
258    }
259}
260
261impl IbeCiphertext {
262    /// Serialize the ciphertext to bytes.
263    pub fn to_bytes(&self) -> Vec<u8> {
264        crate::codec::encode(self).unwrap_or_default()
265    }
266
267    /// Deserialize a ciphertext from bytes.
268    pub fn from_bytes(bytes: &[u8]) -> IbeResult<Self> {
269        crate::codec::decode(bytes).map_err(|_| IbeError::DeserializationFailed)
270    }
271}
272
273/// Hash an identity string to a scalar using BLAKE3.
274fn hash_identity_to_scalar(identity: &str) -> Scalar {
275    let mut hasher = Hasher::new();
276    hasher.update(b"IBE-Identity-Hash:");
277    hasher.update(identity.as_bytes());
278    let hash = hasher.finalize();
279
280    // Use first 32 bytes of hash as scalar
281    let mut scalar_bytes = [0u8; 32];
282    scalar_bytes.copy_from_slice(&hash.as_bytes()[..32]);
283
284    Scalar::from_bytes_mod_order(scalar_bytes)
285}
286
287/// Derive an encryption key from a shared point.
288fn derive_encryption_key(point: &RistrettoPoint) -> [u8; 32] {
289    let point_bytes = point.compress().to_bytes();
290
291    let mut hasher = Hasher::new();
292    hasher.update(b"IBE-Key-Derivation:");
293    hasher.update(&point_bytes);
294    let hash = hasher.finalize();
295
296    let mut key = [0u8; 32];
297    key.copy_from_slice(&hash.as_bytes()[..32]);
298    key
299}
300
301// Custom serde for RistrettoPoint
302mod serde_ristretto_point {
303    use curve25519_dalek::RistrettoPoint;
304    use serde::{Deserialize, Deserializer, Serialize, Serializer};
305
306    pub fn serialize<S>(point: &RistrettoPoint, serializer: S) -> Result<S::Ok, S::Error>
307    where
308        S: Serializer,
309    {
310        point.compress().to_bytes().serialize(serializer)
311    }
312
313    pub fn deserialize<'de, D>(deserializer: D) -> Result<RistrettoPoint, D::Error>
314    where
315        D: Deserializer<'de>,
316    {
317        let bytes: [u8; 32] = Deserialize::deserialize(deserializer)?;
318        let compressed = curve25519_dalek::ristretto::CompressedRistretto(bytes);
319        compressed
320            .decompress()
321            .ok_or_else(|| serde::de::Error::custom("Invalid RistrettoPoint"))
322    }
323}
324
325#[cfg(test)]
326mod tests {
327    use super::*;
328
329    #[test]
330    fn test_ibe_basic() {
331        let master = IbeMaster::generate();
332        let params = master.public_params();
333
334        let alice_id = "alice@example.com";
335        let alice_sk = master.extract_secret_key(alice_id);
336
337        let plaintext = b"Secret message for Alice";
338        let ciphertext = params.encrypt(alice_id, plaintext).unwrap();
339
340        let decrypted = alice_sk.decrypt(&ciphertext).unwrap();
341        assert_eq!(plaintext.as_slice(), decrypted.as_bytes());
342    }
343
344    #[test]
345    fn test_ibe_multiple_users() {
346        let master = IbeMaster::generate();
347        let params = master.public_params();
348
349        let alice_sk = master.extract_secret_key("alice@example.com");
350        let bob_sk = master.extract_secret_key("bob@example.com");
351
352        let msg_for_alice = b"Message for Alice";
353        let msg_for_bob = b"Message for Bob";
354
355        let ct_alice = params.encrypt("alice@example.com", msg_for_alice).unwrap();
356        let ct_bob = params.encrypt("bob@example.com", msg_for_bob).unwrap();
357
358        let dec_alice = alice_sk.decrypt(&ct_alice).unwrap();
359        let dec_bob = bob_sk.decrypt(&ct_bob).unwrap();
360
361        assert_eq!(msg_for_alice.as_slice(), dec_alice.as_bytes());
362        assert_eq!(msg_for_bob.as_slice(), dec_bob.as_bytes());
363    }
364
365    #[test]
366    fn test_ibe_wrong_key() {
367        let master = IbeMaster::generate();
368        let params = master.public_params();
369
370        let _alice_sk = master.extract_secret_key("alice@example.com");
371        let bob_sk = master.extract_secret_key("bob@example.com");
372
373        let ct = params.encrypt("alice@example.com", b"Secret").unwrap();
374
375        // Bob should not be able to decrypt Alice's message
376        assert!(bob_sk.decrypt(&ct).is_err());
377    }
378
379    #[test]
380    fn test_ibe_node_ids() {
381        let master = IbeMaster::generate();
382        let params = master.public_params();
383
384        let node1_sk = master.extract_secret_key("node-12345");
385        let node2_sk = master.extract_secret_key("node-67890");
386
387        let msg = b"P2P message";
388        let ct = params.encrypt("node-12345", msg).unwrap();
389
390        let decrypted = node1_sk.decrypt(&ct).unwrap();
391        assert_eq!(msg.as_slice(), decrypted.as_bytes());
392
393        assert!(node2_sk.decrypt(&ct).is_err());
394    }
395
396    #[test]
397    fn test_ibe_empty_plaintext() {
398        let master = IbeMaster::generate();
399        let params = master.public_params();
400
401        let sk = master.extract_secret_key("test@example.com");
402
403        let ct = params.encrypt("test@example.com", b"").unwrap();
404        let decrypted = sk.decrypt(&ct).unwrap();
405
406        assert_eq!(decrypted.as_bytes(), b"");
407    }
408
409    #[test]
410    fn test_ibe_large_plaintext() {
411        let master = IbeMaster::generate();
412        let params = master.public_params();
413
414        let sk = master.extract_secret_key("test@example.com");
415
416        let plaintext = vec![0x42u8; 10000];
417        let ct = params.encrypt("test@example.com", &plaintext).unwrap();
418        let decrypted = sk.decrypt(&ct).unwrap();
419
420        assert_eq!(decrypted.as_bytes(), plaintext.as_slice());
421    }
422
423    #[test]
424    fn test_ibe_different_encryptions() {
425        let master = IbeMaster::generate();
426        let params = master.public_params();
427
428        let sk = master.extract_secret_key("test@example.com");
429        let msg = b"Same message";
430
431        let ct1 = params.encrypt("test@example.com", msg).unwrap();
432        let ct2 = params.encrypt("test@example.com", msg).unwrap();
433
434        // Ciphertexts should be different due to randomness
435        assert_ne!(ct1.to_bytes(), ct2.to_bytes());
436
437        // But both should decrypt correctly
438        assert_eq!(sk.decrypt(&ct1).unwrap().as_bytes(), msg);
439        assert_eq!(sk.decrypt(&ct2).unwrap().as_bytes(), msg);
440    }
441
442    #[test]
443    fn test_ibe_secret_key_identity() {
444        let master = IbeMaster::generate();
445        let identity = "user@example.com";
446        let sk = master.extract_secret_key(identity);
447
448        assert_eq!(sk.identity(), identity);
449    }
450
451    #[test]
452    fn test_ibe_ciphertext_serialization() {
453        let master = IbeMaster::generate();
454        let params = master.public_params();
455        let sk = master.extract_secret_key("test@example.com");
456
457        let plaintext = b"Test message";
458        let ct = params.encrypt("test@example.com", plaintext).unwrap();
459
460        let serialized = ct.to_bytes();
461        let deserialized = IbeCiphertext::from_bytes(&serialized).unwrap();
462
463        let decrypted = sk.decrypt(&deserialized).unwrap();
464        assert_eq!(decrypted.as_bytes(), plaintext);
465    }
466
467    #[test]
468    fn test_ibe_secret_key_serialization() {
469        let master = IbeMaster::generate();
470        let params = master.public_params();
471
472        let sk = master.extract_secret_key("test@example.com");
473
474        let serialized = sk.to_bytes();
475        let deserialized = IbeSecretKey::from_bytes(&serialized).unwrap();
476
477        assert_eq!(deserialized.identity(), sk.identity());
478
479        // Test decryption with deserialized key
480        let plaintext = b"Test message";
481        let ct = params.encrypt("test@example.com", plaintext).unwrap();
482
483        let decrypted = deserialized.decrypt(&ct).unwrap();
484        assert_eq!(decrypted.as_bytes(), plaintext);
485    }
486
487    #[test]
488    fn test_ibe_params_serialization() {
489        let master = IbeMaster::generate();
490        let params = master.public_params();
491        let sk = master.extract_secret_key("test@example.com");
492
493        // Serialize and deserialize params
494        let serialized = crate::codec::encode(&params).unwrap();
495        let deserialized: IbeParams = crate::codec::decode(&serialized).unwrap();
496
497        // Encrypt with deserialized params
498        let plaintext = b"Test message";
499        let ct = deserialized.encrypt("test@example.com", plaintext).unwrap();
500
501        let decrypted = sk.decrypt(&ct).unwrap();
502        assert_eq!(decrypted.as_bytes(), plaintext);
503    }
504
505    #[test]
506    fn test_ibe_deterministic_key_extraction() {
507        let master = IbeMaster::generate();
508
509        let sk1 = master.extract_secret_key("test@example.com");
510        let sk2 = master.extract_secret_key("test@example.com");
511
512        // Same identity should produce same secret key
513        assert_eq!(sk1.to_bytes(), sk2.to_bytes());
514    }
515
516    #[test]
517    fn test_ibe_corrupted_ciphertext() {
518        let master = IbeMaster::generate();
519        let params = master.public_params();
520        let sk = master.extract_secret_key("test@example.com");
521
522        let ct = params.encrypt("test@example.com", b"Test").unwrap();
523
524        let mut corrupted_bytes = ct.to_bytes();
525        corrupted_bytes[50] ^= 0xFF; // Flip some bits
526
527        let corrupted_ct = IbeCiphertext::from_bytes(&corrupted_bytes).unwrap();
528        assert!(sk.decrypt(&corrupted_ct).is_err());
529    }
530
531    #[test]
532    fn test_ibe_special_characters_in_identity() {
533        let master = IbeMaster::generate();
534        let params = master.public_params();
535
536        let identity = "user+tag@example.com";
537        let sk = master.extract_secret_key(identity);
538
539        let ct = params.encrypt(identity, b"Test").unwrap();
540        let decrypted = sk.decrypt(&ct).unwrap();
541
542        assert_eq!(decrypted.as_bytes(), b"Test");
543    }
544}