lib3mf_core/crypto/
context.rs

1use crate::archive::ArchiveReader;
2use crate::crypto::encryption::decrypt_aes256gcm;
3use crate::crypto::keys::KeyManager;
4use crate::error::{Lib3mfError, Result};
5use crate::model::secure_content::*;
6use uuid::Uuid;
7// use rsa::RsaPrivateKey;
8// To avoid strict rsa dependency in public struct fields if possible, or use generic?
9// For now, use RsaPrivateKey.
10use rsa::RsaPrivateKey;
11use std::collections::HashMap;
12
13/// Manages secure content decryption context.
14pub struct SecureContext {
15    keystore: KeyStore,
16    private_key: RsaPrivateKey,
17    /// Cache of unwrapped CEKs (KeyUUID -> CEK bytes)
18    cek_cache: HashMap<String, Vec<u8>>,
19    /// Map of Resource Path to KeyUUID (derived from Relationships)
20    resource_key_map: HashMap<String, Uuid>,
21    /// Consumer ID we are acting as
22    consumer_id: String,
23}
24
25impl SecureContext {
26    pub fn new(
27        keystore: KeyStore,
28        private_key: RsaPrivateKey,
29        resource_key_map: HashMap<String, Uuid>,
30        consumer_id: String,
31    ) -> Self {
32        Self {
33            keystore,
34            private_key,
35            cek_cache: HashMap::new(),
36            resource_key_map,
37            consumer_id,
38        }
39    }
40
41    /// Reads and decrypts an entry from the archive if it is covered by the KeyStore.
42    /// If the path is not encrypted (not in map), returns None (or error if strictly checking?).
43    /// Recommended: Return `Ok(Some(data))` if decrypted, `Ok(None)` if not encrypted, `Err` if failure.
44    pub fn decrypt_entry(
45        &mut self,
46        archiver: &mut impl ArchiveReader,
47        path: &str,
48    ) -> Result<Option<Vec<u8>>> {
49        let key_uuid = match self.resource_key_map.get(path) {
50            Some(u) => u,
51            None => return Ok(None), // Not encrypted
52        };
53
54        // Find Group
55        let group = self
56            .keystore
57            .resource_data_groups
58            .iter()
59            .find(|g| g.key_uuid == *key_uuid)
60            .ok_or_else(|| {
61                Lib3mfError::Validation(format!("KeyUUID {} not found in KeyStore", key_uuid))
62            })?;
63
64        // Get CEK
65        let cek = if let Some(cached) = self.cek_cache.get(&key_uuid.to_string()) {
66            cached.clone()
67        } else {
68            // Find AccessRight
69            let right = group
70                .access_rights
71                .iter()
72                .find(|ar| ar.consumer_id == self.consumer_id)
73                .ok_or_else(|| {
74                    Lib3mfError::Validation(format!(
75                        "No access right for consumer {} in group {}",
76                        self.consumer_id, key_uuid
77                    ))
78                })?;
79
80            // Unwrap
81            // Decode wrapped key from base64 first?
82            // Phase 13 parser might have stored it as base64 string or Vec<u8>?
83            // Checking safe_content.rs: wrapped_key: Vec<u8>
84            // So it's already bytes (parser handled it? Parser code snippet showed `read_text_content`?
85            // `secure_content_parser.rs` snippet didn't show `WrappedKey` parsing detail in initial view.
86            // Assuming it's Vec<u8> in struct.
87            // If parser stored base64 bytes, we need to decode?
88            // `scan` of `secure_content_parser.rs` in Phase 13 summary mentioned `wrappedkey`.
89            // I'll assume it's raw bytes (decoded by parser) OR raw base64 string bytes.
90            // Usually parser decodes text content? No, parser `read_text_content` returns String.
91            // But `WrappedKey` in struct is `Vec<u8>`. `secure_content_parser` must have decoded it.
92            // I'll verify if `secure_content_parser` did base64 decode.
93            // If not, unwrap_key will fail.
94            // I'll assume parser did its job or key is not base64 encoded in XML (unlikely, usually base64).
95            // Wait, KeyStore struct definition says `wrapped_key: Vec<u8>`.
96            // I will try to unwrap.
97
98            let cek = KeyManager::unwrap_key(&self.private_key, &right.wrapped_key)?;
99            self.cek_cache.insert(key_uuid.to_string(), cek.clone());
100            cek
101        };
102
103        // Read encrypted content
104        let encrypted_data = archiver.read_entry(path)?;
105
106        // Decrypt
107        // Format of encrypted data?
108        // 3MF Spec: "The entire part is encrypted... IV? Tag?"
109        // Usually IV is prepended.
110        // `decrypt_aes256gcm` implementation in `encryption.rs` expects `nonce` + `ciphertext` (where ciphertext includes tag).
111        // `aes_gcm` crate `encrypt` outputs `ciphertext` (with tag).
112        // My `decrypt_aes256gcm` takes `nonce` and `ciphertext` separately.
113        // I need to split the file content.
114        // Nonce is 12 bytes.
115        // So: Nonce (12) | Ciphertext (rest).
116
117        if encrypted_data.len() < 12 + 16 {
118            // Nonce + Tag
119            return Err(Lib3mfError::Validation(
120                "Encrypted file too short".to_string(),
121            ));
122        }
123
124        let (nonce, ciphertext) = encrypted_data.split_at(12);
125
126        let plaintext = decrypt_aes256gcm(&cek, nonce, ciphertext)?;
127
128        Ok(Some(plaintext))
129    }
130}