1 use std::fmt; 2 3 #[cfg(any(test, feature = "quickcheck"))] 4 use quickcheck::{Arbitrary, Gen}; 5 6 use crate::Error; 7 use crate::Fingerprint; 8 use crate::Result; 9 10 /// A short identifier for certificates and keys. 11 /// 12 /// A KeyID is a fingerprint fragment. It identifies a public key, 13 /// but is easy to forge. For more details about how a KeyID is 14 /// generated, see [Section 12.2 of RFC 4880]. 15 /// 16 /// [Section 12.2 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-12.2 17 /// 18 /// Note: This enum cannot be exhaustively matched to allow future 19 /// extensions. 20 #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Hash)] 21 pub enum KeyID { 22 /// Lower 8 byte SHA-1 hash. 23 V4([u8;8]), 24 /// Used for holding keyids that we don't understand. For 25 /// instance, we don't grok v3 keyids. And, it is possible that 26 /// the Issuer subpacket contains the wrong number of bytes. 27 Invalid(Box<[u8]>), 28 29 /// This marks this enum as non-exhaustive. Do not use this 30 /// variant. 31 #[doc(hidden)] __Nonexhaustive, 32 } 33 34 impl fmt::Display for KeyID { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result35 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 36 write!(f, "{}", self.convert_to_string(true)) 37 } 38 } 39 40 impl fmt::Debug for KeyID { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result41 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 42 f.debug_tuple("KeyID") 43 .field(&self.to_string()) 44 .finish() 45 } 46 } 47 48 impl fmt::UpperHex for KeyID { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result49 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 50 f.write_str(&self.convert_to_string(false)) 51 } 52 } 53 54 impl fmt::LowerHex for KeyID { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result55 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 56 let mut hex = self.convert_to_string(false); 57 hex.make_ascii_lowercase(); 58 f.write_str(&hex) 59 } 60 } 61 62 impl std::str::FromStr for KeyID { 63 type Err = anyhow::Error; 64 from_str(s: &str) -> std::result::Result<Self, Self::Err>65 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> { 66 let bytes = crate::fmt::hex::decode_pretty(s)?; 67 68 // A KeyID is exactly 8 bytes long. 69 if bytes.len() == 8 { 70 Ok(KeyID::from_bytes(&bytes[..])) 71 } else { 72 // Maybe a fingerprint was given. Try to parse it and 73 // convert it to a KeyID. 74 Ok(s.parse::<Fingerprint>()?.into()) 75 } 76 } 77 } 78 79 impl From<KeyID> for Vec<u8> { from(id: KeyID) -> Self80 fn from(id: KeyID) -> Self { 81 let mut r = Vec::with_capacity(8); 82 match id { 83 KeyID::V4(ref b) => r.extend_from_slice(b), 84 KeyID::Invalid(ref b) => r.extend_from_slice(b), 85 KeyID::__Nonexhaustive => unreachable!(), 86 } 87 r 88 } 89 } 90 91 impl From<u64> for KeyID { from(id: u64) -> Self92 fn from(id: u64) -> Self { 93 Self::new(id) 94 } 95 } 96 97 impl From<[u8; 8]> for KeyID { from(id: [u8; 8]) -> Self98 fn from(id: [u8; 8]) -> Self { 99 KeyID::from_bytes(&id[..]) 100 } 101 } 102 103 impl From<&Fingerprint> for KeyID { from(fp: &Fingerprint) -> Self104 fn from(fp: &Fingerprint) -> Self { 105 match fp { 106 Fingerprint::V4(fp) => 107 KeyID::from_bytes(&fp[fp.len() - 8..]), 108 Fingerprint::Invalid(fp) => { 109 KeyID::Invalid(fp.clone()) 110 } 111 Fingerprint::__Nonexhaustive => unreachable!(), 112 } 113 } 114 } 115 116 impl From<Fingerprint> for KeyID { from(fp: Fingerprint) -> Self117 fn from(fp: Fingerprint) -> Self { 118 match fp { 119 Fingerprint::V4(fp) => 120 KeyID::from_bytes(&fp[fp.len() - 8..]), 121 Fingerprint::Invalid(fp) => { 122 KeyID::Invalid(fp) 123 } 124 Fingerprint::__Nonexhaustive => unreachable!(), 125 } 126 } 127 } 128 129 impl KeyID { 130 /// Converts a u64 to a KeyID. new(data: u64) -> KeyID131 pub fn new(data: u64) -> KeyID { 132 let bytes = data.to_be_bytes(); 133 Self::from_bytes(&bytes[..]) 134 } 135 136 /// Converts the KeyID to a u64 if possible. as_u64(&self) -> Result<u64>137 pub fn as_u64(&self) -> Result<u64> { 138 match &self { 139 KeyID::V4(ref b) => 140 Ok(u64::from_be_bytes(*b)), 141 KeyID::Invalid(_) => 142 Err(Error::InvalidArgument("Invalid KeyID".into()).into()), 143 KeyID::__Nonexhaustive => unreachable!(), 144 } 145 } 146 147 /// Reads a binary key ID. from_bytes(raw: &[u8]) -> KeyID148 pub fn from_bytes(raw: &[u8]) -> KeyID { 149 if raw.len() == 8 { 150 let mut keyid : [u8; 8] = Default::default(); 151 keyid.copy_from_slice(raw); 152 KeyID::V4(keyid) 153 } else { 154 KeyID::Invalid(raw.to_vec().into_boxed_slice()) 155 } 156 } 157 158 /// Returns a reference to the raw KeyID. as_bytes(&self) -> &[u8]159 pub fn as_bytes(&self) -> &[u8] { 160 match self { 161 &KeyID::V4(ref id) => id, 162 &KeyID::Invalid(ref id) => id, 163 KeyID::__Nonexhaustive => unreachable!(), 164 } 165 } 166 167 /// Returns the wildcard KeyID. wildcard() -> Self168 pub fn wildcard() -> Self { 169 Self::from_bytes(&[0u8; 8][..]) 170 } 171 172 /// Returns true if this is a wild card ID. is_wildcard(&self) -> bool173 pub fn is_wildcard(&self) -> bool { 174 self.as_bytes().iter().all(|b| *b == 0) 175 } 176 177 /// Converts this key ID to its canonical hexadecimal representation. 178 /// 179 /// This representation is always uppercase and without spaces and is 180 /// suitable for stable key identifiers. 181 /// 182 /// The output of this function is exactly the same as formatting this 183 /// object with the `:X` format specifier. 184 /// 185 /// ```rust 186 /// # extern crate sequoia_openpgp as openpgp; 187 /// use openpgp::KeyID; 188 /// 189 /// let keyid = "fb3751f1587daef1".parse::<KeyID>().unwrap(); 190 /// 191 /// assert_eq!("FB3751F1587DAEF1", keyid.to_hex()); 192 /// assert_eq!(format!("{:X}", keyid), keyid.to_hex()); 193 /// ``` to_hex(&self) -> String194 pub fn to_hex(&self) -> String { 195 format!("{:X}", self) 196 } 197 198 /// Parses the hexadecimal representation of an OpenPGP key ID. 199 /// 200 /// This function is the reverse of `to_hex`. It also accepts other variants 201 /// of the key ID notation including lower-case letters, spaces and optional 202 /// leading `0x`. 203 /// 204 /// ```rust 205 /// # extern crate sequoia_openpgp as openpgp; 206 /// use openpgp::KeyID; 207 /// 208 /// let keyid = KeyID::from_hex("0xfb3751f1587daef1").unwrap(); 209 /// 210 /// assert_eq!("FB3751F1587DAEF1", keyid.to_hex()); 211 /// ``` from_hex(s: &str) -> std::result::Result<Self, anyhow::Error>212 pub fn from_hex(s: &str) -> std::result::Result<Self, anyhow::Error> { 213 std::str::FromStr::from_str(s) 214 } 215 216 /// Common code for the above functions. convert_to_string(&self, pretty: bool) -> String217 fn convert_to_string(&self, pretty: bool) -> String { 218 let raw = match self { 219 &KeyID::V4(ref fp) => &fp[..], 220 &KeyID::Invalid(ref fp) => &fp[..], 221 KeyID::__Nonexhaustive => unreachable!(), 222 }; 223 224 // We currently only handle V4 key IDs, which look like: 225 // 226 // AACB 3243 6300 52D9 227 // 228 // Since we have no idea how to format an invalid key ID, just 229 // format it like a V4 fingerprint and hope for the best. 230 231 let mut output = Vec::with_capacity( 232 // Each byte results in to hex characters. 233 raw.len() * 2 234 + if pretty { 235 // Every 2 bytes of output, we insert a space. 236 raw.len() / 2 237 } else { 0 }); 238 239 for (i, b) in raw.iter().enumerate() { 240 if pretty && i > 0 && i % 2 == 0 { 241 output.push(' ' as u8); 242 } 243 244 let top = b >> 4; 245 let bottom = b & 0xFu8; 246 247 if top < 10u8 { 248 output.push('0' as u8 + top) 249 } else { 250 output.push('A' as u8 + (top - 10u8)) 251 } 252 253 if bottom < 10u8 { 254 output.push('0' as u8 + bottom) 255 } else { 256 output.push('A' as u8 + (bottom - 10u8)) 257 } 258 } 259 260 // We know the content is valid UTF-8. 261 String::from_utf8(output).unwrap() 262 } 263 } 264 265 #[cfg(any(test, feature = "quickcheck"))] 266 impl Arbitrary for KeyID { arbitrary<G: Gen>(g: &mut G) -> Self267 fn arbitrary<G: Gen>(g: &mut G) -> Self { 268 KeyID::new(u64::arbitrary(g)) 269 } 270 } 271 272 #[cfg(test)] 273 mod test { 274 use super::*; 275 quickcheck! { 276 fn u64_roundtrip(id: u64) -> bool { 277 KeyID::new(id).as_u64().unwrap() == id 278 } 279 } 280 281 #[test] from_hex()282 fn from_hex() { 283 "FB3751F1587DAEF1".parse::<KeyID>().unwrap(); 284 "39D100AB67D5BD8C04010205FB3751F1587DAEF1".parse::<KeyID>() 285 .unwrap(); 286 "0xFB3751F1587DAEF1".parse::<KeyID>().unwrap(); 287 "0x39D100AB67D5BD8C04010205FB3751F1587DAEF1".parse::<KeyID>() 288 .unwrap(); 289 "FB37 51F1 587D AEF1".parse::<KeyID>().unwrap(); 290 "39D1 00AB 67D5 BD8C 0401 0205 FB37 51F1 587D AEF1".parse::<KeyID>() 291 .unwrap(); 292 "GB3751F1587DAEF1".parse::<KeyID>().unwrap_err(); 293 "EFB3751F1587DAEF1".parse::<KeyID>().unwrap_err(); 294 "%FB3751F1587DAEF1".parse::<KeyID>().unwrap_err(); 295 assert_match!(KeyID::Invalid(_) = "587DAEF1".parse().unwrap()); 296 assert_match!(KeyID::Invalid(_) = "0x587DAEF1".parse().unwrap()); 297 } 298 299 #[test] hex_formatting()300 fn hex_formatting() { 301 let keyid = "FB3751F1587DAEF1".parse::<KeyID>().unwrap(); 302 assert_eq!(format!("{:X}", keyid), "FB3751F1587DAEF1"); 303 assert_eq!(format!("{:x}", keyid), "fb3751f1587daef1"); 304 } 305 306 #[test] keyid_is_send_and_sync()307 fn keyid_is_send_and_sync() { 308 fn f<T: Send + Sync>(_: T) {} 309 f("89AB CDEF 0123 4567".parse::<KeyID>().unwrap()); 310 } 311 } 312