card_backend/lib.rs
1// SPDX-FileCopyrightText: 2021-2023 Heiko Schaefer <heiko@schaefer.name>
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! A thin abstraction layer for accessing smart cards, including, but not
5//! limited to, [openpgp-card](https://gitlab.com/openpgp-card/openpgp-card)
6//! devices.
7
8/// This trait defines a connection with a smart card via a
9/// backend implementation (e.g. via the pcsc backend in the crate
10/// [card-backend-pcsc](https://crates.io/crates/card-backend-pcsc)).
11///
12/// A [CardBackend] is only used to get access to a [CardTransaction] object,
13/// which supports transmitting commands to the card.
14pub trait CardBackend {
15 /// If a CardBackend introduces a additional (possibly backend-specific)
16 /// limits for any fields in CardCaps, this fn can indicate that limit by
17 /// returning an amended [`CardCaps`].
18 fn limit_card_caps(&self, card_caps: CardCaps) -> CardCaps;
19
20 fn transaction(
21 &mut self,
22 reselect_application: Option<&[u8]>,
23 ) -> Result<Box<dyn CardTransaction + Send + Sync + '_>, SmartcardError>;
24}
25
26/// The CardTransaction trait defines communication with a smart card via a
27/// backend implementation (e.g. the pcsc backend in the crate
28/// [card-backend-pcsc](https://crates.io/crates/card-backend-pcsc)),
29/// after opening a transaction from a CardBackend.
30pub trait CardTransaction {
31 /// Transmit the command data in `cmd` to the card.
32 ///
33 /// `buf_size` is a hint to the backend (the backend may ignore it)
34 /// indicating the expected maximum response size.
35 fn transmit(&mut self, cmd: &[u8], buf_size: usize) -> Result<Vec<u8>, SmartcardError>;
36
37 /// Select `application` on the card
38 fn select(&mut self, application: &[u8]) -> Result<Vec<u8>, SmartcardError> {
39 let mut cmd = vec![0x00, 0xa4, 0x04, 0x00]; // CLA, INS, P1, P2
40 cmd.push(application.len() as u8); // Lc
41 cmd.extend_from_slice(application); // Data
42 cmd.push(0x00); // Le
43
44 self.transmit(&cmd, 254)
45 }
46
47 /// If a CardTransaction implementation introduces an additional,
48 /// backend-specific limit for maximum number of bytes per command,
49 /// this fn can indicate that limit by returning `Some(max_cmd_len)`.
50 fn max_cmd_len(&self) -> Option<usize> {
51 None
52 }
53
54 /// Does the reader support FEATURE_VERIFY_PIN_DIRECT?
55 fn feature_pinpad_verify(&self) -> bool;
56
57 /// Does the reader support FEATURE_MODIFY_PIN_DIRECT?
58 fn feature_pinpad_modify(&self) -> bool;
59
60 /// Verify the PIN `pin` via the reader pinpad
61 fn pinpad_verify(
62 &mut self,
63 pin: PinType,
64 card_caps: &Option<CardCaps>,
65 ) -> Result<Vec<u8>, SmartcardError>;
66
67 /// Modify the PIN `pin` via the reader pinpad
68 fn pinpad_modify(
69 &mut self,
70 pin: PinType,
71 card_caps: &Option<CardCaps>,
72 ) -> Result<Vec<u8>, SmartcardError>;
73
74 /// Has a reset been detected while starting this transaction?
75 ///
76 /// (Backends may choose to always return false)
77 fn was_reset(&self) -> bool;
78}
79
80/// Information about the capabilities of a card.
81///
82/// CardCaps is used to signal capabilities (chaining, extended length support, max
83/// command/response sizes, max PIN lengths) of the current card to backends.
84///
85/// CardCaps is not intended for users of this library.
86///
87/// (The information is gathered from the "Card Capabilities", "Extended length information" and
88/// "PWStatus" DOs)
89#[derive(Clone, Copy, Debug)]
90pub struct CardCaps {
91 ext_support: bool,
92 chaining_support: bool,
93 max_cmd_bytes: u16,
94 max_rsp_bytes: u16,
95 pw1_max_len: u8,
96 pw3_max_len: u8,
97}
98
99impl CardCaps {
100 pub fn new(
101 ext_support: bool,
102 chaining_support: bool,
103 max_cmd_bytes: u16,
104 max_rsp_bytes: u16,
105 pw1_max_len: u8,
106 pw3_max_len: u8,
107 ) -> Self {
108 Self {
109 ext_support,
110 chaining_support,
111 max_cmd_bytes,
112 max_rsp_bytes,
113 pw1_max_len,
114 pw3_max_len,
115 }
116 }
117
118 /// Does the card support extended Lc and Le fields?
119 pub fn ext_support(&self) -> bool {
120 self.ext_support
121 }
122
123 /// Does the card support command chaining?
124 pub fn chaining_support(&self) -> bool {
125 self.chaining_support
126 }
127
128 /// Maximum number of bytes in a command APDU
129 pub fn max_cmd_bytes(&self) -> u16 {
130 self.max_cmd_bytes
131 }
132
133 /// Maximum number of bytes in a response APDU
134 pub fn max_rsp_bytes(&self) -> u16 {
135 self.max_rsp_bytes
136 }
137
138 /// Maximum length of PW1
139 pub fn pw1_max_len(&self) -> u8 {
140 self.pw1_max_len
141 }
142
143 /// Maximum length of PW3
144 pub fn pw3_max_len(&self) -> u8 {
145 self.pw3_max_len
146 }
147}
148
149/// Specify a PIN to *verify* (distinguishes between `Sign`, `User` and `Admin`).
150///
151/// (Note that for PIN *management*, in particular changing a PIN, "signing and user" are
152/// not distinguished. They always share the same PIN value `PW1`)
153#[derive(Debug, Clone, Copy, Eq, PartialEq)]
154pub enum PinType {
155 /// Verify PW1 in mode P2=81 (for the PSO:CDS operation)
156 Sign,
157
158 /// Verify PW1 in mode P2=82 (for all other User operations)
159 User,
160
161 /// Verify PW3 (for Admin operations)
162 Admin,
163}
164
165impl PinType {
166 pub fn id(&self) -> u8 {
167 match self {
168 PinType::Sign => 0x81,
169 PinType::User => 0x82,
170 PinType::Admin => 0x83,
171 }
172 }
173}
174
175/// Errors on the smartcard/reader layer
176#[derive(thiserror::Error, Debug)]
177#[non_exhaustive]
178pub enum SmartcardError {
179 #[error("Failed to create a pcsc smartcard context {0}")]
180 ContextError(String),
181
182 #[error("Failed to list readers: {0}")]
183 ReaderError(String),
184
185 #[error("No reader found.")]
186 NoReaderFoundError,
187
188 #[error("The requested card '{0}' was not found.")]
189 CardNotFound(String),
190
191 #[error("Failed to connect to the card: {0}")]
192 SmartCardConnectionError(String),
193
194 #[error("Smart card status: [{0}, {1}]")]
195 SmartCardStatus(u8, u8),
196
197 #[error("NotTransacted (SCARD_E_NOT_TRANSACTED)")]
198 NotTransacted,
199
200 #[error("Generic SmartCard Error: {0}")]
201 Error(String),
202}