use std::convert::TryFrom;
use std::cmp;
use std::cmp::Ordering;
use std::borrow::Borrow;
use crate::{
Error,
Fingerprint,
KeyID,
Result,
};
#[derive(Debug, Clone)]
pub enum KeyHandle {
Fingerprint(Fingerprint),
KeyID(KeyID),
}
assert_send_and_sync!(KeyHandle);
impl std::fmt::Display for KeyHandle {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
KeyHandle::Fingerprint(v) => v.fmt(f),
KeyHandle::KeyID(v) => v.fmt(f),
}
}
}
impl std::fmt::UpperHex for KeyHandle {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match &self {
KeyHandle::Fingerprint(ref fpr) => write!(f, "{:X}", fpr),
KeyHandle::KeyID(ref keyid) => write!(f, "{:X}", keyid),
}
}
}
impl std::fmt::LowerHex for KeyHandle {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match &self {
KeyHandle::Fingerprint(ref fpr) => write!(f, "{:x}", fpr),
KeyHandle::KeyID(ref keyid) => write!(f, "{:x}", keyid),
}
}
}
impl From<KeyID> for KeyHandle {
fn from(i: KeyID) -> Self {
KeyHandle::KeyID(i)
}
}
impl From<&KeyID> for KeyHandle {
fn from(i: &KeyID) -> Self {
KeyHandle::KeyID(i.clone())
}
}
impl From<KeyHandle> for KeyID {
fn from(i: KeyHandle) -> Self {
match i {
KeyHandle::Fingerprint(i) => i.into(),
KeyHandle::KeyID(i) => i,
}
}
}
impl From<&KeyHandle> for KeyID {
fn from(i: &KeyHandle) -> Self {
match i {
KeyHandle::Fingerprint(i) => i.clone().into(),
KeyHandle::KeyID(i) => i.clone(),
}
}
}
impl From<Fingerprint> for KeyHandle {
fn from(i: Fingerprint) -> Self {
KeyHandle::Fingerprint(i)
}
}
impl From<&Fingerprint> for KeyHandle {
fn from(i: &Fingerprint) -> Self {
KeyHandle::Fingerprint(i.clone())
}
}
impl TryFrom<KeyHandle> for Fingerprint {
type Error = anyhow::Error;
fn try_from(i: KeyHandle) -> Result<Self> {
match i {
KeyHandle::Fingerprint(i) => Ok(i),
KeyHandle::KeyID(i) => Err(Error::InvalidOperation(
format!("Cannot convert keyid {} to fingerprint", i)).into()),
}
}
}
impl TryFrom<&KeyHandle> for Fingerprint {
type Error = anyhow::Error;
fn try_from(i: &KeyHandle) -> Result<Self> {
match i {
KeyHandle::Fingerprint(i) => Ok(i.clone()),
KeyHandle::KeyID(i) => Err(Error::InvalidOperation(
format!("Cannot convert keyid {} to fingerprint", i)).into()),
}
}
}
impl PartialOrd for KeyHandle {
fn partial_cmp(&self, other: &KeyHandle) -> Option<Ordering> {
let a = self.as_bytes();
let b = other.as_bytes();
let l = cmp::min(a.len(), b.len());
for (a, b) in a[a.len()-l..].iter().zip(b[b.len()-l..].iter()) {
let cmp = a.cmp(b);
if cmp != Ordering::Equal {
return Some(cmp);
}
}
if a.len() == b.len() {
Some(Ordering::Equal)
} else {
None
}
}
}
impl PartialEq for KeyHandle {
fn eq(&self, other: &Self) -> bool {
self.partial_cmp(other) == Some(Ordering::Equal)
}
}
impl std::str::FromStr for KeyHandle {
type Err = anyhow::Error;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
let bytes = &crate::fmt::hex::decode_pretty(s)?[..];
match Fingerprint::from_bytes(bytes) {
fpr @ Fingerprint::Invalid(_) => {
match KeyID::from_bytes(bytes) {
KeyID::Invalid(_) => Ok(fpr.into()),
kid => Ok(kid.into()),
}
}
fpr => Ok(fpr.into()),
}
}
}
impl KeyHandle {
pub fn as_bytes(&self) -> &[u8] {
match self {
KeyHandle::Fingerprint(i) => i.as_bytes(),
KeyHandle::KeyID(i) => i.as_bytes(),
}
}
pub fn aliases<H>(&self, other: H) -> bool
where H: Borrow<KeyHandle>
{
self.partial_cmp(other.borrow()).unwrap_or(Ordering::Equal)
== Ordering::Equal
}
pub fn is_invalid(&self) -> bool {
match self {
KeyHandle::Fingerprint(Fingerprint::Invalid(_)) => true,
KeyHandle::KeyID(KeyID::Invalid(_)) => true,
_ => false,
}
}
pub fn to_hex(&self) -> String {
format!("{:X}", self)
}
pub fn to_spaced_hex(&self) -> String {
match self {
KeyHandle::Fingerprint(v) => v.to_spaced_hex(),
KeyHandle::KeyID(v) => v.to_spaced_hex(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn upper_hex_formatting() {
let handle = KeyHandle::Fingerprint(Fingerprint::V4([1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]));
assert_eq!(format!("{:X}", handle), "0102030405060708090A0B0C0D0E0F1011121314");
let handle = KeyHandle::Fingerprint(Fingerprint::Invalid(Box::new([10, 2, 3, 4])));
assert_eq!(format!("{:X}", handle), "0A020304");
let handle = KeyHandle::KeyID(KeyID::V4([10, 2, 3, 4, 5, 6, 7, 8]));
assert_eq!(format!("{:X}", handle), "0A02030405060708");
let handle = KeyHandle::KeyID(KeyID::Invalid(Box::new([10, 2])));
assert_eq!(format!("{:X}", handle), "0A02");
}
#[test]
fn lower_hex_formatting() {
let handle = KeyHandle::Fingerprint(Fingerprint::V4([1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]));
assert_eq!(format!("{:x}", handle), "0102030405060708090a0b0c0d0e0f1011121314");
let handle = KeyHandle::Fingerprint(Fingerprint::Invalid(Box::new([10, 2, 3, 4])));
assert_eq!(format!("{:x}", handle), "0a020304");
let handle = KeyHandle::KeyID(KeyID::V4([10, 2, 3, 4, 5, 6, 7, 8]));
assert_eq!(format!("{:x}", handle), "0a02030405060708");
let handle = KeyHandle::KeyID(KeyID::Invalid(Box::new([10, 2])));
assert_eq!(format!("{:x}", handle), "0a02");
}
#[test]
fn parse() -> Result<()> {
let handle: KeyHandle =
"0123 4567 89AB CDEF 0123 4567 89AB CDEF 0123 4567".parse()?;
assert_match!(&KeyHandle::Fingerprint(Fingerprint::V4(_)) = &handle);
assert_eq!(handle.as_bytes(),
[0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23,
0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67]);
let handle: KeyHandle = "89AB CDEF 0123 4567".parse()?;
assert_match!(&KeyHandle::KeyID(KeyID::V4(_)) = &handle);
assert_eq!(handle.as_bytes(),
[0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67]);
let handle: KeyHandle = "4567 89AB CDEF 0123 4567".parse()?;
assert_match!(&KeyHandle::Fingerprint(Fingerprint::Invalid(_)) = &handle);
assert_eq!(handle.as_bytes(),
[0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67]);
let handle: Result<KeyHandle> = "INVALID CHARACTERS".parse();
assert!(handle.is_err());
Ok(())
}
}