pub use failure::{Backtrace, Context, Fail};
use std::error::Error as StdError;
use std::fmt::{self, Display};
use crate::response::{ResponseCode, ResponseMessage};
const NO_DESCRIPTION: &str = "(no description)";
#[derive(Debug)]
pub struct Error<T>
where
T: Copy + Display + Fail + PartialEq + Eq,
{
inner: Context<T>,
description: Option<String>,
}
impl<T> Error<T>
where
T: Copy + Display + Fail + PartialEq + Eq,
{
pub fn new(kind: T, description: Option<String>) -> Self {
Self {
inner: Context::new(kind),
description,
}
}
pub fn kind(&self) -> T {
*self.inner.get_context()
}
}
impl<T> Display for Error<T>
where
T: Copy + Display + Fail + PartialEq + Eq,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.description {
None => Display::fmt(&self.inner, f),
Some(ref desc) => {
if desc == NO_DESCRIPTION {
Display::fmt(&self.inner, f)
} else {
write!(f, "{}: {}", &self.inner, desc)
}
}
}
}
}
impl<T> StdError for Error<T>
where
T: Copy + Display + Fail + PartialEq + Eq,
{
fn description(&self) -> &str {
match self.description {
Some(ref s) => s,
None => NO_DESCRIPTION,
}
}
}
pub type HsmError = Error<HsmErrorKind>;
#[derive(Copy, Clone, Eq, PartialEq, Debug, Fail)]
pub enum HsmErrorKind {
#[fail(display = "unknown HSM error code: 0x{:02x}", code)]
Unknown {
code: u8,
},
#[fail(display = "invalid command")]
InvalidCommand,
#[fail(display = "invalid data")]
InvalidData,
#[fail(display = "invalid session")]
InvalidSession,
#[fail(display = "authentication failed")]
AuthenticationFailed,
#[fail(display = "sessions full (max 16)")]
SessionsFull,
#[fail(display = "session failed")]
SessionFailed,
#[fail(display = "storage failed")]
StorageFailed,
#[fail(display = "incorrect length")]
WrongLength,
#[fail(display = "invalid permissions")]
InsufficientPermissions,
#[fail(display = "audit log full")]
LogFull,
#[fail(display = "object not found")]
ObjectNotFound,
#[fail(display = "invalid ID")]
InvalidId,
#[fail(display = "invalid OTP")]
InvalidOtp,
#[fail(display = "demo mode")]
DemoMode,
#[fail(display = "command unexecuted")]
CommandUnexecuted,
#[fail(display = "generic error")]
GenericError,
#[fail(display = "object already exists")]
ObjectExists,
#[fail(display = "SSH CA constraint violation")]
SshCaConstraintViolation,
}
impl HsmErrorKind {
pub fn from_u8(tag: u8) -> HsmErrorKind {
match tag {
0x01 => HsmErrorKind::InvalidCommand,
0x02 => HsmErrorKind::InvalidData,
0x03 => HsmErrorKind::InvalidSession,
0x04 => HsmErrorKind::AuthenticationFailed,
0x05 => HsmErrorKind::SessionsFull,
0x06 => HsmErrorKind::SessionFailed,
0x07 => HsmErrorKind::StorageFailed,
0x08 => HsmErrorKind::WrongLength,
0x09 => HsmErrorKind::InsufficientPermissions,
0x0a => HsmErrorKind::LogFull,
0x0b => HsmErrorKind::ObjectNotFound,
0x0c => HsmErrorKind::InvalidId,
0x0d => HsmErrorKind::InvalidOtp,
0x0e => HsmErrorKind::DemoMode,
0x0f => HsmErrorKind::CommandUnexecuted,
0x10 => HsmErrorKind::GenericError,
0x11 => HsmErrorKind::ObjectExists,
code => HsmErrorKind::Unknown { code },
}
}
pub fn to_u8(self) -> u8 {
match self {
HsmErrorKind::Unknown { code } => code,
HsmErrorKind::InvalidCommand => 0x01,
HsmErrorKind::InvalidData => 0x02,
HsmErrorKind::InvalidSession => 0x03,
HsmErrorKind::AuthenticationFailed => 0x04,
HsmErrorKind::SessionsFull => 0x05,
HsmErrorKind::SessionFailed => 0x06,
HsmErrorKind::StorageFailed => 0x07,
HsmErrorKind::WrongLength => 0x08,
HsmErrorKind::InsufficientPermissions => 0x09,
HsmErrorKind::LogFull => 0x0a,
HsmErrorKind::ObjectNotFound => 0x0b,
HsmErrorKind::InvalidId => 0x0c,
HsmErrorKind::InvalidOtp => 0x0d,
HsmErrorKind::DemoMode => 0x0e,
HsmErrorKind::CommandUnexecuted => 0x0f,
HsmErrorKind::GenericError => 0x10,
HsmErrorKind::ObjectExists => 0x11,
HsmErrorKind::SshCaConstraintViolation => {
panic!("don't know device code for YHR_DEVICE_SSH_CA_CONSTRAINT_VIOLATION")
}
}
}
pub fn from_response_code(code: ResponseCode) -> Option<HsmErrorKind> {
Some(match code {
ResponseCode::DeviceInvalidCommand => HsmErrorKind::InvalidCommand,
ResponseCode::DeviceInvalidData => HsmErrorKind::InvalidData,
ResponseCode::DeviceInvalidSession => HsmErrorKind::InvalidSession,
ResponseCode::DeviceAuthenticationFailed => HsmErrorKind::AuthenticationFailed,
ResponseCode::DeviceSessionsFull => HsmErrorKind::SessionsFull,
ResponseCode::DeviceSessionFailed => HsmErrorKind::SessionFailed,
ResponseCode::DeviceStorageFailed => HsmErrorKind::StorageFailed,
ResponseCode::DeviceWrongLength => HsmErrorKind::WrongLength,
ResponseCode::DeviceInsufficientPermissions => HsmErrorKind::InsufficientPermissions,
ResponseCode::DeviceLogFull => HsmErrorKind::LogFull,
ResponseCode::DeviceObjectNotFound => HsmErrorKind::ObjectNotFound,
ResponseCode::DeviceInvalidId => HsmErrorKind::InvalidId,
ResponseCode::DeviceInvalidOtp => HsmErrorKind::InvalidOtp,
ResponseCode::DeviceDemoMode => HsmErrorKind::DemoMode,
ResponseCode::DeviceCommandUnexecuted => HsmErrorKind::CommandUnexecuted,
ResponseCode::GenericError => HsmErrorKind::GenericError,
ResponseCode::DeviceObjectExists => HsmErrorKind::ObjectExists,
ResponseCode::DeviceSshCaConstraintViolation => HsmErrorKind::SshCaConstraintViolation,
_ => return None,
})
}
pub(crate) fn from_response_message(response: &ResponseMessage) -> Option<HsmErrorKind> {
if response.is_err() && response.data.len() == 1 {
Some(HsmErrorKind::from_u8(response.data[0]))
} else {
None
}
}
}
macro_rules! err {
($kind:path, $msg:expr) => {
crate::error::Error::new($kind, Some($msg.to_string()))
};
($kind:path, $fmt:expr, $($arg:tt)+) => {
err!($kind, &format!($fmt, $($arg)+))
};
}
macro_rules! fail {
($kind:path, $msg:expr) => {
return Err(err!($kind, $msg).into());
};
($kind:path, $fmt:expr, $($arg:tt)+) => {
fail!($kind, &format!($fmt, $($arg)+));
};
}
macro_rules! ensure {
($cond:expr, $kind:path, $msg:expr) => {
if !($cond) {
return Err(err!($kind, $msg).into());
}
};
($cond:expr, $kind:path, $fmt:expr, $($arg:tt)+) => {
if !($cond) {
return Err(err!($kind, $fmt, $($arg)+).into());
}
};
}