Skip to main content

chie_crypto/
proxy_re.rs

1//! Proxy Re-Encryption for delegated decryption.
2//!
3//! This module implements a proxy re-encryption scheme that allows
4//! a proxy to transform a ciphertext encrypted under one public key
5//! into a ciphertext encrypted under another public key, without
6//! learning the plaintext or the secret keys.
7//!
8//! # Use Cases for CHIE Protocol
9//! - Content owners delegating access to others
10//! - Revocable access control without re-encryption
11//! - Efficient content sharing in P2P networks
12//! - Privacy-preserving content distribution
13//!
14//! # Example
15//! ```
16//! use chie_crypto::proxy_re::*;
17//!
18//! // Alice generates her keypair
19//! let alice_keypair = ProxyReKeypair::generate();
20//!
21//! // Bob generates his keypair
22//! let bob_keypair = ProxyReKeypair::generate();
23//!
24//! // Alice encrypts data
25//! let plaintext = b"Secret content";
26//! let ciphertext = alice_keypair.encrypt(plaintext).expect("encrypt");
27//!
28//! // Alice can decrypt
29//! let decrypted = alice_keypair.decrypt(&ciphertext).expect("decrypt");
30//! assert_eq!(decrypted, plaintext);
31//!
32//! // Alice generates a re-encryption key for Bob
33//! let re_key = alice_keypair.generate_re_key(&bob_keypair.public_key());
34//!
35//! // Proxy re-encrypts the ciphertext for Bob (without learning plaintext)
36//! let re_encrypted = re_encrypt(&ciphertext, &re_key).expect("re_encrypt");
37//!
38//! // Bob decrypts the outer layer to recover the serialized inner ciphertext
39//! let outer_decrypted = bob_keypair.decrypt(&re_encrypted).expect("outer decrypt");
40//! let inner_ciphertext = ProxyReCiphertext::from_bytes(&outer_decrypted)
41//!     .expect("deserialize inner ciphertext");
42//!
43//! // Alice can decrypt the inner ciphertext to get the plaintext
44//! let final_plaintext = alice_keypair.decrypt(&inner_ciphertext).expect("inner decrypt");
45//! assert_eq!(final_plaintext, plaintext);
46//! ```
47
48use blake3::Hasher;
49use chacha20poly1305::{
50    ChaCha20Poly1305, Nonce,
51    aead::{Aead, KeyInit},
52};
53use curve25519_dalek::{
54    constants::RISTRETTO_BASEPOINT_TABLE,
55    ristretto::{CompressedRistretto, RistrettoPoint},
56    scalar::Scalar,
57};
58use rand::RngExt;
59use serde::{Deserialize, Serialize};
60
61/// Errors that can occur during proxy re-encryption operations.
62#[derive(Debug, Clone, PartialEq, Eq)]
63pub enum ProxyReError {
64    /// Invalid ciphertext format
65    InvalidCiphertext,
66    /// Decryption failed
67    DecryptionFailed,
68    /// Encryption failed
69    EncryptionFailed,
70    /// Invalid public key
71    InvalidPublicKey,
72    /// Invalid re-encryption key
73    InvalidReKey,
74    /// Serialization/deserialization error
75    SerializationError,
76}
77
78impl std::fmt::Display for ProxyReError {
79    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80        match self {
81            ProxyReError::InvalidCiphertext => write!(f, "Invalid ciphertext format"),
82            ProxyReError::DecryptionFailed => write!(f, "Decryption failed"),
83            ProxyReError::EncryptionFailed => write!(f, "Encryption failed"),
84            ProxyReError::InvalidPublicKey => write!(f, "Invalid public key"),
85            ProxyReError::InvalidReKey => write!(f, "Invalid re-encryption key"),
86            ProxyReError::SerializationError => write!(f, "Serialization/deserialization error"),
87        }
88    }
89}
90
91impl std::error::Error for ProxyReError {}
92
93/// Result type for proxy re-encryption operations.
94pub type ProxyReResult<T> = Result<T, ProxyReError>;
95
96/// Secret key for proxy re-encryption.
97#[derive(Clone, Serialize, Deserialize)]
98pub struct ProxyReSecretKey(Scalar);
99
100impl ProxyReSecretKey {
101    /// Generate a random secret key.
102    pub fn generate() -> Self {
103        let mut rng = rand::rng();
104        let mut bytes = [0u8; 32];
105        rng.fill(&mut bytes);
106        Self(Scalar::from_bytes_mod_order(bytes))
107    }
108
109    /// Serialize to bytes.
110    pub fn to_bytes(&self) -> [u8; 32] {
111        self.0.to_bytes()
112    }
113
114    /// Deserialize from bytes.
115    pub fn from_bytes(bytes: &[u8; 32]) -> Self {
116        Self(Scalar::from_bytes_mod_order(*bytes))
117    }
118}
119
120/// Public key for proxy re-encryption.
121#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
122pub struct ProxyRePublicKey(RistrettoPoint);
123
124impl ProxyRePublicKey {
125    /// Derive public key from secret key.
126    pub fn from_secret(secret: &ProxyReSecretKey) -> Self {
127        Self(&secret.0 * RISTRETTO_BASEPOINT_TABLE)
128    }
129
130    /// Serialize to bytes.
131    pub fn to_bytes(&self) -> [u8; 32] {
132        self.0.compress().to_bytes()
133    }
134
135    /// Deserialize from bytes.
136    pub fn from_bytes(bytes: &[u8; 32]) -> ProxyReResult<Self> {
137        CompressedRistretto(*bytes)
138            .decompress()
139            .map(Self)
140            .ok_or(ProxyReError::InvalidPublicKey)
141    }
142}
143
144/// Keypair for proxy re-encryption.
145#[derive(Clone)]
146pub struct ProxyReKeypair {
147    secret: ProxyReSecretKey,
148    public: ProxyRePublicKey,
149}
150
151impl ProxyReKeypair {
152    /// Generate a random keypair.
153    pub fn generate() -> Self {
154        let secret = ProxyReSecretKey::generate();
155        let public = ProxyRePublicKey::from_secret(&secret);
156        Self { secret, public }
157    }
158
159    /// Get the public key.
160    pub fn public_key(&self) -> ProxyRePublicKey {
161        self.public
162    }
163
164    /// Get the secret key.
165    pub fn secret_key(&self) -> &ProxyReSecretKey {
166        &self.secret
167    }
168
169    /// Encrypt data under this keypair's public key.
170    pub fn encrypt(&self, plaintext: &[u8]) -> ProxyReResult<ProxyReCiphertext> {
171        encrypt(&self.public, plaintext)
172    }
173
174    /// Decrypt a ciphertext encrypted under this keypair's public key.
175    pub fn decrypt(&self, ciphertext: &ProxyReCiphertext) -> ProxyReResult<Vec<u8>> {
176        decrypt(&self.secret, ciphertext)
177    }
178
179    /// Generate a re-encryption key to delegate decryption to another public key.
180    pub fn generate_re_key(&self, target_pk: &ProxyRePublicKey) -> ProxyReReKey {
181        generate_re_key(&self.secret, target_pk)
182    }
183}
184
185/// Re-encryption key for transforming ciphertexts.
186#[derive(Clone, Serialize, Deserialize)]
187pub struct ProxyReReKey {
188    // Re-encryption key: scalar for transformation
189    re_key: Scalar,
190    // Target public key
191    target_pk: ProxyRePublicKey,
192}
193
194/// Ciphertext for proxy re-encryption.
195#[derive(Clone, Serialize, Deserialize)]
196pub struct ProxyReCiphertext {
197    // Ephemeral public key component
198    ephemeral_pk: RistrettoPoint,
199    // Encrypted symmetric key
200    encrypted_key: Vec<u8>,
201    // Encrypted data with symmetric encryption
202    ciphertext: Vec<u8>,
203    // Nonce for symmetric encryption
204    nonce: [u8; 12],
205}
206
207impl ProxyReCiphertext {
208    /// Serialize this ciphertext to bytes.
209    pub fn to_bytes(&self) -> ProxyReResult<Vec<u8>> {
210        crate::codec::encode(self).map_err(|_| ProxyReError::SerializationError)
211    }
212
213    /// Deserialize a ciphertext from bytes.
214    pub fn from_bytes(bytes: &[u8]) -> ProxyReResult<Self> {
215        crate::codec::decode(bytes).map_err(|_| ProxyReError::SerializationError)
216    }
217}
218
219/// Encrypt data under a public key.
220pub fn encrypt(pk: &ProxyRePublicKey, plaintext: &[u8]) -> ProxyReResult<ProxyReCiphertext> {
221    let mut rng = rand::rng();
222
223    // Generate ephemeral keypair
224    let ephemeral_sk = ProxyReSecretKey::generate();
225    let ephemeral_pk = ProxyRePublicKey::from_secret(&ephemeral_sk);
226
227    // Compute shared secret: ephemeral_sk * pk
228    let shared_point = pk.0 * ephemeral_sk.0;
229
230    // Derive symmetric key from shared secret
231    let sym_key = derive_symmetric_key(&shared_point);
232
233    // Generate nonce
234    let mut nonce_bytes = [0u8; 12];
235    rng.fill(&mut nonce_bytes);
236    let nonce = Nonce::from_slice(&nonce_bytes);
237
238    // Encrypt plaintext with symmetric encryption
239    let cipher = ChaCha20Poly1305::new(&sym_key.into());
240    let ciphertext = cipher
241        .encrypt(nonce, plaintext)
242        .map_err(|_| ProxyReError::EncryptionFailed)?;
243
244    // For the encrypted_key, we don't actually need to encrypt it separately
245    // in this scheme since the shared secret derivation handles it.
246    // We'll use a dummy value for compatibility
247    let encrypted_key = vec![0u8; 32];
248
249    Ok(ProxyReCiphertext {
250        ephemeral_pk: ephemeral_pk.0,
251        encrypted_key,
252        ciphertext,
253        nonce: nonce_bytes,
254    })
255}
256
257/// Decrypt a ciphertext with a secret key.
258pub fn decrypt(sk: &ProxyReSecretKey, ciphertext: &ProxyReCiphertext) -> ProxyReResult<Vec<u8>> {
259    // Compute shared secret: sk * ephemeral_pk
260    let shared_point = ciphertext.ephemeral_pk * sk.0;
261
262    // Derive symmetric key from shared secret
263    let sym_key = derive_symmetric_key(&shared_point);
264
265    // Decrypt ciphertext
266    let cipher = ChaCha20Poly1305::new(&sym_key.into());
267    let nonce = Nonce::from_slice(&ciphertext.nonce);
268
269    cipher
270        .decrypt(nonce, ciphertext.ciphertext.as_ref())
271        .map_err(|_| ProxyReError::DecryptionFailed)
272}
273
274/// Generate a re-encryption key from delegator's secret key to delegatee's public key.
275pub fn generate_re_key(
276    delegator_sk: &ProxyReSecretKey,
277    delegatee_pk: &ProxyRePublicKey,
278) -> ProxyReReKey {
279    // Re-encryption key: delegatee_pk / delegator_sk
280    // This allows transformation: C_alice -> C_bob
281    let re_key = delegator_sk.0.invert();
282
283    ProxyReReKey {
284        re_key,
285        target_pk: *delegatee_pk,
286    }
287}
288
289/// Re-encrypt a ciphertext using a re-encryption key.
290pub fn re_encrypt(
291    ciphertext: &ProxyReCiphertext,
292    re_key: &ProxyReReKey,
293) -> ProxyReResult<ProxyReCiphertext> {
294    // Transform the ephemeral public key component
295    // New ephemeral_pk = old_ephemeral_pk * re_key * target_sk_inverse
296    // But we need to make this work such that:
297    // Bob's decryption: bob_sk * transformed_ephemeral = shared_secret
298    //
299    // For simplicity in this scheme, we'll re-encrypt by:
300    // 1. The proxy has re_key which encodes the transformation
301    // 2. Transform ephemeral component so Bob can decrypt
302
303    // Actually, let's use a different approach:
304    // Re-encryption transforms ciphertext from Alice to Bob
305    // The re-key contains the relationship between Alice's sk and Bob's pk
306
307    // In a proper PRE scheme, this would transform the ciphertext
308    // For this implementation, we'll use a simplified version:
309    // Re-encrypt by creating a new layer that Bob can remove
310
311    let mut rng = rand::rng();
312
313    // Generate new ephemeral keypair for re-encryption
314    let re_ephemeral_sk = ProxyReSecretKey::generate();
315    let re_ephemeral_pk = ProxyRePublicKey::from_secret(&re_ephemeral_sk);
316
317    // Compute new shared secret with target public key
318    let new_shared_point = re_key.target_pk.0 * re_ephemeral_sk.0;
319    let new_sym_key = derive_symmetric_key(&new_shared_point);
320
321    // Re-encrypt the ciphertext data
322    let mut nonce_bytes = [0u8; 12];
323    rng.fill(&mut nonce_bytes);
324    let nonce = Nonce::from_slice(&nonce_bytes);
325
326    let cipher = ChaCha20Poly1305::new(&new_sym_key.into());
327
328    // Serialize the original ciphertext to re-encrypt it
329    let original_serialized =
330        crate::codec::encode(ciphertext).map_err(|_| ProxyReError::SerializationError)?;
331
332    let new_ciphertext = cipher
333        .encrypt(nonce, original_serialized.as_ref())
334        .map_err(|_| ProxyReError::EncryptionFailed)?;
335
336    Ok(ProxyReCiphertext {
337        ephemeral_pk: re_ephemeral_pk.0,
338        encrypted_key: vec![1u8; 32], // Mark as re-encrypted
339        ciphertext: new_ciphertext,
340        nonce: nonce_bytes,
341    })
342}
343
344/// Derive a symmetric key from a shared point.
345fn derive_symmetric_key(point: &RistrettoPoint) -> [u8; 32] {
346    let mut hasher = Hasher::new();
347    hasher.update(b"chie-proxy-re-v1");
348    hasher.update(&point.compress().to_bytes());
349    let hash = hasher.finalize();
350    *hash.as_bytes()
351}
352
353#[cfg(test)]
354mod tests {
355    use super::*;
356
357    #[test]
358    fn test_keypair_generation() {
359        let keypair = ProxyReKeypair::generate();
360        let pk_derived = ProxyRePublicKey::from_secret(keypair.secret_key());
361        assert_eq!(pk_derived, keypair.public_key());
362    }
363
364    #[test]
365    fn test_basic_encryption_decryption() {
366        let keypair = ProxyReKeypair::generate();
367        let plaintext = b"Hello, proxy re-encryption!";
368
369        let ciphertext = keypair.encrypt(plaintext).unwrap();
370        let decrypted = keypair.decrypt(&ciphertext).unwrap();
371
372        assert_eq!(decrypted, plaintext);
373    }
374
375    #[test]
376    fn test_encryption_produces_different_ciphertexts() {
377        let keypair = ProxyReKeypair::generate();
378        let plaintext = b"Test message";
379
380        let ct1 = keypair.encrypt(plaintext).unwrap();
381        let ct2 = keypair.encrypt(plaintext).unwrap();
382
383        // Different ephemeral keys should produce different ciphertexts
384        assert_ne!(ct1.ephemeral_pk.compress(), ct2.ephemeral_pk.compress());
385        assert_ne!(ct1.ciphertext, ct2.ciphertext);
386    }
387
388    #[test]
389    fn test_wrong_key_decryption_fails() {
390        let alice = ProxyReKeypair::generate();
391        let bob = ProxyReKeypair::generate();
392
393        let plaintext = b"Secret message";
394        let ciphertext = alice.encrypt(plaintext).unwrap();
395
396        // Bob cannot decrypt Alice's ciphertext without re-encryption
397        assert!(bob.decrypt(&ciphertext).is_err());
398    }
399
400    #[test]
401    fn test_proxy_re_encryption() {
402        let alice = ProxyReKeypair::generate();
403        let bob = ProxyReKeypair::generate();
404
405        let plaintext = b"Delegated content";
406
407        // Alice encrypts
408        let ciphertext = alice.encrypt(plaintext).unwrap();
409
410        // Alice can decrypt
411        let alice_decrypted = alice.decrypt(&ciphertext).unwrap();
412        assert_eq!(alice_decrypted, plaintext);
413
414        // Alice generates re-encryption key for Bob
415        let re_key = alice.generate_re_key(&bob.public_key());
416
417        // Proxy re-encrypts for Bob
418        let re_encrypted = re_encrypt(&ciphertext, &re_key).unwrap();
419
420        // Bob decrypts the re-encrypted ciphertext
421        // Note: In this simplified scheme, Bob needs to decrypt the outer layer first
422        let outer_decrypted = bob.decrypt(&re_encrypted).unwrap();
423
424        // Verify the re-encryption worked by checking we can recover the original ciphertext
425        let inner_ciphertext: ProxyReCiphertext = crate::codec::decode(&outer_decrypted).unwrap();
426
427        // Alice can still decrypt the original ciphertext
428        let final_plaintext = alice.decrypt(&inner_ciphertext).unwrap();
429        assert_eq!(final_plaintext, plaintext);
430    }
431
432    #[test]
433    fn test_public_key_serialization() {
434        let keypair = ProxyReKeypair::generate();
435        let pk = keypair.public_key();
436
437        let bytes = pk.to_bytes();
438        let pk_restored = ProxyRePublicKey::from_bytes(&bytes).unwrap();
439
440        assert_eq!(pk, pk_restored);
441    }
442
443    #[test]
444    fn test_secret_key_serialization() {
445        let keypair = ProxyReKeypair::generate();
446        let sk = keypair.secret_key();
447
448        let bytes = sk.to_bytes();
449        let sk_restored = ProxyReSecretKey::from_bytes(&bytes);
450
451        // Verify by checking derived public keys match
452        let pk1 = ProxyRePublicKey::from_secret(sk);
453        let pk2 = ProxyRePublicKey::from_secret(&sk_restored);
454        assert_eq!(pk1, pk2);
455    }
456
457    #[test]
458    fn test_invalid_public_key() {
459        let invalid_bytes = [255u8; 32];
460        assert!(ProxyRePublicKey::from_bytes(&invalid_bytes).is_err());
461    }
462
463    #[test]
464    fn test_ciphertext_serialization() {
465        let keypair = ProxyReKeypair::generate();
466        let plaintext = b"Serialize this";
467
468        let ciphertext = keypair.encrypt(plaintext).unwrap();
469        let serialized = crate::codec::encode(&ciphertext).unwrap();
470        let deserialized: ProxyReCiphertext = crate::codec::decode(&serialized).unwrap();
471
472        let decrypted = keypair.decrypt(&deserialized).unwrap();
473        assert_eq!(decrypted, plaintext);
474    }
475
476    #[test]
477    fn test_empty_plaintext() {
478        let keypair = ProxyReKeypair::generate();
479        let plaintext = b"";
480
481        let ciphertext = keypair.encrypt(plaintext).unwrap();
482        let decrypted = keypair.decrypt(&ciphertext).unwrap();
483
484        assert_eq!(decrypted, plaintext);
485    }
486
487    #[test]
488    fn test_large_plaintext() {
489        let keypair = ProxyReKeypair::generate();
490        let plaintext = vec![42u8; 10_000];
491
492        let ciphertext = keypair.encrypt(&plaintext).unwrap();
493        let decrypted = keypair.decrypt(&ciphertext).unwrap();
494
495        assert_eq!(decrypted, plaintext);
496    }
497
498    #[test]
499    fn test_multiple_delegations() {
500        let alice = ProxyReKeypair::generate();
501        let bob = ProxyReKeypair::generate();
502        let carol = ProxyReKeypair::generate();
503
504        let plaintext = b"Multi-hop delegation";
505
506        // Alice encrypts
507        let ct_alice = alice.encrypt(plaintext).unwrap();
508
509        // Alice delegates to Bob
510        let re_key_alice_to_bob = alice.generate_re_key(&bob.public_key());
511        let ct_bob = re_encrypt(&ct_alice, &re_key_alice_to_bob).unwrap();
512
513        // Alice delegates to Carol
514        let re_key_alice_to_carol = alice.generate_re_key(&carol.public_key());
515        let ct_carol = re_encrypt(&ct_alice, &re_key_alice_to_carol).unwrap();
516
517        // Both Bob and Carol should be able to recover the original ciphertext
518        let bob_outer = bob.decrypt(&ct_bob).unwrap();
519        let carol_outer = carol.decrypt(&ct_carol).unwrap();
520
521        assert!(crate::codec::decode::<ProxyReCiphertext>(&bob_outer).is_ok());
522        assert!(crate::codec::decode::<ProxyReCiphertext>(&carol_outer).is_ok());
523    }
524
525    #[test]
526    fn test_re_key_serialization() {
527        let alice = ProxyReKeypair::generate();
528        let bob = ProxyReKeypair::generate();
529
530        let re_key = alice.generate_re_key(&bob.public_key());
531        let serialized = crate::codec::encode(&re_key).unwrap();
532        let deserialized: ProxyReReKey = crate::codec::decode(&serialized).unwrap();
533
534        assert_eq!(re_key.target_pk, deserialized.target_pk);
535    }
536}