1use std::io::Read;
2
3use bitfield::bitfield;
4use bstr::{BStr, BString};
5use byteorder::{BigEndian, ByteOrder};
6use chrono::{DateTime, Duration, Utc};
7use iter_read::IterRead;
8use log::debug;
9use num_enum::{FromPrimitive, IntoPrimitive};
10use smallvec::{smallvec, SmallVec};
11
12use crate::crypto::aead::AeadAlgorithm;
13use crate::crypto::hash::HashAlgorithm;
14use crate::crypto::public_key::PublicKeyAlgorithm;
15use crate::crypto::sym::SymmetricKeyAlgorithm;
16use crate::errors::Result;
17use crate::line_writer::LineBreak;
18use crate::normalize_lines::Normalized;
19use crate::packet::signature::SignatureConfig;
20use crate::packet::{PacketTrait, SignatureVersionSpecific};
21use crate::ser::Serialize;
22use crate::types::{
23 self, CompressionAlgorithm, Fingerprint, KeyId, KeyVersion, PublicKeyTrait, SignatureBytes,
24 Tag, Version,
25};
26
27#[derive(Clone, PartialEq, Eq, derive_more::Debug)]
30pub struct Signature {
31 packet_version: Version,
32
33 pub config: SignatureConfig,
34 #[debug("{}", hex::encode(signed_hash_value))]
35 pub signed_hash_value: [u8; 2],
36 pub signature: SignatureBytes,
37}
38
39impl Signature {
40 #[allow(clippy::too_many_arguments)]
43 pub fn v2(
44 packet_version: Version,
45 typ: SignatureType,
46 pub_alg: PublicKeyAlgorithm,
47 hash_alg: HashAlgorithm,
48 created: DateTime<Utc>,
49 issuer: KeyId,
50 signed_hash_value: [u8; 2],
51 signature: SignatureBytes,
52 ) -> Self {
53 Signature {
54 packet_version,
55 config: SignatureConfig {
56 typ,
57 pub_alg,
58 hash_alg,
59 hashed_subpackets: vec![],
60 unhashed_subpackets: vec![],
61 version_specific: SignatureVersionSpecific::V2 { created, issuer },
62 },
63 signed_hash_value,
64 signature,
65 }
66 }
67
68 #[allow(clippy::too_many_arguments)]
71 pub fn v3(
72 packet_version: Version,
73 typ: SignatureType,
74 pub_alg: PublicKeyAlgorithm,
75 hash_alg: HashAlgorithm,
76 created: DateTime<Utc>,
77 issuer: KeyId,
78 signed_hash_value: [u8; 2],
79 signature: SignatureBytes,
80 ) -> Self {
81 Signature {
82 packet_version,
83 config: SignatureConfig {
84 typ,
85 pub_alg,
86 hash_alg,
87 hashed_subpackets: vec![],
88 unhashed_subpackets: vec![],
89 version_specific: SignatureVersionSpecific::V3 { created, issuer },
90 },
91 signed_hash_value,
92 signature,
93 }
94 }
95
96 #[allow(clippy::too_many_arguments)]
101 pub fn v4(
102 packet_version: Version,
103 typ: SignatureType,
104 pub_alg: PublicKeyAlgorithm,
105 hash_alg: HashAlgorithm,
106 signed_hash_value: [u8; 2],
107 signature: SignatureBytes,
108 hashed_subpackets: Vec<Subpacket>,
109 unhashed_subpackets: Vec<Subpacket>,
110 ) -> Self {
111 Signature {
112 packet_version,
113 config: SignatureConfig {
114 typ,
115 pub_alg,
116 hash_alg,
117 hashed_subpackets,
118 unhashed_subpackets,
119 version_specific: SignatureVersionSpecific::V4,
120 },
121 signed_hash_value,
122 signature,
123 }
124 }
125
126 #[allow(clippy::too_many_arguments)]
130 pub fn v6(
131 packet_version: Version,
132 typ: SignatureType,
133 pub_alg: PublicKeyAlgorithm,
134 hash_alg: HashAlgorithm,
135 signed_hash_value: [u8; 2],
136 signature: SignatureBytes,
137 hashed_subpackets: Vec<Subpacket>,
138 unhashed_subpackets: Vec<Subpacket>,
139 salt: Vec<u8>,
140 ) -> Self {
141 Signature {
142 packet_version,
143 config: SignatureConfig {
144 typ,
145 pub_alg,
146 hash_alg,
147 hashed_subpackets,
148 unhashed_subpackets,
149 version_specific: SignatureVersionSpecific::V6 { salt },
150 },
151 signed_hash_value,
152 signature,
153 }
154 }
155
156 pub fn from_config(
157 config: SignatureConfig,
158 signed_hash_value: [u8; 2],
159 signature: SignatureBytes,
160 ) -> Self {
161 Signature {
162 packet_version: Default::default(),
163 config,
164 signed_hash_value,
165 signature,
166 }
167 }
168
169 pub fn typ(&self) -> SignatureType {
171 self.config.typ()
172 }
173
174 pub fn hash_alg(&self) -> HashAlgorithm {
176 self.config.hash_alg
177 }
178
179 fn match_identity(sig: &Signature, key: &impl PublicKeyTrait) -> bool {
185 let issuers = sig.issuer();
186 let issuer_fps = sig.issuer_fingerprint();
187
188 if issuers.is_empty() && issuer_fps.is_empty() {
191 return true;
192 }
193
194 issuers.iter().any(|&key_id| key_id == &key.key_id())
196 || issuer_fps.iter().any(|&fp| fp == &key.fingerprint())
197 }
198
199 fn check_signature_key_version_alignment(
205 key: &impl PublicKeyTrait,
206 config: &SignatureConfig,
207 ) -> Result<()> {
208 if key.version() == KeyVersion::V6 {
210 ensure_eq!(
211 config.version(),
212 SignatureVersion::V6,
213 "Non v6 signature by a v6 key is not allowed"
214 );
215 }
216
217 if config.version() == SignatureVersion::V6 {
218 ensure_eq!(
219 key.version(),
220 KeyVersion::V6,
221 "v6 signature by a non-v6 key is not allowed"
222 );
223 }
224
225 Ok(())
226 }
227
228 pub fn verify<R>(&self, key: &impl PublicKeyTrait, data: R) -> Result<()>
230 where
231 R: Read,
232 {
233 Self::check_signature_key_version_alignment(&key, &self.config)?;
234
235 ensure!(
236 Self::match_identity(self, key),
237 "verify: No matching issuer or issuer_fingerprint for Key ID: {:?}",
238 &key.key_id(),
239 );
240
241 let mut hasher = self.config.hash_alg.new_hasher()?;
242
243 if let SignatureVersionSpecific::V6 { salt } = &self.config.version_specific {
244 ensure_eq!(
248 self.config.hash_alg.salt_len(),
249 Some(salt.len()),
250 "Illegal salt length {} for a V6 Signature using {:?}",
251 salt.len(),
252 self.config.hash_alg
253 );
254
255 hasher.update(salt.as_ref())
256 }
257
258 if matches!(self.typ(), SignatureType::Text) {
259 let normalized = Normalized::new(data.bytes().flat_map(|b| b.ok()), LineBreak::Crlf);
260
261 self.config
262 .hash_data_to_sign(&mut *hasher, IterRead::new(normalized))?;
263 } else {
264 self.config.hash_data_to_sign(&mut *hasher, data)?;
265 }
266 let len = self.config.hash_signature_data(&mut hasher)?;
267 hasher.update(&self.config.trailer(len)?);
268
269 let hash = &hasher.finish()[..];
270
271 ensure_eq!(
282 &self.signed_hash_value,
283 &hash[0..2],
284 "signature: invalid signed hash value"
285 );
286
287 key.verify_signature(self.config.hash_alg, hash, &self.signature)
288 }
289
290 pub fn verify_certification(
292 &self,
293 key: &impl PublicKeyTrait,
294 tag: Tag,
295 id: &impl Serialize,
296 ) -> Result<()> {
297 self.verify_third_party_certification(&key, &key, tag, id)
298 }
299
300 pub fn verify_third_party_certification(
302 &self,
303 signee: &impl PublicKeyTrait,
304 signer: &impl PublicKeyTrait,
305 tag: Tag,
306 id: &impl Serialize,
307 ) -> Result<()> {
308 let key_id = signee.key_id();
309 debug!("verifying certification {:?} {:#?}", key_id, self);
310
311 Self::check_signature_key_version_alignment(&signer, &self.config)?;
312
313 ensure!(
314 Self::match_identity(self, signer),
315 "verify_certification: No matching issuer or issuer_fingerprint for Key ID: {:?}",
316 key_id,
317 );
318
319 let mut hasher = self.config.hash_alg.new_hasher()?;
320
321 if let SignatureVersionSpecific::V6 { salt } = &self.config.version_specific {
322 hasher.update(salt.as_ref())
323 }
324
325 {
327 let mut key_buf = Vec::new();
328 signee.serialize_for_hashing(&mut key_buf)?;
330 hasher.update(&key_buf);
331 }
332
333 {
335 let mut packet_buf = Vec::new();
336 id.to_writer(&mut packet_buf)?;
337
338 match self.config.version() {
339 SignatureVersion::V2 | SignatureVersion::V3 => {
340 }
342 SignatureVersion::V4 | SignatureVersion::V6 => {
343 let prefix = match tag {
344 Tag::UserId => 0xB4,
345 Tag::UserAttribute => 0xD1,
346 _ => bail!("invalid tag for certification validation: {:?}", tag),
347 };
348
349 let mut prefix_buf = [prefix, 0u8, 0u8, 0u8, 0u8];
350 BigEndian::write_u32(&mut prefix_buf[1..], packet_buf.len().try_into()?);
351
352 hasher.update(&prefix_buf);
354 }
355 SignatureVersion::V5 => {
356 bail!("v5 signature unsupported tpc")
357 }
358 SignatureVersion::Other(version) => {
359 bail!("unsupported signature version: {:?}", version)
360 }
361 }
362
363 hasher.update(&packet_buf);
364 }
365
366 let len = self.config.hash_signature_data(&mut hasher)?;
367 hasher.update(&self.config.trailer(len)?);
368
369 let hash = &hasher.finish()[..];
370 ensure_eq!(
371 &self.signed_hash_value,
372 &hash[0..2],
373 "certification: invalid signed hash value"
374 );
375
376 signer.verify_signature(self.config.hash_alg, hash, &self.signature)
377 }
378
379 pub fn verify_key_binding(
383 &self,
384 signing_key: &impl PublicKeyTrait,
385 key: &impl PublicKeyTrait,
386 ) -> Result<()> {
387 self.verify_key_binding_internal(signing_key, key, false)
388 }
389
390 pub fn verify_backwards_key_binding(
394 &self,
395 signing_key: &impl PublicKeyTrait,
396 key: &impl PublicKeyTrait,
397 ) -> Result<()> {
398 self.verify_key_binding_internal(signing_key, key, true)
399 }
400
401 fn verify_key_binding_internal(
406 &self,
407 signer: &impl PublicKeyTrait,
408 signee: &impl PublicKeyTrait,
409 backsig: bool,
410 ) -> Result<()> {
411 debug!(
412 "verifying key binding: {:#?} - {:#?} - {:#?} (backsig: {})",
413 self, signer, signee, backsig
414 );
415
416 Self::check_signature_key_version_alignment(&signer, &self.config)?;
417
418 let mut hasher = self.config.hash_alg.new_hasher()?;
419
420 if let SignatureVersionSpecific::V6 { salt } = &self.config.version_specific {
421 hasher.update(salt.as_ref())
422 }
423
424 {
430 let mut key_buf = Vec::new();
431 if !backsig {
432 signer.serialize_for_hashing(&mut key_buf)?; } else {
434 signee.serialize_for_hashing(&mut key_buf)?; }
436
437 hasher.update(&key_buf);
438 }
439 {
441 let mut key_buf = Vec::new();
442 if !backsig {
443 signee.serialize_for_hashing(&mut key_buf)?; } else {
445 signer.serialize_for_hashing(&mut key_buf)?; }
447
448 hasher.update(&key_buf);
449 }
450
451 let len = self.config.hash_signature_data(&mut hasher)?;
452 hasher.update(&self.config.trailer(len)?);
453
454 let hash = &hasher.finish()[..];
455 ensure_eq!(
456 &self.signed_hash_value,
457 &hash[0..2],
458 "key binding: invalid signed hash value"
459 );
460
461 signer.verify_signature(self.config.hash_alg, hash, &self.signature)
462 }
463
464 pub fn verify_key(&self, key: &impl PublicKeyTrait) -> Result<()> {
466 debug!("verifying key (revocation): {:#?} - {:#?}", self, key);
467
468 Self::check_signature_key_version_alignment(&key, &self.config)?;
469
470 ensure!(
471 Self::match_identity(self, key),
472 "verify_key: No matching issuer or issuer_fingerprint for Key ID: {:?}",
473 &key.key_id(),
474 );
475
476 let mut hasher = self.config.hash_alg.new_hasher()?;
477
478 if let SignatureVersionSpecific::V6 { salt } = &self.config.version_specific {
479 hasher.update(salt.as_ref())
480 }
481
482 {
483 let mut key_buf = Vec::new();
484 key.serialize_for_hashing(&mut key_buf)?;
485
486 hasher.update(&key_buf);
487 }
488
489 let len = self.config.hash_signature_data(&mut hasher)?;
490 hasher.update(&self.config.trailer(len)?);
491
492 let hash = &hasher.finish()[..];
493 ensure_eq!(
494 &self.signed_hash_value,
495 &hash[0..2],
496 "key: invalid signed hash value"
497 );
498
499 key.verify_signature(self.config.hash_alg, hash, &self.signature)
500 }
501
502 pub fn is_certification(&self) -> bool {
504 self.config.is_certification()
505 }
506
507 pub fn key_expiration_time(&self) -> Option<&Duration> {
508 self.config.hashed_subpackets().find_map(|p| match &p.data {
509 SubpacketData::KeyExpirationTime(d) => Some(d),
510 _ => None,
511 })
512 }
513
514 pub fn signature_expiration_time(&self) -> Option<&Duration> {
515 self.config.hashed_subpackets().find_map(|p| match &p.data {
516 SubpacketData::SignatureExpirationTime(d) => Some(d),
517 _ => None,
518 })
519 }
520
521 pub fn created(&self) -> Option<&DateTime<Utc>> {
522 self.config.created()
523 }
524
525 pub fn issuer(&self) -> Vec<&KeyId> {
526 self.config.issuer()
527 }
528
529 pub fn issuer_fingerprint(&self) -> Vec<&Fingerprint> {
530 self.config.issuer_fingerprint()
531 }
532
533 pub fn preferred_symmetric_algs(&self) -> &[SymmetricKeyAlgorithm] {
534 self.config
535 .hashed_subpackets()
536 .find_map(|p| match &p.data {
537 SubpacketData::PreferredSymmetricAlgorithms(d) => Some(&d[..]),
538 _ => None,
539 })
540 .unwrap_or_else(|| &[][..])
541 }
542
543 pub fn preferred_aead_algs(&self) -> &[(SymmetricKeyAlgorithm, AeadAlgorithm)] {
544 self.config
545 .hashed_subpackets()
546 .find_map(|p| match &p.data {
547 SubpacketData::PreferredAeadAlgorithms(d) => Some(&d[..]),
548 _ => None,
549 })
550 .unwrap_or_else(|| &[][..])
551 }
552
553 pub fn preferred_hash_algs(&self) -> &[HashAlgorithm] {
554 self.config
555 .hashed_subpackets()
556 .find_map(|p| match &p.data {
557 SubpacketData::PreferredHashAlgorithms(d) => Some(&d[..]),
558 _ => None,
559 })
560 .unwrap_or_else(|| &[][..])
561 }
562
563 pub fn preferred_compression_algs(&self) -> &[CompressionAlgorithm] {
564 self.config
565 .hashed_subpackets()
566 .find_map(|p| match &p.data {
567 SubpacketData::PreferredCompressionAlgorithms(d) => Some(&d[..]),
568 _ => None,
569 })
570 .unwrap_or_else(|| &[][..])
571 }
572
573 pub fn key_server_prefs(&self) -> &[u8] {
574 self.config
575 .hashed_subpackets()
576 .find_map(|p| match &p.data {
577 SubpacketData::KeyServerPreferences(d) => Some(&d[..]),
578 _ => None,
579 })
580 .unwrap_or_else(|| &[][..])
581 }
582
583 pub fn key_flags(&self) -> KeyFlags {
584 self.config
585 .hashed_subpackets()
586 .find_map(|p| match &p.data {
587 SubpacketData::KeyFlags(d) => Some(d[..].into()),
588 _ => None,
589 })
590 .unwrap_or_default()
591 }
592
593 pub fn features(&self) -> &[u8] {
594 self.config
595 .hashed_subpackets()
596 .find_map(|p| match &p.data {
597 SubpacketData::Features(d) => Some(&d[..]),
598 _ => None,
599 })
600 .unwrap_or_else(|| &[][..])
601 }
602
603 pub fn revocation_reason_code(&self) -> Option<&RevocationCode> {
604 self.config.hashed_subpackets().find_map(|p| match &p.data {
605 SubpacketData::RevocationReason(code, _) => Some(code),
606 _ => None,
607 })
608 }
609
610 pub fn revocation_reason_string(&self) -> Option<&BStr> {
611 self.config.hashed_subpackets().find_map(|p| match &p.data {
612 SubpacketData::RevocationReason(_, reason) => Some(reason.as_ref()),
613 _ => None,
614 })
615 }
616
617 pub fn is_primary(&self) -> bool {
618 self.config
619 .hashed_subpackets()
620 .find_map(|p| match &p.data {
621 SubpacketData::IsPrimary(d) => Some(*d),
622 _ => None,
623 })
624 .unwrap_or(false)
625 }
626
627 pub fn is_revocable(&self) -> bool {
628 self.config
629 .hashed_subpackets()
630 .find_map(|p| match &p.data {
631 SubpacketData::Revocable(d) => Some(*d),
632 _ => None,
633 })
634 .unwrap_or(true)
635 }
636
637 pub fn embedded_signature(&self) -> Option<&Signature> {
638 self.config
643 .hashed_subpackets()
644 .chain(self.config.unhashed_subpackets())
645 .find_map(|p| match &p.data {
646 SubpacketData::EmbeddedSignature(d) => Some(&**d),
647 _ => None,
648 })
649 }
650
651 pub fn preferred_key_server(&self) -> Option<&str> {
652 self.config.hashed_subpackets().find_map(|p| match &p.data {
653 SubpacketData::PreferredKeyServer(d) => Some(d.as_str()),
654 _ => None,
655 })
656 }
657
658 pub fn notations(&self) -> Vec<&Notation> {
659 self.config
660 .hashed_subpackets()
661 .filter_map(|p| match &p.data {
662 SubpacketData::Notation(d) => Some(d),
663 _ => None,
664 })
665 .collect()
666 }
667
668 pub fn revocation_key(&self) -> Option<&types::RevocationKey> {
669 self.config.hashed_subpackets().find_map(|p| match &p.data {
670 SubpacketData::RevocationKey(d) => Some(d),
671 _ => None,
672 })
673 }
674
675 pub fn signers_userid(&self) -> Option<&BStr> {
681 self.config.hashed_subpackets().find_map(|p| match &p.data {
682 SubpacketData::SignersUserID(d) => Some(d.as_ref()),
683 _ => None,
684 })
685 }
686
687 pub fn policy_uri(&self) -> Option<&str> {
688 self.config.hashed_subpackets().find_map(|p| match &p.data {
689 SubpacketData::PolicyURI(d) => Some(d.as_ref()),
690 _ => None,
691 })
692 }
693
694 pub fn trust_signature(&self) -> Option<(u8, u8)> {
695 self.config.hashed_subpackets().find_map(|p| match &p.data {
696 SubpacketData::TrustSignature(depth, value) => Some((*depth, *value)),
697 _ => None,
698 })
699 }
700
701 pub fn regular_expression(&self) -> Option<&BStr> {
702 self.config.hashed_subpackets().find_map(|p| match &p.data {
703 SubpacketData::RegularExpression(d) => Some(d.as_ref()),
704 _ => None,
705 })
706 }
707
708 pub fn exportable_certification(&self) -> bool {
709 self.config
710 .hashed_subpackets()
711 .find_map(|p| match &p.data {
712 SubpacketData::ExportableCertification(d) => Some(*d),
713 _ => None,
714 })
715 .unwrap_or(true)
716 }
717}
718
719#[derive(Debug, PartialEq, Eq, Clone, Copy, FromPrimitive, IntoPrimitive)]
720#[repr(u8)]
721pub enum SignatureVersion {
722 V2 = 2,
724 V3 = 3,
725 V4 = 4,
726 V5 = 5,
727 V6 = 6,
728
729 #[num_enum(catch_all)]
730 Other(u8),
731}
732
733impl Default for SignatureVersion {
734 fn default() -> Self {
735 Self::V4
736 }
737}
738
739#[derive(Debug, PartialEq, Eq, Copy, Clone, FromPrimitive, IntoPrimitive)]
740#[repr(u8)]
741pub enum SignatureType {
742 Binary = 0x00,
745 Text = 0x01,
750 Standalone = 0x02,
755 CertGeneric = 0x10,
760 CertPersona = 0x11,
764 CertCasual = 0x12,
768 CertPositive = 0x13,
776 SubkeyBinding = 0x18,
785 KeyBinding = 0x19,
791 Key = 0x1F,
799 KeyRevocation = 0x20,
805 SubkeyRevocation = 0x28,
812 CertRevocation = 0x30,
821 Timestamp = 0x40,
825 ThirdParty = 0x50,
834
835 #[num_enum(catch_all)]
836 Other(u8),
837}
838
839#[derive(Debug, PartialEq, Eq, Copy, Clone)]
840pub enum SubpacketType {
842 SignatureCreationTime,
843 SignatureExpirationTime,
844 ExportableCertification,
845 TrustSignature,
846 RegularExpression,
847 Revocable,
848 KeyExpirationTime,
849 PreferredSymmetricAlgorithms,
850 RevocationKey,
851 Issuer,
852 Notation,
853 PreferredHashAlgorithms,
854 PreferredCompressionAlgorithms,
855 KeyServerPreferences,
856 PreferredKeyServer,
857 PrimaryUserId,
858 PolicyURI,
859 KeyFlags,
860 SignersUserID,
861 RevocationReason,
862 Features,
863 SignatureTarget,
864 EmbeddedSignature,
865 IssuerFingerprint,
866 PreferredEncryptionModes, IntendedRecipientFingerprint,
868 PreferredAead,
871 Experimental(u8),
872 Other(u8),
873}
874
875impl SubpacketType {
876 pub fn as_u8(&self, is_critical: bool) -> u8 {
877 let raw: u8 = match self {
878 SubpacketType::SignatureCreationTime => 2,
879 SubpacketType::SignatureExpirationTime => 3,
880 SubpacketType::ExportableCertification => 4,
881 SubpacketType::TrustSignature => 5,
882 SubpacketType::RegularExpression => 6,
883 SubpacketType::Revocable => 7,
884 SubpacketType::KeyExpirationTime => 9,
885 SubpacketType::PreferredSymmetricAlgorithms => 11,
886 SubpacketType::RevocationKey => 12,
887 SubpacketType::Issuer => 16,
888 SubpacketType::Notation => 20,
889 SubpacketType::PreferredHashAlgorithms => 21,
890 SubpacketType::PreferredCompressionAlgorithms => 22,
891 SubpacketType::KeyServerPreferences => 23,
892 SubpacketType::PreferredKeyServer => 24,
893 SubpacketType::PrimaryUserId => 25,
894 SubpacketType::PolicyURI => 26,
895 SubpacketType::KeyFlags => 27,
896 SubpacketType::SignersUserID => 28,
897 SubpacketType::RevocationReason => 29,
898 SubpacketType::Features => 30,
899 SubpacketType::SignatureTarget => 31,
900 SubpacketType::EmbeddedSignature => 32,
901 SubpacketType::IssuerFingerprint => 33,
902 SubpacketType::PreferredEncryptionModes => 34,
903 SubpacketType::IntendedRecipientFingerprint => 35,
904 SubpacketType::PreferredAead => 39,
907 SubpacketType::Experimental(n) => *n,
908 SubpacketType::Other(n) => *n,
909 };
910
911 if is_critical {
912 raw | 0b1000_0000
914 } else {
915 raw
916 }
917 }
918
919 #[inline]
920 pub fn from_u8(n: u8) -> (Self, bool) {
921 let is_critical = (n >> 7) == 1;
922 let n = n & 0b0111_1111;
924
925 let m = match n {
926 2 => SubpacketType::SignatureCreationTime,
927 3 => SubpacketType::SignatureExpirationTime,
928 4 => SubpacketType::ExportableCertification,
929 5 => SubpacketType::TrustSignature,
930 6 => SubpacketType::RegularExpression,
931 7 => SubpacketType::Revocable,
932 9 => SubpacketType::KeyExpirationTime,
933 11 => SubpacketType::PreferredSymmetricAlgorithms,
934 12 => SubpacketType::RevocationKey,
935 16 => SubpacketType::Issuer,
936 20 => SubpacketType::Notation,
937 21 => SubpacketType::PreferredHashAlgorithms,
938 22 => SubpacketType::PreferredCompressionAlgorithms,
939 23 => SubpacketType::KeyServerPreferences,
940 24 => SubpacketType::PreferredKeyServer,
941 25 => SubpacketType::PrimaryUserId,
942 26 => SubpacketType::PolicyURI,
943 27 => SubpacketType::KeyFlags,
944 28 => SubpacketType::SignersUserID,
945 29 => SubpacketType::RevocationReason,
946 30 => SubpacketType::Features,
947 31 => SubpacketType::SignatureTarget,
948 32 => SubpacketType::EmbeddedSignature,
949 33 => SubpacketType::IssuerFingerprint,
950 34 => SubpacketType::PreferredEncryptionModes,
951 35 => SubpacketType::IntendedRecipientFingerprint,
952 39 => SubpacketType::PreferredAead,
955 100..=110 => SubpacketType::Experimental(n),
956 _ => SubpacketType::Other(n),
957 };
958
959 (m, is_critical)
960 }
961}
962
963#[derive(Debug, PartialEq, Eq, Clone)]
964pub struct Subpacket {
965 pub is_critical: bool,
966 pub data: SubpacketData,
967}
968
969impl Subpacket {
970 pub const fn regular(data: SubpacketData) -> Self {
972 Subpacket {
973 is_critical: false,
974 data,
975 }
976 }
977
978 pub const fn critical(data: SubpacketData) -> Self {
980 Subpacket {
981 is_critical: true,
982 data,
983 }
984 }
985}
986
987#[derive(derive_more::Debug, PartialEq, Eq, Clone)]
988pub enum SubpacketData {
989 SignatureCreationTime(DateTime<Utc>),
991 SignatureExpirationTime(Duration),
993 KeyExpirationTime(Duration),
995 Issuer(KeyId),
997 PreferredSymmetricAlgorithms(SmallVec<[SymmetricKeyAlgorithm; 8]>),
1000 PreferredHashAlgorithms(SmallVec<[HashAlgorithm; 8]>),
1002 PreferredCompressionAlgorithms(SmallVec<[CompressionAlgorithm; 8]>),
1004 KeyServerPreferences(#[debug("{}", hex::encode(_0))] SmallVec<[u8; 4]>),
1005 KeyFlags(#[debug("{}", hex::encode(_0))] SmallVec<[u8; 1]>),
1006 Features(#[debug("{}", hex::encode(_0))] SmallVec<[u8; 1]>),
1007 RevocationReason(RevocationCode, BString),
1008 IsPrimary(bool),
1009 Revocable(bool),
1010 EmbeddedSignature(Box<Signature>),
1011 PreferredKeyServer(String),
1012 Notation(Notation),
1013 RevocationKey(types::RevocationKey),
1014 SignersUserID(BString),
1015 PolicyURI(String),
1017 TrustSignature(u8, u8),
1018 RegularExpression(BString),
1019 ExportableCertification(bool),
1020 IssuerFingerprint(Fingerprint),
1021 PreferredEncryptionModes(SmallVec<[AeadAlgorithm; 2]>),
1022 IntendedRecipientFingerprint(Fingerprint),
1023 PreferredAeadAlgorithms(SmallVec<[(SymmetricKeyAlgorithm, AeadAlgorithm); 4]>),
1024 Experimental(u8, #[debug("{}", hex::encode(_1))] SmallVec<[u8; 2]>),
1025 Other(u8, #[debug("{}", hex::encode(_1))] Vec<u8>),
1026 SignatureTarget(
1027 PublicKeyAlgorithm,
1028 HashAlgorithm,
1029 #[debug("{}", hex::encode(_2))] Vec<u8>,
1030 ),
1031}
1032
1033bitfield! {
1034 #[derive(Default, PartialEq, Eq, Copy, Clone)]
1035 pub struct KeyFlags(u8);
1036 impl Debug;
1037
1038 pub certify, set_certify: 0;
1039 pub sign, set_sign: 1;
1040 pub encrypt_comms, set_encrypt_comms: 2;
1041 pub encrypt_storage, set_encrypt_storage: 3;
1042 pub shared, set_shared: 4;
1043 pub authentication, set_authentication: 5;
1044 pub group, set_group: 7;
1045}
1046
1047impl<'a> From<&'a [u8]> for KeyFlags {
1048 fn from(other: &'a [u8]) -> Self {
1049 if other.is_empty() {
1050 Default::default()
1051 } else {
1052 KeyFlags(other[0])
1053 }
1054 }
1055}
1056
1057impl From<KeyFlags> for SmallVec<[u8; 1]> {
1058 fn from(flags: KeyFlags) -> Self {
1059 smallvec![flags.0]
1060 }
1061}
1062
1063#[derive(Debug, PartialEq, Eq, Clone)]
1064pub struct Notation {
1065 pub readable: bool,
1066 pub name: BString,
1067 pub value: BString,
1068}
1069
1070#[derive(Debug, PartialEq, Eq, Copy, Clone, FromPrimitive, IntoPrimitive)]
1072#[repr(u8)]
1073pub enum RevocationCode {
1074 NoReason = 0,
1076 KeySuperseded = 1,
1078 KeyCompromised = 2,
1080 KeyRetired = 3,
1082 CertUserIdInvalid = 32,
1084
1085 Private100 = 100,
1087 Private101 = 101,
1088 Private102 = 102,
1089 Private103 = 103,
1090 Private104 = 104,
1091 Private105 = 105,
1092 Private106 = 106,
1093 Private107 = 107,
1094 Private108 = 108,
1095 Private109 = 109,
1096 Private110 = 110,
1097
1098 #[num_enum(catch_all)]
1100 Other(u8),
1101}
1102
1103impl PacketTrait for Signature {
1104 fn packet_version(&self) -> Version {
1105 self.packet_version
1106 }
1107
1108 fn tag(&self) -> Tag {
1109 Tag::Signature
1110 }
1111}
1112
1113#[cfg(test)]
1114mod tests {
1115 use super::*;
1116
1117 #[test]
1118 fn test_keyflags() {
1119 let flags: KeyFlags = Default::default();
1120 assert_eq!(flags.0, 0x00);
1121
1122 let mut flags = KeyFlags::default();
1123 flags.set_certify(true);
1124 assert!(flags.certify());
1125 assert_eq!(flags.0, 0x01);
1126
1127 let mut flags = KeyFlags::default();
1128 flags.set_sign(true);
1129 assert_eq!(flags.0, 0x02);
1130
1131 let mut flags = KeyFlags::default();
1132 flags.set_encrypt_comms(true);
1133 assert_eq!(flags.0, 0x04);
1134
1135 let mut flags = KeyFlags::default();
1136 flags.set_encrypt_storage(true);
1137 assert_eq!(flags.0, 0x08);
1138
1139 let mut flags = KeyFlags::default();
1140 flags.set_shared(true);
1141 assert_eq!(flags.0, 0x10);
1142
1143 let mut flags = KeyFlags::default();
1144 flags.set_authentication(true);
1145 assert_eq!(flags.0, 0x20);
1146
1147 let mut flags = KeyFlags::default();
1148 flags.set_group(true);
1149 assert_eq!(flags.0, 0x80);
1150 }
1151
1152 #[test]
1153 fn test_critical() {
1154 use SubpacketType::*;
1155
1156 let cases = [
1157 SignatureCreationTime,
1158 SignatureExpirationTime,
1159 ExportableCertification,
1160 TrustSignature,
1161 RegularExpression,
1162 Revocable,
1163 KeyExpirationTime,
1164 PreferredSymmetricAlgorithms,
1165 RevocationKey,
1166 Issuer,
1167 Notation,
1168 PreferredHashAlgorithms,
1169 PreferredCompressionAlgorithms,
1170 KeyServerPreferences,
1171 PreferredKeyServer,
1172 PrimaryUserId,
1173 PolicyURI,
1174 KeyFlags,
1175 SignersUserID,
1176 RevocationReason,
1177 Features,
1178 SignatureTarget,
1179 EmbeddedSignature,
1180 IssuerFingerprint,
1181 PreferredAead,
1182 Experimental(101),
1183 Other(95),
1184 ];
1185 for case in cases {
1186 assert_eq!(SubpacketType::from_u8(case.as_u8(false)), (case, false));
1187 assert_eq!(SubpacketType::from_u8(case.as_u8(true)), (case, true));
1188 }
1189 }
1190}