Skip to main content

chie_crypto/
abe.rs

1//! Attribute-Based Encryption (ABE) for fine-grained access control.
2//!
3//! This module provides Ciphertext-Policy ABE (CP-ABE), where access policies
4//! are embedded in ciphertexts and user keys are associated with attributes.
5//!
6//! # Overview
7//!
8//! ABE enables encryption to a set of attributes rather than to specific public keys.
9//! Only users whose attributes satisfy the access policy can decrypt.
10//!
11//! # Architecture
12//!
13//! - **Authority**: Generates master keys and issues user keys based on attributes
14//! - **Encryptor**: Encrypts data with an access policy (e.g., "admin AND (vip OR premium)")
15//! - **Decryptor**: Can decrypt if their attributes satisfy the policy
16//!
17//! # Use Cases in CHIE
18//!
19//! - Content access control based on subscription tiers
20//! - Geographic restrictions (region attributes)
21//! - Time-based access (time-period attributes)
22//! - Role-based access (role attributes)
23//!
24//! # Example
25//!
26//! ```
27//! use chie_crypto::abe::{AbeAuthority, AccessPolicy, PolicyNode};
28//!
29//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
30//! // Authority generates master keys
31//! let mut authority = AbeAuthority::new();
32//!
33//! // Issue user key with attributes
34//! let user_attrs = vec!["premium".to_string(), "us-region".to_string()];
35//! let user_key = authority.generate_user_key(&user_attrs)?;
36//!
37//! // Encrypt with policy: premium AND us-region
38//! let policy = AccessPolicy::and(vec![
39//!     PolicyNode::Attribute("premium".to_string()),
40//!     PolicyNode::Attribute("us-region".to_string()),
41//! ]);
42//! let plaintext = b"Premium US content";
43//! let ciphertext = authority.encrypt(&policy, plaintext)?;
44//!
45//! // User can decrypt because they have both attributes
46//! let decrypted = authority.decrypt(&user_key, &ciphertext)?;
47//! assert_eq!(decrypted, plaintext);
48//! # Ok(())
49//! # }
50//! ```
51
52#![allow(dead_code)]
53
54use crate::encryption::{EncryptionKey, decrypt, encrypt, generate_nonce};
55use blake3::Hasher;
56use rand::RngExt as _;
57use serde::{Deserialize, Serialize};
58use std::collections::{HashMap, HashSet};
59use thiserror::Error;
60
61/// Errors that can occur in ABE operations.
62#[derive(Debug, Error)]
63pub enum AbeError {
64    #[error("Encryption failed: {0}")]
65    EncryptionFailed(String),
66
67    #[error("Decryption failed: {0}")]
68    DecryptionFailed(String),
69
70    #[error("Policy evaluation failed: {0}")]
71    PolicyFailed(String),
72
73    #[error("Invalid attributes: {0}")]
74    InvalidAttributes(String),
75
76    #[error("Key derivation failed: {0}")]
77    KeyDerivationFailed(String),
78
79    #[error("Serialization error: {0}")]
80    SerializationError(String),
81}
82
83/// Result type for ABE operations.
84pub type AbeResult<T> = Result<T, AbeError>;
85
86/// A node in an access policy tree.
87#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
88pub enum PolicyNode {
89    /// Leaf node: requires a specific attribute
90    Attribute(String),
91    /// AND gate: all children must be satisfied
92    And(Vec<PolicyNode>),
93    /// OR gate: at least one child must be satisfied
94    Or(Vec<PolicyNode>),
95    /// Threshold gate: at least k of n children must be satisfied
96    Threshold { k: usize, children: Vec<PolicyNode> },
97}
98
99impl PolicyNode {
100    /// Evaluate if a set of attributes satisfies this policy node.
101    pub fn evaluate(&self, attributes: &HashSet<String>) -> bool {
102        match self {
103            PolicyNode::Attribute(attr) => attributes.contains(attr),
104            PolicyNode::And(children) => children.iter().all(|c| c.evaluate(attributes)),
105            PolicyNode::Or(children) => children.iter().any(|c| c.evaluate(attributes)),
106            PolicyNode::Threshold { k, children } => {
107                let satisfied = children.iter().filter(|c| c.evaluate(attributes)).count();
108                satisfied >= *k
109            }
110        }
111    }
112
113    /// Get all attributes mentioned in this policy.
114    pub fn get_attributes(&self) -> HashSet<String> {
115        let mut attrs = HashSet::new();
116        self.collect_attributes(&mut attrs);
117        attrs
118    }
119
120    fn collect_attributes(&self, attrs: &mut HashSet<String>) {
121        match self {
122            PolicyNode::Attribute(attr) => {
123                attrs.insert(attr.clone());
124            }
125            PolicyNode::And(children) | PolicyNode::Or(children) => {
126                for child in children {
127                    child.collect_attributes(attrs);
128                }
129            }
130            PolicyNode::Threshold { children, .. } => {
131                for child in children {
132                    child.collect_attributes(attrs);
133                }
134            }
135        }
136    }
137}
138
139/// Access policy for CP-ABE encryption.
140#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct AccessPolicy {
142    /// Root of the policy tree
143    root: PolicyNode,
144}
145
146impl AccessPolicy {
147    /// Create a new access policy from a policy node.
148    pub fn new(root: PolicyNode) -> Self {
149        Self { root }
150    }
151
152    /// Create an AND policy (all attributes required).
153    pub fn and(nodes: Vec<PolicyNode>) -> Self {
154        Self::new(PolicyNode::And(nodes))
155    }
156
157    /// Create an OR policy (any attribute suffices).
158    pub fn or(nodes: Vec<PolicyNode>) -> Self {
159        Self::new(PolicyNode::Or(nodes))
160    }
161
162    /// Create a threshold policy (k-of-n).
163    pub fn threshold(k: usize, children: Vec<PolicyNode>) -> Self {
164        Self::new(PolicyNode::Threshold { k, children })
165    }
166
167    /// Evaluate if attributes satisfy this policy.
168    pub fn evaluate(&self, attributes: &HashSet<String>) -> bool {
169        self.root.evaluate(attributes)
170    }
171
172    /// Get all attributes mentioned in this policy.
173    pub fn get_attributes(&self) -> HashSet<String> {
174        self.root.get_attributes()
175    }
176}
177
178/// Master secret key for ABE authority.
179#[derive(Clone)]
180pub struct MasterSecretKey {
181    /// Secret seed for key derivation
182    seed: [u8; 32],
183}
184
185impl MasterSecretKey {
186    /// Generate a new random master secret key.
187    fn new() -> Self {
188        let mut seed = [0u8; 32];
189        rand::rng().fill(&mut seed);
190        Self { seed }
191    }
192
193    /// Derive an attribute key from the master secret.
194    fn derive_attribute_key(&self, attribute: &str) -> [u8; 32] {
195        let mut hasher = Hasher::new();
196        hasher.update(&self.seed);
197        hasher.update(b"attribute:");
198        hasher.update(attribute.as_bytes());
199        *hasher.finalize().as_bytes()
200    }
201}
202
203/// User secret key containing keys for specific attributes.
204#[derive(Clone, Serialize, Deserialize)]
205pub struct UserSecretKey {
206    /// Map from attribute to derived key
207    attribute_keys: HashMap<String, [u8; 32]>,
208}
209
210impl UserSecretKey {
211    /// Create a new user secret key.
212    fn new(attribute_keys: HashMap<String, [u8; 32]>) -> Self {
213        Self { attribute_keys }
214    }
215
216    /// Get the set of attributes this key possesses.
217    pub fn get_attributes(&self) -> HashSet<String> {
218        self.attribute_keys.keys().cloned().collect()
219    }
220
221    /// Check if this key has a specific attribute.
222    pub fn has_attribute(&self, attribute: &str) -> bool {
223        self.attribute_keys.contains_key(attribute)
224    }
225}
226
227/// Encrypted DEK with its nonce.
228#[derive(Clone, Serialize, Deserialize)]
229struct EncryptedDek {
230    /// Encrypted DEK bytes
231    ciphertext: Vec<u8>,
232    /// Nonce used for encryption
233    nonce: [u8; 12],
234}
235
236/// Attribute-based ciphertext.
237#[derive(Clone, Serialize, Deserialize)]
238pub struct AbeCiphertext {
239    /// Access policy for this ciphertext
240    policy: AccessPolicy,
241    /// Encrypted data encryption key for each attribute with nonces
242    encrypted_keys: HashMap<String, EncryptedDek>,
243    /// Encrypted payload
244    ciphertext: Vec<u8>,
245    /// Nonce for payload encryption
246    nonce: [u8; 12],
247}
248
249impl AbeCiphertext {
250    /// Get the access policy for this ciphertext.
251    pub fn policy(&self) -> &AccessPolicy {
252        &self.policy
253    }
254
255    /// Serialize to bytes.
256    pub fn to_bytes(&self) -> AbeResult<Vec<u8>> {
257        crate::codec::encode(self)
258            .map_err(|e| AbeError::SerializationError(format!("Serialization failed: {}", e)))
259    }
260
261    /// Deserialize from bytes.
262    pub fn from_bytes(bytes: &[u8]) -> AbeResult<Self> {
263        crate::codec::decode(bytes)
264            .map_err(|e| AbeError::SerializationError(format!("Deserialization failed: {}", e)))
265    }
266}
267
268/// ABE authority that manages keys and performs encryption/decryption.
269pub struct AbeAuthority {
270    /// Master secret key
271    master_key: MasterSecretKey,
272}
273
274impl AbeAuthority {
275    /// Create a new ABE authority with a random master key.
276    pub fn new() -> Self {
277        Self {
278            master_key: MasterSecretKey::new(),
279        }
280    }
281
282    /// Create an authority from existing master key bytes.
283    pub fn from_master_key(seed: [u8; 32]) -> Self {
284        Self {
285            master_key: MasterSecretKey { seed },
286        }
287    }
288
289    /// Generate a user secret key for a set of attributes.
290    pub fn generate_user_key(&self, attributes: &[String]) -> AbeResult<UserSecretKey> {
291        if attributes.is_empty() {
292            return Err(AbeError::InvalidAttributes(
293                "Attributes list cannot be empty".to_string(),
294            ));
295        }
296
297        let mut attribute_keys = HashMap::new();
298        for attr in attributes {
299            let key = self.master_key.derive_attribute_key(attr);
300            attribute_keys.insert(attr.clone(), key);
301        }
302
303        Ok(UserSecretKey::new(attribute_keys))
304    }
305
306    /// Encrypt data with an access policy.
307    pub fn encrypt(&self, policy: &AccessPolicy, plaintext: &[u8]) -> AbeResult<AbeCiphertext> {
308        // Generate random data encryption key
309        let mut dek = [0u8; 32];
310        rand::rng().fill(&mut dek);
311
312        // Generate nonce
313        let nonce = generate_nonce();
314
315        // Encrypt plaintext with DEK
316        let encryption_key = EncryptionKey::from(dek);
317        let ciphertext = encrypt(plaintext, &encryption_key, &nonce)
318            .map_err(|e| AbeError::EncryptionFailed(format!("Failed to encrypt: {}", e)))?;
319
320        // Encrypt DEK for each attribute in the policy
321        let mut encrypted_keys = HashMap::new();
322        for attr in policy.get_attributes() {
323            let attr_key = self.master_key.derive_attribute_key(&attr);
324
325            // Derive encryption key from attribute key
326            let attr_enc_key = EncryptionKey::from(attr_key);
327
328            // Generate unique nonce for this attribute
329            let attr_nonce = generate_nonce();
330
331            let encrypted_dek_bytes = encrypt(&dek, &attr_enc_key, &attr_nonce)
332                .map_err(|e| AbeError::EncryptionFailed(format!("Failed to encrypt DEK: {}", e)))?;
333
334            encrypted_keys.insert(
335                attr,
336                EncryptedDek {
337                    ciphertext: encrypted_dek_bytes,
338                    nonce: attr_nonce,
339                },
340            );
341        }
342
343        Ok(AbeCiphertext {
344            policy: policy.clone(),
345            encrypted_keys,
346            ciphertext,
347            nonce,
348        })
349    }
350
351    /// Decrypt ciphertext using a user secret key.
352    pub fn decrypt(
353        &self,
354        user_key: &UserSecretKey,
355        ciphertext: &AbeCiphertext,
356    ) -> AbeResult<Vec<u8>> {
357        // Check if user attributes satisfy the policy
358        let user_attrs = user_key.get_attributes();
359        if !ciphertext.policy.evaluate(&user_attrs) {
360            return Err(AbeError::DecryptionFailed(
361                "User attributes do not satisfy access policy".to_string(),
362            ));
363        }
364
365        // Find an attribute the user has that can decrypt the DEK
366        let mut dek = None;
367        for (attr, attr_key) in &user_key.attribute_keys {
368            if let Some(encrypted_dek) = ciphertext.encrypted_keys.get(attr) {
369                // Try to decrypt the DEK with this attribute key
370                let attr_enc_key = EncryptionKey::from(*attr_key);
371
372                // Try to decrypt the DEK
373                let decrypted = decrypt(
374                    &encrypted_dek.ciphertext,
375                    &attr_enc_key,
376                    &encrypted_dek.nonce,
377                );
378
379                if let Ok(dek_bytes) = decrypted {
380                    if dek_bytes.len() == 32 {
381                        let mut dek_arr = [0u8; 32];
382                        dek_arr.copy_from_slice(&dek_bytes);
383                        dek = Some(dek_arr);
384                        break;
385                    }
386                }
387            }
388        }
389
390        let dek = dek.ok_or_else(|| {
391            AbeError::DecryptionFailed("Could not recover data encryption key".to_string())
392        })?;
393
394        // Decrypt the payload
395        let encryption_key = EncryptionKey::from(dek);
396        decrypt(&ciphertext.ciphertext, &encryption_key, &ciphertext.nonce)
397            .map_err(|e| AbeError::DecryptionFailed(format!("Failed to decrypt payload: {}", e)))
398    }
399
400    /// Get the master key seed (for backup/recovery).
401    pub fn export_master_key(&self) -> [u8; 32] {
402        self.master_key.seed
403    }
404}
405
406impl Default for AbeAuthority {
407    fn default() -> Self {
408        Self::new()
409    }
410}
411
412#[cfg(test)]
413mod tests {
414    use super::*;
415
416    #[test]
417    fn test_policy_evaluation_single_attribute() {
418        let policy = PolicyNode::Attribute("admin".to_string());
419
420        let mut attrs = HashSet::new();
421        attrs.insert("admin".to_string());
422        assert!(policy.evaluate(&attrs));
423
424        attrs.clear();
425        attrs.insert("user".to_string());
426        assert!(!policy.evaluate(&attrs));
427    }
428
429    #[test]
430    fn test_policy_evaluation_and() {
431        let policy = PolicyNode::And(vec![
432            PolicyNode::Attribute("admin".to_string()),
433            PolicyNode::Attribute("premium".to_string()),
434        ]);
435
436        let mut attrs = HashSet::new();
437        attrs.insert("admin".to_string());
438        attrs.insert("premium".to_string());
439        assert!(policy.evaluate(&attrs));
440
441        attrs.clear();
442        attrs.insert("admin".to_string());
443        assert!(!policy.evaluate(&attrs));
444    }
445
446    #[test]
447    fn test_policy_evaluation_or() {
448        let policy = PolicyNode::Or(vec![
449            PolicyNode::Attribute("admin".to_string()),
450            PolicyNode::Attribute("moderator".to_string()),
451        ]);
452
453        let mut attrs = HashSet::new();
454        attrs.insert("admin".to_string());
455        assert!(policy.evaluate(&attrs));
456
457        attrs.clear();
458        attrs.insert("moderator".to_string());
459        assert!(policy.evaluate(&attrs));
460
461        attrs.clear();
462        attrs.insert("user".to_string());
463        assert!(!policy.evaluate(&attrs));
464    }
465
466    #[test]
467    fn test_policy_evaluation_threshold() {
468        let policy = PolicyNode::Threshold {
469            k: 2,
470            children: vec![
471                PolicyNode::Attribute("admin".to_string()),
472                PolicyNode::Attribute("moderator".to_string()),
473                PolicyNode::Attribute("premium".to_string()),
474            ],
475        };
476
477        let mut attrs = HashSet::new();
478        attrs.insert("admin".to_string());
479        attrs.insert("moderator".to_string());
480        assert!(policy.evaluate(&attrs));
481
482        attrs.clear();
483        attrs.insert("admin".to_string());
484        assert!(!policy.evaluate(&attrs));
485    }
486
487    #[test]
488    fn test_user_key_generation() {
489        let authority = AbeAuthority::new();
490        let attrs = vec!["admin".to_string(), "premium".to_string()];
491
492        let user_key = authority.generate_user_key(&attrs).unwrap();
493        assert_eq!(user_key.get_attributes().len(), 2);
494        assert!(user_key.has_attribute("admin"));
495        assert!(user_key.has_attribute("premium"));
496        assert!(!user_key.has_attribute("user"));
497    }
498
499    #[test]
500    fn test_encrypt_decrypt_simple() {
501        let authority = AbeAuthority::new();
502
503        let policy = AccessPolicy::new(PolicyNode::Attribute("premium".to_string()));
504        let user_key = authority
505            .generate_user_key(&["premium".to_string()])
506            .unwrap();
507
508        let plaintext = b"Secret premium content";
509        let ciphertext = authority.encrypt(&policy, plaintext).unwrap();
510        let decrypted = authority.decrypt(&user_key, &ciphertext).unwrap();
511
512        assert_eq!(decrypted, plaintext);
513    }
514
515    #[test]
516    fn test_encrypt_decrypt_and_policy() {
517        let authority = AbeAuthority::new();
518
519        let policy = AccessPolicy::and(vec![
520            PolicyNode::Attribute("premium".to_string()),
521            PolicyNode::Attribute("us-region".to_string()),
522        ]);
523
524        let user_key = authority
525            .generate_user_key(&["premium".to_string(), "us-region".to_string()])
526            .unwrap();
527
528        let plaintext = b"Premium US content";
529        let ciphertext = authority.encrypt(&policy, plaintext).unwrap();
530        let decrypted = authority.decrypt(&user_key, &ciphertext).unwrap();
531
532        assert_eq!(decrypted, plaintext);
533    }
534
535    #[test]
536    fn test_decrypt_fails_without_attributes() {
537        let authority = AbeAuthority::new();
538
539        let policy = AccessPolicy::new(PolicyNode::Attribute("premium".to_string()));
540        let user_key = authority.generate_user_key(&["basic".to_string()]).unwrap();
541
542        let plaintext = b"Secret premium content";
543        let ciphertext = authority.encrypt(&policy, plaintext).unwrap();
544
545        assert!(authority.decrypt(&user_key, &ciphertext).is_err());
546    }
547
548    #[test]
549    fn test_decrypt_fails_partial_and() {
550        let authority = AbeAuthority::new();
551
552        let policy = AccessPolicy::and(vec![
553            PolicyNode::Attribute("premium".to_string()),
554            PolicyNode::Attribute("us-region".to_string()),
555        ]);
556
557        // User has only one of the required attributes
558        let user_key = authority
559            .generate_user_key(&["premium".to_string()])
560            .unwrap();
561
562        let plaintext = b"Premium US content";
563        let ciphertext = authority.encrypt(&policy, plaintext).unwrap();
564
565        assert!(authority.decrypt(&user_key, &ciphertext).is_err());
566    }
567
568    #[test]
569    fn test_or_policy_decryption() {
570        let authority = AbeAuthority::new();
571
572        let policy = AccessPolicy::or(vec![
573            PolicyNode::Attribute("admin".to_string()),
574            PolicyNode::Attribute("premium".to_string()),
575        ]);
576
577        // User with admin attribute
578        let user_key1 = authority.generate_user_key(&["admin".to_string()]).unwrap();
579
580        // User with premium attribute
581        let user_key2 = authority
582            .generate_user_key(&["premium".to_string()])
583            .unwrap();
584
585        let plaintext = b"Admin or Premium content";
586        let ciphertext = authority.encrypt(&policy, plaintext).unwrap();
587
588        // Both should be able to decrypt
589        let decrypted1 = authority.decrypt(&user_key1, &ciphertext).unwrap();
590        assert_eq!(decrypted1, plaintext);
591
592        let decrypted2 = authority.decrypt(&user_key2, &ciphertext).unwrap();
593        assert_eq!(decrypted2, plaintext);
594    }
595
596    #[test]
597    fn test_threshold_policy() {
598        let authority = AbeAuthority::new();
599
600        let policy = AccessPolicy::threshold(
601            2,
602            vec![
603                PolicyNode::Attribute("attr1".to_string()),
604                PolicyNode::Attribute("attr2".to_string()),
605                PolicyNode::Attribute("attr3".to_string()),
606            ],
607        );
608
609        // User with 2 of 3 attributes
610        let user_key = authority
611            .generate_user_key(&["attr1".to_string(), "attr2".to_string()])
612            .unwrap();
613
614        let plaintext = b"Threshold content";
615        let ciphertext = authority.encrypt(&policy, plaintext).unwrap();
616        let decrypted = authority.decrypt(&user_key, &ciphertext).unwrap();
617
618        assert_eq!(decrypted, plaintext);
619    }
620
621    #[test]
622    fn test_complex_nested_policy() {
623        let authority = AbeAuthority::new();
624
625        // (admin OR moderator) AND premium
626        let policy = AccessPolicy::and(vec![
627            PolicyNode::Or(vec![
628                PolicyNode::Attribute("admin".to_string()),
629                PolicyNode::Attribute("moderator".to_string()),
630            ]),
631            PolicyNode::Attribute("premium".to_string()),
632        ]);
633
634        let user_key = authority
635            .generate_user_key(&["moderator".to_string(), "premium".to_string()])
636            .unwrap();
637
638        let plaintext = b"Complex policy content";
639        let ciphertext = authority.encrypt(&policy, plaintext).unwrap();
640        let decrypted = authority.decrypt(&user_key, &ciphertext).unwrap();
641
642        assert_eq!(decrypted, plaintext);
643    }
644
645    #[test]
646    fn test_ciphertext_serialization() {
647        let authority = AbeAuthority::new();
648
649        let policy = AccessPolicy::new(PolicyNode::Attribute("test".to_string()));
650        let plaintext = b"Serialization test";
651        let ciphertext = authority.encrypt(&policy, plaintext).unwrap();
652
653        let bytes = ciphertext.to_bytes().unwrap();
654        let restored = AbeCiphertext::from_bytes(&bytes).unwrap();
655
656        let user_key = authority.generate_user_key(&["test".to_string()]).unwrap();
657        let decrypted = authority.decrypt(&user_key, &restored).unwrap();
658
659        assert_eq!(decrypted, plaintext);
660    }
661
662    #[test]
663    fn test_empty_attributes_fails() {
664        let authority = AbeAuthority::new();
665        let result = authority.generate_user_key(&[]);
666        assert!(result.is_err());
667    }
668
669    #[test]
670    fn test_master_key_export_import() {
671        let authority1 = AbeAuthority::new();
672        let seed = authority1.export_master_key();
673
674        let authority2 = AbeAuthority::from_master_key(seed);
675
676        // Keys generated by both authorities should be compatible
677        let user_key = authority1.generate_user_key(&["test".to_string()]).unwrap();
678
679        let policy = AccessPolicy::new(PolicyNode::Attribute("test".to_string()));
680        let plaintext = b"Cross-authority test";
681        let ciphertext = authority2.encrypt(&policy, plaintext).unwrap();
682
683        let decrypted = authority1.decrypt(&user_key, &ciphertext).unwrap();
684        assert_eq!(decrypted, plaintext);
685    }
686
687    #[test]
688    fn test_multiple_plaintexts_same_policy() {
689        let authority = AbeAuthority::new();
690        let policy = AccessPolicy::new(PolicyNode::Attribute("premium".to_string()));
691        let user_key = authority
692            .generate_user_key(&["premium".to_string()])
693            .unwrap();
694
695        let plaintext1 = b"First message";
696        let plaintext2 = b"Second message";
697
698        let ciphertext1 = authority.encrypt(&policy, plaintext1).unwrap();
699        let ciphertext2 = authority.encrypt(&policy, plaintext2).unwrap();
700
701        let decrypted1 = authority.decrypt(&user_key, &ciphertext1).unwrap();
702        let decrypted2 = authority.decrypt(&user_key, &ciphertext2).unwrap();
703
704        assert_eq!(decrypted1, plaintext1);
705        assert_eq!(decrypted2, plaintext2);
706    }
707}