Skip to main content

sequoia_openpgp/packet/
tag.rs

1use std::fmt;
2use std::cmp::Ordering;
3use std::hash::{Hash, Hasher};
4
5#[cfg(test)]
6use quickcheck::{Arbitrary, Gen};
7
8use crate::packet::Packet;
9
10/// The OpenPGP packet tags as defined in [Section 5 of RFC 9580].
11///
12///   [Section 5 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5
13///
14/// The values correspond to the serialized format.
15#[derive(Clone, Copy, Debug)]
16#[non_exhaustive]
17pub enum Tag {
18    /// Reserved Packet tag.
19    Reserved,
20    /// Public-Key Encrypted Session Key Packet.
21    PKESK,
22    /// Signature Packet.
23    Signature,
24    /// Symmetric-Key Encrypted Session Key Packet.
25    SKESK,
26    /// One-Pass Signature Packet.
27    OnePassSig,
28    /// Secret-Key Packet.
29    SecretKey,
30    /// Public-Key Packet.
31    PublicKey,
32    /// Secret-Subkey Packet.
33    SecretSubkey,
34    /// Compressed Data Packet.
35    CompressedData,
36    /// Symmetrically Encrypted Data Packet.
37    SED,
38    /// Marker Packet (Obsolete Literal Packet).
39    Marker,
40    /// Literal Data Packet.
41    Literal,
42    /// Trust Packet.
43    Trust,
44    /// User ID Packet.
45    UserID,
46    /// Public-Subkey Packet.
47    PublicSubkey,
48    /// User Attribute Packet.
49    UserAttribute,
50    /// Sym. Encrypted and Integrity Protected Data Packet.
51    SEIP,
52    /// Modification Detection Code Packet.
53    MDC,
54    /// Reserved ("AEAD Encrypted Data Packet").
55    AED,
56    /// Padding packet.
57    Padding,
58    /// Unassigned packets (as of RFC4880).
59    Unknown(u8),
60    /// Experimental packets.
61    Private(u8),
62}
63assert_send_and_sync!(Tag);
64
65impl Eq for Tag {}
66
67impl PartialEq for Tag {
68    fn eq(&self, other: &Tag) -> bool {
69        self.cmp(other) == Ordering::Equal
70    }
71}
72
73impl PartialOrd for Tag
74{
75    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
76        Some(self.cmp(other))
77    }
78}
79
80impl Ord for Tag
81{
82    fn cmp(&self, other: &Self) -> Ordering {
83        let a : u8 = (*self).into();
84        let b : u8 = (*other).into();
85        a.cmp(&b)
86    }
87}
88
89impl Hash for Tag {
90    fn hash<H: Hasher>(&self, state: &mut H) {
91        let t: u8 = (*self).into();
92        t.hash(state);
93    }
94}
95
96impl From<u8> for Tag {
97    fn from(u: u8) -> Self {
98        use crate::packet::Tag::*;
99
100        match u {
101            0 => Reserved,
102            1 => PKESK,
103            2 => Signature,
104            3 => SKESK,
105            4 => OnePassSig,
106            5 => SecretKey,
107            6 => PublicKey,
108            7 => SecretSubkey,
109            8 => CompressedData,
110            9 => SED,
111            10 => Marker,
112            11 => Literal,
113            12 => Trust,
114            13 => UserID,
115            14 => PublicSubkey,
116            17 => UserAttribute,
117            18 => SEIP,
118            19 => MDC,
119            20 => AED,
120            21 => Padding,
121            60..=63 => Private(u),
122            _ => Unknown(u),
123        }
124    }
125}
126
127impl From<Tag> for u8 {
128    fn from(t: Tag) -> u8 {
129        (&t).into()
130    }
131}
132
133impl From<&Tag> for u8 {
134    fn from(t: &Tag) -> u8 {
135        match t {
136            Tag::Reserved => 0,
137            Tag::PKESK => 1,
138            Tag::Signature => 2,
139            Tag::SKESK => 3,
140            Tag::OnePassSig => 4,
141            Tag::SecretKey => 5,
142            Tag::PublicKey => 6,
143            Tag::SecretSubkey => 7,
144            Tag::CompressedData => 8,
145            Tag::SED => 9,
146            Tag::Marker => 10,
147            Tag::Literal => 11,
148            Tag::Trust => 12,
149            Tag::UserID => 13,
150            Tag::PublicSubkey => 14,
151            Tag::UserAttribute => 17,
152            Tag::SEIP => 18,
153            Tag::MDC => 19,
154            Tag::AED => 20,
155            Tag::Padding => 21,
156            Tag::Private(x) => *x,
157            Tag::Unknown(x) => *x,
158        }
159    }
160}
161
162impl From<&Packet> for Tag {
163    fn from(p: &Packet) -> Tag {
164        p.tag()
165    }
166}
167
168impl From<Packet> for Tag {
169    fn from(p: Packet) -> Tag {
170        p.tag()
171    }
172}
173
174impl fmt::Display for Tag {
175    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
176        match *self {
177            Tag::Reserved =>
178                f.write_str("Reserved - a packet tag MUST NOT have this value"),
179            Tag::PKESK =>
180                f.write_str("Public-Key Encrypted Session Key Packet"),
181            Tag::Signature =>
182                f.write_str("Signature Packet"),
183            Tag::SKESK =>
184                f.write_str("Symmetric-Key Encrypted Session Key Packet"),
185            Tag::OnePassSig =>
186                f.write_str("One-Pass Signature Packet"),
187            Tag::SecretKey =>
188                f.write_str("Secret-Key Packet"),
189            Tag::PublicKey =>
190                f.write_str("Public-Key Packet"),
191            Tag::SecretSubkey =>
192                f.write_str("Secret-Subkey Packet"),
193            Tag::CompressedData =>
194                f.write_str("Compressed Data Packet"),
195            Tag::SED =>
196                f.write_str("Symmetrically Encrypted Data Packet"),
197            Tag::Marker =>
198                f.write_str("Marker Packet"),
199            Tag::Literal =>
200                f.write_str("Literal Data Packet"),
201            Tag::Trust =>
202                f.write_str("Trust Packet"),
203            Tag::UserID =>
204                f.write_str("User ID Packet"),
205            Tag::PublicSubkey =>
206                f.write_str("Public-Subkey Packet"),
207            Tag::UserAttribute =>
208                f.write_str("User Attribute Packet"),
209            Tag::SEIP =>
210                f.write_str("Sym. Encrypted and Integrity Protected Data Packet"),
211            Tag::MDC =>
212                f.write_str("Modification Detection Code Packet"),
213            Tag::AED =>
214                f.write_str("AEAD Encrypted Data Packet"),
215            Tag::Padding =>
216                f.write_str("Padding Packet"),
217            Tag::Private(u) =>
218                f.write_fmt(format_args!("Private/Experimental Packet {}", u)),
219            Tag::Unknown(u) =>
220                f.write_fmt(format_args!("Unknown Packet {}", u)),
221        }
222    }
223}
224
225const PACKET_TAG_VARIANTS: [Tag; 19] = [
226    Tag::PKESK,
227    Tag::Signature,
228    Tag::SKESK,
229    Tag::OnePassSig,
230    Tag::SecretKey,
231    Tag::PublicKey,
232    Tag::SecretSubkey,
233    Tag::CompressedData,
234    Tag::SED,
235    Tag::Marker,
236    Tag::Literal,
237    Tag::Trust,
238    Tag::UserID,
239    Tag::PublicSubkey,
240    Tag::UserAttribute,
241    Tag::SEIP,
242    Tag::MDC,
243    Tag::AED,
244    Tag::Padding,
245];
246
247#[cfg(test)]
248impl Arbitrary for Tag {
249    fn arbitrary(g: &mut Gen) -> Self {
250        loop {
251            match u8::arbitrary(g) {
252                n @ 0..=63 => break n.into(),
253                _ => (), // try again
254            }
255        }
256    }
257}
258
259impl Tag {
260    /// Returns whether the `Tag` denotes a critical packet.
261    ///
262    /// Upon encountering an unknown critical packet, implementations
263    /// MUST reject the whole packet sequence.  On the other hand,
264    /// unknown non-critical packets MUST be ignored.  See [Section
265    /// 4.3 of RFC 9580].
266    ///
267    /// [Section 4.3 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-4.3
268    pub fn is_critical(&self) -> bool {
269        match u8::from(self) {
270            0..=39 => true,
271            40..=63 => false,
272            // Should never happen, but let's map this to critical as
273            // a conservative choice.
274            64..=255 => true,
275        }
276    }
277
278    /// Returns whether the `Tag` can be at the start of a valid
279    /// message.
280    ///
281    /// [Certs] can start with `PublicKey`, [TSKs] with a `SecretKey`.
282    ///
283    ///   [Certs]: https://www.rfc-editor.org/rfc/rfc9580.html#section-10.1
284    ///   [TSKs]: https://www.rfc-editor.org/rfc/rfc9580.html#section-10.2
285    ///
286    /// [Messages] start with a `OnePassSig`, `Signature` (old style
287    /// non-one pass signatures), `PKESK`, `SKESK`, `CompressedData`,
288    /// or `Literal`.
289    ///
290    ///   [Messages]: https://www.rfc-editor.org/rfc/rfc9580.html#section-10.3
291    ///
292    /// Signatures can stand alone either as a [detached signature], a
293    /// third-party certification, or a revocation certificate.
294    ///
295    ///   [detached signature]: https://www.rfc-editor.org/rfc/rfc9580.html#section-10.3
296    pub fn valid_start_of_message(&self) -> bool {
297        // Cert
298        *self == Tag::PublicKey || *self == Tag::SecretKey
299            // Message.
300            || *self == Tag::PKESK || *self == Tag::SKESK
301            || *self == Tag::Literal || *self == Tag::CompressedData
302            // Signed message.
303            || *self == Tag::OnePassSig
304            // Standalone signature, old-style signature.
305            || *self == Tag::Signature
306    }
307
308    /// Returns an iterator over all valid variants.
309    ///
310    /// Returns an iterator over all known variants.  This does not
311    /// include the [`Tag::Reserved`], [`Tag::Private`], or
312    /// [`Tag::Unknown`] variants.
313    pub fn variants() -> impl Iterator<Item=Tag> {
314        PACKET_TAG_VARIANTS.iter().cloned()
315    }
316}
317
318#[cfg(test)]
319mod tests {
320    use super::*;
321
322    quickcheck! {
323        fn roundtrip(tag: Tag) -> bool {
324            let val: u8 = tag.into();
325            tag == Tag::from(val)
326        }
327    }
328
329    quickcheck! {
330        fn display(tag: Tag) -> bool {
331            let s = format!("{}", tag);
332            !s.is_empty()
333        }
334    }
335
336    quickcheck! {
337        fn unknown_private(tag: Tag) -> bool {
338            match tag {
339                Tag::Unknown(u) => u > 19 || u == 15 || u == 16,
340                Tag::Private(u) => (60..=63).contains(&u),
341                _ => true
342            }
343        }
344    }
345
346    #[test]
347    fn parse() {
348        for i in 0..u8::MAX {
349            let _ = Tag::from(i);
350        }
351    }
352
353    #[test]
354    fn tag_variants() {
355        use std::collections::HashSet;
356        use std::iter::FromIterator;
357
358        // PACKET_TAG_VARIANTS is a list.  Derive it in a different way
359        // to double-check that nothing is missing.
360        let derived_variants = (0..=u8::MAX)
361            .map(Tag::from)
362            .filter(|t| {
363                match t {
364                    Tag::Reserved => false,
365                    Tag::Private(_) => false,
366                    Tag::Unknown(_) => false,
367                    _ => true,
368                }
369            })
370            .collect::<HashSet<_>>();
371
372        let known_variants
373            = HashSet::from_iter(PACKET_TAG_VARIANTS.iter().cloned());
374
375        let missing = known_variants
376            .symmetric_difference(&derived_variants)
377            .collect::<Vec<_>>();
378
379        assert!(missing.is_empty(), "{:?}", missing);
380    }
381}