pub use failure::{Backtrace, Context, Fail};
use std::error::Error as StdError;
use std::fmt::{self, Display};
use response::ResponseCode;
use securechannel::ResponseMessage;
#[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 {
Some(ref desc) => write!(f, "{}: {}", &self.inner, desc),
None => Display::fmt(&self.inner, f),
}
}
}
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")]
CommandInvalid,
#[fail(display = "invalid data")]
DataInvalid,
#[fail(display = "invalid session")]
SessionInvalid,
#[fail(display = "authentication failed")]
AuthFail,
#[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")]
PermissionInvalid,
#[fail(display = "audit log full")]
LogFull,
#[fail(display = "object not found")]
ObjectNotFound,
#[fail(display = "ID illegal")]
IDIllegal,
#[fail(display = "invalid OTP")]
InvalidOTP,
#[fail(display = "demo mode")]
DemoMode,
#[fail(display = "command unexecuted")]
CmdUnexecuted,
#[fail(display = "generic error")]
GenericError,
#[fail(display = "object already exists")]
ObjectExists,
}
impl HsmErrorKind {
pub fn from_u8(tag: u8) -> HsmErrorKind {
match tag {
0x01 => HsmErrorKind::CommandInvalid,
0x02 => HsmErrorKind::DataInvalid,
0x03 => HsmErrorKind::SessionInvalid,
0x04 => HsmErrorKind::AuthFail,
0x05 => HsmErrorKind::SessionsFull,
0x06 => HsmErrorKind::SessionFailed,
0x07 => HsmErrorKind::StorageFailed,
0x08 => HsmErrorKind::WrongLength,
0x09 => HsmErrorKind::PermissionInvalid,
0x0a => HsmErrorKind::LogFull,
0x0b => HsmErrorKind::ObjectNotFound,
0x0c => HsmErrorKind::IDIllegal,
0x0d => HsmErrorKind::InvalidOTP,
0x0e => HsmErrorKind::DemoMode,
0x0f => HsmErrorKind::CmdUnexecuted,
0x10 => HsmErrorKind::GenericError,
0x11 => HsmErrorKind::ObjectExists,
code => HsmErrorKind::Unknown { code },
}
}
pub fn to_u8(self) -> u8 {
match self {
HsmErrorKind::Unknown { code } => code,
HsmErrorKind::CommandInvalid => 0x01,
HsmErrorKind::DataInvalid => 0x02,
HsmErrorKind::SessionInvalid => 0x03,
HsmErrorKind::AuthFail => 0x04,
HsmErrorKind::SessionsFull => 0x05,
HsmErrorKind::SessionFailed => 0x06,
HsmErrorKind::StorageFailed => 0x07,
HsmErrorKind::WrongLength => 0x08,
HsmErrorKind::PermissionInvalid => 0x09,
HsmErrorKind::LogFull => 0x0a,
HsmErrorKind::ObjectNotFound => 0x0b,
HsmErrorKind::IDIllegal => 0x0c,
HsmErrorKind::InvalidOTP => 0x0d,
HsmErrorKind::DemoMode => 0x0e,
HsmErrorKind::CmdUnexecuted => 0x0f,
HsmErrorKind::GenericError => 0x10,
HsmErrorKind::ObjectExists => 0x11,
}
}
pub fn from_response_code(code: ResponseCode) -> Option<HsmErrorKind> {
Some(match code {
ResponseCode::DeviceInvalidCommand => HsmErrorKind::CommandInvalid,
ResponseCode::DeviceInvalidData => HsmErrorKind::DataInvalid,
ResponseCode::DeviceInvalidSession => HsmErrorKind::SessionInvalid,
ResponseCode::DeviceAuthFail => HsmErrorKind::AuthFail,
ResponseCode::DeviceSessionsFull => HsmErrorKind::SessionsFull,
ResponseCode::DeviceSessionFailed => HsmErrorKind::SessionFailed,
ResponseCode::DeviceStorageFailed => HsmErrorKind::StorageFailed,
ResponseCode::DeviceWrongLength => HsmErrorKind::WrongLength,
ResponseCode::DeviceInvalidPermission => HsmErrorKind::PermissionInvalid,
ResponseCode::DeviceLogFull => HsmErrorKind::LogFull,
ResponseCode::DeviceObjNotFound => HsmErrorKind::ObjectNotFound,
ResponseCode::DeviceIDIllegal => HsmErrorKind::IDIllegal,
ResponseCode::DeviceInvalidOTP => HsmErrorKind::InvalidOTP,
ResponseCode::DeviceDemoMode => HsmErrorKind::DemoMode,
ResponseCode::DeviceCmdUnexecuted => HsmErrorKind::CmdUnexecuted,
ResponseCode::GenericError => HsmErrorKind::GenericError,
ResponseCode::DeviceObjectExists => HsmErrorKind::ObjectExists,
_ => 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) => {
::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());
}
};
}