1use futures::StreamExt;
11use lazy_static::lazy_static;
12use sequoia_ipc::assuan::Response;
13use sequoia_ipc::gnupg::{Agent, Context};
14use std::sync::Mutex;
15use tokio::runtime::Runtime;
16
17use openpgp_card::{CardBackend, CardCaps, CardTransaction, Error, PinType, SmartcardError};
18
19lazy_static! {
20 static ref RT: Mutex<Runtime> = Mutex::new(tokio::runtime::Runtime::new().unwrap());
21}
22
23const ASSUAN_LINELENGTH: usize = 1000;
35
36const APDU_CMD_BYTES_MAX: usize = (ASSUAN_LINELENGTH - 25) / 2;
50
51pub struct ScdBackend {
54 agent: Agent,
55 card_caps: Option<CardCaps>,
56}
57
58impl ScdBackend {
59 pub fn open_by_serial(agent: Option<Agent>, serial: &str) -> Result<Self, Error> {
62 let mut card = ScdBackend::new(agent, true)?;
63 card.select_card(serial)?;
64
65 card.transaction()?.initialize()?;
66
67 Ok(card)
68 }
69
70 pub fn open_yolo(agent: Option<Agent>) -> Result<Self, Error> {
77 let mut card = ScdBackend::new(agent, true)?;
78
79 card.transaction()?.initialize()?;
80
81 Ok(card)
82 }
83
84 pub fn shutdown_scd(agent: Option<Agent>) -> Result<(), Error> {
87 let mut scdc = Self::new(agent, false)?;
88
89 scdc.send("SCD RESTART")?;
90 scdc.send("SCD BYE")?;
91
92 Ok(())
93 }
94
95 fn new(agent: Option<Agent>, init: bool) -> Result<Self, Error> {
101 let agent = if let Some(agent) = agent {
102 agent
103 } else {
104 let ctx = Context::new().map_err(|e| {
106 Error::Smartcard(SmartcardError::Error(format!("Context::new failed {}", e)))
107 })?;
108 RT.lock()
109 .unwrap()
110 .block_on(Agent::connect(&ctx))
111 .map_err(|e| {
112 Error::Smartcard(SmartcardError::Error(format!(
113 "Agent::connect failed {}",
114 e
115 )))
116 })?
117 };
118
119 let mut scdc = Self {
120 agent,
121 card_caps: None,
122 };
123
124 if init {
125 scdc.serialno()?;
126 }
127
128 Ok(scdc)
129 }
130
131 fn send2(&mut self, cmd: &str) -> Result<(), Error> {
132 self.agent.send(cmd).map_err(|e| {
133 Error::Smartcard(SmartcardError::Error(format!(
134 "scdc agent send failed: {}",
135 e
136 )))
137 })
138 }
139
140 fn serialno(&mut self) -> Result<(), Error> {
143 let rt = RT.lock().unwrap();
144
145 let send = "SCD SERIALNO";
146 self.send2(send)?;
147
148 while let Some(response) = rt.block_on(self.agent.next()) {
149 log::trace!("init res: {:x?}", response);
150
151 if let Ok(Response::Status { .. }) = response {
152 while let Some(_drop) = rt.block_on(self.agent.next()) {
154 log::trace!("init drop: {:x?}", _drop);
155 }
156
157 return Ok(());
158 }
159 }
160
161 Err(Error::Smartcard(SmartcardError::Error(
162 "SCDC init() failed".into(),
163 )))
164 }
165
166 fn select_card(&mut self, serial: &str) -> Result<(), Error> {
169 let send = format!("SCD SERIALNO --demand={}", serial);
170 self.send2(&send)?;
171
172 let rt = RT.lock().unwrap();
173
174 while let Some(response) = rt.block_on(self.agent.next()) {
175 log::trace!("select res: {:x?}", response);
176
177 if response.is_err() {
178 return Err(Error::Smartcard(SmartcardError::CardNotFound(
179 serial.into(),
180 )));
181 }
182
183 if let Ok(Response::Status { .. }) = response {
184 while let Some(_drop) = rt.block_on(self.agent.next()) {
186 log::trace!("select drop: {:x?}", _drop);
187 }
188
189 return Ok(());
190 }
191 }
192
193 Err(Error::Smartcard(SmartcardError::CardNotFound(
194 serial.into(),
195 )))
196 }
197
198 fn send(&mut self, cmd: &str) -> Result<(), Error> {
199 self.send2(cmd)?;
200
201 let rt = RT.lock().unwrap();
202
203 while let Some(response) = rt.block_on(self.agent.next()) {
204 log::trace!("select res: {:x?}", response);
205
206 if let Err(e) = response {
207 return Err(Error::Smartcard(SmartcardError::Error(format!("{:?}", e))));
208 }
209
210 if let Ok(..) = response {
211 while let Some(_drop) = rt.block_on(self.agent.next()) {
213 log::trace!(" drop: {:x?}", _drop);
214 }
215
216 return Ok(());
217 }
218 }
219
220 Err(Error::Smartcard(SmartcardError::Error(format!(
221 "Error sending command {}",
222 cmd
223 ))))
224 }
225}
226
227impl CardBackend for ScdBackend {
228 fn transaction(&mut self) -> Result<Box<dyn CardTransaction + Send + Sync + '_>, Error> {
229 Ok(Box::new(ScdTransaction { scd: self }))
230 }
231}
232
233pub struct ScdTransaction<'a> {
234 scd: &'a mut ScdBackend,
235}
236
237impl CardTransaction for ScdTransaction<'_> {
238 fn transmit(&mut self, cmd: &[u8], _: usize) -> Result<Vec<u8>, Error> {
239 log::trace!("SCDC cmd len {}", cmd.len());
240
241 let hex = hex::encode(cmd);
242
243 let ext = if self.card_caps().is_some() && self.card_caps().unwrap().ext_support() {
245 format!("--exlen={} ", self.card_caps().unwrap().max_rsp_bytes())
248 } else {
249 "".to_string()
251 };
252
253 let send = format!("SCD APDU {}{}\n", ext, hex);
254 log::trace!("SCDC command: '{}'", send);
255
256 if send.len() > ASSUAN_LINELENGTH {
257 return Err(Error::Smartcard(SmartcardError::Error(format!(
258 "APDU command is too long ({}) to send via Assuan",
259 send.len()
260 ))));
261 }
262
263 self.scd.send2(&send)?;
264
265 let rt = RT.lock().unwrap();
266
267 while let Some(response) = rt.block_on(self.scd.agent.next()) {
268 log::trace!("res: {:x?}", response);
269 if response.is_err() {
270 return Err(Error::Smartcard(SmartcardError::Error(format!(
271 "Unexpected error response from SCD {:?}",
272 response
273 ))));
274 }
275
276 if let Ok(Response::Data { partial }) = response {
277 let res = partial;
278
279 while let Some(drop) = rt.block_on(self.scd.agent.next()) {
281 log::trace!("drop: {:x?}", drop);
282 }
283
284 return Ok(res);
285 }
286 }
287
288 Err(Error::Smartcard(SmartcardError::Error(
289 "no response found".into(),
290 )))
291 }
292
293 fn init_card_caps(&mut self, caps: CardCaps) {
294 self.scd.card_caps = Some(caps);
295 }
296
297 fn card_caps(&self) -> Option<&CardCaps> {
298 self.scd.card_caps.as_ref()
299 }
300
301 fn max_cmd_len(&self) -> Option<usize> {
304 Some(APDU_CMD_BYTES_MAX)
305 }
306
307 fn feature_pinpad_verify(&self) -> bool {
309 false
310 }
311
312 fn feature_pinpad_modify(&self) -> bool {
314 false
315 }
316
317 fn pinpad_verify(&mut self, _id: PinType) -> Result<Vec<u8>, Error> {
319 unimplemented!()
320 }
321
322 fn pinpad_modify(&mut self, _id: PinType) -> Result<Vec<u8>, Error> {
324 unimplemented!()
325 }
326}