browser_commander/browser/migration/
os_crypt_keys.rs1use std::collections::HashMap;
16use std::fmt;
17use std::path::{Path, PathBuf};
18use std::sync::{Arc, Mutex};
19
20use anyhow::{anyhow, Result};
21
22use crate::browser::browser_cookie_credentials::{
23 read_safe_storage_password, read_windows_encryption_key,
24};
25use crate::browser::browser_cookie_crypto::derive_chromium_cookie_key;
26
27pub type SafeStoragePasswordReader = Arc<dyn Fn(&str, &str) -> Result<String> + Send + Sync>;
29pub type WindowsKeyReader = Arc<dyn Fn(&Path) -> Result<Vec<u8>> + Send + Sync>;
31
32#[derive(Clone)]
35pub struct KeystoreHooks {
36 pub read_safe_storage_password: SafeStoragePasswordReader,
38 pub read_windows_encryption_key: WindowsKeyReader,
40}
41
42impl Default for KeystoreHooks {
43 fn default() -> Self {
44 Self {
45 read_safe_storage_password: Arc::new(read_safe_storage_password),
46 read_windows_encryption_key: Arc::new(read_windows_encryption_key),
47 }
48 }
49}
50
51impl fmt::Debug for KeystoreHooks {
52 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
53 formatter
54 .debug_struct("KeystoreHooks")
55 .finish_non_exhaustive()
56 }
57}
58
59pub type SourceKeyResolver = Arc<dyn Fn(&str) -> Result<Vec<u8>> + Send + Sync>;
63
64pub(crate) fn create_source_key_resolver(
66 browser: &str,
67 platform: &str,
68 local_state_path: Option<PathBuf>,
69 hooks: KeystoreHooks,
70) -> SourceKeyResolver {
71 let browser = browser.to_string();
72 let platform = platform.to_string();
73 let cache: Mutex<HashMap<String, Result<Vec<u8>, String>>> = Mutex::new(HashMap::new());
74 Arc::new(move |prefix: &str| {
75 let mut cache = cache
76 .lock()
77 .unwrap_or_else(|poisoned| poisoned.into_inner());
78 let entry = cache.entry(prefix.to_string()).or_insert_with(|| {
79 resolve_source_key(
80 &browser,
81 &platform,
82 prefix,
83 local_state_path.as_deref(),
84 &hooks,
85 )
86 .map_err(|error| format!("{error:#}"))
87 });
88 entry.clone().map_err(|message| anyhow!(message))
89 })
90}
91
92fn resolve_source_key(
93 browser: &str,
94 platform: &str,
95 prefix: &str,
96 local_state_path: Option<&Path>,
97 hooks: &KeystoreHooks,
98) -> Result<Vec<u8>> {
99 match platform {
100 "win32" => {
101 let path = local_state_path
102 .ok_or_else(|| anyhow!("The source Local State path is unknown"))?;
103 (hooks.read_windows_encryption_key)(path)
104 }
105 "linux" if prefix == "v10" => derive_chromium_cookie_key("peanuts", "linux"),
109 "linux" | "darwin" => {
110 let password = (hooks.read_safe_storage_password)(browser, platform)?;
111 derive_chromium_cookie_key(&password, platform)
112 }
113 _ => Err(anyhow!("OSCrypt keys are unsupported on {platform}")),
114 }
115}
116
117#[derive(Debug, Clone, PartialEq, Eq)]
119pub struct TargetKey {
120 pub key: Vec<u8>,
122 pub prefix: String,
124}
125
126pub(crate) fn resolve_target_key(
128 browser: &str,
129 platform: &str,
130 hooks: &KeystoreHooks,
131) -> Result<TargetKey> {
132 if platform == "darwin" || platform == "linux" {
133 let password = (hooks.read_safe_storage_password)(browser, platform)?;
134 return Ok(TargetKey {
135 key: derive_chromium_cookie_key(&password, platform)?,
136 prefix: "v11".to_string(),
137 });
138 }
139 Err(anyhow!(
140 "resolve_target_key does not derive a Windows key; use create_windows_profile_key"
141 ))
142}
143
144pub(crate) use crate::browser::browser_profile_files::local_state_path_for_profile;