use std::borrow::Borrow;
use std::fmt;
#[cfg(test)]
use quickcheck::{Arbitrary, Gen};
use crate::Error;
use crate::Fingerprint;
use crate::KeyHandle;
use crate::Result;
#[non_exhaustive]
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
pub enum KeyID {
V4([u8;8]),
Invalid(Box<[u8]>),
}
assert_send_and_sync!(KeyID);
impl fmt::Display for KeyID {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:X}", self)
}
}
impl fmt::Debug for KeyID {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("KeyID")
.field(&self.to_string())
.finish()
}
}
impl fmt::UpperHex for KeyID {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.write_to_fmt(f, true)
}
}
impl fmt::LowerHex for KeyID {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.write_to_fmt(f, false)
}
}
impl std::str::FromStr for KeyID {
type Err = anyhow::Error;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
if s.chars().filter(|c| ! c.is_whitespace()).count() % 2 == 1 {
return Err(Error::InvalidArgument(
"Odd number of nibbles".into()).into());
}
let bytes = crate::fmt::hex::decode_pretty(s)?;
if bytes.len() == 8 {
Ok(KeyID::from_bytes(&bytes[..]))
} else if bytes.len() == 4 {
Err(Error::ShortKeyID(s.to_string()).into())
} else {
Ok(s.parse::<Fingerprint>()?.into())
}
}
}
impl From<KeyID> for Vec<u8> {
fn from(id: KeyID) -> Self {
let mut r = Vec::with_capacity(8);
match id {
KeyID::V4(ref b) => r.extend_from_slice(b),
KeyID::Invalid(ref b) => r.extend_from_slice(b),
}
r
}
}
impl From<u64> for KeyID {
fn from(id: u64) -> Self {
Self::new(id)
}
}
impl From<[u8; 8]> for KeyID {
fn from(id: [u8; 8]) -> Self {
KeyID::from_bytes(&id[..])
}
}
impl From<&Fingerprint> for KeyID {
fn from(fp: &Fingerprint) -> Self {
match fp {
Fingerprint::V4(fp) =>
KeyID::from_bytes(&fp[fp.len() - 8..]),
Fingerprint::V5(fp) =>
KeyID::Invalid(fp.iter().cloned().collect()),
Fingerprint::Invalid(fp) => {
KeyID::Invalid(fp.clone())
}
}
}
}
impl From<Fingerprint> for KeyID {
fn from(fp: Fingerprint) -> Self {
match fp {
Fingerprint::V4(fp) =>
KeyID::from_bytes(&fp[fp.len() - 8..]),
Fingerprint::V5(fp) =>
KeyID::Invalid(fp.into()),
Fingerprint::Invalid(fp) => {
KeyID::Invalid(fp)
}
}
}
}
impl KeyID {
pub fn new(data: u64) -> KeyID {
let bytes = data.to_be_bytes();
Self::from_bytes(&bytes[..])
}
pub fn as_u64(&self) -> Result<u64> {
match &self {
KeyID::V4(ref b) =>
Ok(u64::from_be_bytes(*b)),
KeyID::Invalid(_) =>
Err(Error::InvalidArgument("Invalid KeyID".into()).into()),
}
}
pub fn from_bytes(raw: &[u8]) -> KeyID {
if raw.len() == 8 {
let mut keyid : [u8; 8] = Default::default();
keyid.copy_from_slice(raw);
KeyID::V4(keyid)
} else {
KeyID::Invalid(raw.to_vec().into_boxed_slice())
}
}
pub fn as_bytes(&self) -> &[u8] {
match self {
KeyID::V4(ref id) => id,
KeyID::Invalid(ref id) => id,
}
}
pub fn wildcard() -> Self {
Self::from_bytes(&[0u8; 8][..])
}
pub fn is_wildcard(&self) -> bool {
self.as_bytes().iter().all(|b| *b == 0)
}
pub fn to_hex(&self) -> String {
use std::fmt::Write;
let raw_len = self.as_bytes().len();
let mut output = String::with_capacity(
raw_len * 2);
write!(output, "{:X}", self).unwrap();
output
}
pub fn to_spaced_hex(&self) -> String {
use std::fmt::Write;
let raw_len = self.as_bytes().len();
let mut output = String::with_capacity(
raw_len * 2
+
raw_len / 2);
write!(output, "{:#X}", self).unwrap();
output
}
pub fn from_hex(s: &str) -> std::result::Result<Self, anyhow::Error> {
std::str::FromStr::from_str(s)
}
fn write_to_fmt(&self, f: &mut fmt::Formatter, upper_case: bool) -> fmt::Result {
use std::fmt::Write;
let a_letter = if upper_case { b'A' } else { b'a' };
let pretty = f.alternate();
let raw = match self {
KeyID::V4(ref fp) => &fp[..],
KeyID::Invalid(ref fp) => &fp[..],
};
for (i, b) in raw.iter().enumerate() {
if pretty && i > 0 && i % 2 == 0 {
f.write_char(' ')?;
}
let top = b >> 4;
let bottom = b & 0xFu8;
if top < 10u8 {
f.write_char((b'0' + top) as char)?;
} else {
f.write_char((a_letter + (top - 10u8)) as char)?;
}
if bottom < 10u8 {
f.write_char((b'0' + bottom) as char)?;
} else {
f.write_char((a_letter + (bottom - 10u8)) as char)?;
}
}
Ok(())
}
pub fn aliases<H>(&self, other: H) -> bool
where H: Borrow<KeyHandle>
{
let other = other.borrow();
match (self, other) {
(k, KeyHandle::KeyID(o)) => {
k == o
},
(KeyID::V4(k), KeyHandle::Fingerprint(Fingerprint::V4(o))) => {
&o[12..] == k
},
(k, o) => {
k == &KeyID::from(o)
},
}
}
}
#[cfg(test)]
impl Arbitrary for KeyID {
fn arbitrary(g: &mut Gen) -> Self {
KeyID::new(u64::arbitrary(g))
}
}
#[cfg(test)]
mod test {
use super::*;
quickcheck! {
fn u64_roundtrip(id: u64) -> bool {
KeyID::new(id).as_u64().unwrap() == id
}
}
#[test]
fn from_hex() {
"FB3751F1587DAEF1".parse::<KeyID>().unwrap();
"39D100AB67D5BD8C04010205FB3751F1587DAEF1".parse::<KeyID>()
.unwrap();
"0xFB3751F1587DAEF1".parse::<KeyID>().unwrap();
"0x39D100AB67D5BD8C04010205FB3751F1587DAEF1".parse::<KeyID>()
.unwrap();
"FB37 51F1 587D AEF1".parse::<KeyID>().unwrap();
"39D1 00AB 67D5 BD8C 0401 0205 FB37 51F1 587D AEF1".parse::<KeyID>()
.unwrap();
"GB3751F1587DAEF1".parse::<KeyID>().unwrap_err();
"EFB3751F1587DAEF1".parse::<KeyID>().unwrap_err();
"%FB3751F1587DAEF1".parse::<KeyID>().unwrap_err();
}
#[test]
fn from_hex_short_keyid() {
for s in &[ "FB3751F1", "0xFB3751F1", "fb3751f1", "0xfb3751f1" ] {
match s.parse::<KeyID>() {
Ok(_) => panic!("Failed to reject short Key ID."),
Err(err) => {
let err = err.downcast_ref::<Error>().unwrap();
assert!(matches!(err, Error::ShortKeyID(_)));
}
}
}
}
#[test]
fn hex_formatting() {
let keyid = "FB3751F1587DAEF1".parse::<KeyID>().unwrap();
assert_eq!(format!("{:X}", keyid), "FB3751F1587DAEF1");
assert_eq!(format!("{:x}", keyid), "fb3751f1587daef1");
}
#[test]
fn aliases() -> crate::Result<()> {
let fp1 = "280C0AB0B94D1302CAAEB71DA299CDCD3884EBEA"
.parse::<Fingerprint>()?;
let fp15 = "1234567890ABCDEF12345678A299CDCD3884EBEA"
.parse::<Fingerprint>()?;
let fp2 = "F8D921C01EE93B65D4C6FEB7B456A7DB5E4274D0"
.parse::<Fingerprint>()?;
let keyid1 = KeyID::from(&fp1);
let keyid15 = KeyID::from(&fp15);
let keyid2 = KeyID::from(&fp2);
eprintln!("fp1: {:?}", fp1);
eprintln!("keyid1: {:?}", keyid1);
eprintln!("fp15: {:?}", fp15);
eprintln!("keyid15: {:?}", keyid15);
eprintln!("fp2: {:?}", fp2);
eprintln!("keyid2: {:?}", keyid2);
assert_ne!(fp1, fp15);
assert_eq!(keyid1, keyid15);
assert!(keyid1.aliases(KeyHandle::from(&fp1)));
assert!(keyid1.aliases(KeyHandle::from(&fp15)));
assert!(! keyid1.aliases(KeyHandle::from(&fp2)));
assert!(keyid15.aliases(KeyHandle::from(&fp1)));
assert!(keyid15.aliases(KeyHandle::from(&fp15)));
assert!(! keyid15.aliases(KeyHandle::from(&fp2)));
assert!(! keyid2.aliases(KeyHandle::from(&fp1)));
assert!(! keyid2.aliases(KeyHandle::from(&fp15)));
assert!(keyid2.aliases(KeyHandle::from(&fp2)));
Ok(())
}
}