1#[cfg(windows)]
44use anyhow::{Context, Result};
45#[cfg(windows)]
46use std::{ptr, slice};
47#[cfg(windows)]
48use winapi::shared::minwindef::DWORD;
49#[cfg(windows)]
50use winapi::um::dpapi::{CryptProtectData, CryptUnprotectData};
51#[cfg(windows)]
52use winapi::um::wincrypt::DATA_BLOB;
53
54#[cfg(windows)]
56#[derive(Clone, Copy, Debug)]
57pub enum Scope {
58 User,
71
72 Machine,
84}
85
86#[cfg(windows)]
87fn to_blob(data: &[u8]) -> DATA_BLOB {
88 DATA_BLOB {
89 cbData: data.len() as DWORD,
90 pbData: data.as_ptr() as *mut u8,
91 }
92}
93
94#[cfg(windows)]
126pub fn encrypt_data(data: &[u8], scope: Scope, entropy: Option<&[u8]>) -> Result<Vec<u8>> {
127 log::debug!("Encrypting with DPAPI ({:?} scope)", scope);
128
129 let flags = match scope {
130 Scope::User => 0, Scope::Machine => 0x4, };
133
134 unsafe {
135 let mut input = to_blob(data);
136 let mut entropy_blob = if let Some(ent) = entropy {
137 to_blob(ent)
138 } else {
139 DATA_BLOB {
140 cbData: 0,
141 pbData: ptr::null_mut(),
142 }
143 };
144
145 let mut output = DATA_BLOB {
146 cbData: 0,
147 pbData: ptr::null_mut(),
148 };
149
150 let success = CryptProtectData(
151 &mut input,
152 ptr::null(),
153 if entropy.is_some() {
154 &mut entropy_blob
155 } else {
156 ptr::null_mut()
157 },
158 ptr::null_mut(),
159 ptr::null_mut(),
160 flags,
161 &mut output,
162 );
163
164 if success == 0 {
165 return Err(std::io::Error::last_os_error()).context("CryptProtectData failed");
166 }
167
168 let encrypted = slice::from_raw_parts(output.pbData, output.cbData as usize).to_vec();
169 winapi::um::winbase::LocalFree(output.pbData as *mut _);
170 Ok(encrypted)
171 }
172}
173
174#[cfg(windows)]
213pub fn decrypt_data(data: &[u8], scope: Scope, entropy: Option<&[u8]>) -> Result<Vec<u8>> {
214 log::debug!("Decrypting with DPAPI ({:?} scope)", scope);
215
216 let flags = match scope {
217 Scope::User => 0,
218 Scope::Machine => 0x4,
219 };
220
221 unsafe {
222 let mut input = to_blob(data);
223 let mut entropy_blob = if let Some(ent) = entropy {
224 to_blob(ent)
225 } else {
226 DATA_BLOB {
227 cbData: 0,
228 pbData: ptr::null_mut(),
229 }
230 };
231 let mut output = DATA_BLOB {
232 cbData: 0,
233 pbData: ptr::null_mut(),
234 };
235
236 let success = CryptUnprotectData(
237 &mut input,
238 ptr::null_mut(),
239 if entropy.is_some() {
240 &mut entropy_blob
241 } else {
242 ptr::null_mut()
243 },
244 ptr::null_mut(),
245 ptr::null_mut(),
246 flags,
247 &mut output,
248 );
249
250 if success == 0 {
251 return Err(std::io::Error::last_os_error()).context("CryptUnprotectData failed");
252 }
253
254 let decrypted = slice::from_raw_parts(output.pbData, output.cbData as usize).to_vec();
255 winapi::um::winbase::LocalFree(output.pbData as *mut _);
256 Ok(decrypted)
257 }
258}
259
260#[cfg(test)]
261#[cfg(windows)]
262mod tests {
263 use super::*;
264
265 #[test]
266 fn round_trip_user_scope() {
267 let original = b"user secret";
268 let encrypted = encrypt_data(original, Scope::User, None).expect("User encryption failed");
269 assert_ne!(original.to_vec(), encrypted);
270 let decrypted =
271 decrypt_data(&encrypted, Scope::User, None).expect("User decryption failed");
272 assert_eq!(original.to_vec(), decrypted);
273 }
274
275 #[test]
276 fn round_trip_user_scope_entropy() {
277 let original = b"user secret";
278 let entropy = b"user entropy";
279 let encrypted =
280 encrypt_data(original, Scope::User, Some(entropy)).expect("User encryption failed");
281 assert_ne!(original.to_vec(), encrypted);
282 let decrypted =
283 decrypt_data(&encrypted, Scope::User, Some(entropy)).expect("User decryption failed");
284 assert_eq!(original.to_vec(), decrypted);
285 }
286
287 #[test]
288 fn round_trip_machine_scope() {
289 let original = b"machine secret";
290 let encrypted =
291 encrypt_data(original, Scope::Machine, None).expect("Machine encryption failed");
292 assert_ne!(original.to_vec(), encrypted);
293 let decrypted =
294 decrypt_data(&encrypted, Scope::Machine, None).expect("Machine decryption failed");
295 assert_eq!(original.to_vec(), decrypted);
296 }
297
298 #[test]
299 fn round_trip_machine_scope_entropy() {
300 let original = b"machine secret";
301 let entropy = b"user entropy";
302 let encrypted = encrypt_data(original, Scope::Machine, Some(entropy))
303 .expect("Machine encryption failed");
304 assert_ne!(original.to_vec(), encrypted);
305 let decrypted = decrypt_data(&encrypted, Scope::Machine, Some(entropy))
306 .expect("Machine decryption failed");
307 assert_eq!(original.to_vec(), decrypted);
308 }
309
310 #[test]
311 fn handles_empty_input() {
312 let data = b"";
313 let encrypted = encrypt_data(data, Scope::Machine, None).expect("Encrypt empty");
314 let decrypted = decrypt_data(&encrypted, Scope::Machine, None).expect("Decrypt empty");
315 assert_eq!(data.to_vec(), decrypted);
316 }
317
318 #[test]
319 fn handles_empty_input_entropy() {
320 let data = b"";
321 let entropy = b"random entropy";
322 let encrypted = encrypt_data(data, Scope::Machine, Some(entropy)).expect("Encrypt empty");
323 let decrypted =
324 decrypt_data(&encrypted, Scope::Machine, Some(entropy)).expect("Decrypt empty");
325 assert_eq!(data.to_vec(), decrypted);
326 }
327
328 #[test]
329 fn handles_empty_entropy() {
330 let data = b"random value";
331 let entropy = b"";
332 let encrypted = encrypt_data(data, Scope::Machine, Some(entropy)).expect("Encrypt empty");
333 let decrypted =
334 decrypt_data(&encrypted, Scope::Machine, Some(entropy)).expect("Decrypt empty");
335 assert_eq!(data.to_vec(), decrypted);
336 }
337
338 #[test]
339 fn handles_large_input() {
340 let data = vec![0xAAu8; 5 * 1024 * 1024];
341 let encrypted = encrypt_data(&data, Scope::Machine, None).expect("Encrypt large");
342 let decrypted = decrypt_data(&encrypted, Scope::Machine, None).expect("Decrypt large");
343 assert_eq!(data, decrypted);
344 }
345
346 #[test]
347 fn handles_large_input_entropy() {
348 let data = vec![0xAAu8; 5 * 1024 * 1024];
349 let entropy = b"random entropy";
350 let encrypted = encrypt_data(&data, Scope::Machine, Some(entropy)).expect("Encrypt large");
351 let decrypted =
352 decrypt_data(&encrypted, Scope::Machine, Some(entropy)).expect("Decrypt large");
353 assert_eq!(data, decrypted);
354 }
355
356 #[test]
357 fn handles_large_entropy() {
358 let data = b"Random input";
359 let entropy = &vec![0xAAu8; 5 * 1024 * 1024];
360 let encrypted = encrypt_data(data, Scope::Machine, Some(entropy)).expect("Encrypt large");
361 let decrypted =
362 decrypt_data(&encrypted, Scope::Machine, Some(entropy)).expect("Decrypt large");
363 assert_eq!(data.to_vec(), decrypted);
364 }
365
366 #[test]
367 fn fails_on_corrupted_data() {
368 let original = b"important";
369 let mut encrypted = encrypt_data(original, Scope::Machine, None).expect("Encrypt failed");
370 encrypted[0] ^= 0xFF;
371 let result = decrypt_data(&encrypted, Scope::Machine, None);
372 assert!(result.is_err(), "Corrupted data should fail");
373 }
374 #[test]
375 fn fails_on_corrupted_data_entropy() {
376 let original = b"important";
377 let entropy = b"entropy";
378 let mut encrypted =
379 encrypt_data(original, Scope::Machine, Some(entropy)).expect("Encrypt failed");
380 encrypted[0] ^= 0xFF;
381 let result = decrypt_data(&encrypted, Scope::Machine, Some(entropy));
382 assert!(result.is_err(), "Corrupted data should fail");
383 }
384
385 #[test]
386 fn fails_on_wrong_entropy() {
387 let original = b"user secret";
388 let entropy = b"user entropy";
389 let bad_entropy = b"bad entropy";
390 let encrypted =
391 encrypt_data(original, Scope::User, Some(entropy)).expect("User encryption failed");
392 assert_ne!(original.to_vec(), encrypted);
393 let result = decrypt_data(&encrypted, Scope::User, Some(bad_entropy));
394 assert!(result.is_err(), "Wrong entropy should fail");
395 }
396
397 #[test]
398 fn entropy_encrypts_differently() {
399 let original = b"user secret";
400 let entropy = b"user entropy";
401 let bad_entropy = b"bad entropy";
402 let encrypted =
403 encrypt_data(original, Scope::User, Some(entropy)).expect("User encryption failed");
404 assert_ne!(original.to_vec(), encrypted);
405 let other_encrypted =
406 encrypt_data(original, Scope::User, Some(bad_entropy)).expect("User encryption failed");
407 assert_ne!(encrypted, other_encrypted);
408 }
409}