1use 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 #[derive(Default, Serialize, Deserialize, Debug, Clone)]
34 #[serde(transparent)]
35 pub struct SecretFlags: u32 {
36 const VERIFY = 0b00000001;
40 }
41}
42
43impl SecretFlags {
44 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#[derive(Debug, Clone)]
111pub enum SecretRef {
112 Id(SecretId),
114 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#[typeshare::typeshare]
156#[derive(
157 Default, Clone, Debug, Copy, Serialize, Deserialize, Eq, PartialEq, Hash,
158)]
159#[serde(rename_all = "lowercase")]
160pub enum SecretType {
161 #[default]
163 Note,
164 File,
166 Account,
168 List,
170 Pem,
172 Page,
174 Signer,
176 Contact,
178 Totp,
180 Card,
182 Bank,
184 Link,
186 Password,
188 Identity,
190 Age,
192}
193
194impl SecretType {
195 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#[typeshare::typeshare]
298#[derive(Debug, Serialize, Deserialize, Default, Clone)]
299#[serde(rename_all = "camelCase")]
300pub struct SecretMeta {
301 pub(crate) kind: SecretType,
303 pub(crate) flags: SecretFlags,
305 #[serde(skip_serializing_if = "String::is_empty")]
307 pub(crate) label: String,
308 #[serde(skip_serializing_if = "HashSet::is_empty")]
310 pub(crate) tags: HashSet<String>,
311 pub(crate) favorite: bool,
313 #[serde(skip_serializing_if = "Option::is_none")]
319 pub(crate) urn: Option<Urn>,
320 #[serde(skip_serializing_if = "Option::is_none")]
326 pub(crate) owner_id: Option<String>,
327 pub(crate) date_created: UtcDateTime,
329 #[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 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 pub fn label(&self) -> &str {
397 &self.label
398 }
399
400 pub fn set_label(&mut self, label: String) {
402 self.label = label;
403 }
404
405 pub fn kind(&self) -> &SecretType {
407 &self.kind
408 }
409
410 pub fn date_created(&self) -> &UtcDateTime {
412 &self.date_created
413 }
414
415 pub fn set_date_created(&mut self, date_created: UtcDateTime) {
417 self.date_created = date_created;
418 }
419
420 pub fn touch(&mut self) {
422 self.last_updated = Default::default();
423 }
424
425 pub fn last_updated(&self) -> &UtcDateTime {
427 &self.last_updated
428 }
429
430 pub fn set_last_updated(&mut self, last_updated: UtcDateTime) {
432 self.last_updated = last_updated;
433 }
434
435 pub fn tags(&self) -> &HashSet<String> {
437 &self.tags
438 }
439
440 pub fn set_tags(&mut self, tags: HashSet<String>) {
442 self.tags = tags;
443 }
444
445 pub fn tags_mut(&mut self) -> &mut HashSet<String> {
447 &mut self.tags
448 }
449
450 pub fn urn(&self) -> Option<&Urn> {
452 self.urn.as_ref()
453 }
454
455 pub fn set_urn(&mut self, urn: Option<Urn>) {
457 self.urn = urn;
458 }
459
460 pub fn owner_id(&self) -> Option<&String> {
462 self.owner_id.as_ref()
463 }
464
465 pub fn set_owner_id(&mut self, owner_id: Option<String>) {
467 self.owner_id = owner_id;
468 }
469
470 pub fn favorite(&self) -> bool {
472 self.favorite
473 }
474
475 pub fn set_favorite(&mut self, favorite: bool) {
477 self.favorite = favorite;
478 }
479
480 pub fn flags(&self) -> &SecretFlags {
482 &self.flags
483 }
484
485 pub fn flags_mut(&mut self) -> &mut SecretFlags {
487 &mut self.flags
488 }
489}
490
491#[derive(Serialize, Deserialize)]
493pub enum SecretSigner {
494 #[serde(serialize_with = "serialize_secret_buffer")]
496 SinglePartyEcdsa(SecretBox<Vec<u8>>),
497 #[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 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 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#[typeshare::typeshare]
581#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
582pub struct SecretRow {
583 pub(crate) id: SecretId,
585 pub(crate) meta: SecretMeta,
587 pub(crate) secret: Secret,
589}
590
591impl SecretRow {
592 pub fn new(id: SecretId, meta: SecretMeta, secret: Secret) -> Self {
594 Self { id, meta, secret }
595 }
596
597 pub fn id(&self) -> &SecretId {
599 &self.id
600 }
601
602 pub fn meta(&self) -> &SecretMeta {
604 &self.meta
605 }
606
607 pub fn meta_mut(&mut self) -> &mut SecretMeta {
609 &mut self.meta
610 }
611
612 pub fn secret(&self) -> &Secret {
614 &self.secret
615 }
616
617 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#[typeshare::typeshare]
643#[derive(Default, Serialize, Deserialize, Clone, PartialEq, Eq)]
644#[serde(rename_all = "camelCase")]
645pub struct UserData {
646 #[serde(skip_serializing_if = "Vec::is_empty")]
648 pub(crate) fields: Vec<SecretRow>,
649 #[serde(skip_serializing_if = "Option::is_none")]
651 pub(crate) comment: Option<String>,
652 #[serde(skip_serializing_if = "Option::is_none")]
659 pub(crate) recovery_note: Option<String>,
660}
661
662impl UserData {
663 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 pub fn len(&self) -> usize {
674 self.fields.len()
675 }
676
677 pub fn is_empty(&self) -> bool {
679 self.len() == 0 && self.recovery_note.is_none()
680 }
681
682 pub fn is_default(&self) -> bool {
684 self.is_empty() && self.recovery_note.is_none()
685 }
686
687 pub fn fields(&self) -> &Vec<SecretRow> {
689 &self.fields
690 }
691
692 pub fn fields_mut(&mut self) -> &mut Vec<SecretRow> {
694 &mut self.fields
695 }
696
697 pub fn push(&mut self, field: SecretRow) {
699 self.fields.push(field);
700 }
701
702 pub fn comment(&self) -> Option<&str> {
704 self.comment.as_ref().map(|s| &s[..])
705 }
706
707 pub fn set_comment(&mut self, comment: Option<String>) {
709 self.comment = comment;
710 }
711
712 pub fn recovery_note(&self) -> Option<&str> {
714 self.recovery_note.as_ref().map(|s| &s[..])
715 }
716
717 pub fn set_recovery_note(&mut self, notes: Option<String>) {
719 self.recovery_note = notes;
720 }
721}
722
723#[typeshare::typeshare]
725#[derive(PartialEq, Eq, Clone, Serialize, Deserialize)]
726#[serde(rename_all = "camelCase")]
727pub enum IdentityKind {
728 PersonalIdNumber,
730 IdCard,
732 Passport,
734 DriverLicense,
736 SocialSecurity,
738 TaxNumber,
740 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#[derive(Default, Clone, Serialize, Deserialize, Eq, PartialEq)]
801pub enum AgeVersion {
802 #[default]
804 Version1,
805}
806
807#[derive(Serialize, Deserialize)]
809#[serde(untagged)]
810pub enum FileContent {
811 Embedded {
813 name: String,
815
816 mime: String,
820
821 #[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 #[serde(with = "hex::serde")]
839 checksum: [u8; 32],
840 },
841 External {
843 name: String,
845
846 mime: String,
850
851 #[serde(with = "hex::serde")]
861 checksum: [u8; 32],
862
863 size: u64,
865
866 #[serde(skip)]
868 path: Option<PathBuf>,
869 },
870}
871
872impl FileContent {
873 pub fn name(&self) -> &str {
875 match self {
876 Self::Embedded { name, .. } => name,
877 Self::External { name, .. } => name,
878 }
879 }
880
881 pub fn mime(&self) -> &str {
883 match self {
884 Self::Embedded { mime, .. } => mime,
885 Self::External { mime, .. } => mime,
886 }
887 }
888
889 pub fn checksum(&self) -> &[u8; 32] {
891 match self {
892 Self::Embedded { checksum, .. } => checksum,
893 Self::External { checksum, .. } => checksum,
894 }
895 }
896
897 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#[derive(Serialize, Deserialize)]
1013#[serde(untagged)]
1014pub enum Secret {
1015 #[serde(rename_all = "camelCase")]
1017 Note {
1018 #[serde(serialize_with = "serialize_secret_string")]
1020 text: SecretString,
1021 #[serde(default, skip_serializing_if = "UserData::is_default")]
1023 user_data: UserData,
1024 },
1025 #[serde(rename_all = "camelCase")]
1027 File {
1028 content: FileContent,
1030
1031 #[serde(default, skip_serializing_if = "UserData::is_default")]
1033 user_data: UserData,
1034 },
1035 #[serde(rename_all = "camelCase")]
1037 Account {
1038 account: String,
1040 #[serde(serialize_with = "serialize_secret_string")]
1042 password: SecretString,
1043 url: Vec<Url>,
1045 #[serde(default, skip_serializing_if = "UserData::is_default")]
1047 user_data: UserData,
1048 },
1049 #[serde(rename_all = "camelCase")]
1051 List {
1052 #[serde(serialize_with = "serialize_secret_string_map")]
1054 items: HashMap<String, SecretString>,
1055 #[serde(default, skip_serializing_if = "UserData::is_default")]
1057 user_data: UserData,
1058 },
1059 #[serde(rename_all = "camelCase")]
1061 Pem {
1062 certificates: Vec<Pem>,
1064 #[serde(default, skip_serializing_if = "UserData::is_default")]
1066 user_data: UserData,
1067 },
1068 #[serde(rename_all = "camelCase")]
1070 Page {
1071 title: String,
1073 mime: String,
1075 #[serde(serialize_with = "serialize_secret_string")]
1077 document: SecretString,
1078 #[serde(default, skip_serializing_if = "UserData::is_default")]
1080 user_data: UserData,
1081 },
1082 #[serde(rename_all = "camelCase")]
1084 Signer {
1085 private_key: SecretSigner,
1087 #[serde(default, skip_serializing_if = "UserData::is_default")]
1089 user_data: UserData,
1090 },
1091 #[serde(rename_all = "camelCase")]
1093 Contact {
1094 vcard: Box<Vcard>,
1096 #[serde(default, skip_serializing_if = "UserData::is_default")]
1098 user_data: UserData,
1099 },
1100 #[serde(rename_all = "camelCase")]
1102 Totp {
1103 totp: TOTP,
1105 #[serde(default, skip_serializing_if = "UserData::is_default")]
1107 user_data: UserData,
1108 },
1109 #[serde(rename_all = "camelCase")]
1111 Card {
1112 #[serde(serialize_with = "serialize_secret_string")]
1114 number: SecretString,
1115 expiry: Option<UtcDateTime>,
1117 #[serde(serialize_with = "serialize_secret_string")]
1119 cvv: SecretString,
1120 #[serde(serialize_with = "serialize_secret_option")]
1122 name: Option<SecretString>,
1123 #[serde(serialize_with = "serialize_secret_option")]
1125 atm_pin: Option<SecretString>,
1126 #[serde(default, skip_serializing_if = "UserData::is_default")]
1128 user_data: UserData,
1129 },
1130 #[serde(rename_all = "camelCase")]
1132 Bank {
1133 #[serde(serialize_with = "serialize_secret_string")]
1135 number: SecretString,
1136 #[serde(serialize_with = "serialize_secret_string")]
1138 routing: SecretString,
1139 #[serde(serialize_with = "serialize_secret_option")]
1141 iban: Option<SecretString>,
1142 #[serde(serialize_with = "serialize_secret_option")]
1144 swift: Option<SecretString>,
1145 #[serde(serialize_with = "serialize_secret_option")]
1147 bic: Option<SecretString>,
1148 #[serde(default, skip_serializing_if = "UserData::is_default")]
1150 user_data: UserData,
1151 },
1152 #[serde(rename_all = "camelCase")]
1154 Link {
1155 #[serde(serialize_with = "serialize_secret_string")]
1157 url: SecretString,
1158 #[serde(
1160 default,
1161 serialize_with = "serialize_secret_option",
1162 skip_serializing_if = "Option::is_none"
1163 )]
1164 label: Option<SecretString>,
1165 #[serde(
1167 default,
1168 serialize_with = "serialize_secret_option",
1169 skip_serializing_if = "Option::is_none"
1170 )]
1171 title: Option<SecretString>,
1172 #[serde(default, skip_serializing_if = "UserData::is_default")]
1174 user_data: UserData,
1175 },
1176 #[serde(rename_all = "camelCase")]
1178 Password {
1179 #[serde(serialize_with = "serialize_secret_string")]
1181 password: SecretString,
1182 #[serde(
1187 default,
1188 serialize_with = "serialize_secret_option",
1189 skip_serializing_if = "Option::is_none"
1190 )]
1191 name: Option<SecretString>,
1192 #[serde(default, skip_serializing_if = "UserData::is_default")]
1194 user_data: UserData,
1195 },
1196 #[serde(rename_all = "camelCase")]
1198 Identity {
1199 id_kind: IdentityKind,
1201 #[serde(serialize_with = "serialize_secret_string")]
1203 number: SecretString,
1204 #[serde(default, skip_serializing_if = "Option::is_none")]
1206 issue_place: Option<String>,
1207 #[serde(default, skip_serializing_if = "Option::is_none")]
1209 issue_date: Option<UtcDateTime>,
1210 #[serde(default, skip_serializing_if = "Option::is_none")]
1212 expiry_date: Option<UtcDateTime>,
1213 #[serde(default, skip_serializing_if = "UserData::is_default")]
1215 user_data: UserData,
1216 },
1217 #[serde(rename_all = "camelCase")]
1219 Age {
1220 version: AgeVersion,
1222 #[serde(serialize_with = "serialize_secret_string")]
1223 key: SecretString,
1225 #[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 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 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 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 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 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 pub fn websites(&self) -> Option<Vec<&Url>> {
1646 match self {
1647 Self::Account { url, .. } => Some(url.iter().collect()),
1648 _ => None,
1649 }
1650 }
1651
1652 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 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 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 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 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 pub fn add_field(&mut self, field: SecretRow) {
1737 self.user_data_mut().fields_mut().push(field);
1738 }
1739
1740 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 pub fn insert_field(&mut self, index: usize, field: SecretRow) {
1753 self.user_data_mut().fields_mut().insert(index, field);
1754 }
1755
1756 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 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 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 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
2078mod kind {
2082 pub const ACCOUNT: u8 = 1;
2084 pub const NOTE: u8 = 2;
2086 pub const LIST: u8 = 3;
2088 pub const FILE: u8 = 4;
2090 pub const PEM: u8 = 5;
2092 pub const PAGE: u8 = 6;
2094 pub const IDENTIFICATION: u8 = 7;
2096 pub const SIGNER: u8 = 8;
2098 pub const CONTACT: u8 = 9;
2100 pub const TOTP: u8 = 10;
2102 pub const CARD: u8 = 11;
2104 pub const BANK: u8 = 12;
2106 pub const LINK: u8 = 13;
2108 pub const PASSWORD: u8 = 14;
2110 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}