sos_vault/
access_point.rs

1//! Access point manages access to a vault.
2use crate::{
3    secret::{Secret, SecretMeta, SecretRow},
4    EncryptedEntry, Error, SharedAccess, Summary, Vault, VaultMeta,
5};
6use async_trait::async_trait;
7use sos_core::{
8    crypto::{AccessKey, AeadPack, KeyDerivation, PrivateKey},
9    decode, encode,
10    events::{ReadEvent, WriteEvent},
11    AuthenticationError, SecretId, VaultCommit, VaultEntry, VaultFlags,
12    VaultId,
13};
14use sos_vfs as vfs;
15use std::path::Path;
16
17pub type VaultMirror<E> =
18    Box<dyn EncryptedEntry<Error = E> + Send + Sync + 'static>;
19
20/// Read and write vault secrets.
21#[async_trait]
22pub trait SecretAccess {
23    /// Error type.
24    type Error: std::error::Error
25        + std::fmt::Debug
26        + From<crate::Error>
27        + From<sos_core::Error>
28        + From<std::io::Error>
29        + Send
30        + Sync
31        + 'static;
32
33    /// Indicates whether the access point is mirroring
34    /// changes to storage.
35    fn is_mirror(&self) -> bool;
36
37    /// In-memory vault.
38    fn vault(&self) -> &Vault;
39
40    /// Replace this vault with a new updated vault.
41    ///
42    /// Setting `mirror_changes` will update a mirror if
43    /// this access point is mirroring to storage.
44    ///
45    /// Callers should take care to lock beforehand and
46    /// unlock again afterwards if the vault access key
47    /// has been changed.
48    async fn replace_vault(
49        &mut self,
50        vault: Vault,
51        mirror_changes: bool,
52    ) -> Result<(), Self::Error>;
53
54    /// Reload the vault from disc.
55    ///
56    /// Replaces the in-memory vault and updates the vault writer
57    /// mirror when mirroring to disc is enabled.
58    ///
59    /// Use this to update the in-memory representation when a vault
60    /// has been modified in a different process.
61    ///
62    /// Assumes the private key for the folder has not changed.
63    async fn reload_vault<P: AsRef<Path> + Send>(
64        &mut self,
65        path: P,
66    ) -> Result<(), Self::Error>;
67
68    /// Vault summary information.
69    fn summary(&self) -> &Summary;
70
71    /// Vault identifier.
72    fn id(&self) -> &VaultId;
73
74    /// Public name for the vault.
75    fn name(&self) -> &str;
76
77    /// Set the public name for the vault.
78    async fn set_vault_name(
79        &mut self,
80        name: String,
81    ) -> Result<WriteEvent, Self::Error>;
82
83    /// Set the vault flags.
84    async fn set_vault_flags(
85        &mut self,
86        flags: VaultFlags,
87    ) -> Result<WriteEvent, Self::Error>;
88
89    /// Attempt to decrypt the meta data for the vault
90    /// using the key assigned to this access point.
91    async fn vault_meta(&self) -> Result<VaultMeta, Self::Error>;
92
93    /// Set the meta data for the vault.
94    async fn set_vault_meta(
95        &mut self,
96        meta_data: &VaultMeta,
97    ) -> Result<WriteEvent, Self::Error>;
98
99    /// Add a secret to the vault.
100    async fn create_secret(
101        &mut self,
102        secret_data: &SecretRow,
103    ) -> Result<WriteEvent, Self::Error>;
104
105    #[doc(hidden)]
106    async fn decrypt_meta(
107        &self,
108        meta_aead: &AeadPack,
109    ) -> Result<VaultMeta, Self::Error>;
110
111    #[doc(hidden)]
112    async fn decrypt_secret(
113        &self,
114        vault_commit: &VaultCommit,
115        private_key: Option<&PrivateKey>,
116    ) -> Result<(SecretMeta, Secret), Self::Error>;
117
118    /// Read the encrypted contents of a secret.
119    async fn raw_secret(
120        &self,
121        id: &SecretId,
122    ) -> Result<Option<(VaultCommit, ReadEvent)>, Self::Error>;
123
124    /// Get a secret and it's meta data.
125    async fn read_secret(
126        &self,
127        id: &SecretId,
128    ) -> Result<Option<(SecretMeta, Secret, ReadEvent)>, Self::Error>;
129
130    /// Update a secret.
131    async fn update_secret(
132        &mut self,
133        id: &SecretId,
134        secret_meta: SecretMeta,
135        secret: Secret,
136    ) -> Result<Option<WriteEvent>, Self::Error>;
137
138    /// Delete a secret and it's meta data.
139    async fn delete_secret(
140        &mut self,
141        id: &SecretId,
142    ) -> Result<Option<WriteEvent>, Self::Error>;
143
144    /// Verify an encryption password.
145    async fn verify(&self, key: &AccessKey) -> Result<(), Self::Error>;
146
147    /// Unlock the vault using the access key.
148    ///
149    /// The derived private key is stored in memory
150    /// until [AccessPoint::lock] is called.
151    async fn unlock(
152        &mut self,
153        key: &AccessKey,
154    ) -> Result<VaultMeta, Self::Error>;
155
156    /// Lock the vault by deleting the stored private key
157    /// associated with the vault and securely zeroing the
158    /// underlying memory.
159    fn lock(&mut self);
160}
161
162/// Access to an in-memory vault optionally mirroring changes to storage.
163///
164/// It stores the derived private key in memory so should only be
165/// used on client implementations.
166///
167/// Calling `lock()` will zeroize the private key in memory and prevent
168/// any access to the vault until `unlock()` is called successfully.
169///
170/// To allow for meta data to be displayed before secret decryption
171/// certain parts of a vault are encrypted separately which means that
172/// technically it would be possible to use different private keys for
173/// different secrets and for the meta data however this would be
174/// a very poor user experience and would lead to confusion so the
175/// access point is also responsible for ensuring the same private key
176/// is used to encrypt the different chunks.
177pub struct AccessPoint<E>
178where
179    E: std::error::Error
180        + std::fmt::Debug
181        + From<crate::Error>
182        + From<sos_core::Error>
183        + From<std::io::Error>
184        + Send
185        + Sync
186        + 'static,
187{
188    /// The private key.
189    private_key: Option<PrivateKey>,
190    /// The underlying vault.
191    vault: Vault,
192    /// Mirror in-memory vault changes to a writer.
193    mirror: Option<VaultMirror<E>>,
194}
195
196impl<E> AccessPoint<E>
197where
198    E: std::error::Error
199        + std::fmt::Debug
200        + From<crate::Error>
201        + From<sos_core::Error>
202        + From<std::io::Error>
203        + Send
204        + Sync
205        + 'static,
206{
207    /// Create a new access point.
208    pub fn new(vault: Vault) -> Self {
209        Self {
210            vault,
211            private_key: None,
212            mirror: None,
213        }
214    }
215
216    /// Create a new access point that writes in-memory
217    /// changes to a mirror.
218    pub fn new_mirror(vault: Vault, mirror: VaultMirror<E>) -> Self {
219        Self {
220            vault,
221            private_key: None,
222            mirror: Some(mirror),
223        }
224    }
225
226    /// Ensure that if shared access is set to readonly that
227    /// this user is allowed to write.
228    async fn enforce_shared_readonly(
229        &self,
230        key: &PrivateKey,
231    ) -> Result<(), E> {
232        if let SharedAccess::ReadOnly(aead) = self.vault.shared_access() {
233            self.vault
234                .decrypt(key, aead)
235                .await
236                .map_err(|_| Error::PermissionDenied)?;
237        }
238        Ok(())
239    }
240}
241
242#[async_trait]
243impl<E> SecretAccess for AccessPoint<E>
244where
245    E: std::error::Error
246        + std::fmt::Debug
247        + From<crate::Error>
248        + From<sos_core::Error>
249        + From<std::io::Error>
250        + Send
251        + Sync
252        + 'static,
253{
254    type Error = E;
255
256    fn is_mirror(&self) -> bool {
257        self.mirror.is_some()
258    }
259
260    fn vault(&self) -> &Vault {
261        &self.vault
262    }
263
264    async fn replace_vault(
265        &mut self,
266        vault: Vault,
267        mirror_changes: bool,
268    ) -> Result<(), E> {
269        if let (true, Some(mirror)) = (mirror_changes, &mut self.mirror) {
270            mirror.replace_vault(&vault).await?;
271        }
272        self.vault = vault;
273        Ok(())
274    }
275
276    async fn reload_vault<P: AsRef<Path> + Send>(
277        &mut self,
278        path: P,
279    ) -> Result<(), E> {
280        let buffer = vfs::read(path.as_ref()).await?;
281        let vault: Vault = decode(&buffer).await?;
282        if let Some(mirror) = &mut self.mirror {
283            mirror.replace_vault(&vault).await?;
284        }
285        self.vault = vault;
286        Ok(())
287    }
288
289    fn summary(&self) -> &Summary {
290        self.vault.summary()
291    }
292
293    fn id(&self) -> &VaultId {
294        self.vault.id()
295    }
296
297    fn name(&self) -> &str {
298        self.vault.name()
299    }
300
301    async fn set_vault_name(
302        &mut self,
303        name: String,
304    ) -> Result<WriteEvent, E> {
305        if let Some(mirror) = self.mirror.as_mut() {
306            mirror.set_vault_name(name.clone()).await?;
307        }
308        Ok(self.vault.set_vault_name(name).await?)
309    }
310
311    async fn set_vault_flags(
312        &mut self,
313        flags: VaultFlags,
314    ) -> Result<WriteEvent, E> {
315        if let Some(mirror) = self.mirror.as_mut() {
316            mirror.set_vault_flags(flags.clone()).await?;
317        }
318        Ok(self.vault.set_vault_flags(flags).await?)
319    }
320
321    async fn vault_meta(&self) -> Result<VaultMeta, E> {
322        if let Some(meta_aead) = self.vault.header().meta() {
323            Ok(self.decrypt_meta(meta_aead).await?)
324        } else {
325            Err(Error::VaultNotInit.into())
326        }
327    }
328
329    async fn set_vault_meta(
330        &mut self,
331        meta_data: &VaultMeta,
332    ) -> Result<WriteEvent, E> {
333        let private_key =
334            self.private_key.as_ref().ok_or(Error::VaultLocked)?;
335        let meta_blob = encode(meta_data).await?;
336        let meta_aead = self.vault.encrypt(private_key, &meta_blob).await?;
337        if let Some(mirror) = self.mirror.as_mut() {
338            mirror.set_vault_meta(meta_aead.clone()).await?;
339        }
340        Ok(self.vault.set_vault_meta(meta_aead).await?)
341    }
342
343    async fn decrypt_meta(
344        &self,
345        meta_aead: &AeadPack,
346    ) -> Result<VaultMeta, E> {
347        let private_key =
348            self.private_key.as_ref().ok_or(Error::VaultLocked)?;
349        let meta_blob =
350            self.vault.decrypt(private_key, meta_aead).await.map_err(
351                |_| {
352                    sos_core::Error::from(
353                        AuthenticationError::PasswordVerification,
354                    )
355                },
356            )?;
357        Ok(decode(&meta_blob).await?)
358    }
359
360    async fn decrypt_secret(
361        &self,
362        vault_commit: &VaultCommit,
363        private_key: Option<&PrivateKey>,
364    ) -> Result<(SecretMeta, Secret), E> {
365        let private_key = private_key
366            .or(self.private_key.as_ref())
367            .ok_or(Error::VaultLocked)?;
368
369        let VaultCommit(_commit, VaultEntry(meta_aead, secret_aead)) =
370            vault_commit;
371        let meta_blob = self.vault.decrypt(private_key, meta_aead).await?;
372        let secret_meta: SecretMeta = decode(&meta_blob).await?;
373
374        let secret_blob =
375            self.vault.decrypt(private_key, secret_aead).await?;
376        let secret: Secret = decode(&secret_blob).await?;
377        Ok((secret_meta, secret))
378    }
379
380    async fn create_secret(
381        &mut self,
382        secret_data: &SecretRow,
383    ) -> Result<WriteEvent, E> {
384        let private_key =
385            self.private_key.as_ref().ok_or(Error::VaultLocked)?;
386
387        self.enforce_shared_readonly(private_key).await?;
388
389        let id = *secret_data.id();
390        let meta_blob = encode(secret_data.meta()).await?;
391        let meta_aead = self.vault.encrypt(private_key, &meta_blob).await?;
392
393        let secret_blob = encode(secret_data.secret()).await?;
394        let secret_aead =
395            self.vault.encrypt(private_key, &secret_blob).await?;
396        let commit = Vault::commit_hash(&meta_aead, &secret_aead).await?;
397
398        if let Some(mirror) = self.mirror.as_mut() {
399            mirror
400                .insert_secret(
401                    id,
402                    commit,
403                    VaultEntry(meta_aead.clone(), secret_aead.clone()),
404                )
405                .await?;
406        }
407
408        let result = self
409            .vault
410            .insert_secret(id, commit, VaultEntry(meta_aead, secret_aead))
411            .await?;
412
413        Ok(result)
414    }
415
416    async fn raw_secret(
417        &self,
418        id: &SecretId,
419    ) -> Result<Option<(VaultCommit, ReadEvent)>, E> {
420        let value = self.vault.read_secret(id).await?;
421        let value = if let Some((commit, event)) = value {
422            Some((commit.into_owned(), event))
423        } else {
424            None
425        };
426        Ok(value)
427    }
428
429    async fn read_secret(
430        &self,
431        id: &SecretId,
432    ) -> Result<Option<(SecretMeta, Secret, ReadEvent)>, E> {
433        self.private_key.as_ref().ok_or(Error::VaultLocked)?;
434
435        if let Some((value, event)) = self.raw_secret(id).await? {
436            let (meta, secret) = self
437                .decrypt_secret(&value, self.private_key.as_ref())
438                .await?;
439            Ok(Some((meta, secret, event)))
440        } else {
441            Ok(None)
442        }
443    }
444
445    async fn update_secret(
446        &mut self,
447        id: &SecretId,
448        secret_meta: SecretMeta,
449        secret: Secret,
450    ) -> Result<Option<WriteEvent>, E> {
451        let private_key =
452            self.private_key.as_ref().ok_or(Error::VaultLocked)?;
453
454        self.enforce_shared_readonly(private_key).await?;
455
456        let meta_blob = encode(&secret_meta).await?;
457        let meta_aead = self.vault.encrypt(private_key, &meta_blob).await?;
458
459        let secret_blob = encode(&secret).await?;
460        let secret_aead =
461            self.vault.encrypt(private_key, &secret_blob).await?;
462        let commit = Vault::commit_hash(&meta_aead, &secret_aead).await?;
463
464        if let Some(mirror) = self.mirror.as_mut() {
465            mirror
466                .update_secret(
467                    id,
468                    commit,
469                    VaultEntry(meta_aead.clone(), secret_aead.clone()),
470                )
471                .await?;
472        }
473
474        Ok(self
475            .vault
476            .update_secret(id, commit, VaultEntry(meta_aead, secret_aead))
477            .await?)
478    }
479
480    async fn delete_secret(
481        &mut self,
482        id: &SecretId,
483    ) -> Result<Option<WriteEvent>, E> {
484        let private_key =
485            self.private_key.as_ref().ok_or(Error::VaultLocked)?;
486        self.enforce_shared_readonly(private_key).await?;
487        if let Some(mirror) = self.mirror.as_mut() {
488            mirror.delete_secret(id).await?;
489        }
490        Ok(self.vault.delete_secret(id).await?)
491    }
492
493    async fn verify(&self, key: &AccessKey) -> Result<(), E> {
494        Ok(self.vault.verify(key).await?)
495    }
496
497    async fn unlock(&mut self, key: &AccessKey) -> Result<VaultMeta, E> {
498        if let Some(salt) = self.vault.salt() {
499            match key {
500                AccessKey::Password(passphrase) => {
501                    let salt = KeyDerivation::parse_salt(salt)?;
502                    let deriver = self.vault.deriver();
503                    let private_key = deriver.derive(
504                        passphrase,
505                        &salt,
506                        self.vault.seed(),
507                    )?;
508                    self.private_key =
509                        Some(PrivateKey::Symmetric(private_key));
510                    self.vault_meta().await
511                }
512                AccessKey::Identity(id) => {
513                    self.private_key =
514                        Some(PrivateKey::Asymmetric(id.clone()));
515                    self.vault_meta().await
516                }
517            }
518        } else {
519            Err(Error::VaultNotInit.into())
520        }
521    }
522
523    fn lock(&mut self) {
524        tracing::debug!(folder = %self.id(), "drop_private_key");
525        self.private_key = None;
526    }
527}
528
529impl<E> From<Vault> for AccessPoint<E>
530where
531    E: std::error::Error
532        + std::fmt::Debug
533        + From<crate::Error>
534        + From<sos_core::Error>
535        + From<std::io::Error>
536        + Send
537        + Sync
538        + 'static,
539{
540    fn from(value: Vault) -> Self {
541        AccessPoint::<E>::new(value)
542    }
543}
544
545impl<E> From<AccessPoint<E>> for Vault
546where
547    E: std::error::Error
548        + std::fmt::Debug
549        + From<crate::Error>
550        + From<sos_core::Error>
551        + From<std::io::Error>
552        + Send
553        + Sync
554        + 'static,
555{
556    fn from(value: AccessPoint<E>) -> Self {
557        value.vault
558    }
559}