use std::fmt; use std::io; use yubikey::{piv::RetiredSlotId, Serial}; use crate::util::slot_to_ui; pub enum Error { CustomManagementKey, InvalidFlagCommand(String, String), InvalidFlagTui(String), InvalidPinLength, InvalidPinPolicy(String), InvalidSlot(u8), InvalidTouchPolicy(String), Io(io::Error), MultipleCommands, MultipleYubiKeys, NoEmptySlots(Serial), NoMatchingSerial(Serial), SlotHasNoIdentity(RetiredSlotId), SlotIsNotEmpty(RetiredSlotId), TimedOut, UseListForSingleSlot, YubiKey(yubikey::Error), } impl From for Error { fn from(e: io::Error) -> Self { Error::Io(e) } } impl From for Error { fn from(e: yubikey::Error) -> Self { Error::YubiKey(e) } } // Rust only supports `fn main() -> Result<(), E: Debug>`, so we implement `Debug` // manually to provide the error output we want. impl fmt::Debug for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Error::CustomManagementKey => { writeln!(f, "Custom unprotected management keys are not supported.")? } Error::InvalidFlagCommand(flag, command) => { writeln!(f, "Flag '{}' cannot be used with '{}'.", flag, command)? } Error::InvalidFlagTui(flag) => writeln!( f, "Flag '{}' cannot be used with the interactive interface.", flag )?, Error::InvalidPinLength => writeln!(f, "The PIN needs to be 1-8 characters.")?, Error::InvalidPinPolicy(s) => writeln!( f, "Invalid PIN policy '{}' (expected [always, once, never]).", s )?, Error::InvalidSlot(slot) => writeln!( f, "Invalid slot '{}' (expected number between 1 and 20).", slot )?, Error::InvalidTouchPolicy(s) => writeln!( f, "Invalid touch policy '{}' (expected [always, cached, never]).", s )?, Error::Io(e) => writeln!(f, "Failed to set up YubiKey: {}", e)?, Error::MultipleCommands => writeln!( f, "Only one of --generate, --identity, --list, --list-all can be specified." )?, Error::MultipleYubiKeys => writeln!( f, "Multiple YubiKeys are plugged in. Use --serial to select a single YubiKey." )?, Error::NoEmptySlots(serial) => { writeln!(f, "YubiKey with serial {} has no empty slots.", serial)? } Error::NoMatchingSerial(serial) => { writeln!(f, "Could not find YubiKey with serial {}.", serial)? } Error::SlotHasNoIdentity(slot) => writeln!( f, "Slot {} does not contain an age identity or compatible key.", slot_to_ui(slot) )?, Error::SlotIsNotEmpty(slot) => writeln!( f, "Slot {} is not empty. Use --force to overwrite the slot.", slot_to_ui(slot) )?, Error::TimedOut => { writeln!(f, "Timed out while waiting for a YubiKey to be inserted.")? } Error::UseListForSingleSlot => { writeln!(f, "Use --list to print the recipient for a single slot.")? } Error::YubiKey(e) => match e { yubikey::Error::NotFound => { writeln!(f, "Please insert the YubiKey you want to set up")? } yubikey::Error::WrongPin { tries } => writeln!( f, "Invalid PIN ({} tries remaining before it is blocked)", tries )?, e => { writeln!(f, "Error while communicating with YubiKey: {}", e)?; use std::error::Error; if let Some(inner) = e.source() { writeln!(f, "Cause: {}", inner)?; } } }, } writeln!(f)?; writeln!( f, "[ Did this not do what you expected? Could an error be more useful? ]" )?; write!( f, "[ Tell us: https://str4d.xyz/age-plugin-yubikey/report ]" ) } }