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}