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