1use core::mem;
12
13use crate::response::SIZE as ResponseSize;
14use crate::App;
15use crate::{
16 interchanges::{self, Responder},
17 response, Command,
18};
19
20use iso7816::{
21 command::{CommandView, FromSliceError},
22 Aid, Instruction, Result, Status,
23};
24
25const MAX_INTERCHANGE_DATA: usize = if interchanges::SIZE < ResponseSize {
28 interchanges::SIZE
29} else {
30 ResponseSize
31} - 2;
32
33pub use iso7816::Interface;
34
35pub enum RequestType {
36 Select(Aid, Interface),
37 GetResponse,
39 NewCommand(Interface),
40 BadCommand(Status),
42 None,
43}
44
45#[derive(PartialEq)]
46enum RawApduBuffer {
47 None,
48 Request(Command),
49 Response(response::Data),
50}
51
52struct ApduBuffer {
53 pub raw: RawApduBuffer,
54}
55
56impl ApduBuffer {
57 fn request(&mut self, command: CommandView<'_>) {
58 match &mut self.raw {
59 RawApduBuffer::Request(buffered) => {
60 buffered.extend_from_command_view(command).ok();
61 }
62 _ => {
63 if self.raw != RawApduBuffer::None {
64 info!("Was buffering the last response, but aborting that now for this new request.");
65 }
66 let mut new_cmd = iso7816::Command::try_from(&[0, 0, 0, 0]).unwrap();
67 new_cmd.extend_from_command_view(command).ok();
68 self.raw = RawApduBuffer::Request(new_cmd);
69 }
70 }
71 }
72
73 fn response(&mut self, response: &response::Data) {
74 self.raw = RawApduBuffer::Response(response.clone());
75 }
76}
77
78pub struct ApduDispatch<'pipe> {
79 current_aid: Option<Aid>,
81 contact: Responder<'pipe>,
82 contactless: Responder<'pipe>,
83 interface: Option<Interface>,
84
85 buffer: ApduBuffer,
86 response_len_expected: usize,
87 was_request_chained: bool,
88}
89
90impl<'pipe> ApduDispatch<'pipe> {
91 fn apdu_type(apdu: CommandView<'_>, interface: Interface) -> RequestType {
92 info!("instruction: {:?} {}", apdu.instruction(), apdu.p1);
93 if apdu.instruction() == Instruction::Select && (apdu.p1 & 0x04) != 0 {
94 Aid::try_new(apdu.data()).map_or_else(
95 |_err| {
96 warn!("Failed to parse AID: {:?}", _err);
97 RequestType::BadCommand(Status::IncorrectDataParameter)
98 },
99 |aid| RequestType::Select(aid, interface),
100 )
101 } else if apdu.instruction() == Instruction::GetResponse {
102 RequestType::GetResponse
103 } else {
104 RequestType::NewCommand(interface)
105 }
106 }
107
108 pub fn new(contact: Responder<'pipe>, contactless: Responder<'pipe>) -> Self {
109 ApduDispatch {
110 current_aid: None,
111 contact,
112 contactless,
113 interface: None,
114 was_request_chained: false,
115 response_len_expected: 0,
116 buffer: ApduBuffer {
117 raw: RawApduBuffer::None,
118 },
119 }
120 }
121
122 fn find_app<'a, 'b>(
125 aid: Option<&Aid>,
126 apps: &'a mut [&'b mut dyn App<ResponseSize>],
127 ) -> Option<&'a mut &'b mut dyn App<ResponseSize>> {
128 aid.and_then(move |aid| {
133 debug!("matching {:?}", aid);
134 apps.iter_mut().find(|app| {
135 debug!("...against {:?}", app.aid());
137 app.aid().matches(aid)
138 })
139 })
140 }
141
142 fn busy(&self) -> bool {
143 use interchange::State::*;
146 let contact_busy = !matches!(self.contact.state(), Idle | Requested);
147 let contactless_busy = !matches!(self.contactless.state(), Idle | Requested);
148 contactless_busy || contact_busy
149 }
150
151 #[inline(never)]
152 fn buffer_chained_apdu_if_needed(
153 &mut self,
154 command: CommandView<'_>,
155 interface: Interface,
156 ) -> RequestType {
157 if !command.class().chain().not_the_last() {
160 let is_chaining = matches!(self.buffer.raw, RawApduBuffer::Request(_));
161
162 if is_chaining {
163 self.buffer.request(command);
164
165 self.was_request_chained = true;
167 info!("combined chained commands.");
168
169 RequestType::NewCommand(interface)
170 } else {
171 if self.buffer.raw == RawApduBuffer::None {
172 self.was_request_chained = false;
173 }
174 let apdu_type = Self::apdu_type(command, interface);
175 match apdu_type {
176 RequestType::GetResponse => (),
178 _ => self.buffer.request(command),
180 }
181 apdu_type
182 }
183 } else {
184 match interface {
185 Interface::Contact => {
187 self.contact
188 .respond(Status::Success.into())
189 .expect("Could not respond");
190 }
191 Interface::Contactless => {
192 self.contactless
193 .respond(Status::Success.into())
194 .expect("Could not respond");
195 }
196 }
197
198 if !command.data().is_empty() {
199 info!("chaining {} bytes", command.data().len());
200 self.buffer.request(command);
201 }
202
203 RequestType::None
205 }
206 }
207
208 fn parse_apdu<const S: usize>(message: &interchanges::Data) -> Result<iso7816::Command<S>> {
209 debug!(">> {}", hex_str!(message.as_slice(), sep:""));
210 match iso7816::Command::try_from(message) {
211 Ok(command) => Ok(command),
212 Err(_error) => {
213 info!("apdu bad");
214 match _error {
215 FromSliceError::TooShort => {
216 info!("TooShort");
217 }
218 FromSliceError::TooLong => {
219 info!("TooLong");
220 }
221 FromSliceError::InvalidClass => {
222 info!("InvalidClass");
223 }
224 FromSliceError::InvalidFirstBodyByteForExtended => {
225 info!("InvalidFirstBodyByteForExtended");
226 }
227 FromSliceError::InvalidSliceLength => {
228 info!("InvalidSliceLength");
229 }
230 }
231 Err(Status::UnspecifiedCheckingError)
232 }
233 }
234 }
235
236 #[inline(never)]
237 fn check_for_request(&mut self) -> RequestType {
238 if !self.busy() {
239 let (message, interface) = if let Some(message) = self.contactless.take_request() {
241 (message, Interface::Contactless)
242 } else if let Some(message) = self.contact.take_request() {
243 (message, Interface::Contact)
244 } else {
245 return RequestType::None;
246 };
247
248 let apdu;
249
250 if let Some(i) = self.interface {
251 if i != interface {
252 apdu = Err(Status::UnspecifiedNonpersistentExecutionError)
253 } else {
254 apdu = Self::parse_apdu::<{ interchanges::SIZE }>(&message);
255 }
256 } else {
257 self.interface = Some(interface);
258 apdu = Self::parse_apdu::<{ interchanges::SIZE }>(&message);
259 }
260
261 match apdu {
263 Ok(command) => {
264 self.response_len_expected = command.expected();
265 self.buffer_chained_apdu_if_needed(command.as_view(), interface)
267 }
268 Err(response) => {
269 info!("Invalid apdu");
271 match interface {
272 Interface::Contactless => self
273 .contactless
274 .respond(response.into())
275 .expect("cant respond"),
276 Interface::Contact => {
277 self.contact.respond(response.into()).expect("cant respond")
278 }
279 }
280 RequestType::None
281 }
282 }
283 } else {
284 RequestType::None
285 }
286 }
287
288 #[inline(never)]
289 fn reply_error(&mut self, status: Status) {
290 self.respond(status.into());
291 self.buffer.raw = RawApduBuffer::None;
292 }
293
294 #[inline(never)]
295 fn handle_reply(&mut self) {
296 let (new_state, response) = match &mut self.buffer.raw {
302 RawApduBuffer::Request(_) | RawApduBuffer::None => {
303 info!("Unexpected GetResponse request.");
304 (RawApduBuffer::None, Status::UnspecifiedCheckingError.into())
305 }
306 RawApduBuffer::Response(res) => {
307 let max_response_len = self.response_len_expected.min(MAX_INTERCHANGE_DATA);
308 if self.was_request_chained || res.len() > max_response_len {
309 let boundary = max_response_len.min(res.len());
311
312 let to_send = &res[..boundary];
313 let remaining = &res[boundary..];
314 let mut message = interchanges::Data::from_slice(to_send).unwrap();
315 let return_code = if remaining.len() > 255 {
316 0x6100u16
318 } else if !remaining.is_empty() {
319 0x6100 + (remaining.len() as u16)
320 } else {
321 0x9000
323 };
324 message
325 .extend_from_slice(&return_code.to_be_bytes())
326 .expect("Failed add to status bytes");
327 if return_code == 0x9000 {
328 (RawApduBuffer::None, message)
329 } else {
330 info!("Still {} bytes in response buffer", remaining.len());
331 (
332 RawApduBuffer::Response(response::Data::from_slice(remaining).unwrap()),
333 message,
334 )
335 }
336 } else {
337 res.extend_from_slice(&[0x90, 00])
339 .expect("Failed to add the status bytes");
340 (
341 RawApduBuffer::None,
342 interchanges::Data::from_slice(res.as_slice()).unwrap(),
343 )
344 }
345 }
346 };
347 self.buffer.raw = new_state;
348 self.respond(response);
349 }
350
351 #[inline(never)]
352 fn handle_app_response(&mut self, response: &Result<()>, data: &response::Data) {
353 match response {
355 Ok(()) => {
356 info!("buffered the response of {} bytes.", data.len());
357 self.buffer.response(data);
358 self.handle_reply();
359 }
360 Err(status) => {
361 info!("buffered app error");
363 self.reply_error(*status);
364 }
365 }
366 }
367
368 #[inline(never)]
369 fn handle_app_select(
370 &mut self,
371 apps: &mut [&mut dyn App<ResponseSize>],
372 aid: Aid,
373 interface: Interface,
374 ) {
375 if let Some(app) = Self::find_app(Some(&aid), apps) {
389 info!("Selected app");
390 let mut response = response::Data::new();
391 let result = match &self.buffer.raw {
392 RawApduBuffer::Request(apdu) => {
393 app.select(interface, apdu.as_view(), &mut response)
394 }
395 _ => panic!("Unexpected buffer state."),
396 };
397
398 let old_aid = mem::replace(&mut self.current_aid, Some(aid));
399 if let Some(old_aid) = old_aid {
400 if old_aid != aid {
401 let app = Self::find_app(self.current_aid.as_ref(), apps).unwrap();
402 app.deselect();
404 }
405 }
406
407 self.handle_app_response(&result, &response);
408 } else {
409 info!("could not find app by aid: {}", hex_str!(&aid.as_bytes()));
410 self.reply_error(Status::NotFound);
411 };
412 }
413
414 #[inline(never)]
415 fn handle_app_command(
416 &mut self,
417 apps: &mut [&mut dyn App<ResponseSize>],
418 interface: Interface,
419 ) {
420 let mut response = response::Data::new();
422 if let Some(app) = Self::find_app(self.current_aid.as_ref(), apps) {
423 let result = match &self.buffer.raw {
424 RawApduBuffer::Request(apdu) => app.call(interface, apdu.as_view(), &mut response),
425 _ => panic!("Unexpected buffer state."),
426 };
427 self.handle_app_response(&result, &response);
428 } else {
429 self.reply_error(Status::NotFound);
431 };
432 }
433
434 pub fn poll(&mut self, apps: &mut [&mut dyn App<ResponseSize>]) -> Option<Interface> {
435 let request_type = self.check_for_request();
437
438 match request_type {
443 RequestType::Select(aid, interface) => {
445 info!("Select");
446 self.handle_app_select(apps, aid, interface);
447 }
448
449 RequestType::GetResponse => {
450 info!("GetResponse");
451 self.handle_reply();
452 }
453
454 RequestType::NewCommand(interface) => {
456 info!("Command");
457 self.handle_app_command(apps, interface);
458 }
459 RequestType::BadCommand(status) => {
460 info!("Bad command");
461 self.reply_error(status);
462 }
463 RequestType::None => {}
464 }
465
466 if self.contactless.state() == interchange::State::Responded {
468 Some(Interface::Contactless)
469 } else if self.contact.state() == interchange::State::Responded {
470 Some(Interface::Contact)
471 } else {
472 None
473 }
474 }
475
476 #[inline(never)]
477 fn respond(&mut self, message: interchanges::Data) {
478 debug!("<< {}", hex_str!(message.as_slice(), sep:""));
479 match self.interface.unwrap() {
480 Interface::Contactless => self.contactless.respond(message).expect("cant respond"),
481 Interface::Contact => self.contact.respond(message).expect("cant respond"),
482 }
483 }
484}