Skip to main content

veilid_core/protected_store/
native.rs

1use super::*;
2use data_encoding::BASE64URL_NOPAD;
3use keyring_manager::*;
4use std::path::Path;
5
6impl_veilid_log_facility!("pstore");
7
8pub struct ProtectedStoreInner {
9    keyring_manager: Option<KeyringManager>,
10}
11impl fmt::Debug for ProtectedStoreInner {
12    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
13        f.debug_struct("ProtectedStoreInner").finish()
14    }
15}
16
17#[derive(Debug)]
18#[must_use]
19pub struct ProtectedStore {
20    registry: VeilidComponentRegistry,
21    inner: Mutex<ProtectedStoreInner>,
22}
23
24impl_veilid_component!(ProtectedStore);
25
26impl ProtectedStore {
27    fn new_inner() -> ProtectedStoreInner {
28        ProtectedStoreInner {
29            keyring_manager: None,
30        }
31    }
32
33    pub(crate) fn new(registry: VeilidComponentRegistry) -> Self {
34        Self {
35            registry,
36            inner: Mutex::new(Self::new_inner()),
37        }
38    }
39
40    #[cfg_attr(feature = "instrument", instrument(level = "trace", skip(self), fields(__VEILID_LOG_KEY = self.log_key())))]
41    pub fn delete_all(&self) {
42        for kpsk in &KNOWN_PROTECTED_STORE_KEYS {
43            if let Err(e) = self.remove_user_secret(kpsk) {
44                veilid_log!(self error "failed to delete '{}': {}", kpsk, e);
45            } else {
46                veilid_log!(self debug "deleted protected store key '{}'", kpsk);
47            }
48        }
49    }
50
51    fn log_facilities_impl(&self) -> VeilidComponentLogFacilities {
52        VeilidComponentLogFacilities::new().with_facility(
53            VeilidComponentLogFacility::try_new_with_tags("pstore", ["#common"]).unwrap(),
54        )
55    }
56
57    #[cfg_attr(feature = "instrument", instrument(level = "debug", skip(self), err, fields(__VEILID_LOG_KEY = self.log_key())))]
58    async fn init_async(&self) -> EyreResult<()> {
59        let delete = {
60            let config = self.config();
61            let mut inner = self.inner.lock();
62            if !config.protected_store.always_use_insecure_storage {
63                // Attempt to open the secure keyring
64                cfg_if! {
65                    if #[cfg(target_os = "android")] {
66                        let maybe_km = KeyringManager::new_secure(&config.program_name, crate::veilid_api::android::get_android_globals());
67                    } else {
68                        let maybe_km = KeyringManager::new_secure(&config.program_name);
69                    }
70                }
71
72                inner.keyring_manager = match maybe_km {
73                    Ok(v) => Some(v),
74                    Err(e) => {
75                        veilid_log!(self info "Secure key storage service unavailable, falling back to direct disk-based storage: {}", e);
76                        None
77                    }
78                };
79            }
80            if (config.protected_store.always_use_insecure_storage
81                || config.protected_store.allow_insecure_fallback)
82                && inner.keyring_manager.is_none()
83            {
84                let directory = Path::new(&config.protected_store.directory);
85                let insecure_keyring_file = directory.to_owned().join(format!(
86                    "insecure_keyring{}",
87                    if config.namespace.is_empty() {
88                        "".to_owned()
89                    } else {
90                        format!("_{}", config.namespace)
91                    }
92                ));
93
94                // Ensure permissions are correct
95                ensure_file_private_owner(&insecure_keyring_file).map_err(|e| eyre!("{}", e))?;
96
97                // Open the insecure keyring
98                inner.keyring_manager = Some(
99                    KeyringManager::new_insecure(&config.program_name, &insecure_keyring_file)
100                        .wrap_err("failed to create insecure keyring")?,
101                );
102            }
103            if inner.keyring_manager.is_none() {
104                bail!("Could not initialize the protected store.");
105            }
106            config.protected_store.delete
107        };
108
109        if delete {
110            self.delete_all();
111        }
112
113        Ok(())
114    }
115
116    #[cfg_attr(feature = "instrument", instrument(level = "debug", skip(self), err, fields(__VEILID_LOG_KEY = self.log_key())))]
117    async fn post_init_async(&self) -> EyreResult<()> {
118        Ok(())
119    }
120
121    #[cfg_attr(feature = "instrument", instrument(level = "debug", skip(self), fields(__VEILID_LOG_KEY = self.log_key())))]
122    async fn pre_terminate_async(&self) {}
123
124    #[cfg_attr(feature = "instrument", instrument(level = "debug", skip(self), fields(__VEILID_LOG_KEY = self.log_key())))]
125    async fn terminate_async(&self) {
126        *self.inner.lock() = Self::new_inner();
127    }
128
129    fn service_name(&self) -> String {
130        let config = self.config();
131        if config.namespace.is_empty() {
132            "veilid_protected_store".to_owned()
133        } else {
134            format!("veilid_protected_store_{}", config.namespace)
135        }
136    }
137
138    #[cfg_attr(
139        feature = "instrument",
140        instrument(level = "trace", skip(self, value), ret, err, fields(__VEILID_LOG_KEY = self.log_key()))
141    )]
142    pub fn save_user_secret_string<K: AsRef<str> + fmt::Debug, V: AsRef<str> + fmt::Debug>(
143        &self,
144        key: K,
145        value: V,
146    ) -> VeilidAPIResult<bool> {
147        let inner = self.inner.lock();
148        inner
149            .keyring_manager
150            .as_ref()
151            .ok_or_else(VeilidAPIError::not_initialized)?
152            .with_keyring(&self.service_name(), key.as_ref(), |kr| {
153                let existed = kr.get_value().is_ok();
154                kr.set_value(value.as_ref())?;
155                Ok(existed)
156            })
157            .map_err(VeilidAPIError::generic)
158    }
159
160    #[cfg_attr(feature = "instrument", instrument(level = "trace", skip(self), err, fields(__VEILID_LOG_KEY = self.log_key())))]
161    pub fn load_user_secret_string<K: AsRef<str> + fmt::Debug>(
162        &self,
163        key: K,
164    ) -> VeilidAPIResult<Option<String>> {
165        let inner = self.inner.lock();
166        match inner
167            .keyring_manager
168            .as_ref()
169            .ok_or_else(VeilidAPIError::not_initialized)?
170            .with_keyring(&self.service_name(), key.as_ref(), |kr| kr.get_value())
171        {
172            Ok(v) => Ok(Some(v)),
173            Err(KeyringError::NoPasswordFound) => Ok(None),
174            Err(e) => Err(VeilidAPIError::generic(format!(
175                "Failed to load user secret: {}",
176                e
177            ))),
178        }
179    }
180
181    #[cfg_attr(feature = "instrument", instrument(level = "trace", skip(self, value), fields(__VEILID_LOG_KEY = self.log_key())))]
182    pub fn save_user_secret_json<K, T>(&self, key: K, value: &T) -> VeilidAPIResult<bool>
183    where
184        K: AsRef<str> + fmt::Debug,
185        T: serde::Serialize,
186    {
187        let v = serde_json::to_vec(value).map_err(VeilidAPIError::generic)?;
188        self.save_user_secret(&key, &v)
189    }
190
191    #[cfg_attr(feature = "instrument", instrument(level = "trace", skip(self), fields(__VEILID_LOG_KEY = self.log_key())))]
192    pub fn load_user_secret_json<K, T>(&self, key: K) -> VeilidAPIResult<Option<T>>
193    where
194        K: AsRef<str> + fmt::Debug,
195        T: for<'de> serde::de::Deserialize<'de>,
196    {
197        let out = self.load_user_secret(key)?;
198        let b = match out {
199            Some(v) => v,
200            None => {
201                return Ok(None);
202            }
203        };
204
205        let obj = serde_json::from_slice(&b).map_err(VeilidAPIError::generic)?;
206        Ok(Some(obj))
207    }
208
209    #[cfg_attr(
210        feature = "instrument",
211        instrument(level = "trace", skip(self, value), ret, err, fields(__VEILID_LOG_KEY = self.log_key()))
212    )]
213    pub fn save_user_secret<K: AsRef<str> + fmt::Debug>(
214        &self,
215        key: K,
216        value: &[u8],
217    ) -> VeilidAPIResult<bool> {
218        let mut s = BASE64URL_NOPAD.encode(value);
219        s.push('!');
220
221        self.save_user_secret_string(key, s.as_str())
222    }
223
224    #[cfg_attr(feature = "instrument", instrument(level = "trace", skip(self), err, fields(__VEILID_LOG_KEY = self.log_key())))]
225    pub fn load_user_secret<K: AsRef<str> + fmt::Debug>(
226        &self,
227        key: K,
228    ) -> VeilidAPIResult<Option<Vec<u8>>> {
229        let mut s = match self.load_user_secret_string(key)? {
230            Some(s) => s,
231            None => {
232                return Ok(None);
233            }
234        };
235
236        if s.pop() != Some('!') {
237            apibail_generic!("User secret is not a buffer");
238        }
239
240        let mut bytes = Vec::<u8>::new();
241        let res = BASE64URL_NOPAD.decode_len(s.len());
242        match res {
243            Ok(l) => {
244                bytes.resize(l, 0u8);
245            }
246            Err(_) => {
247                apibail_generic!("Failed to decode");
248            }
249        }
250
251        let res = BASE64URL_NOPAD.decode_mut(s.as_bytes(), &mut bytes);
252        match res {
253            Ok(_) => Ok(Some(bytes)),
254            Err(_) => apibail_generic!("Failed to decode"),
255        }
256    }
257
258    #[cfg_attr(
259        feature = "instrument",
260        instrument(level = "trace", skip(self), ret, err, fields(__VEILID_LOG_KEY = self.log_key()))
261    )]
262    pub fn remove_user_secret<K: AsRef<str> + fmt::Debug>(&self, key: K) -> VeilidAPIResult<bool> {
263        let inner = self.inner.lock();
264        match inner
265            .keyring_manager
266            .as_ref()
267            .ok_or_else(VeilidAPIError::not_initialized)?
268            .with_keyring(&self.service_name(), key.as_ref(), |kr| kr.delete_value())
269        {
270            Ok(_) => Ok(true),
271            Err(KeyringError::NoPasswordFound) => Ok(false),
272            Err(e) => Err(VeilidAPIError::generic(format!(
273                "Failed to remove user secret: {}",
274                e
275            ))),
276        }
277    }
278}