sequoia_openpgp/
keyhandle.rs

1use std::convert::TryFrom;
2use std::cmp::Ordering;
3use std::borrow::Borrow;
4
5use crate::{
6    Error,
7    Fingerprint,
8    KeyID,
9    Result,
10};
11
12/// Enum representing an identifier for certificates and keys.
13///
14/// A `KeyHandle` contains either a [`Fingerprint`] or a [`KeyID`].
15/// This is needed because signatures can reference their issuer
16/// either by `Fingerprint` or by `KeyID`.
17///
18/// Currently, Sequoia supports *version 6* fingerprints and Key IDs,
19/// and *version 4* fingerprints and Key IDs.  *Version 3*
20/// fingerprints and Key IDs were deprecated by [RFC 4880] in 2007.
21///
22/// Essentially, a fingerprint is a hash over the key's public key
23/// packet.  *Version 6* and *version 4* [`KeyID`]s are a truncated
24/// version of the key's fingerprint. For details, see [Section 5.5.4
25/// of RFC 9580].
26///
27/// Both fingerprint and Key ID are used to identify a key, e.g., the
28/// issuer of a signature.
29///
30///   [RFC 4880]: https://tools.ietf.org/html/rfc4880
31///   [Section 5.5.4 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.5.4
32///
33/// # A Note on Equality
34///
35/// Like other data types, two `KeyHandle`s are considered equal if
36/// their serialized forms are the same.  That is, if you compare a
37/// key handle that contains a `Fingerprint`, and a key handle that
38/// contains a `KeyID`, they will not be considered equal **even if
39/// the key ID aliases the fingerprint**.  If you want to check for
40/// aliasing, you should use [`KeyHandle::aliases`].
41///
42/// # Examples
43///
44/// ```rust
45/// # fn main() -> sequoia_openpgp::Result<()> {
46/// # use sequoia_openpgp as openpgp;
47/// use openpgp::KeyHandle;
48/// use openpgp::KeyID;
49/// use openpgp::Packet;
50/// use openpgp::parse::Parse;
51///
52/// let p = Packet::from_bytes(
53///     "-----BEGIN PGP SIGNATURE-----
54/// #
55/// #    wsBzBAABCgAdFiEEwD+mQRsDrhJXZGEYciO1ZnjgJSgFAlnclx8ACgkQciO1Znjg
56/// #    JShldAf+NBvUTVPnVPhYM4KihWOUlup8lbD6g1IduSM5rpsGvOVb+uKF6ik+GOBB
57/// #    RlMT4s183r3teFxiTkDx2pRhUz0MnOMPfbXovjF6Y93fKCOxCQWLBa0ukjNmE+ax
58/// #    gu9nZ3XXDGXZW22iGE52uVjPGSfuLfqvdMy5bKHn8xow/kepuGHZwy8yn7uFv7sl
59/// #    LnOBUz1FKA7iRl457XKPUhw5K7BnfRW/I2BRlnrwTDkjfXaJZC+bUTIJvm682Bvt
60/// #    ZNn8zc0JucyEkuL9WXYNuZg0znDE3T7D/6+tzfEdSf706unsXFXWHf83vL2eHCcw
61/// #    qhImm1lmcC+agFtWQ6/qD923LR9xmg==
62/// #    =htNu
63/// #    -----END PGP SIGNATURE-----" /* docstring trickery ahead:
64///      // ...
65///      -----END PGP SIGNATURE-----")?;
66/// #    */)?;
67/// if let Packet::Signature(sig) = p {
68///     let issuers = sig.get_issuers();
69///     assert_eq!(issuers.len(), 2);
70///     let kh: KeyHandle
71///         = "C03F A641 1B03 AE12 5764  6118 7223 B566 78E0 2528".parse()?;
72///     assert!(&issuers[0].aliases(&kh));
73///     assert!(&issuers[1].aliases(&kh));
74/// } else {
75///     unreachable!("It's a signature!");
76/// }
77/// # Ok(()) }
78/// ```
79#[derive(Debug, Clone)]
80pub enum KeyHandle {
81    /// A Fingerprint.
82    Fingerprint(Fingerprint),
83    /// A KeyID.
84    KeyID(KeyID),
85}
86assert_send_and_sync!(KeyHandle);
87
88impl std::fmt::Display for KeyHandle {
89    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
90        match self {
91            KeyHandle::Fingerprint(v) => v.fmt(f),
92            KeyHandle::KeyID(v) => v.fmt(f),
93        }
94    }
95}
96
97impl std::fmt::UpperHex for KeyHandle {
98    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
99        match &self {
100            KeyHandle::Fingerprint(ref fpr) => write!(f, "{:X}", fpr),
101            KeyHandle::KeyID(ref keyid) => write!(f, "{:X}", keyid),
102        }
103    }
104}
105
106impl std::fmt::LowerHex for KeyHandle {
107    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
108        match &self {
109            KeyHandle::Fingerprint(ref fpr) => write!(f, "{:x}", fpr),
110            KeyHandle::KeyID(ref keyid) => write!(f, "{:x}", keyid),
111        }
112    }
113}
114
115impl From<KeyID> for KeyHandle {
116    fn from(i: KeyID) -> Self {
117        KeyHandle::KeyID(i)
118    }
119}
120
121impl From<&KeyID> for KeyHandle {
122    fn from(i: &KeyID) -> Self {
123        KeyHandle::KeyID(i.clone())
124    }
125}
126
127impl From<KeyHandle> for KeyID {
128    fn from(i: KeyHandle) -> Self {
129        match i {
130            KeyHandle::Fingerprint(i) => i.into(),
131            KeyHandle::KeyID(i) => i,
132        }
133    }
134}
135
136impl From<Option<KeyHandle>> for KeyID {
137    fn from(i: Option<KeyHandle>) -> Self {
138        match i {
139            Some(KeyHandle::Fingerprint(i)) => i.into(),
140            Some(KeyHandle::KeyID(i)) => i,
141            None => KeyID::wildcard(),
142        }
143    }
144}
145
146impl From<&KeyHandle> for KeyID {
147    fn from(i: &KeyHandle) -> Self {
148        match i {
149            KeyHandle::Fingerprint(i) => i.clone().into(),
150            KeyHandle::KeyID(i) => i.clone(),
151        }
152    }
153}
154
155impl From<Fingerprint> for KeyHandle {
156    fn from(i: Fingerprint) -> Self {
157        KeyHandle::Fingerprint(i)
158    }
159}
160
161impl From<&Fingerprint> for KeyHandle {
162    fn from(i: &Fingerprint) -> Self {
163        KeyHandle::Fingerprint(i.clone())
164    }
165}
166
167impl TryFrom<KeyHandle> for Fingerprint {
168    type Error = anyhow::Error;
169    fn try_from(i: KeyHandle) -> Result<Self> {
170        match i {
171            KeyHandle::Fingerprint(i) => Ok(i),
172            KeyHandle::KeyID(i) => Err(Error::InvalidOperation(
173                format!("Cannot convert keyid {} to fingerprint", i)).into()),
174        }
175    }
176}
177
178impl TryFrom<&KeyHandle> for Fingerprint {
179    type Error = anyhow::Error;
180    fn try_from(i: &KeyHandle) -> Result<Self> {
181        match i {
182            KeyHandle::Fingerprint(i) => Ok(i.clone()),
183            KeyHandle::KeyID(i) => Err(Error::InvalidOperation(
184                format!("Cannot convert keyid {} to fingerprint", i)).into()),
185        }
186    }
187}
188
189impl PartialOrd for KeyHandle {
190    fn partial_cmp(&self, other: &KeyHandle) -> Option<Ordering> {
191        let a = self.as_bytes();
192        let b = other.as_bytes();
193        Some(a.cmp(b))
194    }
195}
196
197impl PartialEq for KeyHandle {
198    fn eq(&self, other: &Self) -> bool {
199        self.partial_cmp(other) == Some(Ordering::Equal)
200    }
201}
202
203impl std::str::FromStr for KeyHandle {
204    type Err = anyhow::Error;
205
206    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
207        let bytes = &crate::fmt::hex::decode_pretty(s)?[..];
208        match Fingerprint::from_bytes_intern(None, bytes)? {
209            fpr @ Fingerprint::Unknown { .. } => {
210                match KeyID::from_bytes(bytes) {
211                    // If it can't be parsed as either a Fingerprint or a
212                    // KeyID, return Fingerprint::Invalid.
213                    KeyID::Invalid(_) => Ok(fpr.into()),
214                    kid => Ok(kid.into()),
215                }
216            }
217            fpr => Ok(fpr.into()),
218        }
219    }
220}
221
222impl KeyHandle {
223    /// Returns the raw identifier as a byte slice.
224    pub fn as_bytes(&self) -> &[u8] {
225        match self {
226            KeyHandle::Fingerprint(i) => i.as_bytes(),
227            KeyHandle::KeyID(i) => i.as_bytes(),
228        }
229    }
230
231    /// Returns whether `self` and `other` could be aliases of each
232    /// other.
233    ///
234    /// `KeyHandle`'s `PartialEq` implementation cannot assert that a
235    /// `Fingerprint` and a `KeyID` are equal, because distinct
236    /// fingerprints may have the same `KeyID`, and `PartialEq` must
237    /// be [transitive], i.e.,
238    ///
239    /// ```text
240    /// a == b and b == c implies a == c.
241    /// ```
242    ///
243    /// [transitive]: std::cmp::PartialEq
244    ///
245    /// That is, if `fpr1` and `fpr2` are distinct fingerprints with the
246    /// same key ID then:
247    ///
248    /// ```text
249    /// fpr1 == keyid and fpr2 == keyid, but fpr1 != fpr2.
250    /// ```
251    ///
252    /// This definition of equality makes searching for a given
253    /// `KeyHandle` using `PartialEq` awkward.  This function fills
254    /// that gap.  It answers the question: given two `KeyHandles`,
255    /// could they be aliases?  That is, it implements the desired,
256    /// non-transitive equality relation:
257    ///
258    /// ```
259    /// # fn main() -> sequoia_openpgp::Result<()> {
260    /// # use sequoia_openpgp as openpgp;
261    /// # use openpgp::Fingerprint;
262    /// # use openpgp::KeyID;
263    /// # use openpgp::KeyHandle;
264    /// #
265    /// # let fpr1: KeyHandle
266    /// #     = "8F17 7771 18A3 3DDA 9BA4  8E62 AACB 3243 6300 52D9"
267    /// #       .parse::<Fingerprint>()?.into();
268    /// #
269    /// # let fpr2: KeyHandle
270    /// #     = "0123 4567 8901 2345 6789  0123 AACB 3243 6300 52D9"
271    /// #       .parse::<Fingerprint>()?.into();
272    /// #
273    /// # let keyid: KeyHandle = "AACB 3243 6300 52D9".parse::<KeyID>()?
274    /// #     .into();
275    /// #
276    /// // fpr1 and fpr2 are different fingerprints with the same KeyID.
277    /// assert_ne!(fpr1, fpr2);
278    /// assert_eq!(KeyID::from(&fpr1), KeyID::from(&fpr2));
279    ///
280    /// # let v6_fpr1 : KeyHandle
281    /// #     = "AACB3243630052D9AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
282    /// #       .parse::<Fingerprint>()?.into();
283    /// #
284    /// # let v6_fpr2 : KeyHandle
285    /// #     = "AACB3243630052D9BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
286    /// #       .parse::<Fingerprint>()?.into();
287    /// #
288    /// # let keyid : KeyHandle = "AACB 3243 6300 52D9".parse::<KeyID>()?
289    /// #     .into();
290    /// #
291    /// // fpr1 and fpr2 are different v4 fingerprints with the same KeyID.
292    /// assert!(! fpr1.eq(&fpr2));
293    /// assert!(fpr1.aliases(&keyid));
294    /// assert!(fpr2.aliases(&keyid));
295    /// assert!(! fpr1.aliases(&fpr2));
296    ///
297    /// // v6_fpr1 and v6_fpr2 are different v6 fingerprints with the same KeyID.
298    /// assert!(! v6_fpr1.eq(&v6_fpr2));
299    /// assert!(v6_fpr1.aliases(&keyid));
300    /// assert!(v6_fpr2.aliases(&keyid));
301    /// assert!(! v6_fpr1.aliases(&v6_fpr2));
302    ///
303    /// // And of course, v4 and v6 don't alias.
304    /// assert!(! fpr1.aliases(&v6_fpr1));
305    /// # Ok(()) }
306    /// ```
307    pub fn aliases<H>(&self, other: H) -> bool
308        where H: Borrow<KeyHandle>
309    {
310        match self {
311            KeyHandle::Fingerprint(fpr) => fpr.aliases(other),
312            KeyHandle::KeyID(keyid) => keyid.aliases(other),
313        }
314    }
315
316    /// Returns whether the KeyHandle is invalid.
317    ///
318    /// A KeyHandle is invalid if the `Fingerprint` or `KeyID` that it
319    /// contains is invalid.
320    ///
321    /// ```
322    /// use sequoia_openpgp as openpgp;
323    /// use openpgp::Fingerprint;
324    /// use openpgp::KeyID;
325    /// use openpgp::KeyHandle;
326    ///
327    /// # fn main() -> sequoia_openpgp::Result<()> {
328    /// // A perfectly valid fingerprint:
329    /// let kh : KeyHandle = "8F17 7771 18A3 3DDA 9BA4  8E62 AACB 3243 6300 52D9"
330    ///     .parse()?;
331    /// assert!(! kh.is_invalid());
332    ///
333    /// // But, V3 fingerprints are invalid.
334    /// let kh : KeyHandle = "9E 94 45 13 39 83 5F 70 7B E7 D8 ED C4 BE 5A A6"
335    ///     .parse()?;
336    /// assert!(kh.is_invalid());
337    ///
338    /// // A perfectly valid Key ID:
339    /// let kh : KeyHandle = "AACB 3243 6300 52D9"
340    ///     .parse()?;
341    /// assert!(! kh.is_invalid());
342    ///
343    /// // But, short Key IDs are invalid:
344    /// let kh : KeyHandle = "6300 52D9"
345    ///     .parse()?;
346    /// assert!(kh.is_invalid());
347    /// # Ok(()) }
348    /// ```
349    pub fn is_invalid(&self) -> bool {
350        matches!(self,
351                 KeyHandle::Fingerprint(Fingerprint::Unknown { .. })
352                 | KeyHandle::KeyID(KeyID::Invalid(_)))
353    }
354
355    /// Returns whether the KeyHandle contains a fingerprint.
356    ///
357    /// # Examples
358    ///
359    /// ```
360    /// # use sequoia_openpgp as openpgp;
361    /// # use openpgp::Fingerprint;
362    /// # use openpgp::KeyID;
363    /// # use openpgp::KeyHandle;
364    /// #
365    /// # fn main() -> sequoia_openpgp::Result<()> {
366    /// let fpr: KeyHandle = "8F17 7771 18A3 3DDA 9BA4  8E62 AACB 3243 6300 52D9"
367    ///     .parse()?;
368    /// let keyid: KeyHandle = KeyHandle::from(KeyID::from(&fpr));
369    ///
370    /// assert!(fpr.is_fingerprint());
371    /// assert!(! keyid.is_fingerprint());
372    /// # Ok(()) }
373    /// ```
374    pub fn is_fingerprint(&self) -> bool {
375        match self {
376            KeyHandle::Fingerprint(_) => true,
377            KeyHandle::KeyID(_) => false,
378        }
379    }
380
381    /// Returns whether the KeyHandle contains a key ID.
382    ///
383    /// # Examples
384    ///
385    /// ```
386    /// # use sequoia_openpgp as openpgp;
387    /// # use openpgp::Fingerprint;
388    /// # use openpgp::KeyID;
389    /// # use openpgp::KeyHandle;
390    /// #
391    /// # fn main() -> sequoia_openpgp::Result<()> {
392    /// let fpr: KeyHandle = "8F17 7771 18A3 3DDA 9BA4  8E62 AACB 3243 6300 52D9"
393    ///     .parse()?;
394    /// let keyid: KeyHandle = KeyHandle::from(KeyID::from(&fpr));
395    ///
396    /// assert!(! fpr.is_keyid());
397    /// assert!(keyid.is_keyid());
398    /// # Ok(()) }
399    /// ```
400    pub fn is_keyid(&self) -> bool {
401        match self {
402            KeyHandle::Fingerprint(_) => false,
403            KeyHandle::KeyID(_) => true,
404        }
405    }
406
407    /// Converts this `KeyHandle` to its canonical hexadecimal
408    /// representation.
409    ///
410    /// This representation is always uppercase and without spaces and
411    /// is suitable for stable key identifiers.
412    ///
413    /// The output of this function is exactly the same as formatting
414    /// this object with the `:X` format specifier.
415    ///
416    /// ```rust
417    /// # fn main() -> sequoia_openpgp::Result<()> {
418    /// # use sequoia_openpgp as openpgp;
419    /// use openpgp::KeyHandle;
420    ///
421    /// let h: KeyHandle =
422    ///     "0123 4567 89AB CDEF 0123 4567 89AB CDEF 0123 4567".parse()?;
423    ///
424    /// assert_eq!("0123456789ABCDEF0123456789ABCDEF01234567", h.to_hex());
425    /// assert_eq!(format!("{:X}", h), h.to_hex());
426    /// # Ok(()) }
427    /// ```
428    pub fn to_hex(&self) -> String {
429        format!("{:X}", self)
430    }
431
432    /// Converts this `KeyHandle` to its hexadecimal representation
433    /// with spaces.
434    ///
435    /// This representation is always uppercase and with spaces
436    /// grouping the hexadecimal digits into groups of four.  It is
437    /// only suitable for manual comparison of key handles.
438    ///
439    /// Note: The spaces will hinder other kind of use cases.  For
440    /// example, it is harder to select the whole key handle for
441    /// copying, and it has to be quoted when used as a command line
442    /// argument.  Only use this form for displaying a key handle with
443    /// the intent of manual comparisons.
444    ///
445    /// ```rust
446    /// # fn main() -> sequoia_openpgp::Result<()> {
447    /// # use sequoia_openpgp as openpgp;
448    /// use openpgp::KeyHandle;
449    ///
450    /// let h: KeyHandle =
451    ///     "0123 4567 89AB CDEF 0123 4567 89AB CDEF 0123 4567".parse()?;
452    ///
453    /// assert_eq!("0123 4567 89AB CDEF 0123  4567 89AB CDEF 0123 4567",
454    ///            h.to_spaced_hex());
455    /// # Ok(()) }
456    /// ```
457    pub fn to_spaced_hex(&self) -> String {
458        match self {
459            KeyHandle::Fingerprint(v) => v.to_spaced_hex(),
460            KeyHandle::KeyID(v) => v.to_spaced_hex(),
461        }
462    }
463}
464
465#[cfg(test)]
466mod tests {
467    use quickcheck::{Arbitrary, Gen};
468    use super::*;
469
470    impl Arbitrary for KeyHandle {
471        fn arbitrary(g: &mut Gen) -> Self {
472            if bool::arbitrary(g) {
473                Fingerprint::arbitrary(g).into()
474            } else {
475                KeyID::arbitrary(g).into()
476            }
477        }
478    }
479
480    #[test]
481    fn upper_hex_formatting() {
482        let handle = KeyHandle::Fingerprint(Fingerprint::V4([1, 2, 3, 4, 5, 6, 7,
483            8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]));
484        assert_eq!(format!("{:X}", handle), "0102030405060708090A0B0C0D0E0F1011121314");
485
486        let handle = KeyHandle::Fingerprint(Fingerprint::Unknown {
487            version: None,
488            bytes: Box::new([10, 2, 3, 4]),
489        });
490        assert_eq!(format!("{:X}", handle), "0A020304");
491
492        let handle = KeyHandle::KeyID(KeyID::Long([10, 2, 3, 4, 5, 6, 7, 8]));
493        assert_eq!(format!("{:X}", handle), "0A02030405060708");
494
495        let handle = KeyHandle::KeyID(KeyID::Invalid(Box::new([10, 2])));
496        assert_eq!(format!("{:X}", handle), "0A02");
497    }
498
499    #[test]
500    fn lower_hex_formatting() {
501        let handle = KeyHandle::Fingerprint(Fingerprint::V4([1, 2, 3, 4, 5, 6, 7,
502            8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]));
503        assert_eq!(format!("{:x}", handle), "0102030405060708090a0b0c0d0e0f1011121314");
504
505        let handle = KeyHandle::Fingerprint(Fingerprint::Unknown {
506            version: None,
507            bytes: Box::new([10, 2, 3, 4]),
508        });
509        assert_eq!(format!("{:x}", handle), "0a020304");
510
511        let handle = KeyHandle::KeyID(KeyID::Long([10, 2, 3, 4, 5, 6, 7, 8]));
512        assert_eq!(format!("{:x}", handle), "0a02030405060708");
513
514        let handle = KeyHandle::KeyID(KeyID::Invalid(Box::new([10, 2])));
515        assert_eq!(format!("{:x}", handle), "0a02");
516    }
517
518    #[test]
519    fn parse() -> Result<()> {
520        let handle: KeyHandle =
521            "0123 4567 89AB CDEF 0123 4567 89AB CDEF 0123 4567".parse()?;
522        assert_match!(&KeyHandle::Fingerprint(Fingerprint::V4(_)) = &handle);
523        assert_eq!(handle.as_bytes(),
524                   [0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23,
525                    0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67]);
526
527        let handle: KeyHandle = "89AB CDEF 0123 4567".parse()?;
528        assert_match!(&KeyHandle::KeyID(KeyID::Long(_)) = &handle);
529        assert_eq!(handle.as_bytes(),
530                   [0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67]);
531
532        // Invalid handles are parsed as invalid Fingerprints, not
533        // invalid KeyIDs.
534        let handle: KeyHandle = "4567 89AB CDEF 0123 4567".parse()?;
535        assert_match!(&KeyHandle::Fingerprint(Fingerprint::Unknown { .. }) = &handle);
536        assert_eq!(handle.as_bytes(),
537                   [0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67]);
538
539        let handle: Result<KeyHandle> = "INVALID CHARACTERS".parse();
540        assert!(handle.is_err());
541
542        Ok(())
543    }
544
545    quickcheck! {
546        fn partial_cmp_is_asymmetric(a: KeyHandle, b: KeyHandle)
547                                     -> bool {
548            use Ordering::*;
549            true
550                && (! (a.partial_cmp(&b) == Some(Less))
551                    || ! (a.partial_cmp(&b) == Some(Greater)))
552                && (! (a.partial_cmp(&b) == Some(Greater))
553                    || ! (a.partial_cmp(&b) == Some(Less)))
554        }
555    }
556
557    quickcheck! {
558        fn partial_cmp_is_transitive(a: KeyHandle, b: KeyHandle, c: KeyHandle)
559                                     -> bool {
560            use Ordering::*;
561            true
562                && (! (a.partial_cmp(&b) == Some(Less)
563                       && b.partial_cmp(&c) == Some(Less))
564                    || a.partial_cmp(&c) == Some(Less))
565                && (! (a.partial_cmp(&b) == Some(Equal)
566                       && b.partial_cmp(&c) == Some(Equal))
567                    || a.partial_cmp(&c) == Some(Equal))
568                && (! (a.partial_cmp(&b) == Some(Greater)
569                       && b.partial_cmp(&c) == Some(Greater))
570                    || a.partial_cmp(&c) == Some(Greater))
571
572        }
573    }
574}