use serde::Deserialize;
use std::fmt::Debug;
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum CkTapError {
#[error(transparent)]
Card(#[from] CardError),
#[error("CBOR deserialization error: {0}")]
CborDe(String),
#[error("CBOR value error: {0}")]
CborValue(String),
#[error("APDU transport error: {0}")]
Transport(String),
#[error("Unknown card type")]
UnknownCardType,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
pub enum CardError {
#[error("Rare or unlucky value used/occurred. Start again")]
UnluckyNumber,
#[error("Invalid/incorrect/incomplete arguments provided to command")]
BadArguments,
#[error("Authentication details (CVC/epubkey) are wrong")]
BadAuth,
#[error("Command requires auth, and none was provided")]
NeedsAuth,
#[error("The 'cmd' field is an unsupported command")]
UnknownCommand,
#[error("Command is not valid at this time, no point retrying")]
InvalidCommand,
#[error("You can't do that right now when card is in this state")]
InvalidState,
#[error("Nonce is not unique-looking enough")]
WeakNonce,
#[error("Unable to decode CBOR data stream")]
BadCBOR,
#[error("Can't change CVC without doing a backup first")]
BackupFirst,
#[error("Due to auth failures, delay required")]
RateLimited,
}
impl CardError {
pub fn error_from_code(code: u16) -> Option<CardError> {
match code {
205 => Some(CardError::UnluckyNumber),
400 => Some(CardError::BadArguments),
401 => Some(CardError::BadAuth),
403 => Some(CardError::NeedsAuth),
404 => Some(CardError::UnknownCommand),
405 => Some(CardError::InvalidCommand),
406 => Some(CardError::InvalidState),
417 => Some(CardError::WeakNonce),
422 => Some(CardError::BadCBOR),
425 => Some(CardError::BackupFirst),
429 => Some(CardError::RateLimited),
_ => None,
}
}
pub fn error_code(&self) -> u16 {
match self {
CardError::UnluckyNumber => 205,
CardError::BadArguments => 400,
CardError::BadAuth => 401,
CardError::NeedsAuth => 403,
CardError::UnknownCommand => 404,
CardError::InvalidCommand => 405,
CardError::InvalidState => 406,
CardError::WeakNonce => 417,
CardError::BadCBOR => 422,
CardError::BackupFirst => 425,
CardError::RateLimited => 429,
}
}
}
impl<T> From<ciborium::de::Error<T>> for CkTapError
where
T: Debug,
{
fn from(e: ciborium::de::Error<T>) -> Self {
CkTapError::CborDe(e.to_string())
}
}
impl From<ciborium::value::Error> for CkTapError {
fn from(e: ciborium::value::Error) -> Self {
CkTapError::CborValue(e.to_string())
}
}
#[cfg(feature = "pcsc")]
impl From<pcsc::Error> for CkTapError {
fn from(e: pcsc::Error) -> Self {
CkTapError::Transport(e.to_string())
}
}
#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct ErrorResponse {
pub error: String,
pub code: u16,
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum StatusError {
#[error(transparent)]
CkTap(#[from] CkTapError),
#[error(transparent)]
KeyFromSlice(#[from] bitcoin::key::FromSliceError),
}
#[cfg(feature = "pcsc")]
impl From<pcsc::Error> for StatusError {
fn from(e: pcsc::Error) -> Self {
StatusError::CkTap(CkTapError::Transport(e.to_string()))
}
}
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
pub enum ChangeError {
#[error(transparent)]
CkTap(#[from] CkTapError),
#[error("new cvc is too short, must be at least 6 bytes, was only {0} bytes")]
TooShort(usize),
#[error("new cvc is too long, must be at most 32 bytes, was {0} bytes")]
TooLong(usize),
#[error("new cvc is the same as the old one")]
SameAsOld,
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum ReadError {
#[error(transparent)]
CkTap(#[from] CkTapError),
#[error(transparent)]
Secp256k1(#[from] bitcoin::secp256k1::Error),
#[error(transparent)]
KeyFromSlice(#[from] bitcoin::key::FromSliceError),
}
impl From<ReadError> for CertsError {
fn from(e: ReadError) -> Self {
match e {
ReadError::CkTap(e) => CertsError::CkTap(e),
ReadError::Secp256k1(e) => CertsError::Secp256k1(e),
ReadError::KeyFromSlice(e) => CertsError::KeyFromSlice(e),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum CertsError {
#[error(transparent)]
CkTap(#[from] CkTapError),
#[error(transparent)]
Secp256k1(#[from] bitcoin::secp256k1::Error),
#[error(transparent)]
KeyFromSlice(#[from] bitcoin::key::FromSliceError),
#[error("Root cert is not from Coinkite. Card is counterfeit: {0}")]
InvalidRootCert(String),
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum DeriveError {
#[error(transparent)]
CkTap(#[from] CkTapError),
#[error(transparent)]
Secp256k1(#[from] bitcoin::secp256k1::Error),
#[error(transparent)]
KeyFromSlice(#[from] bitcoin::key::FromSliceError),
#[error("Invalid chain code: {0}")]
InvalidChainCode(String),
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum XpubError {
#[error(transparent)]
CkTap(#[from] CkTapError),
#[error(transparent)]
Bip32(#[from] bitcoin::bip32::Error),
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum UnsealError {
#[error(transparent)]
CkTap(#[from] CkTapError),
#[error(transparent)]
Secp256k1(#[from] bitcoin::secp256k1::Error),
#[error(transparent)]
KeyFromSlice(#[from] bitcoin::key::FromSliceError),
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum DumpError {
#[error(transparent)]
CkTap(#[from] CkTapError),
#[error(transparent)]
Secp256k1(#[from] bitcoin::secp256k1::Error),
#[error(transparent)]
KeyFromSlice(#[from] bitcoin::key::FromSliceError),
#[error("Slot is sealed: {0}")]
SlotSealed(u8),
#[error("Slot is unused: {0}")]
SlotUnused(u8),
#[error("Slot was unsealed improperly: {0}")]
SlotTampered(u8),
}
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
pub enum SignPsbtError {
#[error("Invalid path at index: {0}")]
InvalidPath(usize),
#[error("Invalid script at index: {0}")]
InvalidScript(usize),
#[error("Missing pubkey at index: {0}")]
MissingPubkey(usize),
#[error("Missing UTXO at index: {0}")]
MissingUtxo(usize),
#[error("Pubkey mismatch at index: {0}")]
PubkeyMismatch(usize),
#[error("Sighash error: {0}")]
SighashError(String),
#[error("Signature error: {0}")]
SignatureError(String),
#[error("Signing slot is not unsealed: {0}")]
SlotNotUnsealed(u8),
#[error(transparent)]
CkTap(#[from] CkTapError),
#[error("Witness program error: {0}")]
WitnessProgram(String),
}