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        Vault {
304            header: value.into(),
305            ..Default::default()
306        }
307    }
308}
309
310/// File header, identifier and version information
311#[derive(Serialize, Deserialize, Clone, Default, Debug, Eq, PartialEq)]
312#[serde(rename_all = "camelCase")]
313pub struct Header {
314    /// Information about the vault.
315    pub(crate) summary: Summary,
316    /// Encrypted meta data.
317    #[serde(default, skip_serializing_if = "Option::is_none")]
318    pub(crate) meta: Option<AeadPack>,
319    /// Additional authentication information such as
320    /// the salt and seed entropy.
321    pub(crate) auth: Auth,
322    /// Recipients for a shared vault.
323    #[serde(default, skip_serializing_if = "SharedAccess::is_empty")]
324    pub(crate) shared_access: SharedAccess,
325}
326
327impl Header {
328    /// Create a new header.
329    pub fn new(
330        id: VaultId,
331        name: String,
332        cipher: Cipher,
333        kdf: KeyDerivation,
334        flags: VaultFlags,
335    ) -> Self {
336        Self {
337            summary: Summary::new(VERSION, id, name, cipher, kdf, flags),
338            meta: None,
339            auth: Default::default(),
340            shared_access: Default::default(),
341        }
342    }
343
344    /// Vault identifier.
345    pub fn id(&self) -> &VaultId {
346        self.summary.id()
347    }
348
349    /// Mutable identifier for this vault.
350    pub fn id_mut(&mut self) -> &mut VaultId {
351        &mut self.summary.id
352    }
353
354    /// Reference to the vault flags.
355    pub fn flags(&self) -> &VaultFlags {
356        self.summary.flags()
357    }
358
359    /// Mutable reference to the vault flags.
360    pub fn flags_mut(&mut self) -> &mut VaultFlags {
361        self.summary.flags_mut()
362    }
363
364    /// Clear an existing salt.
365    ///
366    /// Required when changing passwords so we can initialize
367    /// a vault that is already initialized.
368    pub(crate) fn clear_salt(&mut self) {
369        self.auth.salt = None;
370    }
371
372    /// Get the public name for this vault.
373    pub fn name(&self) -> &str {
374        &self.summary.name
375    }
376
377    /// Set the public name for this vault.
378    pub fn set_name(&mut self, name: String) {
379        self.summary.set_name(name);
380    }
381
382    /// Set the salt for key derivation.
383    pub fn set_salt(&mut self, salt: Option<String>) {
384        self.auth.salt = salt;
385    }
386
387    /// Get the encrypted meta data for the vault.
388    pub fn meta(&self) -> Option<&AeadPack> {
389        self.meta.as_ref()
390    }
391
392    /// Set the encrypted meta data for the vault.
393    pub fn set_meta(&mut self, meta: Option<AeadPack>) {
394        self.meta = meta;
395    }
396
397    /// Set the seed entropy for key derivation.
398    pub fn set_seed(&mut self, seed: Option<Seed>) {
399        self.auth.seed = seed;
400    }
401
402    /// Set shared access permissions.
403    pub fn set_shared_access(&mut self, shared_access: SharedAccess) {
404        self.shared_access = shared_access;
405    }
406
407    /// Read the content offset for a vault file verifying
408    /// the identity bytes first.
409    pub async fn read_content_offset<P: AsRef<Path>>(path: P) -> Result<u64> {
410        let mut stream = File::open(path.as_ref()).await?;
411        Header::read_content_offset_stream(&mut stream).await
412    }
413
414    /// Read the content offset for a vault slice verifying
415    /// the identity bytes first.
416    pub async fn read_content_offset_slice(buffer: &[u8]) -> Result<u64> {
417        let mut stream = BufReader::new(Cursor::new(buffer));
418        Header::read_content_offset_stream(&mut stream).await
419    }
420
421    /// Read the content offset for a stream verifying
422    /// the identity bytes first.
423    pub async fn read_content_offset_stream<
424        R: AsyncReadExt + AsyncSeek + Unpin + Send,
425    >(
426        stream: R,
427    ) -> Result<u64> {
428        let mut reader = BinaryReader::new(stream, encoding_options());
429        let identity = reader.read_bytes(VAULT_IDENTITY.len()).await?;
430        FileIdentity::read_slice(&identity, &VAULT_IDENTITY)?;
431        let header_len = reader.read_u32().await? as u64;
432        let content_offset = VAULT_IDENTITY.len() as u64 + 4 + header_len;
433        Ok(content_offset)
434    }
435
436    /// Read the summary for a vault from a file.
437    pub async fn read_summary_file<P: AsRef<Path>>(
438        file: P,
439    ) -> Result<Summary> {
440        let mut stream = File::open(file.as_ref()).await?;
441        Header::read_summary_stream(&mut stream).await
442    }
443
444    /// Read the summary for a slice of bytes.
445    pub async fn read_summary_slice(buffer: &[u8]) -> Result<Summary> {
446        let mut stream = BufReader::new(Cursor::new(buffer));
447        Header::read_summary_stream(&mut stream).await
448    }
449
450    /// Read the summary from a stream.
451    async fn read_summary_stream<
452        R: AsyncReadExt + AsyncSeek + Unpin + Send,
453    >(
454        stream: R,
455    ) -> Result<Summary> {
456        let mut reader = BinaryReader::new(stream, encoding_options());
457
458        // Read magic identity bytes
459        FileIdentity::read_identity(&mut reader, &VAULT_IDENTITY).await?;
460
461        // Read in the header length
462        let _ = reader.read_u32().await?;
463
464        // Read the summary
465        let mut summary: Summary = Default::default();
466        summary.decode(&mut reader).await?;
467
468        Ok(summary)
469    }
470
471    /// Read the header for a vault from a file.
472    pub async fn read_header_file<P: AsRef<Path>>(file: P) -> Result<Header> {
473        let mut stream = File::open(file.as_ref()).await?;
474        Header::read_header_stream(&mut stream).await
475    }
476
477    /// Read the header from a stream.
478    pub(crate) async fn read_header_stream<
479        R: AsyncReadExt + AsyncSeek + Unpin + Send,
480    >(
481        stream: R,
482    ) -> Result<Header> {
483        let mut reader = BinaryReader::new(stream, encoding_options());
484        let mut header: Header = Default::default();
485        header.decode(&mut reader).await?;
486        Ok(header)
487    }
488}
489
490impl fmt::Display for Header {
491    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
492        write!(f, "{}", self.summary)
493    }
494}
495
496/// Access controls for shared vaults.
497#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
498pub enum SharedAccess {
499    /// List of recipients for a shared vault.
500    ///
501    /// Every recipient is able to write to the shared vault.
502    #[serde(rename = "write")]
503    WriteAccess(Vec<String>),
504    /// Private list of recipients managed by an owner.
505    ///
506    /// Only the owner can write to the vault, other recipients
507    /// can only read.
508    #[serde(rename = "read")]
509    ReadOnly(AeadPack),
510}
511
512impl Default for SharedAccess {
513    fn default() -> Self {
514        Self::WriteAccess(vec![])
515    }
516}
517
518impl SharedAccess {
519    /// Determine if the shared access configuration is empty.
520    pub fn is_empty(&self) -> bool {
521        match self {
522            Self::WriteAccess(recipients) => recipients.is_empty(),
523            Self::ReadOnly(_) => false,
524        }
525    }
526
527    /// Parse recipeients list.
528    pub fn parse_recipients(access: &Vec<String>) -> Result<Vec<Recipient>> {
529        let mut recipients = Vec::new();
530        for recipient in access {
531            let recipient = recipient.parse().map_err(|s: &str| {
532                Error::InvalidX25519Identity(s.to_owned())
533            })?;
534            recipients.push(recipient);
535        }
536        Ok(recipients)
537    }
538}
539
540/// The vault contents
541#[doc(hidden)]
542#[derive(Serialize, Deserialize, Clone, Debug, Default, Eq, PartialEq)]
543pub struct Contents {
544    #[serde(flatten)]
545    pub(crate) data: IndexMap<SecretId, VaultCommit>,
546}
547
548/// Vault file storage.
549#[derive(Serialize, Deserialize, Clone, Debug, Default, Eq, PartialEq)]
550pub struct Vault {
551    pub(crate) header: Header,
552    pub(crate) contents: Contents,
553}
554
555impl Vault {
556    /// Create a new vault.
557    pub fn new(
558        id: VaultId,
559        name: String,
560        cipher: Cipher,
561        kdf: KeyDerivation,
562        flags: VaultFlags,
563    ) -> Self {
564        Self {
565            header: Header::new(id, name, cipher, kdf, flags),
566            contents: Default::default(),
567        }
568    }
569
570    /// Shared access.
571    pub fn shared_access(&self) -> &SharedAccess {
572        &self.header.shared_access
573    }
574
575    /// Get the URN for a vault identifier.
576    pub fn vault_urn(id: &VaultId) -> Result<Urn> {
577        // FIXME: use UrnBuilder
578        let vault_urn = format!("urn:{}:{}{}", URN_NID, VAULT_NSS, id);
579        Ok(vault_urn.parse()?)
580    }
581
582    /// Initialize this vault using a password for
583    /// a symmetric cipher.
584    pub(crate) async fn symmetric(
585        &mut self,
586        password: SecretString,
587        seed: Option<Seed>,
588    ) -> Result<PrivateKey> {
589        if self.header.auth.salt.is_none() {
590            let salt = KeyDerivation::generate_salt();
591            let deriver = self.deriver();
592            let derived_private_key =
593                deriver.derive(&password, &salt, seed.as_ref())?;
594            let private_key = PrivateKey::Symmetric(derived_private_key);
595
596            // Store the salt and seed so we can derive the same
597            // private key later
598            self.header.auth.salt = Some(salt.to_string());
599            self.header.auth.seed = seed;
600
601            Ok(private_key)
602        } else {
603            Err(Error::VaultAlreadyInit)
604        }
605    }
606
607    /// Initialize this vault using asymmetric encryption.
608    pub(crate) async fn asymmetric(
609        &mut self,
610        owner: &Identity,
611        mut recipients: Vec<Recipient>,
612        read_only: bool,
613    ) -> Result<PrivateKey> {
614        if self.header.auth.salt.is_none() {
615            // Ensure the owner public key is always in the list
616            // of recipients
617            let owner_public = owner.to_public();
618            if !recipients
619                .iter()
620                .any(|r| r.to_string() == owner_public.to_string())
621            {
622                recipients.push(owner_public);
623            }
624
625            self.flags_mut().set(VaultFlags::SHARED, true);
626
627            let salt = KeyDerivation::generate_salt();
628            let private_key = PrivateKey::Asymmetric(owner.clone());
629            self.header.summary.cipher = Cipher::X25519;
630
631            let recipients: Vec<_> =
632                recipients.into_iter().map(|r| r.to_string()).collect();
633
634            self.header.shared_access = if read_only {
635                let access = SharedAccess::WriteAccess(recipients);
636                let buffer = encode(&access).await?;
637                let private_key = PrivateKey::Asymmetric(owner.clone());
638                let cipher = self.header.summary.cipher;
639                let owner_recipients = vec![owner.to_public()];
640                let aead = cipher
641                    .encrypt_asymmetric(
642                        &private_key,
643                        &buffer,
644                        owner_recipients,
645                    )
646                    .await?;
647                SharedAccess::ReadOnly(aead)
648            } else {
649                SharedAccess::WriteAccess(recipients)
650            };
651
652            // Store the salt so we know that the vault has
653            // already been initialized, for asymmetric encryption
654            // it is not used
655            self.header.auth.salt = Some(salt.to_string());
656
657            Ok(private_key)
658        } else {
659            Err(Error::VaultAlreadyInit)
660        }
661    }
662
663    /// Key derivation function deriver.
664    pub fn deriver(&self) -> Box<dyn Deriver<Sha256> + Send + 'static> {
665        self.header.summary.kdf.deriver()
666    }
667
668    /// Set whether this vault is a default vault.
669    pub fn set_default_flag(&mut self, value: bool) {
670        self.flags_mut().set(VaultFlags::DEFAULT, value);
671    }
672
673    /// Set whether this vault is an archive vault.
674    pub fn set_archive_flag(&mut self, value: bool) {
675        self.flags_mut().set(VaultFlags::ARCHIVE, value);
676    }
677
678    /// Set whether this vault is an authenticator vault.
679    pub fn set_authenticator_flag(&mut self, value: bool) {
680        self.flags_mut().set(VaultFlags::AUTHENTICATOR, value);
681    }
682
683    /// Set whether this vault is for contacts.
684    pub fn set_contact_flag(&mut self, value: bool) {
685        self.flags_mut().set(VaultFlags::CONTACT, value);
686    }
687
688    /// Set whether this vault is for system specific information.
689    pub fn set_system_flag(&mut self, value: bool) {
690        self.flags_mut().set(VaultFlags::SYSTEM, value);
691    }
692
693    /// Set whether this vault is for device specific information.
694    pub fn set_device_flag(&mut self, value: bool) {
695        self.flags_mut().set(VaultFlags::DEVICE, value);
696    }
697
698    /// Set whether this vault should not sync with own devices.
699    pub fn set_no_sync_flag(&mut self, value: bool) {
700        self.flags_mut().set(VaultFlags::NO_SYNC, value);
701    }
702
703    /// Insert a secret into this vault.
704    pub fn insert_entry(&mut self, id: SecretId, entry: VaultCommit) {
705        self.contents.data.insert(id, entry);
706    }
707
708    /// Get a secret in this vault.
709    pub fn get(&self, id: &SecretId) -> Option<&VaultCommit> {
710        self.contents.data.get(id)
711    }
712
713    /// Encrypt plaintext using the cipher assigned to this vault.
714    pub async fn encrypt(
715        &self,
716        key: &PrivateKey,
717        plaintext: &[u8],
718    ) -> Result<AeadPack> {
719        match self.cipher() {
720            Cipher::XChaCha20Poly1305 | Cipher::AesGcm256 => Ok(self
721                .cipher()
722                .encrypt_symmetric(key, plaintext, None)
723                .await?),
724            Cipher::X25519 => {
725                let recipients = match &self.header.shared_access {
726                    SharedAccess::WriteAccess(access) => {
727                        SharedAccess::parse_recipients(access)?
728                    }
729                    SharedAccess::ReadOnly(aead) => {
730                        let buffer = self
731                            .decrypt(key, aead)
732                            .await
733                            .map_err(|_| Error::PermissionDenied)?;
734                        let shared_access: SharedAccess =
735                            decode(&buffer).await?;
736                        if let SharedAccess::WriteAccess(access) =
737                            &shared_access
738                        {
739                            SharedAccess::parse_recipients(access)?
740                        } else {
741                            return Err(Error::PermissionDenied);
742                        }
743                    }
744                };
745
746                Ok(self
747                    .cipher()
748                    .encrypt_asymmetric(key, plaintext, recipients)
749                    .await?)
750            }
751        }
752    }
753
754    /// Decrypt ciphertext using the cipher assigned to this vault.
755    pub async fn decrypt(
756        &self,
757        key: &PrivateKey,
758        aead: &AeadPack,
759    ) -> Result<Vec<u8>> {
760        match self.cipher() {
761            Cipher::XChaCha20Poly1305 | Cipher::AesGcm256 => {
762                Ok(self.cipher().decrypt_symmetric(key, aead).await?)
763            }
764            Cipher::X25519 => {
765                Ok(self.cipher().decrypt_asymmetric(key, aead).await?)
766            }
767        }
768    }
769
770    /// Choose a new identifier for this vault.
771    ///
772    /// This is useful when importing a vault and the identifier
773    /// collides with an existing vault; rather than overwriting the
774    /// existing vault we can import it alongside by choosing a new
775    /// identifier.
776    pub fn rotate_identifier(&mut self) {
777        self.header.summary.id = Uuid::new_v4();
778    }
779
780    /// Verify an access key.
781    pub async fn verify(&self, key: &AccessKey) -> Result<()> {
782        let salt = self.salt().ok_or(Error::VaultNotInit)?;
783        let meta_aead = self.header().meta().ok_or(Error::VaultNotInit)?;
784        let private_key = match key {
785            AccessKey::Password(password) => {
786                let salt = KeyDerivation::parse_salt(salt)?;
787                let deriver = self.deriver();
788                PrivateKey::Symmetric(deriver.derive(
789                    password,
790                    &salt,
791                    self.seed(),
792                )?)
793            }
794            AccessKey::Identity(id) => PrivateKey::Asymmetric(id.clone()),
795        };
796
797        let _ = self
798            .decrypt(&private_key, meta_aead)
799            .await
800            .map_err(|_| AuthenticationError::PasswordVerification)?;
801
802        Ok(())
803    }
804
805    /// Iterator for the secret keys and values.
806    pub fn iter(&self) -> impl Iterator<Item = (&Uuid, &VaultCommit)> {
807        self.contents.data.iter()
808    }
809
810    /// Iterator for the secret keys.
811    pub fn keys(&self) -> impl Iterator<Item = &Uuid> {
812        self.contents.data.keys()
813    }
814
815    /// Iterator for the secret values.
816    pub fn values(&self) -> impl Iterator<Item = &VaultCommit> {
817        self.contents.data.values()
818    }
819
820    /// Number of secrets in this vault.
821    pub fn len(&self) -> usize {
822        self.contents.data.len()
823    }
824
825    /// Determine if this vault is empty.
826    pub fn is_empty(&self) -> bool {
827        self.len() == 0
828    }
829
830    /// Convert this vault into a create vault event.
831    ///
832    /// Ensures the vault is head-only before encoding into the event.
833    pub async fn into_event(&self) -> Result<WriteEvent> {
834        let buffer = if self.is_empty() {
835            encode(self).await?
836        } else {
837            let header = self.header.clone();
838            let vault: Vault = header.into();
839            encode(&vault).await?
840        };
841        Ok(WriteEvent::CreateVault(buffer))
842    }
843
844    /// Iterator for the secret keys and commit hashes.
845    pub fn commits(&self) -> impl Iterator<Item = (&Uuid, &CommitHash)> {
846        self.contents
847            .data
848            .keys()
849            .zip(self.contents.data.values().map(|v| &v.0))
850    }
851
852    /// Get the salt used for passphrase authentication.
853    pub fn salt(&self) -> Option<&String> {
854        self.header.auth.salt.as_ref()
855    }
856
857    /// Get the seed used for passphrase authentication.
858    pub fn seed(&self) -> Option<&Seed> {
859        self.header.auth.seed.as_ref()
860    }
861
862    /// Get the summary for this vault.
863    pub fn summary(&self) -> &Summary {
864        &self.header.summary
865    }
866
867    /// Reference to the vault flags.
868    pub fn flags(&self) -> &VaultFlags {
869        self.header.flags()
870    }
871
872    /// Mutable reference to the vault flags.
873    pub fn flags_mut(&mut self) -> &mut VaultFlags {
874        self.header.flags_mut()
875    }
876
877    /// Unique identifier for this vault.
878    pub fn id(&self) -> &VaultId {
879        &self.header.summary.id
880    }
881
882    /// Public name for this vault.
883    pub fn name(&self) -> &str {
884        self.header.name()
885    }
886
887    /// Set the public name of this vault.
888    pub fn set_name(&mut self, name: String) {
889        self.header.set_name(name);
890    }
891
892    /// Encryption cipher for this vault.
893    pub fn cipher(&self) -> &Cipher {
894        &self.header.summary.cipher
895    }
896
897    /// Key derivation function.
898    pub fn kdf(&self) -> &KeyDerivation {
899        &self.header.summary.kdf
900    }
901
902    /// Vault header.
903    pub fn header(&self) -> &Header {
904        &self.header
905    }
906
907    /// Mutable vault header.
908    pub fn header_mut(&mut self) -> &mut Header {
909        &mut self.header
910    }
911
912    /// Vault data.
913    pub fn data(&self) -> &IndexMap<SecretId, VaultCommit> {
914        &self.contents.data
915    }
916
917    /// Mutable vault data.
918    pub fn data_mut(&mut self) -> &mut IndexMap<SecretId, VaultCommit> {
919        &mut self.contents.data
920    }
921
922    /// Get the meta data for all the secrets.
923    pub fn meta_data(&self) -> HashMap<&Uuid, &AeadPack> {
924        self.contents
925            .data
926            .iter()
927            .map(|(k, v)| (k, &v.1 .0))
928            .collect::<HashMap<_, _>>()
929    }
930
931    /// Compute the hash of the encoded encrypted buffer
932    /// for the meta and secret data.
933    #[doc(hidden)]
934    pub async fn commit_hash(
935        meta_aead: &AeadPack,
936        secret_aead: &AeadPack,
937    ) -> Result<CommitHash> {
938        // Compute the hash of the encrypted and encoded bytes
939        let encoded_meta = encode(meta_aead).await?;
940        let encoded_data = encode(secret_aead).await?;
941
942        let mut hasher = Sha256::new();
943        hasher.update(&encoded_meta);
944        hasher.update(&encoded_data);
945        let digest = hasher.finalize();
946        Ok(CommitHash(digest.as_slice().try_into()?))
947    }
948}
949
950impl From<Header> for Vault {
951    fn from(header: Header) -> Self {
952        Vault {
953            header,
954            contents: Default::default(),
955        }
956    }
957}
958
959impl From<Vault> for Header {
960    fn from(value: Vault) -> Self {
961        value.header
962    }
963}
964
965impl IntoIterator for Vault {
966    type Item = (SecretId, VaultCommit);
967    type IntoIter = indexmap::map::IntoIter<SecretId, VaultCommit>;
968
969    fn into_iter(self) -> Self::IntoIter {
970        self.contents.data.into_iter()
971    }
972}
973
974#[async_trait]
975impl EncryptedEntry for Vault {
976    type Error = Error;
977
978    async fn summary(&self) -> Result<Summary> {
979        Ok(self.header.summary.clone())
980    }
981
982    async fn vault_name(&self) -> Result<Cow<'_, str>> {
983        Ok(Cow::Borrowed(self.name()))
984    }
985
986    async fn set_vault_name(&mut self, name: String) -> Result<WriteEvent> {
987        self.set_name(name.clone());
988        Ok(WriteEvent::SetVaultName(name))
989    }
990
991    async fn set_vault_flags(
992        &mut self,
993        flags: VaultFlags,
994    ) -> Result<WriteEvent> {
995        *self.header.flags_mut() = flags.clone();
996        Ok(WriteEvent::SetVaultFlags(flags))
997    }
998
999    async fn set_vault_meta(
1000        &mut self,
1001        meta_data: AeadPack,
1002    ) -> Result<WriteEvent> {
1003        self.header.set_meta(Some(meta_data.clone()));
1004        Ok(WriteEvent::SetVaultMeta(meta_data))
1005    }
1006
1007    async fn create_secret(
1008        &mut self,
1009        commit: CommitHash,
1010        secret: VaultEntry,
1011    ) -> Result<WriteEvent> {
1012        let id = Uuid::new_v4();
1013        self.insert_secret(id, commit, secret).await
1014    }
1015
1016    async fn insert_secret(
1017        &mut self,
1018        id: SecretId,
1019        commit: CommitHash,
1020        secret: VaultEntry,
1021    ) -> Result<WriteEvent> {
1022        let value = self
1023            .contents
1024            .data
1025            .entry(id)
1026            .or_insert(VaultCommit(commit, secret));
1027        Ok(WriteEvent::CreateSecret(id, value.clone()))
1028    }
1029
1030    async fn read_secret<'a>(
1031        &'a self,
1032        id: &SecretId,
1033    ) -> Result<Option<(Cow<'a, VaultCommit>, ReadEvent)>> {
1034        let result = self
1035            .contents
1036            .data
1037            .get(id)
1038            .map(|c| (Cow::Borrowed(c), ReadEvent::ReadSecret(*id)));
1039        Ok(result)
1040    }
1041
1042    async fn update_secret(
1043        &mut self,
1044        id: &SecretId,
1045        commit: CommitHash,
1046        secret: VaultEntry,
1047    ) -> Result<Option<WriteEvent>> {
1048        let _vault_id = *self.id();
1049        if let Some(value) = self.contents.data.get_mut(id) {
1050            *value = VaultCommit(commit, secret);
1051            Ok(Some(WriteEvent::UpdateSecret(*id, value.clone())))
1052        } else {
1053            Ok(None)
1054        }
1055    }
1056
1057    async fn delete_secret(
1058        &mut self,
1059        id: &SecretId,
1060    ) -> Result<Option<WriteEvent>> {
1061        let entry = self.contents.data.shift_remove(id);
1062        Ok(entry.map(|_| WriteEvent::DeleteSecret(*id)))
1063    }
1064
1065    async fn replace_vault(&mut self, vault: &Vault) -> Result<()> {
1066        *self = vault.clone();
1067        Ok(())
1068    }
1069}