sos_vault/
secret.rs

1//! Types used to represent vault meta data and secrets.
2use crate::{Error, Result};
3use bitflags::bitflags;
4use ed25519_dalek::SECRET_KEY_LENGTH;
5use pem::Pem;
6use secrecy::{ExposeSecret, SecretBox, SecretString};
7use serde::{
8    ser::{SerializeMap, SerializeSeq},
9    Deserialize, Serialize, Serializer,
10};
11use sos_core::{basename, guess_mime, UtcDateTime};
12use sos_signer::{
13    ecdsa::{self, BoxedEcdsaSigner},
14    ed25519::{self, BoxedEd25519Signer},
15};
16use std::path::PathBuf;
17use std::{
18    cmp::Ordering,
19    collections::{HashMap, HashSet},
20    fmt,
21    str::FromStr,
22};
23use totp_rs::TOTP;
24use url::Url;
25use urn::Urn;
26use uuid::Uuid;
27use vcard4::{self, Vcard};
28
29pub use sos_core::{SecretId, SecretPath};
30
31bitflags! {
32    /// Bit flags for a secret.
33    #[derive(Default, Serialize, Deserialize, Debug, Clone)]
34    #[serde(transparent)]
35    pub struct SecretFlags: u32 {
36        /// Clients should verify user presence either using
37        /// the platform authenticator or the account password
38        /// before revealing this secret.
39        const VERIFY            =        0b00000001;
40    }
41}
42
43impl SecretFlags {
44    /// Determine if this secret requires verification to access.
45    pub fn must_verify(&self) -> bool {
46        self.contains(SecretFlags::VERIFY)
47    }
48}
49
50fn serialize_secret_string<S>(
51    secret: &SecretString,
52    ser: S,
53) -> std::result::Result<S::Ok, S::Error>
54where
55    S: Serializer,
56{
57    ser.serialize_str(secret.expose_secret())
58}
59
60fn serialize_secret_option<S>(
61    secret: &Option<SecretString>,
62    ser: S,
63) -> std::result::Result<S::Ok, S::Error>
64where
65    S: Serializer,
66{
67    match secret {
68        Some(ref value) => ser.serialize_some(value.expose_secret()),
69        None => ser.serialize_none(),
70    }
71}
72
73fn serialize_secret_string_map<S>(
74    secret: &HashMap<String, SecretString>,
75    ser: S,
76) -> std::result::Result<S::Ok, S::Error>
77where
78    S: Serializer,
79{
80    let mut map = ser.serialize_map(Some(secret.len()))?;
81    for (k, v) in secret {
82        map.serialize_entry(k, v.expose_secret())?;
83    }
84    map.end()
85}
86
87fn serialize_secret_buffer<S>(
88    secret: &SecretBox<Vec<u8>>,
89    ser: S,
90) -> std::result::Result<S::Ok, S::Error>
91where
92    S: Serializer,
93{
94    let mut seq = ser.serialize_seq(Some(secret.expose_secret().len()))?;
95    for element in secret.expose_secret() {
96        seq.serialize_element(element)?;
97    }
98    seq.end()
99}
100
101fn is_empty_secret_vec(value: &SecretBox<Vec<u8>>) -> bool {
102    value.expose_secret().is_empty()
103}
104
105fn default_secret_vec() -> SecretBox<Vec<u8>> {
106    SecretBox::new(Box::new(Vec::new()))
107}
108
109/// Reference to a secret using an id or a named label.
110#[derive(Debug, Clone)]
111pub enum SecretRef {
112    /// Secret identifier.
113    Id(SecretId),
114    /// Secret label.
115    Name(String),
116}
117
118impl fmt::Display for SecretRef {
119    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120        match self {
121            Self::Id(id) => write!(f, "{}", id),
122            Self::Name(name) => write!(f, "{}", name),
123        }
124    }
125}
126
127impl From<SecretId> for SecretRef {
128    fn from(value: SecretId) -> Self {
129        Self::Id(value)
130    }
131}
132
133impl From<String> for SecretRef {
134    fn from(value: String) -> Self {
135        Self::Name(value)
136    }
137}
138
139impl FromStr for SecretRef {
140    type Err = Error;
141    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
142        if let Ok(id) = Uuid::parse_str(s) {
143            Ok(Self::Id(id))
144        } else {
145            Ok(Self::Name(s.to_string()))
146        }
147    }
148}
149
150/// Type of secret assigned to the secret meta data.
151///
152/// Matches the enum variants for a secret and is used
153/// so we can know the type of secret from the meta data
154/// before secret data has been decrypted.
155#[typeshare::typeshare]
156#[derive(
157    Default, Clone, Debug, Copy, Serialize, Deserialize, Eq, PartialEq, Hash,
158)]
159#[serde(rename_all = "lowercase")]
160pub enum SecretType {
161    /// UTF-8 encoded note.
162    #[default]
163    Note,
164    /// Binary blob.
165    File,
166    /// Account with login password.
167    Account,
168    /// Collection of credentials as key/value pairs.
169    List,
170    /// PEM encoded binary data.
171    Pem,
172    /// UTF-8 text document.
173    Page,
174    /// Private signing key.
175    Signer,
176    /// Contact for an organization, person, group or location.
177    Contact,
178    /// Two-factor authentication using a TOTP.
179    Totp,
180    /// Credit or debit card.
181    Card,
182    /// Bank account.
183    Bank,
184    /// External link; intended to be used in embedded user fields.
185    Link,
186    /// Standalone password; intended to be used in embedded user fields.
187    Password,
188    /// Identity secret for passports, driving licenses etc.
189    Identity,
190    /// AGE encryption standard.
191    Age,
192}
193
194impl SecretType {
195    /// Get an abbreviated short name.
196    pub fn short_name(&self) -> &str {
197        match self {
198            Self::Note => "note",
199            Self::File => "file",
200            Self::Account => "acct",
201            Self::List => "list",
202            Self::Pem => "cert",
203            Self::Page => "page",
204            Self::Identity => "iden",
205            Self::Signer => "sign",
206            Self::Contact => "cont",
207            Self::Totp => "totp",
208            Self::Card => "card",
209            Self::Bank => "bank",
210            Self::Link => "link",
211            Self::Password => "pass",
212            Self::Age => "age",
213        }
214    }
215}
216
217impl fmt::Display for SecretType {
218    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219        write!(
220            f,
221            "{}",
222            match self {
223                Self::Note => "Note",
224                Self::File => "File",
225                Self::Account => "Account",
226                Self::List => "List",
227                Self::Pem => "Certificate",
228                Self::Page => "Page",
229                Self::Identity => "Identity",
230                Self::Signer => "Signer",
231                Self::Contact => "Contact",
232                Self::Totp => "Authenticator",
233                Self::Card => "Card",
234                Self::Bank => "Bank",
235                Self::Link => "Link",
236                Self::Password => "Password",
237                Self::Age => "Age",
238            }
239        )
240    }
241}
242
243impl From<&SecretType> for u8 {
244    fn from(value: &SecretType) -> Self {
245        match value {
246            SecretType::Note => kind::NOTE,
247            SecretType::File => kind::FILE,
248            SecretType::Account => kind::ACCOUNT,
249            SecretType::List => kind::LIST,
250            SecretType::Pem => kind::PEM,
251            SecretType::Page => kind::PAGE,
252            SecretType::Identity => kind::IDENTIFICATION,
253            SecretType::Signer => kind::SIGNER,
254            SecretType::Contact => kind::CONTACT,
255            SecretType::Totp => kind::TOTP,
256            SecretType::Card => kind::CARD,
257            SecretType::Bank => kind::BANK,
258            SecretType::Link => kind::LINK,
259            SecretType::Password => kind::PASSWORD,
260            SecretType::Age => kind::AGE,
261        }
262    }
263}
264
265impl From<SecretType> for u8 {
266    fn from(value: SecretType) -> Self {
267        (&value).into()
268    }
269}
270
271impl TryFrom<u8> for SecretType {
272    type Error = Error;
273
274    fn try_from(value: u8) -> Result<Self> {
275        Ok(match value {
276            kind::NOTE => Self::Note,
277            kind::FILE => Self::File,
278            kind::ACCOUNT => Self::Account,
279            kind::LIST => Self::List,
280            kind::PEM => Self::Pem,
281            kind::PAGE => Self::Page,
282            kind::IDENTIFICATION => Self::Identity,
283            kind::SIGNER => Self::Signer,
284            kind::CONTACT => Self::Contact,
285            kind::TOTP => Self::Totp,
286            kind::CARD => Self::Card,
287            kind::BANK => Self::Bank,
288            kind::LINK => Self::Link,
289            kind::PASSWORD => Self::Password,
290            kind::AGE => Self::Age,
291            _ => return Err(Error::UnknownSecretKind(value)),
292        })
293    }
294}
295
296/// Encapsulates the meta data for a secret.
297#[typeshare::typeshare]
298#[derive(Debug, Serialize, Deserialize, Default, Clone)]
299#[serde(rename_all = "camelCase")]
300pub struct SecretMeta {
301    /// Kind of the secret.
302    pub(crate) kind: SecretType,
303    /// Flags for the secret.
304    pub(crate) flags: SecretFlags,
305    /// Human-friendly label for the secret.
306    #[serde(skip_serializing_if = "String::is_empty")]
307    pub(crate) label: String,
308    /// Collection of tags.
309    #[serde(skip_serializing_if = "HashSet::is_empty")]
310    pub(crate) tags: HashSet<String>,
311    /// Whether this secret is a favorite.
312    pub(crate) favorite: bool,
313    /// A URN identifier for this secret.
314    ///
315    /// This is used when an identity vault stores passphrases
316    /// for other vault folders on behalf of a user and can also
317    /// be used to assign a predictable identifier for a secret.
318    #[serde(skip_serializing_if = "Option::is_none")]
319    pub(crate) urn: Option<Urn>,
320    /// An optional owner identifier.
321    ///
322    /// This can be used when creating secrets on behalf of a
323    /// third-party plugin or application to indicate the identifier
324    /// of the third-party application.
325    #[serde(skip_serializing_if = "Option::is_none")]
326    pub(crate) owner_id: Option<String>,
327    /// Date created timestamp.
328    pub(crate) date_created: UtcDateTime,
329    /// Last updated timestamp.
330    #[serde(skip_deserializing)]
331    pub(crate) last_updated: UtcDateTime,
332}
333
334impl fmt::Display for SecretMeta {
335    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
336        writeln!(f, "[{}] {}", self.kind, self.label)?;
337        if let Some(urn) = &self.urn {
338            writeln!(f, "{}", urn)?;
339        }
340        writeln!(f)?;
341
342        if !self.tags.is_empty() {
343            let mut list = Vec::new();
344            for tag in &self.tags {
345                list.push(&tag[..]);
346            }
347            list.sort();
348            let tags = list.join(", ");
349            writeln!(f, "Tags: {}", tags)?;
350        }
351        writeln!(
352            f,
353            "Favorite: {}",
354            if self.favorite { "yes" } else { "no" }
355        )?;
356
357        writeln!(f)?;
358        writeln!(f, "Created: {}", self.date_created)?;
359        write!(f, "Updated: {}", self.last_updated)
360    }
361}
362
363impl PartialEq for SecretMeta {
364    fn eq(&self, other: &Self) -> bool {
365        self.kind == other.kind
366            && self.label == other.label
367            && self.urn == other.urn
368    }
369}
370
371impl Eq for SecretMeta {}
372
373impl PartialOrd for SecretMeta {
374    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
375        self.label.partial_cmp(&other.label)
376    }
377}
378
379impl SecretMeta {
380    /// Create new meta data for a secret.
381    pub fn new(label: String, kind: SecretType) -> Self {
382        Self {
383            kind,
384            flags: Default::default(),
385            label,
386            date_created: Default::default(),
387            last_updated: Default::default(),
388            tags: Default::default(),
389            urn: None,
390            owner_id: None,
391            favorite: false,
392        }
393    }
394
395    /// Label for the secret.
396    pub fn label(&self) -> &str {
397        &self.label
398    }
399
400    /// Set the label for the secret.
401    pub fn set_label(&mut self, label: String) {
402        self.label = label;
403    }
404
405    /// Kind of the secret.
406    pub fn kind(&self) -> &SecretType {
407        &self.kind
408    }
409
410    /// Created date and time.
411    pub fn date_created(&self) -> &UtcDateTime {
412        &self.date_created
413    }
414
415    /// Set the created date and time.
416    pub fn set_date_created(&mut self, date_created: UtcDateTime) {
417        self.date_created = date_created;
418    }
419
420    /// Update the last updated timestamp to now.
421    pub fn touch(&mut self) {
422        self.last_updated = Default::default();
423    }
424
425    /// Last updated date and time.
426    pub fn last_updated(&self) -> &UtcDateTime {
427        &self.last_updated
428    }
429
430    /// Set the updated date and time.
431    pub fn set_last_updated(&mut self, last_updated: UtcDateTime) {
432        self.last_updated = last_updated;
433    }
434
435    /// Secret tags.
436    pub fn tags(&self) -> &HashSet<String> {
437        &self.tags
438    }
439
440    /// Set the tags.
441    pub fn set_tags(&mut self, tags: HashSet<String>) {
442        self.tags = tags;
443    }
444
445    /// Mutable tags reference.
446    pub fn tags_mut(&mut self) -> &mut HashSet<String> {
447        &mut self.tags
448    }
449
450    /// Get the URN for this secret.
451    pub fn urn(&self) -> Option<&Urn> {
452        self.urn.as_ref()
453    }
454
455    /// Set the URN for this secret.
456    pub fn set_urn(&mut self, urn: Option<Urn>) {
457        self.urn = urn;
458    }
459
460    /// Get the owner identifier for this secret.
461    pub fn owner_id(&self) -> Option<&String> {
462        self.owner_id.as_ref()
463    }
464
465    /// Set the owner identifier for this secret.
466    pub fn set_owner_id(&mut self, owner_id: Option<String>) {
467        self.owner_id = owner_id;
468    }
469
470    /// The favorite for the secret.
471    pub fn favorite(&self) -> bool {
472        self.favorite
473    }
474
475    /// Set the favorite for the secret.
476    pub fn set_favorite(&mut self, favorite: bool) {
477        self.favorite = favorite;
478    }
479
480    /// The flags for the secret.
481    pub fn flags(&self) -> &SecretFlags {
482        &self.flags
483    }
484
485    /// The mutable flags for the secret.
486    pub fn flags_mut(&mut self) -> &mut SecretFlags {
487        &mut self.flags
488    }
489}
490
491/// Secret type that encapsulates a signing private key.
492#[derive(Serialize, Deserialize)]
493pub enum SecretSigner {
494    /// Single party Ethereum-compatible ECDSA signing private key.
495    #[serde(serialize_with = "serialize_secret_buffer")]
496    SinglePartyEcdsa(SecretBox<Vec<u8>>),
497    /// Single party Ed25519 signing private key.
498    #[serde(serialize_with = "serialize_secret_buffer")]
499    SinglePartyEd25519(SecretBox<Vec<u8>>),
500}
501
502impl From<ecdsa::SingleParty> for SecretSigner {
503    fn from(value: ecdsa::SingleParty) -> Self {
504        Self::SinglePartyEcdsa(SecretBox::new(
505            value.0.to_bytes().to_vec().into(),
506        ))
507    }
508}
509
510impl From<ed25519::SingleParty> for SecretSigner {
511    fn from(value: ed25519::SingleParty) -> Self {
512        Self::SinglePartyEd25519(SecretBox::new(
513            value.0.to_bytes().to_vec().into(),
514        ))
515    }
516}
517
518impl SecretSigner {
519    /// Try to convert this signing key into an ECDSA signer.
520    pub fn try_into_ecdsa_signer(self) -> Result<BoxedEcdsaSigner> {
521        match self {
522            Self::SinglePartyEcdsa(key) => {
523                let private_key: [u8; 32] =
524                    key.expose_secret().as_slice().try_into()?;
525                let signer: ecdsa::SingleParty = private_key.try_into()?;
526                Ok(Box::new(signer))
527            }
528            _ => Err(Error::NotEcdsaKey),
529        }
530    }
531
532    /// Try to convert this signing key into an Ed25519 signer.
533    pub fn try_into_ed25519_signer(self) -> Result<BoxedEd25519Signer> {
534        match self {
535            Self::SinglePartyEd25519(key) => {
536                let keypair: [u8; SECRET_KEY_LENGTH] =
537                    key.expose_secret().as_slice().try_into()?;
538                let signer: ed25519::SingleParty = keypair.try_into()?;
539                Ok(Box::new(signer))
540            }
541            _ => Err(Error::NotEd25519Key),
542        }
543    }
544}
545
546impl Default for SecretSigner {
547    fn default() -> Self {
548        Self::SinglePartyEcdsa(SecretBox::new(vec![].into()))
549    }
550}
551
552impl Clone for SecretSigner {
553    fn clone(&self) -> Self {
554        match self {
555            Self::SinglePartyEcdsa(buffer) => Self::SinglePartyEcdsa(
556                SecretBox::new(buffer.expose_secret().to_vec().into()),
557            ),
558            Self::SinglePartyEd25519(buffer) => Self::SinglePartyEd25519(
559                SecretBox::new(buffer.expose_secret().to_vec().into()),
560            ),
561        }
562    }
563}
564
565impl PartialEq for SecretSigner {
566    fn eq(&self, other: &Self) -> bool {
567        match (self, other) {
568            (Self::SinglePartyEcdsa(a), Self::SinglePartyEcdsa(b)) => {
569                a.expose_secret() == b.expose_secret()
570            }
571            (Self::SinglePartyEd25519(a), Self::SinglePartyEd25519(b)) => {
572                a.expose_secret() == b.expose_secret()
573            }
574            _ => false,
575        }
576    }
577}
578
579/// Secret with it's associated meta data and identifier.
580#[typeshare::typeshare]
581#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
582pub struct SecretRow {
583    /// Identifier for the secret.
584    pub(crate) id: SecretId,
585    /// Meta data for the secret.
586    pub(crate) meta: SecretMeta,
587    /// The data for the secret.
588    pub(crate) secret: Secret,
589}
590
591impl SecretRow {
592    /// Create a new secret row with known identifier.
593    pub fn new(id: SecretId, meta: SecretMeta, secret: Secret) -> Self {
594        Self { id, meta, secret }
595    }
596
597    /// Identifier for the row.
598    pub fn id(&self) -> &SecretId {
599        &self.id
600    }
601
602    /// Meta data for the secret.
603    pub fn meta(&self) -> &SecretMeta {
604        &self.meta
605    }
606
607    /// Mutable meta data for the secret.
608    pub fn meta_mut(&mut self) -> &mut SecretMeta {
609        &mut self.meta
610    }
611
612    /// Secret data.
613    pub fn secret(&self) -> &Secret {
614        &self.secret
615    }
616
617    /// Mutable secret data.
618    pub fn secret_mut(&mut self) -> &mut Secret {
619        &mut self.secret
620    }
621}
622
623impl From<SecretRow> for (SecretId, SecretMeta, Secret) {
624    fn from(value: SecretRow) -> Self {
625        (value.id, value.meta, value.secret)
626    }
627}
628
629impl From<SecretRow> for SecretMeta {
630    fn from(value: SecretRow) -> Self {
631        value.meta
632    }
633}
634
635impl From<SecretRow> for Secret {
636    fn from(value: SecretRow) -> Self {
637        value.secret
638    }
639}
640
641/// Collection of custom user data.
642#[typeshare::typeshare]
643#[derive(Default, Serialize, Deserialize, Clone, PartialEq, Eq)]
644#[serde(rename_all = "camelCase")]
645pub struct UserData {
646    /// Collection of custom user fields.
647    #[serde(skip_serializing_if = "Vec::is_empty")]
648    pub(crate) fields: Vec<SecretRow>,
649    /// Comment for the secret.
650    #[serde(skip_serializing_if = "Option::is_none")]
651    pub(crate) comment: Option<String>,
652    /// Recovery notes.
653    ///
654    /// These are notes specific for a person that might recover
655    /// the vault information and is intended to provide additional
656    /// information on how to use this secret in the event of an
657    /// emergency.
658    #[serde(skip_serializing_if = "Option::is_none")]
659    pub(crate) recovery_note: Option<String>,
660}
661
662impl UserData {
663    /// Create user data with a comment.
664    pub fn new_comment(value: String) -> Self {
665        Self {
666            fields: Default::default(),
667            comment: Some(value),
668            recovery_note: Default::default(),
669        }
670    }
671
672    /// Get the number of user data.
673    pub fn len(&self) -> usize {
674        self.fields.len()
675    }
676
677    /// Determine of there are any user data fields.
678    pub fn is_empty(&self) -> bool {
679        self.len() == 0 && self.recovery_note.is_none()
680    }
681
682    /// Determine of there are any user data.
683    pub fn is_default(&self) -> bool {
684        self.is_empty() && self.recovery_note.is_none()
685    }
686
687    /// Get the user fields.
688    pub fn fields(&self) -> &Vec<SecretRow> {
689        &self.fields
690    }
691
692    /// Get a mutable reference to the user fields.
693    pub fn fields_mut(&mut self) -> &mut Vec<SecretRow> {
694        &mut self.fields
695    }
696
697    /// Add a custom field to this collection.
698    pub fn push(&mut self, field: SecretRow) {
699        self.fields.push(field);
700    }
701
702    /// Get the comment.
703    pub fn comment(&self) -> Option<&str> {
704        self.comment.as_ref().map(|s| &s[..])
705    }
706
707    /// Set the comment.
708    pub fn set_comment(&mut self, comment: Option<String>) {
709        self.comment = comment;
710    }
711
712    /// Get the recovery notes.
713    pub fn recovery_note(&self) -> Option<&str> {
714        self.recovery_note.as_ref().map(|s| &s[..])
715    }
716
717    /// Set the recovery notes.
718    pub fn set_recovery_note(&mut self, notes: Option<String>) {
719        self.recovery_note = notes;
720    }
721}
722
723/// Enumeration of types of identification.
724#[typeshare::typeshare]
725#[derive(PartialEq, Eq, Clone, Serialize, Deserialize)]
726#[serde(rename_all = "camelCase")]
727pub enum IdentityKind {
728    /// Personal identification number (PIN).
729    PersonalIdNumber,
730    /// Generic id card.
731    IdCard,
732    /// Passport identification.
733    Passport,
734    ///  Driver license identification.
735    DriverLicense,
736    /// Social security identification.
737    SocialSecurity,
738    /// Tax number identification.
739    TaxNumber,
740    /// Medical card identification.
741    MedicalCard,
742}
743
744impl fmt::Display for IdentityKind {
745    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
746        write!(
747            f,
748            "{}",
749            match self {
750                Self::PersonalIdNumber => "PIN",
751                Self::IdCard => "Id",
752                Self::Passport => "Passport",
753                Self::DriverLicense => "Driver license",
754                Self::SocialSecurity => "Social security",
755                Self::TaxNumber => "Tax number",
756                Self::MedicalCard => "Medical card",
757            }
758        )
759    }
760}
761
762impl From<IdentityKind> for u8 {
763    fn from(value: IdentityKind) -> Self {
764        (&value).into()
765    }
766}
767
768impl From<&IdentityKind> for u8 {
769    fn from(value: &IdentityKind) -> Self {
770        match value {
771            IdentityKind::PersonalIdNumber => 1,
772            IdentityKind::IdCard => 2,
773            IdentityKind::Passport => 3,
774            IdentityKind::DriverLicense => 4,
775            IdentityKind::SocialSecurity => 5,
776            IdentityKind::TaxNumber => 6,
777            IdentityKind::MedicalCard => 7,
778        }
779    }
780}
781
782impl TryFrom<u8> for IdentityKind {
783    type Error = Error;
784
785    fn try_from(value: u8) -> Result<Self> {
786        match value {
787            1 => Ok(IdentityKind::PersonalIdNumber),
788            2 => Ok(IdentityKind::IdCard),
789            3 => Ok(IdentityKind::Passport),
790            4 => Ok(IdentityKind::DriverLicense),
791            5 => Ok(IdentityKind::SocialSecurity),
792            6 => Ok(IdentityKind::TaxNumber),
793            7 => Ok(IdentityKind::MedicalCard),
794            _ => Err(Error::UnknownIdentityKind(value)),
795        }
796    }
797}
798
799/// Enumeration of AGE versions.
800#[derive(Default, Clone, Serialize, Deserialize, Eq, PartialEq)]
801pub enum AgeVersion {
802    /// The v1 AGE version.
803    #[default]
804    Version1,
805}
806
807/// Variants for embedded and external file secrets.
808#[derive(Serialize, Deserialize)]
809#[serde(untagged)]
810pub enum FileContent {
811    /// Embedded file buffer.
812    Embedded {
813        /// File name.
814        name: String,
815
816        /// Mime type for the data.
817        ///
818        /// Use application/octet-stream if no mime-type is available.
819        mime: String,
820
821        /// The binary data.
822        #[serde(
823            default = "default_secret_vec",
824            serialize_with = "serialize_secret_buffer",
825            skip_serializing_if = "is_empty_secret_vec"
826        )]
827        buffer: SecretBox<Vec<u8>>,
828
829        /// The SHA-256 digest of the buffer.
830        ///
831        /// Using the SHA-256 digest allows the checksum to be computed
832        /// using the Javascript SubtleCrypto API and in Dart using the
833        /// crypto package.
834        ///
835        /// This is used primarily during the public migration export
836        /// to identify files that have been extracted to another location
837        /// in the archive rather than embedding the binary data.
838        #[serde(with = "hex::serde")]
839        checksum: [u8; 32],
840    },
841    /// Encrypted data is stored in an external file.
842    External {
843        /// File name.
844        name: String,
845
846        /// Mime type for the data.
847        ///
848        /// Use application/octet-stream if no mime-type is available.
849        mime: String,
850
851        /// The SHA-256 digest of the buffer.
852        ///
853        /// Using the SHA-256 digest allows the checksum to be computed
854        /// using the Javascript SubtleCrypto API and in Dart using the
855        /// crypto package.
856        ///
857        /// This is used primarily during the public migration export
858        /// to identify files that have been extracted to another location
859        /// in the archive rather than embedding the binary data.
860        #[serde(with = "hex::serde")]
861        checksum: [u8; 32],
862
863        /// Size of the encrypted file content.
864        size: u64,
865
866        /// Optional path to a source file; never encoded or serialized.
867        #[serde(skip)]
868        path: Option<PathBuf>,
869    },
870}
871
872impl FileContent {
873    /// File name.
874    pub fn name(&self) -> &str {
875        match self {
876            Self::Embedded { name, .. } => name,
877            Self::External { name, .. } => name,
878        }
879    }
880
881    /// File mime type.
882    pub fn mime(&self) -> &str {
883        match self {
884            Self::Embedded { mime, .. } => mime,
885            Self::External { mime, .. } => mime,
886        }
887    }
888
889    /// File checksum.
890    pub fn checksum(&self) -> &[u8; 32] {
891        match self {
892            Self::Embedded { checksum, .. } => checksum,
893            Self::External { checksum, .. } => checksum,
894        }
895    }
896
897    /// File size.
898    pub fn size(&self) -> u64 {
899        match self {
900            Self::Embedded { buffer, .. } => {
901                buffer.expose_secret().len() as u64
902            }
903            Self::External { size, .. } => *size,
904        }
905    }
906}
907
908impl Default for FileContent {
909    fn default() -> Self {
910        Self::Embedded {
911            name: String::new(),
912            mime: String::new(),
913            buffer: SecretBox::new(vec![].into()),
914            checksum: [0; 32],
915        }
916    }
917}
918
919impl PartialEq for FileContent {
920    fn eq(&self, other: &Self) -> bool {
921        match (self, other) {
922            (
923                Self::Embedded {
924                    name: name_a,
925                    mime: mime_a,
926                    buffer: buffer_a,
927                    checksum: checksum_a,
928                },
929                Self::Embedded {
930                    name: name_b,
931                    mime: mime_b,
932                    buffer: buffer_b,
933                    checksum: checksum_b,
934                },
935            ) => {
936                name_a == name_b
937                    && mime_a == mime_b
938                    && buffer_a.expose_secret() == buffer_b.expose_secret()
939                    && checksum_a == checksum_b
940            }
941            (
942                Self::External {
943                    name: name_a,
944                    mime: mime_a,
945                    checksum: checksum_a,
946                    size: size_a,
947                    path: path_a,
948                },
949                Self::External {
950                    name: name_b,
951                    mime: mime_b,
952                    checksum: checksum_b,
953                    size: size_b,
954                    path: path_b,
955                },
956            ) => {
957                name_a == name_b
958                    && mime_a == mime_b
959                    && checksum_a == checksum_b
960                    && size_a == size_b
961                    && path_a == path_b
962            }
963            _ => false,
964        }
965    }
966}
967
968impl Clone for FileContent {
969    fn clone(&self) -> Self {
970        match self {
971            FileContent::Embedded {
972                name,
973                mime,
974                buffer,
975                checksum,
976            } => FileContent::Embedded {
977                name: name.to_owned(),
978                mime: mime.to_owned(),
979                buffer: SecretBox::new(
980                    buffer.expose_secret().to_vec().into(),
981                ),
982                checksum: *checksum,
983            },
984            FileContent::External {
985                name,
986                mime,
987                checksum,
988                size,
989                path,
990            } => FileContent::External {
991                name: name.to_owned(),
992                mime: mime.to_owned(),
993                checksum: *checksum,
994                size: *size,
995                path: path.clone(),
996            },
997        }
998    }
999}
1000
1001/// Represents the various types of secret.
1002///
1003/// Some variants can be created from other types
1004/// using a `From` or `TryFrom` implementation:
1005///
1006/// * `String`                              -> `Secret::Note`
1007/// * `HashMap<String, SecretString>`       -> `Secret::List`
1008/// * `SecretString`                        -> `Secret::Password`
1009/// * `PathBuf`                             -> `Secret::File`
1010/// * `Url`                                 -> `Secret::Link`
1011///
1012#[derive(Serialize, Deserialize)]
1013#[serde(untagged)]
1014pub enum Secret {
1015    /// A UTF-8 encoded note.
1016    #[serde(rename_all = "camelCase")]
1017    Note {
1018        /// Note text.
1019        #[serde(serialize_with = "serialize_secret_string")]
1020        text: SecretString,
1021        /// Custom user data.
1022        #[serde(default, skip_serializing_if = "UserData::is_default")]
1023        user_data: UserData,
1024    },
1025    /// A binary blob.
1026    #[serde(rename_all = "camelCase")]
1027    File {
1028        /// File secret data.
1029        content: FileContent,
1030
1031        /// Custom user data.
1032        #[serde(default, skip_serializing_if = "UserData::is_default")]
1033        user_data: UserData,
1034    },
1035    /// Account with login password.
1036    #[serde(rename_all = "camelCase")]
1037    Account {
1038        /// Name of the account.
1039        account: String,
1040        /// The account password.
1041        #[serde(serialize_with = "serialize_secret_string")]
1042        password: SecretString,
1043        /// Optional URLs associated with the account.
1044        url: Vec<Url>,
1045        /// Custom user data.
1046        #[serde(default, skip_serializing_if = "UserData::is_default")]
1047        user_data: UserData,
1048    },
1049    /// Collection of credentials as key/value pairs.
1050    #[serde(rename_all = "camelCase")]
1051    List {
1052        /// The items in the list.
1053        #[serde(serialize_with = "serialize_secret_string_map")]
1054        items: HashMap<String, SecretString>,
1055        /// Custom user data.
1056        #[serde(default, skip_serializing_if = "UserData::is_default")]
1057        user_data: UserData,
1058    },
1059    /// PEM encoded binary data.
1060    #[serde(rename_all = "camelCase")]
1061    Pem {
1062        /// Collection of PEM encoded certificates or keys.
1063        certificates: Vec<Pem>,
1064        /// Custom user data.
1065        #[serde(default, skip_serializing_if = "UserData::is_default")]
1066        user_data: UserData,
1067    },
1068    /// A UTF-8 text document.
1069    #[serde(rename_all = "camelCase")]
1070    Page {
1071        /// Title of the page.
1072        title: String,
1073        /// Mime type for the text, default is `text/markdown`.
1074        mime: String,
1075        /// The binary data.
1076        #[serde(serialize_with = "serialize_secret_string")]
1077        document: SecretString,
1078        /// Custom user data.
1079        #[serde(default, skip_serializing_if = "UserData::is_default")]
1080        user_data: UserData,
1081    },
1082    /// Private signing key.
1083    #[serde(rename_all = "camelCase")]
1084    Signer {
1085        /// The private key.
1086        private_key: SecretSigner,
1087        /// Custom user data.
1088        #[serde(default, skip_serializing_if = "UserData::is_default")]
1089        user_data: UserData,
1090    },
1091    /// Contact for an organization or person.
1092    #[serde(rename_all = "camelCase")]
1093    Contact {
1094        /// The contact vCard.
1095        vcard: Box<Vcard>,
1096        /// Custom user data.
1097        #[serde(default, skip_serializing_if = "UserData::is_default")]
1098        user_data: UserData,
1099    },
1100    /// Two-factor authentication using a TOTP.
1101    #[serde(rename_all = "camelCase")]
1102    Totp {
1103        /// Time-based one-time passcode.
1104        totp: TOTP,
1105        /// Custom user data.
1106        #[serde(default, skip_serializing_if = "UserData::is_default")]
1107        user_data: UserData,
1108    },
1109    /// Credit or debit card.
1110    #[serde(rename_all = "camelCase")]
1111    Card {
1112        /// The card number.
1113        #[serde(serialize_with = "serialize_secret_string")]
1114        number: SecretString,
1115        /// The expiry data for the card.
1116        expiry: Option<UtcDateTime>,
1117        /// Card verification value.
1118        #[serde(serialize_with = "serialize_secret_string")]
1119        cvv: SecretString,
1120        /// Name that appears on the card.
1121        #[serde(serialize_with = "serialize_secret_option")]
1122        name: Option<SecretString>,
1123        /// ATM PIN.
1124        #[serde(serialize_with = "serialize_secret_option")]
1125        atm_pin: Option<SecretString>,
1126        /// Custom user data.
1127        #[serde(default, skip_serializing_if = "UserData::is_default")]
1128        user_data: UserData,
1129    },
1130    /// Bank account.
1131    #[serde(rename_all = "camelCase")]
1132    Bank {
1133        /// The account number.
1134        #[serde(serialize_with = "serialize_secret_string")]
1135        number: SecretString,
1136        /// The routing number (US) or sort code (UK).
1137        #[serde(serialize_with = "serialize_secret_string")]
1138        routing: SecretString,
1139        /// IBAN.
1140        #[serde(serialize_with = "serialize_secret_option")]
1141        iban: Option<SecretString>,
1142        /// SWIFT.
1143        #[serde(serialize_with = "serialize_secret_option")]
1144        swift: Option<SecretString>,
1145        /// BIC.
1146        #[serde(serialize_with = "serialize_secret_option")]
1147        bic: Option<SecretString>,
1148        /// Custom user data.
1149        #[serde(default, skip_serializing_if = "UserData::is_default")]
1150        user_data: UserData,
1151    },
1152    /// External link; intended to be used in embedded user fields.
1153    #[serde(rename_all = "camelCase")]
1154    Link {
1155        /// External link URL.
1156        #[serde(serialize_with = "serialize_secret_string")]
1157        url: SecretString,
1158        /// Optional label for the link.
1159        #[serde(
1160            default,
1161            serialize_with = "serialize_secret_option",
1162            skip_serializing_if = "Option::is_none"
1163        )]
1164        label: Option<SecretString>,
1165        /// Optional title for the link.
1166        #[serde(
1167            default,
1168            serialize_with = "serialize_secret_option",
1169            skip_serializing_if = "Option::is_none"
1170        )]
1171        title: Option<SecretString>,
1172        /// Custom user data.
1173        #[serde(default, skip_serializing_if = "UserData::is_default")]
1174        user_data: UserData,
1175    },
1176    /// Standalone password; intended to be used in embedded user fields.
1177    #[serde(rename_all = "camelCase")]
1178    Password {
1179        /// Password secret.
1180        #[serde(serialize_with = "serialize_secret_string")]
1181        password: SecretString,
1182        /// Optional name for the password.
1183        ///
1184        /// This could be a username, account name or other label
1185        /// the user wants to associate with this password.
1186        #[serde(
1187            default,
1188            serialize_with = "serialize_secret_option",
1189            skip_serializing_if = "Option::is_none"
1190        )]
1191        name: Option<SecretString>,
1192        /// Custom user data.
1193        #[serde(default, skip_serializing_if = "UserData::is_default")]
1194        user_data: UserData,
1195    },
1196    /// Identity secret for passports, driving licenses etc.
1197    #[serde(rename_all = "camelCase")]
1198    Identity {
1199        /// The kind of this identification.
1200        id_kind: IdentityKind,
1201        /// The number for the identifier.
1202        #[serde(serialize_with = "serialize_secret_string")]
1203        number: SecretString,
1204        /// Issue place.
1205        #[serde(default, skip_serializing_if = "Option::is_none")]
1206        issue_place: Option<String>,
1207        /// Issue date.
1208        #[serde(default, skip_serializing_if = "Option::is_none")]
1209        issue_date: Option<UtcDateTime>,
1210        /// Expiration date.
1211        #[serde(default, skip_serializing_if = "Option::is_none")]
1212        expiry_date: Option<UtcDateTime>,
1213        /// Custom user data.
1214        #[serde(default, skip_serializing_if = "UserData::is_default")]
1215        user_data: UserData,
1216    },
1217    /// AGE encryption standard.
1218    #[serde(rename_all = "camelCase")]
1219    Age {
1220        /// The version of AGE.
1221        version: AgeVersion,
1222        #[serde(serialize_with = "serialize_secret_string")]
1223        /// Secret key for the AGE identity.
1224        key: SecretString,
1225        /// Custom user data.
1226        #[serde(default, skip_serializing_if = "UserData::is_default")]
1227        user_data: UserData,
1228    },
1229}
1230
1231impl Clone for Secret {
1232    fn clone(&self) -> Self {
1233        match self {
1234            Secret::Note { text, user_data } => Secret::Note {
1235                text: SecretBox::new(text.expose_secret().to_owned().into()),
1236                user_data: user_data.clone(),
1237            },
1238            Secret::File { content, user_data } => Secret::File {
1239                content: content.clone(),
1240                user_data: user_data.clone(),
1241            },
1242            Secret::Account {
1243                account,
1244                url,
1245                password,
1246                user_data,
1247            } => Secret::Account {
1248                account: account.to_owned(),
1249                url: url.clone(),
1250                password: SecretBox::new(
1251                    password.expose_secret().to_owned().into(),
1252                ),
1253                user_data: user_data.clone(),
1254            },
1255            Secret::List { items, user_data } => {
1256                let copy = items
1257                    .iter()
1258                    .map(|(k, v)| {
1259                        (
1260                            k.to_owned(),
1261                            SecretBox::new(
1262                                v.expose_secret().to_owned().into(),
1263                            ),
1264                        )
1265                    })
1266                    .collect::<HashMap<_, _>>();
1267                Secret::List {
1268                    items: copy,
1269                    user_data: user_data.clone(),
1270                }
1271            }
1272            Secret::Pem {
1273                certificates,
1274                user_data,
1275            } => Secret::Pem {
1276                certificates: certificates.clone(),
1277                user_data: user_data.clone(),
1278            },
1279            Secret::Page {
1280                title,
1281                mime,
1282                document,
1283                user_data,
1284            } => Secret::Page {
1285                title: title.to_owned(),
1286                mime: mime.to_owned(),
1287                document: SecretBox::new(
1288                    document.expose_secret().to_owned().into(),
1289                ),
1290                user_data: user_data.clone(),
1291            },
1292            Secret::Signer {
1293                private_key,
1294                user_data,
1295            } => Secret::Signer {
1296                private_key: private_key.clone(),
1297                user_data: user_data.clone(),
1298            },
1299            Secret::Contact { vcard, user_data } => Secret::Contact {
1300                vcard: vcard.clone(),
1301                user_data: user_data.clone(),
1302            },
1303            Secret::Totp { totp, user_data } => Secret::Totp {
1304                totp: totp.clone(),
1305                user_data: user_data.clone(),
1306            },
1307            Secret::Card {
1308                number,
1309                expiry,
1310                cvv,
1311                name,
1312                atm_pin,
1313                user_data,
1314            } => Secret::Card {
1315                number: number.clone(),
1316                expiry: expiry.clone(),
1317                cvv: cvv.clone(),
1318                name: name.clone(),
1319                atm_pin: atm_pin.clone(),
1320                user_data: user_data.clone(),
1321            },
1322            Secret::Bank {
1323                number,
1324                routing,
1325                iban,
1326                swift,
1327                bic,
1328                user_data,
1329            } => Secret::Bank {
1330                number: number.clone(),
1331                routing: routing.clone(),
1332                iban: iban.clone(),
1333                swift: swift.clone(),
1334                bic: bic.clone(),
1335                user_data: user_data.clone(),
1336            },
1337            Secret::Link {
1338                url,
1339                label,
1340                title,
1341                user_data,
1342            } => Secret::Link {
1343                url: url.clone(),
1344                label: label.clone(),
1345                title: title.clone(),
1346                user_data: user_data.clone(),
1347            },
1348            Secret::Password {
1349                password,
1350                name,
1351                user_data,
1352            } => Secret::Password {
1353                password: password.clone(),
1354                name: name.clone(),
1355                user_data: user_data.clone(),
1356            },
1357            Secret::Identity {
1358                id_kind,
1359                number,
1360                issue_place,
1361                issue_date,
1362                expiry_date,
1363                user_data,
1364            } => Secret::Identity {
1365                id_kind: id_kind.clone(),
1366                number: number.expose_secret().to_owned().into(),
1367                issue_place: issue_place.clone(),
1368                issue_date: issue_date.clone(),
1369                expiry_date: expiry_date.clone(),
1370                user_data: user_data.clone(),
1371            },
1372            Secret::Age {
1373                version,
1374                key,
1375                user_data,
1376            } => Secret::Age {
1377                version: version.clone(),
1378                key: SecretBox::new(key.expose_secret().to_owned().into()),
1379                user_data: user_data.clone(),
1380            },
1381        }
1382    }
1383}
1384
1385impl fmt::Debug for Secret {
1386    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1387        match self {
1388            Secret::Note { .. } => f.debug_struct("Note").finish(),
1389            Secret::File { content, .. } => f
1390                .debug_struct("File")
1391                .field("name", &content.name())
1392                .field("mime", &content.mime())
1393                .finish(),
1394            Secret::Account { account, url, .. } => f
1395                .debug_struct("Account")
1396                .field("account", account)
1397                .field("url", url)
1398                .finish(),
1399            Secret::List { items, .. } => {
1400                let keys = items.keys().collect::<Vec<_>>();
1401                f.debug_struct("List").field("keys", &keys).finish()
1402            }
1403            Secret::Pem { certificates, .. } => f
1404                .debug_struct("Pem")
1405                .field("size", &certificates.len())
1406                .finish(),
1407            Secret::Page { title, mime, .. } => f
1408                .debug_struct("Page")
1409                .field("title", title)
1410                .field("mime", mime)
1411                .finish(),
1412            Secret::Identity { .. } => f.debug_struct("Identity").finish(),
1413            Secret::Signer { .. } => f.debug_struct("Signer").finish(),
1414            Secret::Contact { .. } => f.debug_struct("Contact").finish(),
1415            Secret::Totp { .. } => f.debug_struct("TOTP").finish(),
1416            Secret::Card { .. } => f.debug_struct("Card").finish(),
1417            Secret::Bank { .. } => f.debug_struct("Bank").finish(),
1418            Secret::Link { .. } => f.debug_struct("Link").finish(),
1419            Secret::Password { .. } => f.debug_struct("Password").finish(),
1420            Secret::Age { .. } => f.debug_struct("AGE").finish(),
1421        }
1422    }
1423}
1424
1425impl Secret {
1426    /// Try to redact a secret.
1427    ///
1428    /// When `preserve_length` is set fields that are redacted preserve the
1429    /// original byte length so UIs that show the field will leak the
1430    /// secret length. Otherwise the value of `default_length` will be used.
1431    ///
1432    /// Not all secret types may be redacted; unsupported types are:
1433    ///
1434    /// * File
1435    /// * Contact
1436    /// * Totp
1437    /// * Link
1438    /// * Age
1439    /// * Signer
1440    ///
1441    /// Attempting to redact a secret for these types will return `false`;
1442    /// this may change in the future.
1443    ///
1444    pub fn redact(
1445        &mut self,
1446        preserve_length: bool,
1447        default_length: usize,
1448    ) -> bool {
1449        let redact_string = |s: &mut SecretString| {
1450            let value = s.expose_secret();
1451            let len = if preserve_length {
1452                value.len()
1453            } else {
1454                default_length
1455            };
1456            let redacted = "0".repeat(len);
1457            *s = SecretString::new(redacted.into());
1458        };
1459
1460        let redact_buffer = |s: &mut SecretBox<Vec<u8>>| {
1461            let value = s.expose_secret();
1462            let len = if preserve_length {
1463                value.len()
1464            } else {
1465                default_length
1466            };
1467            let redacted = "0".repeat(len);
1468            *s = SecretBox::new(redacted.as_bytes().to_vec().into());
1469        };
1470
1471        match self {
1472            Secret::Account { password, .. } => {
1473                redact_string(password);
1474                true
1475            }
1476            Secret::Note { text, .. } => {
1477                redact_string(text);
1478                true
1479            }
1480            Secret::File { .. } => false,
1481            Secret::List { items, .. } => {
1482                for (_, value) in items {
1483                    redact_string(value);
1484                }
1485                true
1486            }
1487            Secret::Pem { certificates, .. } => {
1488                for cert in certificates {
1489                    let tag = cert.tag().to_owned();
1490                    let mut pem =
1491                        SecretBox::new(cert.contents().to_vec().into());
1492                    redact_buffer(&mut pem);
1493                    *cert = Pem::new(tag, pem.expose_secret().to_vec());
1494                }
1495                true
1496            }
1497            Secret::Page { document, .. } => {
1498                redact_string(document);
1499                true
1500            }
1501            Secret::Contact { .. } => false,
1502            Secret::Totp { .. } => false,
1503            Secret::Card { number, .. } => {
1504                redact_string(number);
1505                true
1506            }
1507            Secret::Bank { number, .. } => {
1508                redact_string(number);
1509                true
1510            }
1511            Secret::Link { .. } => false,
1512            Secret::Password { password, .. } => {
1513                redact_string(password);
1514                true
1515            }
1516            Secret::Identity { number, .. } => {
1517                redact_string(number);
1518                true
1519            }
1520            Secret::Age { .. } => false,
1521            Secret::Signer { .. } => false,
1522        }
1523    }
1524
1525    /// Value formatted to copy to the clipboard.
1526    pub fn copy_value_unsafe(&self) -> Option<String> {
1527        match self {
1528            Secret::Account { password, .. } => {
1529                Some(password.expose_secret().to_owned())
1530            }
1531            Secret::Note { text, .. } => {
1532                Some(text.expose_secret().to_owned())
1533            }
1534            Secret::File { content, .. } => Some(content.name().to_string()),
1535            Secret::List { items, .. } => {
1536                let mut s = String::new();
1537                for (name, value) in items {
1538                    s.push_str(name);
1539                    s.push('=');
1540                    s.push_str(value.expose_secret());
1541                    s.push('\n');
1542                }
1543                Some(s)
1544            }
1545            Secret::Pem { certificates, .. } => {
1546                let text: Vec<String> =
1547                    certificates.iter().map(|s| s.to_string()).collect::<_>();
1548                Some(text.join("\n"))
1549            }
1550            Secret::Page { document, .. } => {
1551                Some(document.expose_secret().to_owned())
1552            }
1553            Secret::Contact { vcard, .. } => Some(vcard.to_string()),
1554            Secret::Totp { totp, .. } => Some(totp.get_url()),
1555            Secret::Card { number, .. } => {
1556                Some(number.expose_secret().to_string())
1557            }
1558            // TODO: concatenate fields
1559            Secret::Bank { number, .. } => {
1560                Some(number.expose_secret().to_string())
1561            }
1562            Secret::Link { url, .. } => Some(url.expose_secret().to_string()),
1563            Secret::Password { password, .. } => {
1564                Some(password.expose_secret().to_string())
1565            }
1566            Secret::Identity { number, .. } => {
1567                Some(number.expose_secret().to_string())
1568            }
1569            Secret::Age { .. } => None,
1570            Secret::Signer { .. } => None,
1571        }
1572    }
1573
1574    /// Plain text unencrypted display for secrets
1575    /// that can be represented as UTF-8 text.
1576    ///
1577    /// Typically used to copy secrets to the
1578    /// clipboard.
1579    ///
1580    /// This method is preferred over implementing the
1581    /// `Display` trait to make it easier to audit where
1582    /// secrets may be exposed as plain text.
1583    ///
1584    /// For some secrets where it may be ambiguous which field
1585    /// to expose we keep it simple, for example, the `Card` variant
1586    /// exposes the card number. An exception is the `Bank` variant
1587    /// where we expose the bank number and routing number delimited
1588    /// by a newline.
1589    ///
1590    /// The `Signer`, `File` and `Age` secret variants are not supported.
1591    pub fn display_unsafe(&self) -> Option<String> {
1592        match self {
1593            Secret::Note { text, .. } => {
1594                Some(text.expose_secret().to_string())
1595            }
1596            Secret::Account { password, .. }
1597            | Secret::Password { password, .. } => {
1598                Some(password.expose_secret().to_string())
1599            }
1600            Secret::List { items, .. } => Some(Secret::encode_list(items)),
1601            Secret::Link { url, .. } => Some(url.expose_secret().to_string()),
1602            Secret::Pem { certificates, .. } => {
1603                Some(pem::encode_many(certificates))
1604            }
1605            Secret::Page { document, .. } => {
1606                Some(document.expose_secret().to_string())
1607            }
1608            Secret::Card { number, .. } | Secret::Identity { number, .. } => {
1609                Some(number.expose_secret().to_string())
1610            }
1611            Secret::Bank {
1612                number, routing, ..
1613            } => Some(format!(
1614                "{}\n{}",
1615                number.expose_secret(),
1616                routing.expose_secret()
1617            )),
1618            Secret::Contact { vcard, .. } => Some(vcard.to_string()),
1619            Secret::Totp { totp, .. } => Some(totp.get_url()),
1620            _ => None,
1621        }
1622    }
1623
1624    /// Decode key value pairs into a map.
1625    pub fn decode_list<S: AsRef<str>>(
1626        list: S,
1627    ) -> Result<HashMap<String, SecretString>> {
1628        let mut credentials = HashMap::new();
1629        for line in list.as_ref().lines() {
1630            let key_value = line.split_once('=');
1631            if let Some((key, value)) = key_value {
1632                credentials.insert(key.to_string(), value.to_string().into());
1633            } else {
1634                return Err(Error::InvalidKeyValue(line.to_string()));
1635            }
1636        }
1637        Ok(credentials)
1638    }
1639
1640    /// Collection of website URLs associated with
1641    /// this secret.
1642    ///
1643    /// Used by the search index to locate secrets by
1644    /// associated URL.
1645    pub fn websites(&self) -> Option<Vec<&Url>> {
1646        match self {
1647            Self::Account { url, .. } => Some(url.iter().collect()),
1648            _ => None,
1649        }
1650    }
1651
1652    /// Encode a map into key value pairs.
1653    pub fn encode_list(list: &HashMap<String, SecretString>) -> String {
1654        let mut output = String::new();
1655        for (k, v) in list {
1656            output.push_str(&format!("{}={}", k, v.expose_secret()));
1657            output.push('\n');
1658        }
1659        output
1660    }
1661
1662    /// Ensure all the bytes are ASCII digits.
1663    pub fn ensure_ascii_digits<B: AsRef<[u8]>>(bytes: B) -> Result<()> {
1664        for byte in bytes.as_ref() {
1665            if !byte.is_ascii_digit() {
1666                return Err(Error::NotDigit);
1667            }
1668        }
1669        Ok(())
1670    }
1671
1672    /// Get the type of this secret.
1673    pub fn kind(&self) -> SecretType {
1674        match self {
1675            Secret::Note { .. } => SecretType::Note,
1676            Secret::File { .. } => SecretType::File,
1677            Secret::Account { .. } => SecretType::Account,
1678            Secret::List { .. } => SecretType::List,
1679            Secret::Pem { .. } => SecretType::Pem,
1680            Secret::Page { .. } => SecretType::Page,
1681            Secret::Identity { .. } => SecretType::Identity,
1682            Secret::Signer { .. } => SecretType::Signer,
1683            Secret::Contact { .. } => SecretType::Contact,
1684            Secret::Totp { .. } => SecretType::Totp,
1685            Secret::Card { .. } => SecretType::Card,
1686            Secret::Bank { .. } => SecretType::Bank,
1687            Secret::Link { .. } => SecretType::Link,
1688            Secret::Password { .. } => SecretType::Password,
1689            Secret::Age { .. } => SecretType::Age,
1690        }
1691    }
1692
1693    /// Get the user data for this secret.
1694    pub fn user_data(&self) -> &UserData {
1695        match self {
1696            Secret::Note { user_data, .. } => user_data,
1697            Secret::File { user_data, .. } => user_data,
1698            Secret::Account { user_data, .. } => user_data,
1699            Secret::List { user_data, .. } => user_data,
1700            Secret::Pem { user_data, .. } => user_data,
1701            Secret::Page { user_data, .. } => user_data,
1702            Secret::Identity { user_data, .. } => user_data,
1703            Secret::Signer { user_data, .. } => user_data,
1704            Secret::Contact { user_data, .. } => user_data,
1705            Secret::Totp { user_data, .. } => user_data,
1706            Secret::Card { user_data, .. } => user_data,
1707            Secret::Bank { user_data, .. } => user_data,
1708            Secret::Link { user_data, .. } => user_data,
1709            Secret::Password { user_data, .. } => user_data,
1710            Secret::Age { user_data, .. } => user_data,
1711        }
1712    }
1713
1714    /// Get the mutable user data for this secret.
1715    pub fn user_data_mut(&mut self) -> &mut UserData {
1716        match self {
1717            Secret::Note { user_data, .. } => user_data,
1718            Secret::File { user_data, .. } => user_data,
1719            Secret::Account { user_data, .. } => user_data,
1720            Secret::List { user_data, .. } => user_data,
1721            Secret::Pem { user_data, .. } => user_data,
1722            Secret::Page { user_data, .. } => user_data,
1723            Secret::Identity { user_data, .. } => user_data,
1724            Secret::Signer { user_data, .. } => user_data,
1725            Secret::Contact { user_data, .. } => user_data,
1726            Secret::Totp { user_data, .. } => user_data,
1727            Secret::Card { user_data, .. } => user_data,
1728            Secret::Bank { user_data, .. } => user_data,
1729            Secret::Link { user_data, .. } => user_data,
1730            Secret::Password { user_data, .. } => user_data,
1731            Secret::Age { user_data, .. } => user_data,
1732        }
1733    }
1734
1735    /// Attach a custom field to this secret's user data.
1736    pub fn add_field(&mut self, field: SecretRow) {
1737        self.user_data_mut().fields_mut().push(field);
1738    }
1739
1740    /// Remove a custom field from this secret's user data.
1741    pub fn remove_field(&mut self, id: &SecretId) {
1742        self.user_data_mut()
1743            .fields_mut()
1744            .retain(|row| row.id() != id);
1745    }
1746
1747    /// Insert a custom field at an index.
1748    ///
1749    /// # Panics
1750    ///
1751    /// Panics if `index > len`.
1752    pub fn insert_field(&mut self, index: usize, field: SecretRow) {
1753        self.user_data_mut().fields_mut().insert(index, field);
1754    }
1755
1756    /// Find a custom field by reference.
1757    pub fn find_field_by_ref(
1758        &self,
1759        target: &SecretRef,
1760    ) -> Option<&SecretRow> {
1761        match target {
1762            SecretRef::Id(id) => self.find_field_by_id(id),
1763            SecretRef::Name(name) => self.find_field_by_name(name),
1764        }
1765    }
1766
1767    /// Find a custom field by identifier.
1768    pub fn find_field_by_id(&self, id: &SecretId) -> Option<&SecretRow> {
1769        self.user_data().fields().iter().find(|row| row.id() == id)
1770    }
1771
1772    /// Find a custom field by name.
1773    pub fn find_field_by_name(&self, label: &str) -> Option<&SecretRow> {
1774        self.user_data()
1775            .fields()
1776            .iter()
1777            .find(|row| row.meta().label() == label)
1778    }
1779
1780    /// Update a custom field secret.
1781    pub fn update_field(&mut self, field: SecretRow) -> Result<()> {
1782        let existing = self
1783            .user_data_mut()
1784            .fields_mut()
1785            .iter_mut()
1786            .find(|row| row.id() == field.id());
1787
1788        if let Some(existing) = existing {
1789            *existing = field;
1790            Ok(())
1791        } else {
1792            Err(Error::FieldNotFound(*field.id()))
1793        }
1794    }
1795}
1796
1797impl PartialEq for Secret {
1798    fn eq(&self, other: &Self) -> bool {
1799        match (self, other) {
1800            (
1801                Self::Note {
1802                    text: text_a,
1803                    user_data: user_data_a,
1804                },
1805                Self::Note {
1806                    text: text_b,
1807                    user_data: user_data_b,
1808                },
1809            ) => {
1810                text_a.expose_secret() == text_b.expose_secret()
1811                    && user_data_a == user_data_b
1812            }
1813            (
1814                Self::Account {
1815                    account: account_a,
1816                    url: url_a,
1817                    password: password_a,
1818                    user_data: user_data_a,
1819                },
1820                Self::Account {
1821                    account: account_b,
1822                    url: url_b,
1823                    password: password_b,
1824                    user_data: user_data_b,
1825                },
1826            ) => {
1827                account_a == account_b
1828                    && url_a == url_b
1829                    && password_a.expose_secret()
1830                        == password_b.expose_secret()
1831                    && user_data_a == user_data_b
1832            }
1833            (
1834                Self::File {
1835                    content: content_a,
1836                    user_data: user_data_a,
1837                },
1838                Self::File {
1839                    content: content_b,
1840                    user_data: user_data_b,
1841                },
1842            ) => content_a == content_b && user_data_a == user_data_b,
1843            (
1844                Self::List {
1845                    items: items_a,
1846                    user_data: user_data_a,
1847                },
1848                Self::List {
1849                    items: items_b,
1850                    user_data: user_data_b,
1851                },
1852            ) => {
1853                items_a.iter().zip(items_b.iter()).all(|(a, b)| {
1854                    a.0 == b.0 && a.1.expose_secret() == b.1.expose_secret()
1855                }) && user_data_a == user_data_b
1856            }
1857            (
1858                Self::Pem {
1859                    certificates: certificates_a,
1860                    user_data: user_data_a,
1861                },
1862                Self::Pem {
1863                    certificates: certificates_b,
1864                    user_data: user_data_b,
1865                },
1866            ) => {
1867                certificates_a.iter().zip(certificates_b.iter()).all(
1868                    |(a, b)| {
1869                        a.tag() == b.tag() && a.contents() == b.contents()
1870                    },
1871                ) && user_data_a == user_data_b
1872            }
1873            (
1874                Self::Page {
1875                    title: title_a,
1876                    mime: mime_a,
1877                    document: document_a,
1878                    user_data: user_data_a,
1879                },
1880                Self::Page {
1881                    title: title_b,
1882                    mime: mime_b,
1883                    document: document_b,
1884                    user_data: user_data_b,
1885                },
1886            ) => {
1887                title_a == title_b
1888                    && mime_a == mime_b
1889                    && document_a.expose_secret()
1890                        == document_b.expose_secret()
1891                    && user_data_a == user_data_b
1892            }
1893            (
1894                Self::Identity {
1895                    id_kind: id_kind_a,
1896                    number: number_a,
1897                    issue_place: issue_place_a,
1898                    issue_date: issue_date_a,
1899                    expiry_date: expiry_date_a,
1900                    user_data: user_data_a,
1901                },
1902                Self::Identity {
1903                    id_kind: id_kind_b,
1904                    number: number_b,
1905                    issue_place: issue_place_b,
1906                    issue_date: issue_date_b,
1907                    expiry_date: expiry_date_b,
1908                    user_data: user_data_b,
1909                },
1910            ) => {
1911                id_kind_a == id_kind_b
1912                    && number_a.expose_secret() == number_b.expose_secret()
1913                    && issue_place_a == issue_place_b
1914                    && issue_date_a == issue_date_b
1915                    && expiry_date_a == expiry_date_b
1916                    && user_data_a == user_data_b
1917            }
1918
1919            (
1920                Self::Signer {
1921                    private_key: private_key_a,
1922                    user_data: user_data_a,
1923                },
1924                Self::Signer {
1925                    private_key: private_key_b,
1926                    user_data: user_data_b,
1927                },
1928            ) => private_key_a == private_key_b && user_data_a == user_data_b,
1929            (
1930                Self::Contact {
1931                    vcard: vcard_a,
1932                    user_data: user_data_a,
1933                },
1934                Self::Contact {
1935                    vcard: vcard_b,
1936                    user_data: user_data_b,
1937                },
1938            ) => vcard_a == vcard_b && user_data_a == user_data_b,
1939            (
1940                Self::Totp {
1941                    totp: totp_a,
1942                    user_data: user_data_a,
1943                },
1944                Self::Totp {
1945                    totp: totp_b,
1946                    user_data: user_data_b,
1947                },
1948            ) => totp_a == totp_b && user_data_a == user_data_b,
1949            (
1950                Self::Card {
1951                    number: number_a,
1952                    expiry: expiry_a,
1953                    cvv: cvv_a,
1954                    name: name_a,
1955                    atm_pin: atm_pin_a,
1956                    user_data: user_data_a,
1957                },
1958                Self::Card {
1959                    number: number_b,
1960                    expiry: expiry_b,
1961                    cvv: cvv_b,
1962                    name: name_b,
1963                    atm_pin: atm_pin_b,
1964                    user_data: user_data_b,
1965                },
1966            ) => {
1967                number_a.expose_secret() == number_b.expose_secret()
1968                    && expiry_a == expiry_b
1969                    && cvv_a.expose_secret() == cvv_b.expose_secret()
1970                    && name_a.as_ref().map(|s| s.expose_secret())
1971                        == name_b.as_ref().map(|s| s.expose_secret())
1972                    && atm_pin_a.as_ref().map(|s| s.expose_secret())
1973                        == atm_pin_b.as_ref().map(|s| s.expose_secret())
1974                    && user_data_a == user_data_b
1975            }
1976
1977            (
1978                Self::Bank {
1979                    number: number_a,
1980                    routing: routing_a,
1981                    iban: iban_a,
1982                    swift: swift_a,
1983                    bic: bic_a,
1984                    user_data: user_data_a,
1985                },
1986                Self::Bank {
1987                    number: number_b,
1988                    routing: routing_b,
1989                    iban: iban_b,
1990                    swift: swift_b,
1991                    bic: bic_b,
1992                    user_data: user_data_b,
1993                },
1994            ) => {
1995                number_a.expose_secret() == number_b.expose_secret()
1996                    && routing_a.expose_secret() == routing_b.expose_secret()
1997                    && iban_a.as_ref().map(|s| s.expose_secret())
1998                        == iban_b.as_ref().map(|s| s.expose_secret())
1999                    && swift_a.as_ref().map(|s| s.expose_secret())
2000                        == swift_b.as_ref().map(|s| s.expose_secret())
2001                    && bic_a.as_ref().map(|s| s.expose_secret())
2002                        == bic_b.as_ref().map(|s| s.expose_secret())
2003                    && user_data_a == user_data_b
2004            }
2005
2006            (
2007                Self::Link {
2008                    url: url_a,
2009                    label: label_a,
2010                    title: title_a,
2011                    user_data: user_data_a,
2012                },
2013                Self::Link {
2014                    url: url_b,
2015                    label: label_b,
2016                    title: title_b,
2017                    user_data: user_data_b,
2018                },
2019            ) => {
2020                url_a.expose_secret() == url_b.expose_secret()
2021                    && label_a.as_ref().map(|s| s.expose_secret())
2022                        == label_b.as_ref().map(|s| s.expose_secret())
2023                    && title_a.as_ref().map(|s| s.expose_secret())
2024                        == title_b.as_ref().map(|s| s.expose_secret())
2025                    && user_data_a == user_data_b
2026            }
2027
2028            (
2029                Self::Password {
2030                    password: password_a,
2031                    name: name_a,
2032                    user_data: user_data_a,
2033                },
2034                Self::Password {
2035                    password: password_b,
2036                    name: name_b,
2037                    user_data: user_data_b,
2038                },
2039            ) => {
2040                password_a.expose_secret() == password_b.expose_secret()
2041                    && name_a.as_ref().map(|s| s.expose_secret())
2042                        == name_b.as_ref().map(|s| s.expose_secret())
2043                    && user_data_a == user_data_b
2044            }
2045
2046            (
2047                Self::Age {
2048                    version: version_a,
2049                    key: key_a,
2050                    user_data: user_data_a,
2051                },
2052                Self::Age {
2053                    version: version_b,
2054                    key: key_b,
2055                    user_data: user_data_b,
2056                },
2057            ) => {
2058                version_a == version_b
2059                    && key_a.expose_secret() == key_b.expose_secret()
2060                    && user_data_a == user_data_b
2061            }
2062
2063            _ => false,
2064        }
2065    }
2066}
2067impl Eq for Secret {}
2068
2069impl Default for Secret {
2070    fn default() -> Self {
2071        Self::Note {
2072            text: SecretBox::new(String::new().into()),
2073            user_data: Default::default(),
2074        }
2075    }
2076}
2077
2078/// Type identifiers for the secret enum variants.
2079///
2080/// Used internally for encoding / decoding.
2081mod kind {
2082    /// Account password type.
2083    pub const ACCOUNT: u8 = 1;
2084    /// Note UTF-8 text type.
2085    pub const NOTE: u8 = 2;
2086    /// List of credentials key / value pairs.
2087    pub const LIST: u8 = 3;
2088    /// Binary blob, may be file content.
2089    pub const FILE: u8 = 4;
2090    /// List of PEM encoded binary blobs.
2091    pub const PEM: u8 = 5;
2092    /// UTF-8 page that can be rendered to HTML.
2093    pub const PAGE: u8 = 6;
2094    /// Identity numbers.
2095    pub const IDENTIFICATION: u8 = 7;
2096    /// Private signing key.
2097    pub const SIGNER: u8 = 8;
2098    /// Contact vCard.
2099    pub const CONTACT: u8 = 9;
2100    /// Time-based one time passcode.
2101    pub const TOTP: u8 = 10;
2102    /// Credit or debit card.
2103    pub const CARD: u8 = 11;
2104    /// Bank account.
2105    pub const BANK: u8 = 12;
2106    /// External link.
2107    pub const LINK: u8 = 13;
2108    /// Standalone password.
2109    pub const PASSWORD: u8 = 14;
2110    /// [AGE](https://age-encryption.org/v1) identity.
2111    pub const AGE: u8 = 15;
2112}
2113
2114impl TryFrom<PathBuf> for Secret {
2115    type Error = Error;
2116    fn try_from(path: PathBuf) -> Result<Self> {
2117        Ok(Secret::File {
2118            content: FileContent::External {
2119                name: basename(&path),
2120                size: 0,
2121                checksum: [0; 32],
2122                mime: guess_mime(&path)?,
2123                path: Some(path),
2124            },
2125            user_data: Default::default(),
2126        })
2127    }
2128}
2129
2130impl From<SecretString> for Secret {
2131    fn from(password: SecretString) -> Self {
2132        Secret::Password {
2133            password,
2134            name: None,
2135            user_data: Default::default(),
2136        }
2137    }
2138}
2139
2140impl From<Url> for Secret {
2141    fn from(url: Url) -> Self {
2142        Secret::Link {
2143            url: url.to_string().into(),
2144            label: None,
2145            title: None,
2146            user_data: Default::default(),
2147        }
2148    }
2149}
2150
2151impl From<String> for Secret {
2152    fn from(text: String) -> Self {
2153        Secret::Note {
2154            text: text.into(),
2155            user_data: Default::default(),
2156        }
2157    }
2158}
2159
2160impl From<HashMap<String, SecretString>> for Secret {
2161    fn from(items: HashMap<String, SecretString>) -> Self {
2162        Secret::List {
2163            items,
2164            user_data: Default::default(),
2165        }
2166    }
2167}