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}