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}