1#[cfg(all(feature = "session-encryption", not(target_arch = "wasm32")))]
6use aes_gcm::{
7 aead::{Aead, KeyInit},
8 Aes256Gcm, Key, Nonce,
9};
10
11#[cfg(all(feature = "session-encryption", not(target_arch = "wasm32")))]
12use argon2::Argon2;
13
14#[cfg(feature = "session-encryption")]
15use crate::error::{Error, Result};
16#[cfg(feature = "session-encryption")]
17use crate::session::SessionData;
18#[cfg(feature = "session-encryption")]
19use rand::RngCore;
20#[cfg(feature = "session-encryption")]
21use serde::{Deserialize, Serialize};
22
23#[cfg(feature = "session-encryption")]
25#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct EncryptedSessionData {
27 pub encrypted_data: Vec<u8>,
29 pub nonce: Vec<u8>,
31 pub salt: Option<Vec<u8>>,
33 pub metadata: EncryptionMetadata,
35}
36
37#[cfg(feature = "session-encryption")]
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct EncryptionMetadata {
41 pub algorithm: String,
43 pub kdf: Option<String>,
45 pub version: u32,
47}
48
49#[cfg(all(feature = "session-encryption", not(target_arch = "wasm32")))]
51pub struct SessionEncryptor {
52 cipher: Aes256Gcm,
53}
54
55#[cfg(all(feature = "session-encryption", not(target_arch = "wasm32")))]
56impl SessionEncryptor {
57 pub fn new(key: [u8; 32]) -> Result<Self> {
59 #[allow(deprecated)]
60 let key = Key::<Aes256Gcm>::from_slice(&key);
61 let cipher = Aes256Gcm::new(key);
62 Ok(Self { cipher })
63 }
64
65 pub fn from_password(password: &str, salt: Option<&[u8]>) -> Result<(Self, Vec<u8>)> {
67 let salt = match salt {
68 Some(s) => s.to_vec(),
69 None => {
70 let mut salt = vec![0u8; 16];
71 rand::thread_rng().fill_bytes(&mut salt);
72 salt
73 }
74 };
75
76 let argon2 = Argon2::default();
77 let mut key = [0u8; 32];
78
79 argon2
80 .hash_password_into(password.as_bytes(), &salt, &mut key)
81 .map_err(|e| Error::crypto(format!("Failed to derive key from password: {}", e)))?;
82
83 let encryptor = Self::new(key)?;
84 Ok((encryptor, salt))
85 }
86
87 pub fn encrypt_session(&self, session_data: &SessionData) -> Result<SessionData> {
89 let serialized = serde_json::to_vec(session_data).map_err(|e| {
91 Error::crypto(format!("Failed to serialize session for encryption: {}", e))
92 })?;
93
94 let nonce_bytes = rand::random::<[u8; 12]>();
96 #[allow(deprecated)]
97 let nonce = Nonce::from_slice(&nonce_bytes);
98
99 #[allow(clippy::needless_borrow)]
101 let encrypted_data = self
102 .cipher
103 .encrypt(&nonce, serialized.as_ref())
104 .map_err(|e| Error::crypto(format!("Failed to encrypt session: {}", e)))?;
105
106 let encrypted_container = EncryptedSessionData {
108 encrypted_data,
109 nonce: nonce.to_vec(),
110 salt: None,
111 metadata: EncryptionMetadata {
112 algorithm: "AES-256-GCM".to_string(),
113 kdf: None,
114 version: 1,
115 },
116 };
117
118 let mut encrypted_session = session_data.clone();
120 encrypted_session.platform_data.insert(
121 "encrypted_payload".to_string(),
122 serde_json::to_value(&encrypted_container).map_err(|e| {
123 Error::crypto(format!("Failed to serialize encrypted container: {}", e))
124 })?,
125 );
126
127 encrypted_session.session.access_token = "***ENCRYPTED***".to_string();
129 encrypted_session.session.refresh_token = "***ENCRYPTED***".to_string();
130 encrypted_session.session.user.email = None;
131 encrypted_session.session.user.phone = None;
132
133 Ok(encrypted_session)
134 }
135
136 pub fn decrypt_session(&self, encrypted_session: &SessionData) -> Result<SessionData> {
138 let encrypted_container_value = encrypted_session
140 .platform_data
141 .get("encrypted_payload")
142 .ok_or_else(|| Error::crypto("No encrypted payload found in session"))?;
143
144 let encrypted_container: EncryptedSessionData =
145 serde_json::from_value(encrypted_container_value.clone()).map_err(|e| {
146 Error::crypto(format!("Failed to deserialize encrypted container: {}", e))
147 })?;
148
149 if encrypted_container.metadata.algorithm != "AES-256-GCM" {
151 return Err(Error::crypto(format!(
152 "Unsupported encryption algorithm: {}",
153 encrypted_container.metadata.algorithm
154 )));
155 }
156
157 if encrypted_container.metadata.version != 1 {
158 return Err(Error::crypto(format!(
159 "Unsupported encryption version: {}",
160 encrypted_container.metadata.version
161 )));
162 }
163
164 #[allow(deprecated)]
166 let nonce = Nonce::from_slice(&encrypted_container.nonce);
167 #[allow(clippy::needless_borrow)]
168 let decrypted_data = self
169 .cipher
170 .decrypt(&nonce, encrypted_container.encrypted_data.as_ref())
171 .map_err(|e| Error::crypto(format!("Failed to decrypt session: {}", e)))?;
172
173 let original_session: SessionData =
175 serde_json::from_slice(&decrypted_data).map_err(|e| {
176 Error::crypto(format!("Failed to deserialize decrypted session: {}", e))
177 })?;
178
179 Ok(original_session)
180 }
181
182 pub fn generate_key() -> [u8; 32] {
184 rand::random()
185 }
186}
187
188#[cfg(all(feature = "session-encryption", target_arch = "wasm32"))]
190#[derive(Debug)]
191pub struct SessionEncryptor {
192 key: [u8; 32],
193}
194
195#[cfg(all(feature = "session-encryption", target_arch = "wasm32"))]
196impl SessionEncryptor {
197 pub fn new(key: [u8; 32]) -> Result<Self> {
199 Ok(Self { key })
200 }
201
202 pub fn encrypt_session(&self, session_data: &SessionData) -> Result<SessionData> {
204 let serialized = serde_json::to_vec(session_data).map_err(|e| {
209 Error::crypto(format!("Failed to serialize session for encryption: {}", e))
210 })?;
211
212 let mut encrypted_data = Vec::with_capacity(serialized.len());
214 for (i, byte) in serialized.iter().enumerate() {
215 encrypted_data.push(byte ^ self.key[i % 32]);
216 }
217
218 let encrypted_container = EncryptedSessionData {
220 encrypted_data,
221 nonce: vec![0; 12], salt: None,
223 metadata: EncryptionMetadata {
224 algorithm: "XOR-DEMO".to_string(),
225 kdf: None,
226 version: 1,
227 },
228 };
229
230 let mut encrypted_session = session_data.clone();
232 encrypted_session.platform_data.insert(
233 "encrypted_payload".to_string(),
234 serde_json::to_value(&encrypted_container).map_err(|e| {
235 Error::crypto(format!("Failed to serialize encrypted container: {}", e))
236 })?,
237 );
238
239 encrypted_session.session.access_token = "***ENCRYPTED***".to_string();
241 encrypted_session.session.refresh_token = "***ENCRYPTED***".to_string();
242 encrypted_session.session.user.email = None;
243 encrypted_session.session.user.phone = None;
244
245 Ok(encrypted_session)
246 }
247
248 pub fn decrypt_session(&self, encrypted_session: &SessionData) -> Result<SessionData> {
250 let encrypted_container_value = encrypted_session
252 .platform_data
253 .get("encrypted_payload")
254 .ok_or_else(|| Error::crypto("No encrypted payload found in session"))?;
255
256 let encrypted_container: EncryptedSessionData =
257 serde_json::from_value(encrypted_container_value.clone()).map_err(|e| {
258 Error::crypto(format!("Failed to deserialize encrypted container: {}", e))
259 })?;
260
261 if encrypted_container.metadata.algorithm != "XOR-DEMO" {
263 return Err(Error::crypto(format!(
264 "Unsupported encryption algorithm: {}",
265 encrypted_container.metadata.algorithm
266 )));
267 }
268
269 let mut decrypted_data = Vec::with_capacity(encrypted_container.encrypted_data.len());
271 for (i, byte) in encrypted_container.encrypted_data.iter().enumerate() {
272 decrypted_data.push(byte ^ self.key[i % 32]);
273 }
274
275 let original_session: SessionData =
277 serde_json::from_slice(&decrypted_data).map_err(|e| {
278 Error::crypto(format!("Failed to deserialize decrypted session: {}", e))
279 })?;
280
281 Ok(original_session)
282 }
283
284 pub fn generate_key() -> [u8; 32] {
286 [0; 32] }
290}
291
292#[cfg(feature = "session-encryption")]
294pub struct KeyManager;
295
296#[cfg(feature = "session-encryption")]
297impl KeyManager {
298 pub fn generate_encryption_key() -> [u8; 32] {
300 SessionEncryptor::generate_key()
301 }
302
303 #[cfg(not(target_arch = "wasm32"))]
305 pub fn derive_key_from_password(
306 password: &str,
307 salt: Option<&[u8]>,
308 ) -> Result<([u8; 32], Vec<u8>)> {
309 use rand::RngCore;
310
311 let salt = salt.map(|s| s.to_vec()).unwrap_or_else(|| {
312 let mut salt = vec![0u8; 16];
313 rand::thread_rng().fill_bytes(&mut salt);
314 salt
315 });
316
317 let mut key = [0u8; 32];
320 let combined = format!("{}{}", password, hex::encode(&salt));
321 let hash = combined.bytes().cycle().take(32).collect::<Vec<_>>();
322 key.copy_from_slice(&hash);
323
324 Ok((key, salt))
325 }
326
327 #[cfg(all(feature = "session-encryption", not(target_arch = "wasm32")))]
329 pub fn store_key_securely(service: &str, username: &str, key: &[u8; 32]) -> Result<()> {
330 let entry = keyring::Entry::new(service, username)
331 .map_err(|e| Error::crypto(format!("Failed to create keyring entry: {}", e)))?;
332
333 let key_hex = hex::encode(key);
334 entry
335 .set_password(&key_hex)
336 .map_err(|e| Error::crypto(format!("Failed to store key in keyring: {}", e)))?;
337
338 Ok(())
339 }
340
341 #[cfg(all(feature = "session-encryption", not(target_arch = "wasm32")))]
343 pub fn retrieve_key_securely(service: &str, username: &str) -> Result<[u8; 32]> {
344 let entry = keyring::Entry::new(service, username)
345 .map_err(|e| Error::crypto(format!("Failed to create keyring entry: {}", e)))?;
346
347 let key_hex = entry
348 .get_password()
349 .map_err(|e| Error::crypto(format!("Failed to retrieve key from keyring: {}", e)))?;
350
351 let key_bytes = hex::decode(&key_hex)
352 .map_err(|e| Error::crypto(format!("Failed to decode key from hex: {}", e)))?;
353
354 if key_bytes.len() != 32 {
355 return Err(Error::crypto("Invalid key length"));
356 }
357
358 let mut key = [0u8; 32];
359 key.copy_from_slice(&key_bytes);
360 Ok(key)
361 }
362}