diff options
author | Nora Widdecke <nora@sequoia-pgp.org> | 2020-04-06 16:42:57 +0200 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2020-11-30 15:56:37 +0100 |
commit | 98316651354ff2ead858833e1c0e855f67fb8234 (patch) | |
tree | be80d654a206cdde42b837f315fff6059d504f4d | |
parent | 714695daad6dfcfbc94c5071328c9846e4294641 (diff) |
openpgp: Improve documentation of Fingerprint, KeyID, and KeyHandle.
- Fixes #465.
-rw-r--r-- | openpgp/src/fingerprint.rs | 169 | ||||
-rw-r--r-- | openpgp/src/keyhandle.rs | 68 | ||||
-rw-r--r-- | openpgp/src/keyid.rs | 165 |
3 files changed, 334 insertions, 68 deletions
diff --git a/openpgp/src/fingerprint.rs b/openpgp/src/fingerprint.rs index 42a5c313..3af4a70e 100644 --- a/openpgp/src/fingerprint.rs +++ b/openpgp/src/fingerprint.rs @@ -5,21 +5,52 @@ use quickcheck::{Arbitrary, Gen}; /// A long identifier for certificates and keys. /// -/// A fingerprint uniquely identifies a public key. For more details -/// about how a fingerprint is generated, see [Section 12.2 of RFC -/// 4880]. +/// A `Fingerprint` uniquely identifies a public key. /// +/// Currently, sequoia supports *version 4* fingerprints and Key IDs +/// only. *Version 3* fingerprints and Key IDs were deprecated by +/// [RFC 4880] in 2007. +/// +/// Essentially, a *v4* fingerprint is a SHA-1 hash over the key's +/// public key packet. For details, see [Section 12.2 of RFC 4880]. +/// +/// Fingerprints are used, for example, to reference the issuing key +/// of a signature in its [`IssuerFingerprint`] subpacket. As a +/// general rule of thumb, you should prefer using fingerprints over +/// KeyIDs because the latter are vulnerable to [birthday attack]s. +/// +/// See also [`KeyID`] and [`KeyHandle`]. +/// +/// [RFC 4880]: https://tools.ietf.org/html/rfc4880 /// [Section 12.2 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-12.2 +/// [`KeyID`]: ./enum.KeyID.html +/// [`KeyHandle`]: ./enum.KeyHandle.html +/// [`IssuerFingerprint`]: ./packet/signature/subpacket/enum.SubpacketValue.html#variant.IssuerFingerprint +/// [birthday attack]: https://nullprogram.com/blog/2019/07/22/ /// /// Note: This enum cannot be exhaustively matched to allow future /// extensions. +/// +/// # Examples +/// +/// ```rust +/// # fn main() -> sequoia_openpgp::Result<()> { +/// # use sequoia_openpgp as openpgp; +/// use openpgp::Fingerprint; +/// +/// let fp: Fingerprint = +/// "0123 4567 89AB CDEF 0123 4567 89AB CDEF 0123 4567".parse()?; +/// +/// assert_eq!("0123456789ABCDEF0123456789ABCDEF01234567", fp.to_hex()); +/// # Ok(()) } +/// ``` #[non_exhaustive] #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Hash)] pub enum Fingerprint { - /// 20 byte SHA-1 hash. + /// A 20 byte SHA-1 hash of the public key packet as defined in the RFC. V4([u8;20]), - /// Used for holding fingerprints that we don't understand. For - /// instance, we don't grok v3 fingerprints. + /// Used for holding fingerprint data that is not a V4 fingerprint, e.g. a + /// V3 fingerprint (deprecated) or otherwise wrong-length data. Invalid(Box<[u8]>), } @@ -60,7 +91,25 @@ impl std::str::FromStr for Fingerprint { } impl Fingerprint { - /// Reads a binary fingerprint. + /// Creates a `Fingerprint` from a byte slice in big endian + /// representation. + /// + /// # Examples + /// + /// ```rust + /// # fn main() -> sequoia_openpgp::Result<()> { + /// # use sequoia_openpgp as openpgp; + /// use openpgp::Fingerprint; + /// + /// let fp: Fingerprint = + /// "0123 4567 89AB CDEF 0123 4567 89AB CDEF 0123 4567".parse()?; + /// let bytes = + /// [0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, + /// 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67]; + /// + /// assert_eq!(Fingerprint::from_bytes(&bytes), fp); + /// # Ok(()) } + /// ``` pub fn from_bytes(raw: &[u8]) -> Fingerprint { if raw.len() == 20 { let mut fp : [u8; 20] = Default::default(); @@ -71,7 +120,24 @@ impl Fingerprint { } } - /// Returns a reference to the raw Fingerprint. + /// Returns the raw fingerprint as a byte slice in big endian + /// representation. + /// + /// # Examples + /// + /// ```rust + /// # fn main() -> sequoia_openpgp::Result<()> { + /// # use sequoia_openpgp as openpgp; + /// use openpgp::Fingerprint; + /// + /// let fp: Fingerprint = + /// "0123 4567 89AB CDEF 0123 4567 89AB CDEF 0123 4567".parse()?; + /// + /// assert_eq!(fp.as_bytes(), + /// [0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, + /// 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67]); + /// # Ok(()) } + /// ``` pub fn as_bytes(&self) -> &[u8] { match self { &Fingerprint::V4(ref fp) => fp, @@ -79,44 +145,53 @@ impl Fingerprint { } } - /// Converts this fingerprint to its canonical hexadecimal representation. + /// Converts this fingerprint to its canonical hexadecimal + /// representation. /// - /// This representation is always uppercase and without spaces and is - /// suitable for stable key identifiers. + /// This representation is always uppercase and without spaces and + /// is suitable for stable key identifiers. /// - /// The output of this function is exactly the same as formatting this - /// object with the `:X` format specifier. + /// The output of this function is exactly the same as formatting + /// this object with the `:X` format specifier. /// /// ```rust + /// # fn main() -> sequoia_openpgp::Result<()> { /// # use sequoia_openpgp as openpgp; /// use openpgp::Fingerprint; /// - /// let fpr = "0123 4567 89AB CDEF 0123 4567 89AB CDEF 0123 4567".parse::<Fingerprint>().unwrap(); + /// let fp: Fingerprint = + /// "0123 4567 89AB CDEF 0123 4567 89AB CDEF 0123 4567".parse()?; /// - /// assert_eq!("0123456789ABCDEF0123456789ABCDEF01234567", fpr.to_hex()); - /// assert_eq!(format!("{:X}", fpr), fpr.to_hex()); + /// assert_eq!("0123456789ABCDEF0123456789ABCDEF01234567", fp.to_hex()); + /// assert_eq!(format!("{:X}", fp), fp.to_hex()); + /// # Ok(()) } /// ``` pub fn to_hex(&self) -> String { format!("{:X}", self) } - /// Parses the hexadecimal representation of an OpenPGP fingerprint. + /// Parses the hexadecimal representation of an OpenPGP + /// fingerprint. /// - /// This function is the reverse of `to_hex`. It also accepts other variants - /// of the fingerprint notation including lower-case letters, spaces and - /// optional leading `0x`. + /// This function is the reverse of `to_hex`. It also accepts + /// other variants of the fingerprint notation including + /// lower-case letters, spaces and optional leading `0x`. /// /// ```rust + /// # fn main() -> sequoia_openpgp::Result<()> { /// # use sequoia_openpgp as openpgp; /// use openpgp::Fingerprint; /// - /// let fpr = Fingerprint::from_hex("0123456789ABCDEF0123456789ABCDEF01234567").unwrap(); + /// let fp = + /// Fingerprint::from_hex("0123456789ABCDEF0123456789ABCDEF01234567")?; /// - /// assert_eq!("0123456789ABCDEF0123456789ABCDEF01234567", fpr.to_hex()); + /// assert_eq!("0123456789ABCDEF0123456789ABCDEF01234567", fp.to_hex()); /// - /// let fpr = Fingerprint::from_hex("0123 4567 89ab cdef 0123 4567 89ab cdef 0123 4567").unwrap(); + /// let fp = + /// Fingerprint::from_hex("0123 4567 89ab cdef 0123 4567 89ab cdef 0123 4567")?; /// - /// assert_eq!("0123456789ABCDEF0123456789ABCDEF01234567", fpr.to_hex()); + /// assert_eq!("0123456789ABCDEF0123456789ABCDEF01234567", fp.to_hex()); + /// # Ok(()) } /// ``` pub fn from_hex(s: &str) -> std::result::Result<Self, anyhow::Error> { std::str::FromStr::from_str(s) @@ -175,8 +250,32 @@ impl Fingerprint { String::from_utf8(output).unwrap() } - /// Converts the hex representation of the fingerprint to a phrase in the - /// ICAO alphabet. + /// Converts the hex representation of the `Fingerprint` to a + /// phrase in the [ICAO spelling alphabet]. + /// + /// [ICAO spelling alphabet]: https://en.wikipedia.org/wiki/ICAO_spelling_alphabet + /// + /// # Examples + /// + /// ```rust + /// # fn main() -> sequoia_openpgp::Result<()> { + /// # use sequoia_openpgp as openpgp; + /// use openpgp::Fingerprint; + /// + /// let fp: Fingerprint = + /// "01AB 4567 89AB CDEF 0123 4567 89AB CDEF 0123 4567".parse()?; + /// + /// assert!(fp.to_icao().starts_with("Zero One Alfa Bravo")); + /// + /// # let expected = "\ + /// # Zero One Alfa Bravo Four Five Six Seven Eight Niner Alfa Bravo \ + /// # Charlie Delta Echo Foxtrot Zero One Two Three Four Five Six Seven \ + /// # Eight Niner Alfa Bravo Charlie Delta Echo Foxtrot Zero One Two \ + /// # Three Four Five Six Seven"; + /// # assert_eq!(fp.to_icao(), expected); + /// # + /// # Ok(()) } + /// ``` pub fn to_icao(&self) -> String { let mut ret = String::default(); @@ -226,23 +325,11 @@ mod tests { use super::*; #[test] - fn icao() { - let fpr = "0123 4567 89AB CDEF 0123 4567 89AB CDEF 0123 4567" - .parse::<Fingerprint>().unwrap(); - let expected = "\ -Zero One Two Three Four Five Six Seven Eight Niner Alfa Bravo Charlie Delta \ -Echo Foxtrot Zero One Two Three Four Five Six Seven Eight Niner Alfa Bravo \ -Charlie Delta Echo Foxtrot Zero One Two Three Four Five Six Seven"; - - assert_eq!(fpr.to_icao(), expected); - } - - #[test] fn hex_formatting() { - let fpr = "0123 4567 89AB CDEF 0123 4567 89AB CDEF 0123 4567" + let fp = "0123 4567 89AB CDEF 0123 4567 89AB CDEF 0123 4567" .parse::<Fingerprint>().unwrap(); - assert_eq!(format!("{:X}", fpr), "0123456789ABCDEF0123456789ABCDEF01234567"); - assert_eq!(format!("{:x}", fpr), "0123456789abcdef0123456789abcdef01234567"); + assert_eq!(format!("{:X}", fp), "0123456789ABCDEF0123456789ABCDEF01234567"); + assert_eq!(format!("{:x}", fp), "0123456789abcdef0123456789abcdef01234567"); } #[test] diff --git a/openpgp/src/keyhandle.rs b/openpgp/src/keyhandle.rs index d3cd18a1..cfa78f3a 100644 --- a/openpgp/src/keyhandle.rs +++ b/openpgp/src/keyhandle.rs @@ -10,9 +10,68 @@ use crate::{ Result, }; -/// Identifies certificates and keys. +/// Enum representing an identifier for certificates and keys. /// -/// A `KeyHandle` is either a `Fingerprint` or a `KeyID`. +/// A `KeyHandle` contains either a [`Fingerprint`] or a [`KeyID`]. +/// This is needed because signatures can reference their issuer +/// either by `Fingerprint` or by `KeyID`. +/// +/// Currently, sequoia supports *version 4* fingerprints and Key ID +/// only. *Version 3* fingerprints and Key ID were deprecated by [RFC +/// 4880] in 2007. +/// +/// A *v4* fingerprint is, essentially, a 20-byte SHA-1 hash over the +/// key's public key packet. A *v4* Key ID is defined as the +/// fingerprint's lower 8 bytes. +/// +/// For the exact definition, see [Section 12.2 of RFC 4880]. +/// +/// Both fingerprint and Key ID are used to identify a key, e.g., the +/// issuer of a signature. +/// +/// [RFC 4880]: https://tools.ietf.org/html/rfc4880 +/// [Section 12.2 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-12.2 +/// [`Fingerprint`]: ./enum.Fingerprint.html +/// [`KeyID`]: ./enum.KeyID.html +/// +/// # Examples +/// +/// ```rust +/// # fn main() -> sequoia_openpgp::Result<()> { +/// # use sequoia_openpgp as openpgp; +/// use openpgp::KeyHandle; +/// use openpgp::Packet; +/// use openpgp::parse::Parse; +/// +/// let p = Packet::from_bytes( +/// "-----BEGIN PGP SIGNATURE----- +/// # +/// # wsBzBAABCgAdFiEEwD+mQRsDrhJXZGEYciO1ZnjgJSgFAlnclx8ACgkQciO1Znjg +/// # JShldAf+NBvUTVPnVPhYM4KihWOUlup8lbD6g1IduSM5rpsGvOVb+uKF6ik+GOBB +/// # RlMT4s183r3teFxiTkDx2pRhUz0MnOMPfbXovjF6Y93fKCOxCQWLBa0ukjNmE+ax +/// # gu9nZ3XXDGXZW22iGE52uVjPGSfuLfqvdMy5bKHn8xow/kepuGHZwy8yn7uFv7sl +/// # LnOBUz1FKA7iRl457XKPUhw5K7BnfRW/I2BRlnrwTDkjfXaJZC+bUTIJvm682Bvt +/// # ZNn8zc0JucyEkuL9WXYNuZg0znDE3T7D/6+tzfEdSf706unsXFXWHf83vL2eHCcw +/// # qhImm1lmcC+agFtWQ6/qD923LR9xmg== +/// # =htNu +/// # -----END PGP SIGNATURE-----" /* docstring trickery ahead: +/// // ... +/// -----END PGP SIGNATURE-----")?; +/// # */)?; +/// if let Packet::Signature(sig) = p { +/// let issuers = sig.get_issuers(); +/// assert_eq!(issuers.len(), 2); +/// assert_eq!(&issuers[0], +/// &KeyHandle::Fingerprint( +/// "C03F A641 1B03 AE12 5764 6118 7223 B566 78E0 2528" +/// .parse()?)); +/// assert_eq!(&issuers[1], +/// &KeyHandle::KeyID("7223 B566 78E0 2528".parse()?)); +/// } else { +/// unreachable!("It's a signature!"); +/// } +/// # Ok(()) } +/// ``` #[derive(Debug, Clone)] pub enum KeyHandle { /// A Fingerprint. @@ -146,7 +205,7 @@ impl PartialEq for KeyHandle { } impl KeyHandle { - /// Returns a reference to the raw identifier. + /// Returns the raw identifier as a byte slice. pub fn as_bytes(&self) -> &[u8] { match self { KeyHandle::Fingerprint(i) => i.as_bytes(), @@ -154,7 +213,8 @@ impl KeyHandle { } } - /// Returns whether `self` and `other` could be aliases of each other. + /// Returns whether `self` and `other` could be aliases of each + /// other. /// /// `KeyHandle`'s `PartialEq` implementation cannot assert that a /// `Fingerprint` and a `KeyID` are equal, because distinct diff --git a/openpgp/src/keyid.rs b/openpgp/src/keyid.rs index a507527f..973b059e 100644 --- a/openpgp/src/keyid.rs +++ b/openpgp/src/keyid.rs @@ -9,22 +9,60 @@ use crate::Result; /// A short identifier for certificates and keys. /// -/// A KeyID is a fingerprint fragment. It identifies a public key, -/// but is easy to forge. For more details about how a KeyID is -/// generated, see [Section 12.2 of RFC 4880]. +/// A `KeyID` identifies a public key. It is used, for example, to +/// reference the issuing key of a signature in its [`Issuer`] +/// subpacket. /// +/// Currently, sequoia supports *version 4* fingerprints and Key IDs +/// only. *Version 3* fingerprints and Key IDs were deprecated by +/// [RFC 4880] in 2007. +/// +/// A *v4* `KeyID` is defined as a fragment (the lower 8 bytes) of the +/// key's fingerprint, which in turn is essentially a SHA-1 hash of +/// the public key packet. As a general rule of thumb, you should +/// prefer the fingerprint as it is possible to create keys with a +/// colliding KeyID using a [birthday attack]. +/// +/// For more details about how a `KeyID` is generated, see [Section +/// 12.2 of RFC 4880]. +/// +/// In previous versions of OpenPGP, the Key ID used to be called +/// "long Key ID", as there even was a "short Key ID". At only 4 bytes +/// length, short Key IDs vulnerable to preimage attacks. That is, an +/// attacker can create a key with any given short key ID in short +/// amount of time. +/// +/// See also [`Fingerprint`] and [`KeyHandle`]. +/// +/// [RFC 4880]: https://tools.ietf.org/html/rfc4880 /// [Section 12.2 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-12.2 +/// [`Fingerprint`]: ./enum.Fingerprint.html +/// [`KeyHandle`]: ./enum.KeyHandle.html +/// [birthday attack]: https://nullprogram.com/blog/2019/07/22/ +/// [`Issuer`]: ./packet/signature/subpacket/enum.SubpacketValue.html#variant.Issuer /// /// Note: This enum cannot be exhaustively matched to allow future /// extensions. +/// +/// # Examples +/// +/// ```rust +/// # fn main() -> sequoia_openpgp::Result<()> { +/// # use sequoia_openpgp as openpgp; +/// use openpgp::KeyID; +/// +/// let id: KeyID = "0123 4567 89AB CDEF".parse()?; +/// +/// assert_eq!("0123456789ABCDEF", id.to_hex()); +/// # Ok(()) } +/// ``` #[non_exhaustive] #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Hash)] pub enum KeyID { /// Lower 8 byte SHA-1 hash. V4([u8;8]), - /// Used for holding keyids that we don't understand. For - /// instance, we don't grok v3 keyids. And, it is possible that - /// the Issuer subpacket contains the wrong number of bytes. + /// Used for holding invalid keyids encountered during parsing + /// e.g. wrong number of bytes. Invalid(Box<[u8]>), } @@ -121,13 +159,35 @@ impl From<Fingerprint> for KeyID { } impl KeyID { - /// Converts a u64 to a KeyID. + /// Converts a `u64` to a `KeyID`. + /// + /// # Examples + /// + /// ```rust + /// # extern crate sequoia_openpgp as openpgp; + /// use openpgp::KeyID; + /// + /// let keyid = KeyID::new(0x0123456789ABCDEF); + /// ``` pub fn new(data: u64) -> KeyID { let bytes = data.to_be_bytes(); Self::from_bytes(&bytes[..]) } - /// Converts the KeyID to a u64 if possible. + /// Converts the `KeyID` to a `u64` if possible. + /// + /// # Examples + /// + /// ```rust + /// # fn main() -> sequoia_openpgp::Result<()> { + /// # extern crate sequoia_openpgp as openpgp; + /// use openpgp::KeyID; + /// + /// let keyid = KeyID::new(0x0123456789ABCDEF); + /// + /// assert_eq!(keyid.as_u64()?, 0x0123456789ABCDEF); + /// # Ok(()) } + /// ``` pub fn as_u64(&self) -> Result<u64> { match &self { KeyID::V4(ref b) => @@ -137,7 +197,21 @@ impl KeyID { } } - /// Reads a binary key ID. + /// Creates a `KeyID` from a big endian byte slice. + /// + /// # Examples + /// + /// ```rust + /// # fn main() -> sequoia_openpgp::Result<()> { + /// # extern crate sequoia_openpgp as openpgp; + /// use openpgp::KeyID; + /// + /// let keyid: KeyID = "0123 4567 89AB CDEF".parse()?; + /// + /// let bytes = [0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF]; + /// assert_eq!(KeyID::from_bytes(&bytes), keyid); + /// # Ok(()) } + /// ``` pub fn from_bytes(raw: &[u8]) -> KeyID { if raw.len() == 8 { let mut keyid : [u8; 8] = Default::default(); @@ -148,7 +222,21 @@ impl KeyID { } } - /// Returns a reference to the raw KeyID. + /// Returns a reference to the raw `KeyID` as a byte slice in big + /// endian representation. + /// + /// # Examples + /// + /// ```rust + /// # use sequoia_openpgp as openpgp; + /// use openpgp::KeyID; + /// + /// # fn main() -> sequoia_openpgp::Result<()> { + /// let keyid: KeyID = "0123 4567 89AB CDEF".parse()?; + /// + /// assert_eq!(keyid.as_bytes(), [0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF]); + /// # Ok(()) } + /// ``` pub fn as_bytes(&self) -> &[u8] { match self { &KeyID::V4(ref id) => id, @@ -156,50 +244,81 @@ impl KeyID { } } - /// Returns the wildcard KeyID. + /// Creates a wildcard `KeyID`. + /// + /// Refer to [Section 5.1 of RFC 4880] for details. + /// + /// [Section 5.1 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-5.1 + /// + /// # Examples + /// + /// ```rust + /// # use sequoia_openpgp as openpgp; + /// use openpgp::KeyID; + /// + /// assert_eq!(KeyID::wildcard(), KeyID::new(0x0000000000000000)); + /// ``` pub fn wildcard() -> Self { Self::from_bytes(&[0u8; 8][..]) } - /// Returns true if this is a wild card ID. + /// Returns `true` if this is the wildcard `KeyID`. + /// + /// Refer to [Section 5.1 of RFC 4880] for details. + /// + /// [Section 5.1 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-5.1 + /// + /// # Examples + /// + /// ```rust + /// # use sequoia_openpgp as openpgp; + /// use openpgp::KeyID; + /// + /// assert!(KeyID::new(0x0000000000000000).is_wildcard()); + /// ``` pub fn is_wildcard(&self) -> bool { self.as_bytes().iter().all(|b| *b == 0) } - /// Converts this key ID to its canonical hexadecimal representation. + /// Converts this `KeyID` to its canonical hexadecimal + /// representation. /// - /// This representation is always uppercase and without spaces and is - /// suitable for stable key identifiers. + /// This representation is always uppercase and without spaces and + /// is suitable for stable key identifiers. /// - /// The output of this function is exactly the same as formatting this - /// object with the `:X` format specifier. + /// The output of this function is exactly the same as formatting + /// this object with the `:X` format specifier. /// /// ```rust + /// # fn main() -> sequoia_openpgp::Result<()> { /// # use sequoia_openpgp as openpgp; /// use openpgp::KeyID; /// - /// let keyid = "fb3751f1587daef1".parse::<KeyID>().unwrap(); + /// let keyid: KeyID = "fb3751f1587daef1".parse()?; /// /// assert_eq!("FB3751F1587DAEF1", keyid.to_hex()); /// assert_eq!(format!("{:X}", keyid), keyid.to_hex()); + /// # Ok(()) } /// ``` pub fn to_hex(&self) -> String { format!("{:X}", self) } - /// Parses the hexadecimal representation of an OpenPGP key ID. + /// Parses the hexadecimal representation of an OpenPGP `KeyID`. /// - /// This function is the reverse of `to_hex`. It also accepts other variants - /// of the key ID notation including lower-case letters, spaces and optional - /// leading `0x`. + /// This function is the reverse of `to_hex`. It also accepts + /// other variants of the `keyID` notation including lower-case + /// letters, spaces and optional leading `0x`. /// /// ```rust + /// # fn main() -> sequoia_openpgp::Result<()> { /// # use sequoia_openpgp as openpgp; /// use openpgp::KeyID; /// - /// let keyid = KeyID::from_hex("0xfb3751f1587daef1").unwrap(); + /// let keyid = KeyID::from_hex("0xfb3751f1587daef1")?; /// /// assert_eq!("FB3751F1587DAEF1", keyid.to_hex()); + /// # Ok(()) } /// ``` pub fn from_hex(s: &str) -> std::result::Result<Self, anyhow::Error> { std::str::FromStr::from_str(s) |