1mod catalog;
16
17pub use catalog::{explain_pcsc, render, Explanation, RenderEnv};
18
19use crate::piv::MgmtAlgo;
20use std::fmt;
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub enum CardOp {
25 SelectPiv,
26 VerifyPin,
27 ChangePin,
29 ChangePuk,
31 MgmtAuth,
32 SetMgmtKey,
33 ReadObject(u32),
34 ReadCertificate(u8),
36 WriteObject(u32),
37 GenerateKey(u8),
38 Sign(u8),
39 Ecdh(u8),
40 GetMetadata(u8),
41 Command,
43}
44
45impl CardOp {
46 pub fn describe(&self) -> String {
48 match self {
49 Self::SelectPiv => "selecting the PIV application".to_owned(),
50 Self::VerifyPin => "PIN verification".to_owned(),
51 Self::ChangePin => "PIN change".to_owned(),
52 Self::ChangePuk => "PUK change".to_owned(),
53 Self::MgmtAuth => "management key authentication".to_owned(),
54 Self::SetMgmtKey => "changing the management key".to_owned(),
55 Self::ReadObject(id) => format!("read object 0x{id:06X}"),
56 Self::ReadCertificate(slot) => format!("read certificate of slot 0x{slot:02x}"),
57 Self::WriteObject(id) => format!("write object 0x{id:06X}"),
58 Self::GenerateKey(slot) => format!("key generation in slot 0x{slot:02x}"),
59 Self::Sign(slot) => format!("signature with slot 0x{slot:02x}"),
60 Self::Ecdh(slot) => format!("key agreement with slot 0x{slot:02x}"),
61 Self::GetMetadata(slot) => format!("metadata of slot 0x{slot:02x}"),
62 Self::Command => "card command".to_owned(),
63 }
64 }
65}
66
67#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
69pub struct ErrCtx {
70 pub algo: Option<MgmtAlgo>,
72}
73
74#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76pub enum PcscOp {
77 Establish,
78 ListReaders,
79 Connect,
80 Transmit,
81}
82
83impl PcscOp {
84 fn describe(self) -> &'static str {
85 match self {
86 Self::Establish => "PC/SC context",
87 Self::ListReaders => "listing PC/SC readers",
88 Self::Connect => "connecting to the YubiKey",
89 Self::Transmit => "exchanging data with the YubiKey",
90 }
91 }
92}
93
94#[derive(Debug, Clone, PartialEq, Eq)]
96pub enum PcscCode {
97 NoService,
98 NoReaders,
99 SharingViolation,
100 RemovedCard,
101 ResetCard,
102 Other(String),
104}
105
106impl From<pcsc::Error> for PcscCode {
107 fn from(e: pcsc::Error) -> Self {
108 match e {
109 pcsc::Error::NoService | pcsc::Error::ServiceStopped => Self::NoService,
110 pcsc::Error::NoReadersAvailable | pcsc::Error::UnknownReader => Self::NoReaders,
111 pcsc::Error::SharingViolation => Self::SharingViolation,
112 pcsc::Error::RemovedCard | pcsc::Error::NoSmartcard => Self::RemovedCard,
113 pcsc::Error::ResetCard => Self::ResetCard,
114 other => Self::Other(format!("{other:?}")),
115 }
116 }
117}
118
119impl fmt::Display for PcscCode {
120 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121 match self {
122 Self::NoService => f.write_str("SCARD_E_NO_SERVICE"),
123 Self::NoReaders => f.write_str("SCARD_E_NO_READERS_AVAILABLE"),
124 Self::SharingViolation => f.write_str("SCARD_E_SHARING_VIOLATION"),
125 Self::RemovedCard => f.write_str("SCARD_W_REMOVED_CARD"),
126 Self::ResetCard => f.write_str("SCARD_W_RESET_CARD"),
127 Self::Other(name) => write!(f, "PC/SC {name}"),
128 }
129 }
130}
131
132#[derive(Debug, Clone, PartialEq, Eq)]
134pub enum CardError {
135 Status { op: CardOp, sw: u16, ctx: ErrCtx },
137 Pcsc { op: PcscOp, code: PcscCode },
139 Protocol { op: CardOp, what: String },
142}
143
144impl CardError {
145 pub fn status(op: CardOp, sw1: u8, sw2: u8) -> Self {
146 Self::Status {
147 op,
148 sw: u16::from_be_bytes([sw1, sw2]),
149 ctx: ErrCtx::default(),
150 }
151 }
152
153 pub fn with_ctx(mut self, new_ctx: ErrCtx) -> Self {
154 if let Self::Status { ref mut ctx, .. } = self {
155 *ctx = new_ctx;
156 }
157 self
158 }
159
160 pub fn pcsc(op: PcscOp, e: pcsc::Error) -> Self {
161 Self::Pcsc { op, code: e.into() }
162 }
163
164 pub fn protocol(op: CardOp, what: impl Into<String>) -> Self {
165 Self::Protocol {
166 op,
167 what: what.into(),
168 }
169 }
170
171 pub fn details(&self) -> String {
173 match self {
174 Self::Status { op, sw, .. } => format!("{} → SW {sw:04X}", op.describe()),
175 Self::Pcsc { op, code } => format!("{} → {code}", op.describe()),
176 Self::Protocol { op, what } => format!("{} → {what}", op.describe()),
177 }
178 }
179}
180
181impl fmt::Display for CardError {
182 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183 f.write_str(&self.details())
184 }
185}
186
187impl std::error::Error for CardError {}
188
189#[derive(Debug, Clone, PartialEq, Eq)]
191pub struct YbError {
192 pub what: String,
193 pub why: Option<String>,
194 pub fix: Option<String>,
195}
196
197impl YbError {
198 pub fn new(what: impl Into<String>) -> Self {
199 Self {
200 what: what.into(),
201 why: None,
202 fix: None,
203 }
204 }
205
206 pub fn why(mut self, why: impl Into<String>) -> Self {
207 self.why = Some(why.into());
208 self
209 }
210
211 pub fn fix(mut self, fix: impl Into<String>) -> Self {
212 self.fix = Some(fix.into());
213 self
214 }
215}
216
217impl fmt::Display for YbError {
218 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219 f.write_str(&self.what)?;
220 if let Some(ref why) = self.why {
221 write!(f, " ({why})")?;
222 }
223 if let Some(ref fix) = self.fix {
224 write!(f, "; {fix}")?;
225 }
226 Ok(())
227 }
228}
229
230impl std::error::Error for YbError {}