slightly better error handling

by implementing the proper traits for error types
and propagating error where possible
This commit is contained in:
Bilal Elmoussaoui 2025-02-13 12:35:40 +01:00
parent 821229af35
commit dfdc63d424
4 changed files with 50 additions and 49 deletions

View file

@ -24,19 +24,6 @@ pub enum ErrorResponse {
} }
impl ErrorResponse { impl ErrorResponse {
pub fn as_string(self) -> String {
match self {
ErrorResponse::NoSpace => "No Space left on device",
ErrorResponse::CommandAborted => "Command aborted",
ErrorResponse::InvalidInstruction => "Invalid instruction",
ErrorResponse::AuthRequired => "Authentication required",
ErrorResponse::WrongSyntax => "Wrong syntax",
ErrorResponse::GenericError => "Generic Error",
ErrorResponse::NoSuchObject => "No such Object",
}
.to_string()
}
pub fn any_match(code: u16) -> Option<ErrorResponse> { pub fn any_match(code: u16) -> Option<ErrorResponse> {
for resp in ErrorResponse::iter() { for resp in ErrorResponse::iter() {
if code == resp as u16 { if code == resp as u16 {
@ -47,6 +34,22 @@ impl ErrorResponse {
} }
} }
impl std::fmt::Display for ErrorResponse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::NoSpace => f.write_str("No Space left on device"),
Self::CommandAborted => f.write_str("Command aborted"),
Self::InvalidInstruction => f.write_str("Invalid instruction"),
Self::AuthRequired => f.write_str("Authentication required"),
Self::WrongSyntax => f.write_str("Wrong syntax"),
Self::GenericError => f.write_str("Generic Error"),
Self::NoSuchObject => f.write_str("No such Object"),
}
}
}
impl std::error::Error for ErrorResponse {}
#[derive(Debug, EnumIter, Clone, Copy)] #[derive(Debug, EnumIter, Clone, Copy)]
#[repr(u16)] #[repr(u16)]
pub enum SuccessResponse { pub enum SuccessResponse {

View file

@ -30,7 +30,7 @@ fn main() {
for yubikey in yubikeys { for yubikey in yubikeys {
let device_label: &str = yubikey; let device_label: &str = yubikey;
println!("Found device with label {}", device_label); println!("Found device with label {}", device_label);
let session = OathSession::new(yubikey); let session = OathSession::new(yubikey).unwrap();
println!("YubiKey version is {:?}", session.get_version()); println!("YubiKey version is {:?}", session.get_version());
for c in session.list_oath_codes().unwrap() { for c in session.list_oath_codes().unwrap() {
println!("{}", c); println!("{}", c);

View file

@ -133,8 +133,8 @@ impl<'a> RefreshableOathCredential<'a> {
} }
impl<'a> OathSession<'a> { impl<'a> OathSession<'a> {
pub fn new(name: &str) -> Self { pub fn new(name: &str) -> Result<Self, FormattableErrorResponse> {
let transaction_context = TransactionContext::from_name(name); let transaction_context = TransactionContext::from_name(name)?;
let info_buffer = transaction_context let info_buffer = transaction_context
.apdu_read_all(0, INS_SELECT, 0x04, 0, Some(&OATH_AID)) .apdu_read_all(0, INS_SELECT, 0x04, 0, Some(&OATH_AID))
.unwrap(); .unwrap();
@ -145,7 +145,7 @@ impl<'a> OathSession<'a> {
println!("{:?}: {:?}", tag, data); println!("{:?}: {:?}", tag, data);
} }
OathSession { Ok(Self {
version: clone_with_lifetime( version: clone_with_lifetime(
info_map.get(&(Tag::Version as u8)).unwrap_or(&vec![0u8; 0]), info_map.get(&(Tag::Version as u8)).unwrap_or(&vec![0u8; 0]),
) )
@ -160,7 +160,7 @@ impl<'a> OathSession<'a> {
.leak(), .leak(),
name: name.to_string(), name: name.to_string(),
transaction_context, transaction_context,
} })
} }
pub fn get_version(&self) -> &[u8] { pub fn get_version(&self) -> &[u8] {

View file

@ -18,23 +18,23 @@ pub enum FormattableErrorResponse {
} }
impl FormattableErrorResponse { impl FormattableErrorResponse {
pub fn from_apdu_response(sw1: u8, sw2: u8) -> FormattableErrorResponse { pub fn from_apdu_response(sw1: u8, sw2: u8) -> Self {
let code: u16 = (sw1 as u16 | sw2 as u16) << 8; let code: u16 = (sw1 as u16 | sw2 as u16) << 8;
if let Some(e) = ErrorResponse::any_match(code) { if let Some(e) = ErrorResponse::any_match(code) {
return FormattableErrorResponse::Protocol(e); return Self::Protocol(e);
} }
if SuccessResponse::any_match(code) if SuccessResponse::any_match(code)
.or(SuccessResponse::any_match(sw1.into())) .or(SuccessResponse::any_match(sw1.into()))
.is_some() .is_some()
{ {
return FormattableErrorResponse::NoError; return Self::NoError;
} }
FormattableErrorResponse::Unknown(String::from("Unknown error")) Self::Unknown(String::from("Unknown error"))
} }
pub fn is_ok(&self) -> bool { pub fn is_ok(&self) -> bool {
*self == FormattableErrorResponse::NoError *self == Self::NoError
} }
pub fn as_opt(self) -> Option<FormattableErrorResponse> { pub fn as_opt(self) -> Option<Self> {
if self.is_ok() { if self.is_ok() {
None None
} else { } else {
@ -42,25 +42,27 @@ impl FormattableErrorResponse {
} }
} }
fn from_transmit(err: pcsc::Error) -> FormattableErrorResponse { fn from_transmit(err: pcsc::Error) -> Self {
FormattableErrorResponse::PcscError(err) Self::PcscError(err)
} }
}
fn as_string(&self) -> String { impl From<pcsc::Error> for FormattableErrorResponse {
match self { fn from(value: pcsc::Error) -> Self {
FormattableErrorResponse::NoError => "ok".to_string(), Self::PcscError(value)
FormattableErrorResponse::Unknown(msg) => msg.to_owned(),
FormattableErrorResponse::Protocol(error_response) => error_response.as_string(),
FormattableErrorResponse::PcscError(error) => format!("{}", error),
FormattableErrorResponse::ParsingError(msg) => msg.to_owned(),
FormattableErrorResponse::DeviceMismatchError => "Devices do not match".to_string(),
}
} }
} }
impl Display for FormattableErrorResponse { impl Display for FormattableErrorResponse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.as_string()) match self {
Self::NoError => f.write_str("ok"),
Self::Unknown(msg) => f.write_str(msg),
Self::Protocol(error_response) => f.write_fmt(format_args!("{}", error_response)),
Self::PcscError(error) => f.write_fmt(format_args!("{}", error)),
Self::ParsingError(msg) => f.write_str(msg),
Self::DeviceMismatchError => f.write_str("Devices do not match"),
}
} }
} }
@ -139,26 +141,22 @@ pub struct TransactionContext {
} }
impl TransactionContext { impl TransactionContext {
pub fn from_name(name: &str) -> Self { pub fn from_name(name: &str) -> Result<Self, FormattableErrorResponse> {
// FIXME: error handling here
// Establish a PC/SC context // Establish a PC/SC context
let ctx = pcsc::Context::establish(pcsc::Scope::User).unwrap(); let ctx = pcsc::Context::establish(pcsc::Scope::User)?;
// Connect to the card // Connect to the card
let card = ctx let card = ctx.connect(
.connect( &CString::new(name).unwrap(),
&CString::new(name).unwrap(), pcsc::ShareMode::Shared,
pcsc::ShareMode::Shared, pcsc::Protocols::ANY,
pcsc::Protocols::ANY, )?;
)
.unwrap();
TransactionContextBuilder { Ok(TransactionContextBuilder {
card, card,
transaction_builder: |c| c.transaction().unwrap(), transaction_builder: |c| c.transaction().unwrap(),
} }
.build() .build())
} }
pub fn apdu( pub fn apdu(