sequoia_openpgp/
keyid.rs

1use std::borrow::Borrow;
2use std::fmt;
3
4#[cfg(test)]
5use quickcheck::{Arbitrary, Gen};
6
7use crate::Error;
8use crate::Fingerprint;
9use crate::KeyHandle;
10use crate::Result;
11
12/// A short identifier for certificates and keys.
13///
14/// A `KeyID` identifies a public key.  It was used in [RFC 4880]: for
15/// example to reference the issuing key of a signature in its
16/// [`Issuer`] subpacket.  You should prefer [`Fingerprint`] over
17/// [`KeyID`] in data structures, interfaces, and wire formats, unless
18/// space is of the utmost concern.
19///
20/// Currently, Sequoia supports *version 6* fingerprints and Key IDs,
21/// and *version 4* fingerprints and Key IDs.  *Version 3*
22/// fingerprints and Key IDs were deprecated by [RFC 4880] in 2007.
23///
24/// *Version 6* and *version 4* [`KeyID`]s are a truncated version of
25/// the key's fingerprint, which in turn is hash of the public key
26/// packet.  As a general rule of thumb, you should prefer the
27/// fingerprint as it is possible to create keys with a colliding
28/// KeyID using a [birthday attack].
29///
30/// For more details about how a `KeyID` is generated, see [Section
31/// 5.5.4 of RFC 9580].
32///
33/// In previous versions of OpenPGP, the Key ID used to be called
34/// "long Key ID", as there even was a "short Key ID". At only 4 bytes
35/// length, short Key IDs vulnerable to preimage attacks. That is, an
36/// attacker can create a key with any given short Key ID in short
37/// amount of time.
38///
39/// See also [`Fingerprint`] and [`KeyHandle`].
40///
41///   [RFC 4880]: https://tools.ietf.org/html/rfc4880
42///   [Section 5.5.4 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.5.4
43///   [birthday attack]: https://nullprogram.com/blog/2019/07/22/
44///   [`Issuer`]: crate::packet::signature::subpacket::SubpacketValue::Issuer
45///   [`Fingerprint`]: crate::Fingerprint
46///   [`KeyHandle`]: crate::KeyHandle
47///
48/// # Examples
49///
50/// ```rust
51/// # fn main() -> sequoia_openpgp::Result<()> {
52/// # use sequoia_openpgp as openpgp;
53/// use openpgp::KeyID;
54///
55/// let id: KeyID = "0123 4567 89AB CDEF".parse()?;
56///
57/// assert_eq!("0123456789ABCDEF", id.to_hex());
58/// # Ok(()) }
59/// ```
60#[non_exhaustive]
61#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
62pub enum KeyID {
63    /// A long (8 bytes) key ID.
64    ///
65    /// For v4, this is the right-most 8 bytes of the v4 fingerprint.
66    /// For v6, this is the left-most 8 bytes of the v6 fingerprint.
67    Long([u8; 8]),
68
69    /// Used for holding invalid keyids encountered during parsing
70    /// e.g. wrong number of bytes.
71    Invalid(Box<[u8]>),
72}
73assert_send_and_sync!(KeyID);
74
75impl fmt::Display for KeyID {
76    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
77        write!(f, "{:X}", self)
78    }
79}
80
81impl fmt::Debug for KeyID {
82    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
83        f.debug_tuple("KeyID")
84            .field(&self.to_string())
85            .finish()
86    }
87}
88
89impl fmt::UpperHex for KeyID {
90    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
91        self.write_to_fmt(f, true)
92    }
93}
94
95impl fmt::LowerHex for KeyID {
96    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
97        self.write_to_fmt(f, false)
98    }
99}
100
101impl std::str::FromStr for KeyID {
102    type Err = anyhow::Error;
103
104    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
105        if s.chars().filter(|c| ! c.is_whitespace()).count() % 2 == 1 {
106            return Err(Error::InvalidArgument(
107                "Odd number of nibbles".into()).into());
108        }
109
110        let bytes = crate::fmt::hex::decode_pretty(s)?;
111
112        // A KeyID is exactly 8 bytes long.
113        if bytes.len() == 8 {
114            Ok(KeyID::from_bytes(&bytes[..]))
115        } else if bytes.len() == 4 {
116            Err(Error::ShortKeyID(s.to_string()).into())
117        } else {
118            // Maybe a fingerprint was given.  Try to parse it and
119            // convert it to a KeyID.
120            Ok(s.parse::<Fingerprint>()?.into())
121        }
122    }
123}
124
125impl From<KeyID> for Vec<u8> {
126    fn from(id: KeyID) -> Self {
127        let mut r = Vec::with_capacity(8);
128        match id {
129            KeyID::Long(ref b) => r.extend_from_slice(b),
130            KeyID::Invalid(ref b) => r.extend_from_slice(b),
131        }
132        r
133    }
134}
135
136impl From<u64> for KeyID {
137    fn from(id: u64) -> Self {
138        Self::new(id)
139    }
140}
141
142impl From<[u8; 8]> for KeyID {
143    fn from(id: [u8; 8]) -> Self {
144        KeyID::from_bytes(&id[..])
145    }
146}
147
148impl From<&Fingerprint> for KeyID {
149    fn from(fp: &Fingerprint) -> Self {
150        match fp {
151            Fingerprint::V4(fp) =>
152                KeyID::from_bytes(&fp[fp.len() - 8..]),
153            Fingerprint::V6(fp) =>
154                KeyID::from_bytes(&fp[..8]),
155            Fingerprint::Unknown { bytes, .. } => {
156                KeyID::Invalid(bytes.clone())
157            }
158        }
159    }
160}
161
162impl From<Fingerprint> for KeyID {
163    fn from(fp: Fingerprint) -> Self {
164        match fp {
165            Fingerprint::V4(fp) =>
166                KeyID::from_bytes(&fp[fp.len() - 8..]),
167            Fingerprint::V6(fp) =>
168                KeyID::from_bytes(&fp[..8]),
169            Fingerprint::Unknown { bytes, .. } => {
170                KeyID::Invalid(bytes)
171            }
172        }
173    }
174}
175
176impl KeyID {
177    /// Converts a `u64` to a `KeyID`.
178    ///
179    /// # Examples
180    ///
181    /// ```rust
182    /// # extern crate sequoia_openpgp as openpgp;
183    /// use openpgp::KeyID;
184    ///
185    /// let keyid = KeyID::new(0x0123456789ABCDEF);
186    /// ```
187    pub fn new(data: u64) -> KeyID {
188        let bytes = data.to_be_bytes();
189        Self::from_bytes(&bytes[..])
190    }
191
192    /// Converts the `KeyID` to a `u64` if possible.
193    ///
194    /// # Examples
195    ///
196    /// ```rust
197    /// # fn main() -> sequoia_openpgp::Result<()> {
198    /// # extern crate sequoia_openpgp as openpgp;
199    /// use openpgp::KeyID;
200    ///
201    /// let keyid = KeyID::new(0x0123456789ABCDEF);
202    ///
203    /// assert_eq!(keyid.as_u64()?, 0x0123456789ABCDEF);
204    /// # Ok(()) }
205    /// ```
206    pub fn as_u64(&self) -> Result<u64> {
207        match &self {
208            KeyID::Long(ref b) =>
209                Ok(u64::from_be_bytes(*b)),
210            KeyID::Invalid(_) =>
211                Err(Error::InvalidArgument("Invalid KeyID".into()).into()),
212        }
213    }
214
215    /// Creates a `KeyID` from a big endian byte slice.
216    ///
217    /// # Examples
218    ///
219    /// ```rust
220    /// # fn main() -> sequoia_openpgp::Result<()> {
221    /// # extern crate sequoia_openpgp as openpgp;
222    /// use openpgp::KeyID;
223    ///
224    /// let keyid: KeyID = "0123 4567 89AB CDEF".parse()?;
225    ///
226    /// let bytes = [0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF];
227    /// assert_eq!(KeyID::from_bytes(&bytes), keyid);
228    /// # Ok(()) }
229    /// ```
230    pub fn from_bytes(raw: &[u8]) -> KeyID {
231        if raw.len() == 8 {
232            let mut keyid : [u8; 8] = Default::default();
233            keyid.copy_from_slice(raw);
234            KeyID::Long(keyid)
235        } else {
236            KeyID::Invalid(raw.to_vec().into_boxed_slice())
237        }
238    }
239
240    /// Returns a reference to the raw `KeyID` as a byte slice in big
241    /// endian representation.
242    ///
243    /// # Examples
244    ///
245    /// ```rust
246    /// # use sequoia_openpgp as openpgp;
247    /// use openpgp::KeyID;
248    ///
249    /// # fn main() -> sequoia_openpgp::Result<()> {
250    /// let keyid: KeyID = "0123 4567 89AB CDEF".parse()?;
251    ///
252    /// assert_eq!(keyid.as_bytes(), [0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF]);
253    /// # Ok(()) }
254    /// ```
255    pub fn as_bytes(&self) -> &[u8] {
256        match self {
257            KeyID::Long(ref id) => id,
258            KeyID::Invalid(ref id) => id,
259        }
260    }
261
262    /// Creates a wildcard `KeyID`.
263    ///
264    /// Refer to [Section 5.1 of RFC 9580] for details.
265    ///
266    ///   [Section 5.1 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.1
267    ///
268    /// # Examples
269    ///
270    /// ```rust
271    /// # use sequoia_openpgp as openpgp;
272    /// use openpgp::KeyID;
273    ///
274    /// assert_eq!(KeyID::wildcard(), KeyID::new(0x0000000000000000));
275    /// ```
276    pub fn wildcard() -> Self {
277        Self::from_bytes(&[0u8; 8][..])
278    }
279
280    /// Returns `true` if this is the wildcard `KeyID`.
281    ///
282    /// Refer to [Section 5.1 of RFC 9580] for details.
283    ///
284    ///   [Section 5.1 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.1
285    ///
286    /// # Examples
287    ///
288    /// ```rust
289    /// # use sequoia_openpgp as openpgp;
290    /// use openpgp::KeyID;
291    ///
292    /// assert!(KeyID::new(0x0000000000000000).is_wildcard());
293    /// ```
294    pub fn is_wildcard(&self) -> bool {
295        self.as_bytes().iter().all(|b| *b == 0)
296    }
297
298    /// Converts this `KeyID` to its canonical hexadecimal
299    /// representation.
300    ///
301    /// This representation is always uppercase and without spaces and
302    /// is suitable for stable key identifiers.
303    ///
304    /// The output of this function is exactly the same as formatting
305    /// this object with the `:X` format specifier.
306    ///
307    /// ```rust
308    /// # fn main() -> sequoia_openpgp::Result<()> {
309    /// # use sequoia_openpgp as openpgp;
310    /// use openpgp::KeyID;
311    ///
312    /// let keyid: KeyID = "fb3751f1587daef1".parse()?;
313    ///
314    /// assert_eq!("FB3751F1587DAEF1", keyid.to_hex());
315    /// assert_eq!(format!("{:X}", keyid), keyid.to_hex());
316    /// # Ok(()) }
317    /// ```
318    pub fn to_hex(&self) -> String {
319        use std::fmt::Write;
320
321        let raw_len = self.as_bytes().len();
322        let mut output = String::with_capacity(
323            // Each byte results in two hex characters.
324            raw_len * 2);
325
326        // We write to String that never fails but the Write API
327        // returns Results.
328        write!(output, "{:X}", self).unwrap();
329
330        output
331    }
332
333    /// Converts this `KeyID` to its hexadecimal representation with
334    /// spaces.
335    ///
336    /// This representation is always uppercase and with spaces
337    /// grouping the hexadecimal digits into groups of four.  It is
338    /// suitable for manual comparison of Key IDs.
339    ///
340    /// Note: The spaces will hinder other kind of use cases.  For
341    /// example, it is harder to select the whole Key ID for copying,
342    /// and it has to be quoted when used as a command line argument.
343    /// Only use this form for displaying a Key ID with the intent of
344    /// manual comparisons.
345    ///
346    /// ```rust
347    /// # fn main() -> sequoia_openpgp::Result<()> {
348    /// # use sequoia_openpgp as openpgp;
349    /// let keyid: openpgp::KeyID = "fb3751f1587daef1".parse()?;
350    ///
351    /// assert_eq!("FB37 51F1 587D AEF1", keyid.to_spaced_hex());
352    /// # Ok(()) }
353    /// ```
354    pub fn to_spaced_hex(&self) -> String {
355        use std::fmt::Write;
356
357        let raw_len = self.as_bytes().len();
358        let mut output = String::with_capacity(
359            // Each byte results in two hex characters.
360            raw_len * 2
361            +
362            // Every 2 bytes of output, we insert a space.
363            raw_len / 2);
364
365        // We write to String that never fails but the Write API
366        // returns Results.
367        write!(output, "{:#X}", self).unwrap();
368
369        output
370    }
371
372    /// Parses the hexadecimal representation of an OpenPGP `KeyID`.
373    ///
374    /// This function is the reverse of `to_hex`. It also accepts
375    /// other variants of the `keyID` notation including lower-case
376    /// letters, spaces and optional leading `0x`.
377    ///
378    /// ```rust
379    /// # fn main() -> sequoia_openpgp::Result<()> {
380    /// # use sequoia_openpgp as openpgp;
381    /// use openpgp::KeyID;
382    ///
383    /// let keyid = KeyID::from_hex("0xfb3751f1587daef1")?;
384    ///
385    /// assert_eq!("FB3751F1587DAEF1", keyid.to_hex());
386    /// # Ok(()) }
387    /// ```
388    pub fn from_hex(s: &str) -> std::result::Result<Self, anyhow::Error> {
389        std::str::FromStr::from_str(s)
390    }
391
392    /// Common code for the above functions.
393    fn write_to_fmt(&self, f: &mut fmt::Formatter, upper_case: bool) -> fmt::Result {
394        use std::fmt::Write;
395
396        let a_letter = if upper_case { b'A' } else { b'a' };
397        let pretty = f.alternate();
398
399        let raw = match self {
400            KeyID::Long(ref fp) => &fp[..],
401            KeyID::Invalid(ref fp) => &fp[..],
402        };
403
404        // We currently only handle long Key IDs, which look like:
405        //
406        //   AACB 3243 6300 52D9
407        //
408        // Since we have no idea how to format an invalid Key ID, just
409        // format it like a V4 fingerprint and hope for the best.
410
411        for (i, b) in raw.iter().enumerate() {
412            if pretty && i > 0 && i % 2 == 0 {
413                f.write_char(' ')?;
414            }
415
416            let top = b >> 4;
417            let bottom = b & 0xFu8;
418
419            if top < 10u8 {
420                f.write_char((b'0' + top) as char)?;
421            } else {
422                f.write_char((a_letter + (top - 10u8)) as char)?;
423            }
424
425            if bottom < 10u8 {
426                f.write_char((b'0' + bottom) as char)?;
427            } else {
428                f.write_char((a_letter + (bottom - 10u8)) as char)?;
429            }
430        }
431
432        Ok(())
433    }
434    /// Returns whether `self` and `other` could be aliases of each
435    /// other.
436    ///
437    /// `KeyHandle`'s `PartialEq` implementation cannot assert that a
438    /// `Fingerprint` and a `KeyID` are equal, because distinct
439    /// fingerprints may have the same `KeyID`, and `PartialEq` must
440    /// be [transitive], i.e.,
441    ///
442    /// ```text
443    /// a == b and b == c implies a == c.
444    /// ```
445    ///
446    /// [transitive]: std::cmp::PartialEq
447    ///
448    /// That is, if `fpr1` and `fpr2` are distinct fingerprints with the
449    /// same key ID then:
450    ///
451    /// ```text
452    /// fpr1 == keyid and fpr2 == keyid, but fpr1 != fpr2.
453    /// ```
454    ///
455    /// This definition of equality makes searching for a given
456    /// `KeyHandle` using `PartialEq` awkward.  This function fills
457    /// that gap.  It answers the question: given a `KeyHandle` and a
458    /// `KeyID`, could they be aliases?  That is, it implements the
459    /// desired, non-transitive equality relation:
460    ///
461    /// ```
462    /// # fn main() -> sequoia_openpgp::Result<()> {
463    /// # use sequoia_openpgp as openpgp;
464    /// # use openpgp::Fingerprint;
465    /// # use openpgp::KeyID;
466    /// # use openpgp::KeyHandle;
467    /// #
468    /// # let fpr1: Fingerprint
469    /// #     = "8F17 7771 18A3 3DDA 9BA4  8E62 AACB 3243 6300 52D9"
470    /// #       .parse::<Fingerprint>()?;
471    /// #
472    /// # let fpr2: Fingerprint
473    /// #     = "0123 4567 8901 2345 6789  0123 AACB 3243 6300 52D9"
474    /// #       .parse::<Fingerprint>()?;
475    /// #
476    /// # let keyid: KeyID = "AACB 3243 6300 52D9".parse::<KeyID>()?;
477    /// #
478    /// // fpr1 and fpr2 are different fingerprints with the same KeyID.
479    /// assert_ne!(fpr1, fpr2);
480    /// assert_eq!(KeyID::from(&fpr1), KeyID::from(&fpr2));
481    /// assert!(keyid.aliases(KeyHandle::from(&fpr1)));
482    /// assert!(keyid.aliases(KeyHandle::from(&fpr2)));
483    /// # Ok(()) }
484    /// ```
485    pub fn aliases<H>(&self, other: H) -> bool
486        where H: Borrow<KeyHandle>
487    {
488        let other = other.borrow();
489
490        match (self, other) {
491            (k, KeyHandle::KeyID(o)) => {
492                k == o
493            },
494            (KeyID::Long(k), KeyHandle::Fingerprint(Fingerprint::V4(o))) => {
495                // Avoid a heap allocation by embedding our
496                // knowledge of how a v4 key ID is derived from a
497                // v4 fingerprint:
498                //
499                // A v4 key ID are the 8 right-most octets of a v4
500                // fingerprint.
501                &o[12..] == k
502            },
503
504            (KeyID::Long(k), KeyHandle::Fingerprint(Fingerprint::V6(f))) => {
505                // A v6 key ID are the 8 left-most octets of a v6
506                // fingerprint.
507                k == &f[..8]
508            },
509
510            (k, o) => {
511                k == &KeyID::from(o)
512            },
513        }
514    }
515}
516
517#[cfg(test)]
518impl Arbitrary for KeyID {
519    fn arbitrary(g: &mut Gen) -> Self {
520        KeyID::new(u64::arbitrary(g))
521    }
522}
523
524#[cfg(test)]
525mod test {
526    use super::*;
527    quickcheck! {
528        fn u64_roundtrip(id: u64) -> bool {
529            KeyID::new(id).as_u64().unwrap() == id
530        }
531    }
532
533    #[test]
534    fn from_hex() {
535        "FB3751F1587DAEF1".parse::<KeyID>().unwrap();
536        "39D100AB67D5BD8C04010205FB3751F1587DAEF1".parse::<KeyID>()
537            .unwrap();
538        "0xFB3751F1587DAEF1".parse::<KeyID>().unwrap();
539        "0x39D100AB67D5BD8C04010205FB3751F1587DAEF1".parse::<KeyID>()
540            .unwrap();
541        "FB37 51F1 587D AEF1".parse::<KeyID>().unwrap();
542        "39D1 00AB 67D5 BD8C 0401  0205 FB37 51F1 587D AEF1".parse::<KeyID>()
543            .unwrap();
544        "GB3751F1587DAEF1".parse::<KeyID>().unwrap_err();
545        "EFB3751F1587DAEF1".parse::<KeyID>().unwrap_err();
546        "%FB3751F1587DAEF1".parse::<KeyID>().unwrap_err();
547    }
548
549    #[test]
550    fn from_hex_short_keyid() {
551        for s in &[ "FB3751F1", "0xFB3751F1", "fb3751f1",  "0xfb3751f1" ] {
552            match s.parse::<KeyID>() {
553                Ok(_) => panic!("Failed to reject short Key ID."),
554                Err(err) => {
555                    let err = err.downcast_ref::<Error>().unwrap();
556                    assert!(matches!(err, Error::ShortKeyID(_)));
557                }
558            }
559        }
560    }
561
562    #[test]
563    fn hex_formatting() {
564        let keyid = "FB3751F1587DAEF1".parse::<KeyID>().unwrap();
565        assert_eq!(format!("{:X}", keyid), "FB3751F1587DAEF1");
566        assert_eq!(format!("{:x}", keyid), "fb3751f1587daef1");
567    }
568
569    #[test]
570    fn aliases() -> crate::Result<()> {
571        // fp1 and fp15 have the same key ID, but are different
572        // fingerprints.
573        let fp1 = "280C0AB0B94D1302CAAEB71DA299CDCD3884EBEA"
574            .parse::<Fingerprint>()?;
575        let fp15 = "1234567890ABCDEF12345678A299CDCD3884EBEA"
576            .parse::<Fingerprint>()?;
577        let fp2 = "F8D921C01EE93B65D4C6FEB7B456A7DB5E4274D0"
578            .parse::<Fingerprint>()?;
579
580        let keyid1 = KeyID::from(&fp1);
581        let keyid15 = KeyID::from(&fp15);
582        let keyid2 = KeyID::from(&fp2);
583
584        eprintln!("fp1: {:?}", fp1);
585        eprintln!("keyid1: {:?}", keyid1);
586        eprintln!("fp15: {:?}", fp15);
587        eprintln!("keyid15: {:?}", keyid15);
588        eprintln!("fp2: {:?}", fp2);
589        eprintln!("keyid2: {:?}", keyid2);
590
591        assert_ne!(fp1, fp15);
592        assert_eq!(keyid1, keyid15);
593
594        assert!(keyid1.aliases(KeyHandle::from(&fp1)));
595        assert!(keyid1.aliases(KeyHandle::from(&fp15)));
596        assert!(! keyid1.aliases(KeyHandle::from(&fp2)));
597
598        assert!(keyid15.aliases(KeyHandle::from(&fp1)));
599        assert!(keyid15.aliases(KeyHandle::from(&fp15)));
600        assert!(! keyid15.aliases(KeyHandle::from(&fp2)));
601
602        assert!(! keyid2.aliases(KeyHandle::from(&fp1)));
603        assert!(! keyid2.aliases(KeyHandle::from(&fp15)));
604        assert!(keyid2.aliases(KeyHandle::from(&fp2)));
605
606        Ok(())
607    }
608}