sos_vault/
vault.rs

1use crate::{Error, Result};
2use age::x25519::{Identity, Recipient};
3use async_trait::async_trait;
4use binary_stream::futures::{BinaryReader, Decodable};
5use indexmap::IndexMap;
6use secrecy::SecretString;
7use serde::{Deserialize, Serialize};
8use sha2::{Digest, Sha256};
9use sos_core::{
10    commit::CommitHash,
11    constants::{DEFAULT_VAULT_NAME, URN_NID, VAULT_IDENTITY, VAULT_NSS},
12    crypto::{
13        AccessKey, AeadPack, Cipher, Deriver, KeyDerivation, PrivateKey, Seed,
14    },
15    decode, encode,
16    encoding::{encoding_options, VERSION},
17    events::{ReadEvent, WriteEvent},
18    file_identity::FileIdentity,
19    AuthenticationError, SecretId, UtcDateTime, VaultCommit, VaultEntry,
20    VaultFlags, VaultId,
21};
22use sos_vfs::File;
23use std::io::Cursor;
24use std::{
25    borrow::Cow, cmp::Ordering, collections::HashMap, fmt, path::Path,
26};
27use tokio::io::{AsyncReadExt, AsyncSeek, BufReader};
28use typeshare::typeshare;
29use urn::Urn;
30use uuid::Uuid;
31
32/// Vault meta data.
33#[derive(Default, Serialize, Deserialize)]
34#[serde(rename_all = "camelCase")]
35pub struct VaultMeta {
36    /// Date created timestamp.
37    pub(crate) date_created: UtcDateTime,
38    /// Private human-friendly description of the vault.
39    #[serde(skip_serializing_if = "String::is_empty")]
40    pub(crate) description: String,
41}
42
43impl VaultMeta {
44    /// Get the vault description.
45    pub fn description(&self) -> &str {
46        &self.description
47    }
48
49    /// Get the vault description.
50    pub fn set_description(&mut self, description: String) {
51        self.description = description;
52    }
53
54    /// Date this vault was initialized.
55    pub fn date_created(&self) -> &UtcDateTime {
56        &self.date_created
57    }
58}
59
60/// Read and write encrypted data to a vault.
61///
62/// The storage may be in-memory, backed by a file on disc or another
63/// destination for the encrypted bytes.
64///
65/// Uses `Cow` smart pointers because when we are reading
66/// from an in-memory `Vault` we can return references whereas
67/// other containers such as file access would return owned data.
68#[async_trait]
69pub trait EncryptedEntry {
70    /// Error type for vault access.
71    type Error: std::error::Error
72        + std::fmt::Debug
73        + From<crate::Error>
74        + From<sos_core::Error>
75        + Send
76        + Sync
77        + 'static;
78
79    /// Get the vault summary.
80    async fn summary(&self) -> std::result::Result<Summary, Self::Error>;
81
82    /// Get the name of a vault.
83    async fn vault_name(
84        &self,
85    ) -> std::result::Result<Cow<'_, str>, Self::Error>;
86
87    /// Set the name of a vault.
88    async fn set_vault_name(
89        &mut self,
90        name: String,
91    ) -> std::result::Result<WriteEvent, Self::Error>;
92
93    /// Set the flags for a vault.
94    async fn set_vault_flags(
95        &mut self,
96        flags: VaultFlags,
97    ) -> std::result::Result<WriteEvent, Self::Error>;
98
99    /// Set the vault meta data.
100    async fn set_vault_meta(
101        &mut self,
102        meta_data: AeadPack,
103    ) -> std::result::Result<WriteEvent, Self::Error>;
104
105    /// Add an encrypted secret to the vault.
106    async fn create_secret(
107        &mut self,
108        commit: CommitHash,
109        secret: VaultEntry,
110    ) -> std::result::Result<WriteEvent, Self::Error>;
111
112    /// Insert an encrypted secret to the vault with the given id.
113    ///
114    /// Used internally to support consistent identifiers when
115    /// mirroring in the `AccessPoint` implementation.
116    #[doc(hidden)]
117    async fn insert_secret(
118        &mut self,
119        id: SecretId,
120        commit: CommitHash,
121        secret: VaultEntry,
122    ) -> std::result::Result<WriteEvent, Self::Error>;
123
124    /// Get an encrypted secret from the vault.
125    async fn read_secret<'a>(
126        &'a self,
127        id: &SecretId,
128    ) -> std::result::Result<
129        Option<(Cow<'a, VaultCommit>, ReadEvent)>,
130        Self::Error,
131    >;
132
133    /// Update an encrypted secret in the vault.
134    async fn update_secret(
135        &mut self,
136        id: &SecretId,
137        commit: CommitHash,
138        secret: VaultEntry,
139    ) -> std::result::Result<Option<WriteEvent>, Self::Error>;
140
141    /// Remove an encrypted secret from the vault.
142    async fn delete_secret(
143        &mut self,
144        id: &SecretId,
145    ) -> std::result::Result<Option<WriteEvent>, Self::Error>;
146
147    /// Replace the vault with new vault content.
148    async fn replace_vault(
149        &mut self,
150        vault: &Vault,
151    ) -> std::result::Result<(), Self::Error>;
152}
153
154/// Authentication information.
155#[derive(Serialize, Deserialize, Clone, Debug, Default, Eq, PartialEq)]
156#[serde(rename_all = "camelCase")]
157pub struct Auth {
158    /// Salt used to derive a secret key from the password.
159    #[serde(default, skip_serializing_if = "Option::is_none")]
160    pub(crate) salt: Option<String>,
161    /// Additional entropy to concatenate with the password
162    /// before deriving the secret key.
163    #[serde(default, skip_serializing_if = "Option::is_none")]
164    pub(crate) seed: Option<Seed>,
165}
166
167/// Summary holding basic file information such as version,
168/// unique identifier and name.
169#[typeshare]
170#[derive(Serialize, Deserialize, Debug, Hash, Eq, PartialEq, Clone)]
171#[serde(rename_all = "camelCase")]
172pub struct Summary {
173    /// Encoding version.
174    pub(crate) version: u16,
175    /// Unique identifier for the vault.
176    pub(crate) id: VaultId,
177    /// Vault name.
178    pub(crate) name: String,
179    /// Encryption cipher.
180    pub(crate) cipher: Cipher,
181    /// Key derivation function.
182    pub(crate) kdf: KeyDerivation,
183    /// Flags for the vault.
184    pub(crate) flags: VaultFlags,
185}
186
187impl Ord for Summary {
188    fn cmp(&self, other: &Self) -> Ordering {
189        self.name.cmp(&other.name)
190    }
191}
192
193impl PartialOrd for Summary {
194    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
195        Some(self.cmp(other))
196    }
197}
198
199impl fmt::Display for Summary {
200    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201        write!(
202            f,
203            "Version {} using {} with {}\n{} {}",
204            self.version, self.cipher, self.kdf, self.name, self.id
205        )
206    }
207}
208
209impl Default for Summary {
210    fn default() -> Self {
211        Self {
212            version: VERSION,
213            cipher: Default::default(),
214            kdf: Default::default(),
215            id: Uuid::new_v4(),
216            name: DEFAULT_VAULT_NAME.to_string(),
217            flags: Default::default(),
218        }
219    }
220}
221
222impl Summary {
223    /// Create a new summary.
224    pub fn new(
225        version: u16,
226        id: VaultId,
227        name: String,
228        cipher: Cipher,
229        kdf: KeyDerivation,
230        flags: VaultFlags,
231    ) -> Self {
232        Self {
233            version,
234            cipher,
235            kdf,
236            id,
237            name,
238            flags,
239        }
240    }
241
242    /// Get the version identifier.
243    pub fn version(&self) -> &u16 {
244        &self.version
245    }
246
247    /// Encryption cipher.
248    pub fn cipher(&self) -> &Cipher {
249        &self.cipher
250    }
251
252    /// Key derivation function.
253    pub fn kdf(&self) -> &KeyDerivation {
254        &self.kdf
255    }
256
257    /// Vault identifier.
258    pub fn id(&self) -> &VaultId {
259        &self.id
260    }
261
262    /// Public name.
263    pub fn name(&self) -> &str {
264        &self.name
265    }
266
267    /// Set the public name.
268    pub fn set_name(&mut self, name: String) {
269        self.name = name;
270    }
271
272    /// Vault flags.
273    pub fn flags(&self) -> &VaultFlags {
274        &self.flags
275    }
276
277    /// Mutable reference to the vault flags.
278    pub fn flags_mut(&mut self) -> &mut VaultFlags {
279        &mut self.flags
280    }
281}
282
283impl From<Summary> for VaultId {
284    fn from(value: Summary) -> Self {
285        value.id
286    }
287}
288
289impl From<Summary> for Header {
290    fn from(value: Summary) -> Self {
291        Header::new(
292            value.id,
293            value.name,
294            value.cipher,
295            value.kdf,
296            value.flags,
297        )
298    }
299}
300
301impl From<Summary> for Vault {
302    fn from(value: Summary) -> Self {
303        let mut vault: Vault = Default::default();
304        vault.header = value.into();
305        vault
306    }
307}
308
309/// File header, identifier and version information
310#[derive(Serialize, Deserialize, Clone, Default, Debug, Eq, PartialEq)]
311#[serde(rename_all = "camelCase")]
312pub struct Header {
313    /// Information about the vault.
314    pub(crate) summary: Summary,
315    /// Encrypted meta data.
316    #[serde(default, skip_serializing_if = "Option::is_none")]
317    pub(crate) meta: Option<AeadPack>,
318    /// Additional authentication information such as
319    /// the salt and seed entropy.
320    pub(crate) auth: Auth,
321    /// Recipients for a shared vault.
322    #[serde(default, skip_serializing_if = "SharedAccess::is_empty")]
323    pub(crate) shared_access: SharedAccess,
324}
325
326impl Header {
327    /// Create a new header.
328    pub fn new(
329        id: VaultId,
330        name: String,
331        cipher: Cipher,
332        kdf: KeyDerivation,
333        flags: VaultFlags,
334    ) -> Self {
335        Self {
336            summary: Summary::new(VERSION, id, name, cipher, kdf, flags),
337            meta: None,
338            auth: Default::default(),
339            shared_access: Default::default(),
340        }
341    }
342
343    /// Vault identifier.
344    pub fn id(&self) -> &VaultId {
345        self.summary.id()
346    }
347
348    /// Mutable identifier for this vault.
349    pub fn id_mut(&mut self) -> &mut VaultId {
350        &mut self.summary.id
351    }
352
353    /// Reference to the vault flags.
354    pub fn flags(&self) -> &VaultFlags {
355        self.summary.flags()
356    }
357
358    /// Mutable reference to the vault flags.
359    pub fn flags_mut(&mut self) -> &mut VaultFlags {
360        self.summary.flags_mut()
361    }
362
363    /// Clear an existing salt.
364    ///
365    /// Required when changing passwords so we can initialize
366    /// a vault that is already initialized.
367    pub(crate) fn clear_salt(&mut self) {
368        self.auth.salt = None;
369    }
370
371    /// Get the public name for this vault.
372    pub fn name(&self) -> &str {
373        &self.summary.name
374    }
375
376    /// Set the public name for this vault.
377    pub fn set_name(&mut self, name: String) {
378        self.summary.set_name(name);
379    }
380
381    /// Set the salt for key derivation.
382    pub fn set_salt(&mut self, salt: Option<String>) {
383        self.auth.salt = salt;
384    }
385
386    /// Get the encrypted meta data for the vault.
387    pub fn meta(&self) -> Option<&AeadPack> {
388        self.meta.as_ref()
389    }
390
391    /// Set the encrypted meta data for the vault.
392    pub fn set_meta(&mut self, meta: Option<AeadPack>) {
393        self.meta = meta;
394    }
395
396    /// Set the seed entropy for key derivation.
397    pub fn set_seed(&mut self, seed: Option<Seed>) {
398        self.auth.seed = seed;
399    }
400
401    /// Read the content offset for a vault file verifying
402    /// the identity bytes first.
403    pub async fn read_content_offset<P: AsRef<Path>>(path: P) -> Result<u64> {
404        let mut stream = File::open(path.as_ref()).await?;
405        Header::read_content_offset_stream(&mut stream).await
406    }
407
408    /// Read the content offset for a vault slice verifying
409    /// the identity bytes first.
410    pub async fn read_content_offset_slice(buffer: &[u8]) -> Result<u64> {
411        let mut stream = BufReader::new(Cursor::new(buffer));
412        Header::read_content_offset_stream(&mut stream).await
413    }
414
415    /// Read the content offset for a stream verifying
416    /// the identity bytes first.
417    pub async fn read_content_offset_stream<
418        R: AsyncReadExt + AsyncSeek + Unpin + Send,
419    >(
420        stream: R,
421    ) -> Result<u64> {
422        let mut reader = BinaryReader::new(stream, encoding_options());
423        let identity = reader.read_bytes(VAULT_IDENTITY.len()).await?;
424        FileIdentity::read_slice(&identity, &VAULT_IDENTITY)?;
425        let header_len = reader.read_u32().await? as u64;
426        let content_offset = VAULT_IDENTITY.len() as u64 + 4 + header_len;
427        Ok(content_offset)
428    }
429
430    /// Read the summary for a vault from a file.
431    pub async fn read_summary_file<P: AsRef<Path>>(
432        file: P,
433    ) -> Result<Summary> {
434        let mut stream = File::open(file.as_ref()).await?;
435        Header::read_summary_stream(&mut stream).await
436    }
437
438    /// Read the summary for a slice of bytes.
439    pub async fn read_summary_slice(buffer: &[u8]) -> Result<Summary> {
440        let mut stream = BufReader::new(Cursor::new(buffer));
441        Header::read_summary_stream(&mut stream).await
442    }
443
444    /// Read the summary from a stream.
445    async fn read_summary_stream<
446        R: AsyncReadExt + AsyncSeek + Unpin + Send,
447    >(
448        stream: R,
449    ) -> Result<Summary> {
450        let mut reader = BinaryReader::new(stream, encoding_options());
451
452        // Read magic identity bytes
453        FileIdentity::read_identity(&mut reader, &VAULT_IDENTITY).await?;
454
455        // Read in the header length
456        let _ = reader.read_u32().await?;
457
458        // Read the summary
459        let mut summary: Summary = Default::default();
460        summary.decode(&mut reader).await?;
461
462        Ok(summary)
463    }
464
465    /// Read the header for a vault from a file.
466    pub async fn read_header_file<P: AsRef<Path>>(file: P) -> Result<Header> {
467        let mut stream = File::open(file.as_ref()).await?;
468        Header::read_header_stream(&mut stream).await
469    }
470
471    /// Read the header from a stream.
472    pub(crate) async fn read_header_stream<
473        R: AsyncReadExt + AsyncSeek + Unpin + Send,
474    >(
475        stream: R,
476    ) -> Result<Header> {
477        let mut reader = BinaryReader::new(stream, encoding_options());
478        let mut header: Header = Default::default();
479        header.decode(&mut reader).await?;
480        Ok(header)
481    }
482}
483
484impl fmt::Display for Header {
485    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
486        write!(f, "{}", self.summary)
487    }
488}
489
490/// Access controls for shared vaults.
491#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
492pub enum SharedAccess {
493    /// List of recipients for a shared vault.
494    ///
495    /// Every recipient is able to write to the shared vault.
496    #[serde(rename = "write")]
497    WriteAccess(Vec<String>),
498    /// Private list of recipients managed by an owner.
499    ///
500    /// Only the owner can write to the vault, other recipients
501    /// can only read.
502    #[serde(rename = "read")]
503    ReadOnly(AeadPack),
504}
505
506impl Default for SharedAccess {
507    fn default() -> Self {
508        Self::WriteAccess(vec![])
509    }
510}
511
512impl SharedAccess {
513    /// Determine if the shared access configuration is empty.
514    pub fn is_empty(&self) -> bool {
515        match self {
516            Self::WriteAccess(recipients) => recipients.is_empty(),
517            Self::ReadOnly(_) => false,
518        }
519    }
520
521    fn parse_recipients(access: &Vec<String>) -> Result<Vec<Recipient>> {
522        let mut recipients = Vec::new();
523        for recipient in access {
524            let recipient = recipient.parse().map_err(|s: &str| {
525                Error::InvalidX25519Identity(s.to_owned())
526            })?;
527            recipients.push(recipient);
528        }
529        Ok(recipients)
530    }
531}
532
533/// The vault contents
534#[doc(hidden)]
535#[derive(Serialize, Deserialize, Clone, Debug, Default, Eq, PartialEq)]
536pub struct Contents {
537    #[serde(flatten)]
538    pub(crate) data: IndexMap<SecretId, VaultCommit>,
539}
540
541/// Vault file storage.
542#[derive(Serialize, Deserialize, Clone, Debug, Default, Eq, PartialEq)]
543pub struct Vault {
544    pub(crate) header: Header,
545    pub(crate) contents: Contents,
546}
547
548impl Vault {
549    /// Create a new vault.
550    pub fn new(
551        id: VaultId,
552        name: String,
553        cipher: Cipher,
554        kdf: KeyDerivation,
555        flags: VaultFlags,
556    ) -> Self {
557        Self {
558            header: Header::new(id, name, cipher, kdf, flags),
559            contents: Default::default(),
560        }
561    }
562
563    /// Shared access.
564    pub fn shared_access(&self) -> &SharedAccess {
565        &self.header.shared_access
566    }
567
568    /// Get the URN for a vault identifier.
569    pub fn vault_urn(id: &VaultId) -> Result<Urn> {
570        // FIXME: use UrnBuilder
571        let vault_urn = format!("urn:{}:{}{}", URN_NID, VAULT_NSS, id);
572        Ok(vault_urn.parse()?)
573    }
574
575    /// Initialize this vault using a password for
576    /// a symmetric cipher.
577    pub(crate) async fn symmetric(
578        &mut self,
579        password: SecretString,
580        seed: Option<Seed>,
581    ) -> Result<PrivateKey> {
582        if self.header.auth.salt.is_none() {
583            let salt = KeyDerivation::generate_salt();
584            let deriver = self.deriver();
585            let derived_private_key =
586                deriver.derive(&password, &salt, seed.as_ref())?;
587            let private_key = PrivateKey::Symmetric(derived_private_key);
588
589            // Store the salt and seed so we can derive the same
590            // private key later
591            self.header.auth.salt = Some(salt.to_string());
592            self.header.auth.seed = seed;
593
594            Ok(private_key)
595        } else {
596            Err(Error::VaultAlreadyInit)
597        }
598    }
599
600    /// Initialize this vault using asymmetric encryption.
601    pub(crate) async fn asymmetric(
602        &mut self,
603        owner: &Identity,
604        mut recipients: Vec<Recipient>,
605        read_only: bool,
606    ) -> Result<PrivateKey> {
607        if self.header.auth.salt.is_none() {
608            // Ensure the owner public key is always in the list
609            // of recipients
610            let owner_public = owner.to_public();
611            if !recipients
612                .iter()
613                .any(|r| r.to_string() == owner_public.to_string())
614            {
615                recipients.push(owner_public);
616            }
617
618            self.flags_mut().set(VaultFlags::SHARED, true);
619
620            let salt = KeyDerivation::generate_salt();
621            let private_key = PrivateKey::Asymmetric(owner.clone());
622            self.header.summary.cipher = Cipher::X25519;
623
624            let recipients: Vec<_> =
625                recipients.into_iter().map(|r| r.to_string()).collect();
626
627            self.header.shared_access = if read_only {
628                let access = SharedAccess::WriteAccess(recipients);
629                let buffer = encode(&access).await?;
630                let private_key = PrivateKey::Asymmetric(owner.clone());
631                let cipher = self.header.summary.cipher.clone();
632                let owner_recipients = vec![owner.to_public()];
633                let aead = cipher
634                    .encrypt_asymmetric(
635                        &private_key,
636                        &buffer,
637                        owner_recipients,
638                    )
639                    .await?;
640                SharedAccess::ReadOnly(aead)
641            } else {
642                SharedAccess::WriteAccess(recipients)
643            };
644
645            // Store the salt so we know that the vault has
646            // already been initialized, for asymmetric encryption
647            // it is not used
648            self.header.auth.salt = Some(salt.to_string());
649
650            Ok(private_key)
651        } else {
652            Err(Error::VaultAlreadyInit)
653        }
654    }
655
656    /// Key derivation function deriver.
657    pub fn deriver(&self) -> Box<dyn Deriver<Sha256> + Send + 'static> {
658        self.header.summary.kdf.deriver()
659    }
660
661    /// Set whether this vault is a default vault.
662    pub fn set_default_flag(&mut self, value: bool) {
663        self.flags_mut().set(VaultFlags::DEFAULT, value);
664    }
665
666    /// Set whether this vault is an archive vault.
667    pub fn set_archive_flag(&mut self, value: bool) {
668        self.flags_mut().set(VaultFlags::ARCHIVE, value);
669    }
670
671    /// Set whether this vault is an authenticator vault.
672    pub fn set_authenticator_flag(&mut self, value: bool) {
673        self.flags_mut().set(VaultFlags::AUTHENTICATOR, value);
674    }
675
676    /// Set whether this vault is for contacts.
677    pub fn set_contact_flag(&mut self, value: bool) {
678        self.flags_mut().set(VaultFlags::CONTACT, value);
679    }
680
681    /// Set whether this vault is for system specific information.
682    pub fn set_system_flag(&mut self, value: bool) {
683        self.flags_mut().set(VaultFlags::SYSTEM, value);
684    }
685
686    /// Set whether this vault is for device specific information.
687    pub fn set_device_flag(&mut self, value: bool) {
688        self.flags_mut().set(VaultFlags::DEVICE, value);
689    }
690
691    /// Set whether this vault should not sync with own devices.
692    pub fn set_no_sync_flag(&mut self, value: bool) {
693        self.flags_mut().set(VaultFlags::NO_SYNC, value);
694    }
695
696    /// Insert a secret into this vault.
697    pub fn insert_entry(&mut self, id: SecretId, entry: VaultCommit) {
698        self.contents.data.insert(id, entry);
699    }
700
701    /// Get a secret in this vault.
702    pub fn get(&self, id: &SecretId) -> Option<&VaultCommit> {
703        self.contents.data.get(id)
704    }
705
706    /// Encrypt plaintext using the cipher assigned to this vault.
707    pub async fn encrypt(
708        &self,
709        key: &PrivateKey,
710        plaintext: &[u8],
711    ) -> Result<AeadPack> {
712        match self.cipher() {
713            Cipher::XChaCha20Poly1305 | Cipher::AesGcm256 => Ok(self
714                .cipher()
715                .encrypt_symmetric(key, plaintext, None)
716                .await?),
717            Cipher::X25519 => {
718                let recipients = match &self.header.shared_access {
719                    SharedAccess::WriteAccess(access) => {
720                        SharedAccess::parse_recipients(access)?
721                    }
722                    SharedAccess::ReadOnly(aead) => {
723                        let buffer = self
724                            .decrypt(key, aead)
725                            .await
726                            .map_err(|_| Error::PermissionDenied)?;
727                        let shared_access: SharedAccess =
728                            decode(&buffer).await?;
729                        if let SharedAccess::WriteAccess(access) =
730                            &shared_access
731                        {
732                            SharedAccess::parse_recipients(access)?
733                        } else {
734                            return Err(Error::PermissionDenied);
735                        }
736                    }
737                };
738
739                Ok(self
740                    .cipher()
741                    .encrypt_asymmetric(key, plaintext, recipients)
742                    .await?)
743            }
744        }
745    }
746
747    /// Decrypt ciphertext using the cipher assigned to this vault.
748    pub async fn decrypt(
749        &self,
750        key: &PrivateKey,
751        aead: &AeadPack,
752    ) -> Result<Vec<u8>> {
753        match self.cipher() {
754            Cipher::XChaCha20Poly1305 | Cipher::AesGcm256 => {
755                Ok(self.cipher().decrypt_symmetric(key, aead).await?)
756            }
757            Cipher::X25519 => {
758                Ok(self.cipher().decrypt_asymmetric(key, aead).await?)
759            }
760        }
761    }
762
763    /// Choose a new identifier for this vault.
764    ///
765    /// This is useful when importing a vault and the identifier
766    /// collides with an existing vault; rather than overwriting the
767    /// existing vault we can import it alongside by choosing a new
768    /// identifier.
769    pub fn rotate_identifier(&mut self) {
770        self.header.summary.id = Uuid::new_v4();
771    }
772
773    /// Verify an access key.
774    pub async fn verify(&self, key: &AccessKey) -> Result<()> {
775        let salt = self.salt().ok_or(Error::VaultNotInit)?;
776        let meta_aead = self.header().meta().ok_or(Error::VaultNotInit)?;
777        let private_key = match key {
778            AccessKey::Password(password) => {
779                let salt = KeyDerivation::parse_salt(salt)?;
780                let deriver = self.deriver();
781                PrivateKey::Symmetric(deriver.derive(
782                    password,
783                    &salt,
784                    self.seed(),
785                )?)
786            }
787            AccessKey::Identity(id) => PrivateKey::Asymmetric(id.clone()),
788        };
789
790        let _ = self
791            .decrypt(&private_key, meta_aead)
792            .await
793            .map_err(|_| AuthenticationError::PasswordVerification)?;
794
795        Ok(())
796    }
797
798    /// Iterator for the secret keys and values.
799    pub fn iter(&self) -> impl Iterator<Item = (&Uuid, &VaultCommit)> {
800        self.contents.data.iter()
801    }
802
803    /// Iterator for the secret keys.
804    pub fn keys(&self) -> impl Iterator<Item = &Uuid> {
805        self.contents.data.keys()
806    }
807
808    /// Iterator for the secret values.
809    pub fn values(&self) -> impl Iterator<Item = &VaultCommit> {
810        self.contents.data.values()
811    }
812
813    /// Number of secrets in this vault.
814    pub fn len(&self) -> usize {
815        self.contents.data.len()
816    }
817
818    /// Determine if this vault is empty.
819    pub fn is_empty(&self) -> bool {
820        self.len() == 0
821    }
822
823    /// Convert this vault into a create vault event.
824    ///
825    /// Ensures the vault is head-only before encoding into the event.
826    pub async fn into_event(&self) -> Result<WriteEvent> {
827        let buffer = if self.is_empty() {
828            encode(self).await?
829        } else {
830            let header = self.header.clone();
831            let vault: Vault = header.into();
832            encode(&vault).await?
833        };
834        Ok(WriteEvent::CreateVault(buffer))
835    }
836
837    /// Iterator for the secret keys and commit hashes.
838    pub fn commits(&self) -> impl Iterator<Item = (&Uuid, &CommitHash)> {
839        self.contents
840            .data
841            .keys()
842            .zip(self.contents.data.values().map(|v| &v.0))
843    }
844
845    /// Get the salt used for passphrase authentication.
846    pub fn salt(&self) -> Option<&String> {
847        self.header.auth.salt.as_ref()
848    }
849
850    /// Get the seed used for passphrase authentication.
851    pub fn seed(&self) -> Option<&Seed> {
852        self.header.auth.seed.as_ref()
853    }
854
855    /// Get the summary for this vault.
856    pub fn summary(&self) -> &Summary {
857        &self.header.summary
858    }
859
860    /// Reference to the vault flags.
861    pub fn flags(&self) -> &VaultFlags {
862        self.header.flags()
863    }
864
865    /// Mutable reference to the vault flags.
866    pub fn flags_mut(&mut self) -> &mut VaultFlags {
867        self.header.flags_mut()
868    }
869
870    /// Unique identifier for this vault.
871    pub fn id(&self) -> &VaultId {
872        &self.header.summary.id
873    }
874
875    /// Public name for this vault.
876    pub fn name(&self) -> &str {
877        self.header.name()
878    }
879
880    /// Set the public name of this vault.
881    pub fn set_name(&mut self, name: String) {
882        self.header.set_name(name);
883    }
884
885    /// Encryption cipher for this vault.
886    pub fn cipher(&self) -> &Cipher {
887        &self.header.summary.cipher
888    }
889
890    /// Key derivation function.
891    pub fn kdf(&self) -> &KeyDerivation {
892        &self.header.summary.kdf
893    }
894
895    /// Vault header.
896    pub fn header(&self) -> &Header {
897        &self.header
898    }
899
900    /// Mutable vault header.
901    pub fn header_mut(&mut self) -> &mut Header {
902        &mut self.header
903    }
904
905    /// Vault data.
906    pub fn data(&self) -> &IndexMap<SecretId, VaultCommit> {
907        &self.contents.data
908    }
909
910    /// Mutable vault data.
911    pub fn data_mut(&mut self) -> &mut IndexMap<SecretId, VaultCommit> {
912        &mut self.contents.data
913    }
914
915    /// Get the meta data for all the secrets.
916    pub fn meta_data(&self) -> HashMap<&Uuid, &AeadPack> {
917        self.contents
918            .data
919            .iter()
920            .map(|(k, v)| (k, &v.1 .0))
921            .collect::<HashMap<_, _>>()
922    }
923
924    /// Compute the hash of the encoded encrypted buffer
925    /// for the meta and secret data.
926    #[doc(hidden)]
927    pub async fn commit_hash(
928        meta_aead: &AeadPack,
929        secret_aead: &AeadPack,
930    ) -> Result<CommitHash> {
931        // Compute the hash of the encrypted and encoded bytes
932        let encoded_meta = encode(meta_aead).await?;
933        let encoded_data = encode(secret_aead).await?;
934
935        let mut hasher = Sha256::new();
936        hasher.update(&encoded_meta);
937        hasher.update(&encoded_data);
938        let digest = hasher.finalize();
939        Ok(CommitHash(digest.as_slice().try_into()?))
940    }
941}
942
943impl From<Header> for Vault {
944    fn from(header: Header) -> Self {
945        Vault {
946            header,
947            contents: Default::default(),
948        }
949    }
950}
951
952impl From<Vault> for Header {
953    fn from(value: Vault) -> Self {
954        value.header
955    }
956}
957
958impl IntoIterator for Vault {
959    type Item = (SecretId, VaultCommit);
960    type IntoIter = indexmap::map::IntoIter<SecretId, VaultCommit>;
961
962    fn into_iter(self) -> Self::IntoIter {
963        self.contents.data.into_iter()
964    }
965}
966
967#[async_trait]
968impl EncryptedEntry for Vault {
969    type Error = Error;
970
971    async fn summary(&self) -> Result<Summary> {
972        Ok(self.header.summary.clone())
973    }
974
975    async fn vault_name(&self) -> Result<Cow<'_, str>> {
976        Ok(Cow::Borrowed(self.name()))
977    }
978
979    async fn set_vault_name(&mut self, name: String) -> Result<WriteEvent> {
980        self.set_name(name.clone());
981        Ok(WriteEvent::SetVaultName(name))
982    }
983
984    async fn set_vault_flags(
985        &mut self,
986        flags: VaultFlags,
987    ) -> Result<WriteEvent> {
988        *self.header.flags_mut() = flags.clone();
989        Ok(WriteEvent::SetVaultFlags(flags))
990    }
991
992    async fn set_vault_meta(
993        &mut self,
994        meta_data: AeadPack,
995    ) -> Result<WriteEvent> {
996        self.header.set_meta(Some(meta_data.clone()));
997        Ok(WriteEvent::SetVaultMeta(meta_data))
998    }
999
1000    async fn create_secret(
1001        &mut self,
1002        commit: CommitHash,
1003        secret: VaultEntry,
1004    ) -> Result<WriteEvent> {
1005        let id = Uuid::new_v4();
1006        self.insert_secret(id, commit, secret).await
1007    }
1008
1009    async fn insert_secret(
1010        &mut self,
1011        id: SecretId,
1012        commit: CommitHash,
1013        secret: VaultEntry,
1014    ) -> Result<WriteEvent> {
1015        let value = self
1016            .contents
1017            .data
1018            .entry(id)
1019            .or_insert(VaultCommit(commit, secret));
1020        Ok(WriteEvent::CreateSecret(id, value.clone()))
1021    }
1022
1023    async fn read_secret<'a>(
1024        &'a self,
1025        id: &SecretId,
1026    ) -> Result<Option<(Cow<'a, VaultCommit>, ReadEvent)>> {
1027        let result = self
1028            .contents
1029            .data
1030            .get(id)
1031            .map(|c| (Cow::Borrowed(c), ReadEvent::ReadSecret(*id)));
1032        Ok(result)
1033    }
1034
1035    async fn update_secret(
1036        &mut self,
1037        id: &SecretId,
1038        commit: CommitHash,
1039        secret: VaultEntry,
1040    ) -> Result<Option<WriteEvent>> {
1041        let _vault_id = *self.id();
1042        if let Some(value) = self.contents.data.get_mut(id) {
1043            *value = VaultCommit(commit, secret);
1044            Ok(Some(WriteEvent::UpdateSecret(*id, value.clone())))
1045        } else {
1046            Ok(None)
1047        }
1048    }
1049
1050    async fn delete_secret(
1051        &mut self,
1052        id: &SecretId,
1053    ) -> Result<Option<WriteEvent>> {
1054        let entry = self.contents.data.shift_remove(id);
1055        Ok(entry.map(|_| WriteEvent::DeleteSecret(*id)))
1056    }
1057
1058    async fn replace_vault(&mut self, vault: &Vault) -> Result<()> {
1059        *self = vault.clone();
1060        Ok(())
1061    }
1062}