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