1use crate::{
6 ApduEncoding, ApduHeader, CommandApdu, Error, ErrorKind, ExpectedLength, Phase, ResponseApdu,
7 SecretBytes, StatusWord,
8};
9use std::collections::VecDeque;
10
11#[derive(Clone, Copy, Debug)]
14pub struct ExchangeOptions {
15 pub max_command_bytes: usize,
17 pub max_response_bytes: usize,
19 pub allow_extended: bool,
22}
23impl Default for ExchangeOptions {
24 fn default() -> Self {
25 Self {
26 max_command_bytes: 261,
27 max_response_bytes: 258,
28 allow_extended: false,
29 }
30 }
31}
32#[derive(Clone, Copy, Debug)]
34pub struct OperationLimits {
35 pub max_total_response_bytes: usize,
38 pub max_exchanges: usize,
41 pub max_input_bytes: usize,
44}
45impl Default for OperationLimits {
46 fn default() -> Self {
47 Self {
48 max_total_response_bytes: 1024 * 1024,
49 max_exchanges: 4096,
50 max_input_bytes: 1024 * 1024,
51 }
52 }
53}
54#[derive(Clone, Copy, Debug, Default)]
56pub struct OperationOptions {
57 pub exchange: ExchangeOptions,
59 pub limits: OperationLimits,
61}
62impl OperationOptions {
63 pub fn validate(self) -> Result<Self, Error> {
69 if self.exchange.max_command_bytes < 5
70 || self.exchange.max_response_bytes < 3
71 || self.limits.max_exchanges == 0
72 || self.limits.max_total_response_bytes == 0
73 || self.limits.max_input_bytes == 0
74 {
75 return Err(Error::new(ErrorKind::InvalidArgument));
76 }
77 Ok(self)
78 }
79}
80#[derive(Clone, Copy, Debug, PartialEq, Eq)]
82pub enum Step {
83 Exchange,
85 Done,
87}
88#[derive(Clone, Copy, Debug, PartialEq, Eq)]
90pub enum OperationState {
91 Created,
93 AwaitingResponse,
95 Completed,
97 Failed,
99 Cancelled,
101 ResultTaken,
103}
104
105#[derive(Clone, Copy, Debug)]
107pub enum Continuation {
108 Oath {
112 instruction: u8,
114 probe_after_success: bool,
116 },
117 Iso7816 {
119 cla: u8,
121 },
122 None,
124}
125#[derive(Clone, Debug)]
128pub struct LogicalCommand {
129 pub header: ApduHeader,
131 pub data: SecretBytes,
133 pub le: ExpectedLength,
135 pub allow_chaining: bool,
137 pub allow_extended: bool,
140 pub correct_le: bool,
142 pub continuation: Continuation,
144}
145impl LogicalCommand {
146 pub fn new(header: ApduHeader, data: Vec<u8>, le: ExpectedLength) -> Self {
150 Self {
151 header,
152 data: SecretBytes::new(data),
153 le,
154 allow_chaining: false,
155 allow_extended: false,
156 correct_le: false,
157 continuation: Continuation::Iso7816 { cla: 0 },
158 }
159 }
160}
161#[derive(Debug)]
164pub struct ResponseData {
165 pub data: SecretBytes,
167 pub status: StatusWord,
169}
170impl ResponseData {
171 pub fn ensure_success(&self, phase: Phase) -> Result<(), Error> {
177 if self.status.is_success() {
178 Ok(())
179 } else {
180 Err(Error::status(self.status, phase, None))
181 }
182 }
183}
184
185#[doc(hidden)]
188pub mod engine {
189 use super::*;
190 pub enum Action<T> {
191 Command(LogicalCommand),
192 Done(T),
193 }
194 pub trait Machine<T>: Send {
195 fn next(&mut self, response: Option<ResponseData>) -> Result<Action<T>, Error>;
196 fn progress(&self) -> Option<&T> {
197 None
198 }
199 fn take_progress(&mut self) -> Option<T> {
200 None
201 }
202 }
203}
204use engine::{Action, Machine};
205
206struct ConversationState {
207 pending: VecDeque<CommandApdu>,
208 current: CommandApdu,
209 corrected: bool,
210 correct_le: bool,
211 continuation: Continuation,
212 continuing: bool,
213 oath_poll: bool,
214 data: SecretBytes,
215}
216impl ConversationState {
217 fn new(mut logical: LogicalCommand, options: OperationOptions) -> Result<Self, Error> {
218 if matches!(logical.continuation, Continuation::Oath { instruction, .. } if ![0x06, 0xa5].contains(&instruction))
219 {
220 return Err(Error::new(ErrorKind::InvalidArgument));
221 }
222 if logical.data.len() > options.limits.max_input_bytes {
223 return Err(Error::new(ErrorKind::LimitExceeded));
224 }
225 if matches!(logical.le, ExpectedLength::Exact(n) if n == 0 || n > 65536) {
226 return Err(Error::new(ErrorKind::InvalidArgument));
227 }
228 if let ExpectedLength::Exact(n) = logical.le {
229 if n as usize > options.exchange.max_response_bytes - 2 {
230 if matches!(logical.continuation, Continuation::Iso7816 { .. })
231 && logical.correct_le
232 {
233 logical.le =
234 ExpectedLength::Exact((options.exchange.max_response_bytes - 2) as u32);
235 } else {
236 return Err(Error::new(ErrorKind::LimitExceeded));
237 }
238 }
239 }
240 let max_short = options
242 .exchange
243 .max_command_bytes
244 .saturating_sub(6)
245 .min(255);
246 let mut frames = VecDeque::new();
247 let extended = logical.allow_extended && options.exchange.allow_extended;
248 if extended && logical.data.len() <= 65535 {
249 let cmd = CommandApdu::encode(
250 logical.header,
251 logical.data.as_bytes(),
252 logical.le,
253 ApduEncoding::Extended,
254 )?;
255 if cmd.as_bytes().len() <= options.exchange.max_command_bytes {
256 frames.push_back(cmd);
257 }
258 }
259 if frames.is_empty() {
260 if matches!(logical.le, ExpectedLength::Exact(n) if n > 256) {
261 return Err(Error::new(ErrorKind::LimitExceeded));
262 }
263 let data = logical.data.as_bytes();
264 if data.is_empty() || data.len() <= max_short {
265 frames.push_back(CommandApdu::encode(
266 logical.header,
267 data,
268 logical.le,
269 ApduEncoding::Short,
270 )?);
271 } else {
272 if !logical.allow_chaining || max_short == 0 || logical.header.cla & 0x10 != 0 {
273 return Err(Error::new(ErrorKind::LimitExceeded));
274 }
275 let count = data.len().div_ceil(max_short);
276 if count > options.limits.max_exchanges {
277 return Err(Error::new(ErrorKind::LimitExceeded));
278 }
279 for (i, chunk) in data.chunks(max_short).enumerate() {
280 let last = i + 1 == count;
281 let mut header = logical.header;
282 if !last {
283 header.cla |= 0x10;
284 }
285 frames.push_back(CommandApdu::encode(
286 header,
287 chunk,
288 if last {
289 logical.le
290 } else {
291 ExpectedLength::Absent
292 },
293 ApduEncoding::Short,
294 )?);
295 }
296 }
297 }
298 let current = frames
299 .pop_front()
300 .ok_or_else(|| Error::new(ErrorKind::ProtocolViolation))?;
301 if current.as_bytes().len() > options.exchange.max_command_bytes {
302 return Err(Error::new(ErrorKind::LimitExceeded));
303 }
304 Ok(Self {
305 current,
306 pending: frames,
307 corrected: false,
308 correct_le: logical.correct_le,
309 continuation: logical.continuation,
310 continuing: false,
311 oath_poll: false,
312 data: SecretBytes::default(),
313 })
314 }
315 fn advance(
316 &mut self,
317 response: ResponseApdu<'_>,
318 options: OperationOptions,
319 ) -> Result<Option<ResponseData>, Error> {
320 let sw = response.status().raw();
321 if sw >> 8 == 0x6c && self.correct_le && self.pending.is_empty() && !self.corrected {
322 let le = if sw & 255 == 0 { 256 } else { sw & 255 } as u32;
323 if le as usize > options.exchange.max_response_bytes - 2 {
324 return Err(Error::new(ErrorKind::LimitExceeded).at(Phase::Conversation));
325 }
326 self.current = self.current.corrected(le)?;
327 if self.current.as_bytes().len() > options.exchange.max_command_bytes {
328 return Err(Error::new(ErrorKind::LimitExceeded).at(Phase::Conversation));
329 }
330 self.corrected = true;
331 return Ok(None);
332 }
333 if !self.pending.is_empty() {
334 if !response.status().is_success() {
335 return Err(Error::status(response.status(), Phase::Conversation, None));
336 }
337 if !response.data().is_empty() {
338 return Err(Error::new(ErrorKind::ProtocolViolation).at(Phase::Conversation));
339 }
340 self.current = self
341 .pending
342 .pop_front()
343 .ok_or_else(|| Error::new(ErrorKind::ProtocolViolation))?;
344 self.corrected = false;
345 return Ok(None);
346 }
347 if let Continuation::Oath {
348 instruction,
349 probe_after_success,
350 } = self.continuation
351 {
352 if self.oath_poll && sw == 0x6985 && response.data().is_empty() {
353 return Ok(Some(ResponseData {
354 data: std::mem::take(&mut self.data),
355 status: StatusWord::new(0x9000),
356 }));
357 }
358 if sw >> 8 == 0x61
359 || (probe_after_success && sw == 0x9000 && !response.data().is_empty())
360 {
361 if response.data().is_empty() {
362 return Err(Error::new(ErrorKind::ProtocolViolation).at(Phase::Conversation));
363 }
364 self.data.extend(response.data());
365 self.oath_poll = sw == 0x9000;
366 self.current = CommandApdu::encode(
367 ApduHeader::new(0, instruction, 0, 0),
368 &[],
369 ExpectedLength::Exact(255.min(options.exchange.max_response_bytes - 2) as u32),
370 ApduEncoding::Short,
371 )?;
372 self.corrected = false;
373 self.correct_le = false;
374 return Ok(None);
375 }
376 }
377 self.data.extend(response.data());
378 if let Continuation::Iso7816 { cla } = self.continuation {
379 if sw >> 8 == 0x61 {
380 if self.continuing && response.data().is_empty() {
381 return Err(Error::new(ErrorKind::ProtocolViolation).at(Phase::Conversation));
382 }
383 self.continuing = true;
384 let le = if sw & 255 == 0 {
385 256
386 } else {
387 (sw & 255) as usize
388 };
389 let le = le.min(options.exchange.max_response_bytes - 2) as u32;
390 self.current = CommandApdu::encode(
391 ApduHeader::new(cla, 0xc0, 0, 0),
392 &[],
393 ExpectedLength::Exact(le),
394 ApduEncoding::Short,
395 )?;
396 self.corrected = false;
397 self.correct_le = true;
398 return Ok(None);
399 }
400 }
401 Ok(Some(ResponseData {
402 data: std::mem::take(&mut self.data),
403 status: response.status(),
404 }))
405 }
406}
407
408pub struct Operation<T> {
433 machine: Option<Box<dyn Machine<T>>>,
434 conversation: Option<ConversationState>,
435 output: Option<T>,
436 progress: Option<T>,
437 error: Option<Error>,
438 state: OperationState,
439 options: OperationOptions,
440 exchanges: usize,
441 response_bytes: usize,
442}
443impl<T> std::fmt::Debug for Operation<T> {
444 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
445 f.debug_struct("Operation")
446 .field("state", &self.state)
447 .finish_non_exhaustive()
448 }
449}
450impl<T> Operation<T> {
451 #[doc(hidden)]
452 pub fn from_machine(
453 machine: impl Machine<T> + 'static,
454 options: OperationOptions,
455 ) -> Result<Self, Error> {
456 Ok(Self {
457 machine: Some(Box::new(machine)),
458 conversation: None,
459 output: None,
460 progress: None,
461 error: None,
462 state: OperationState::Created,
463 options: options.validate()?,
464 exchanges: 0,
465 response_bytes: 0,
466 })
467 }
468 pub fn state(&self) -> OperationState {
470 self.state
471 }
472 pub fn error(&self) -> Option<&Error> {
475 self.error.as_ref()
476 }
477 pub fn progress(&self) -> Option<&T> {
483 self.progress
484 .as_ref()
485 .or_else(|| self.machine.as_ref().and_then(|m| m.progress()))
486 }
487 pub fn command(&self) -> Result<&CommandApdu, Error> {
493 if self.state != OperationState::AwaitingResponse {
494 return Err(state_error());
495 }
496 self.conversation
497 .as_ref()
498 .map(|c| &c.current)
499 .ok_or_else(state_error)
500 }
501 pub fn result(&self) -> Result<&T, Error> {
506 if self.state != OperationState::Completed {
507 return Err(state_error());
508 }
509 self.output.as_ref().ok_or_else(state_error)
510 }
511 pub fn take_result(&mut self) -> Result<T, Error> {
518 if self.state != OperationState::Completed {
519 return Err(state_error());
520 }
521 let value = self.output.take().ok_or_else(state_error)?;
522 self.state = OperationState::ResultTaken;
523 Ok(value)
524 }
525 pub fn cancel(&mut self) {
529 if matches!(
530 self.state,
531 OperationState::Created | OperationState::AwaitingResponse
532 ) {
533 self.machine = None;
534 self.conversation = None;
535 self.state = OperationState::Cancelled;
536 }
537 }
538 pub fn start(&mut self) -> Result<Step, Error> {
544 if self.state != OperationState::Created {
545 return Err(state_error());
546 }
547 let result = self.drive(None);
548 self.record(result)
549 }
550 pub fn advance(&mut self, bytes: &[u8]) -> Result<Step, Error> {
559 if self.state != OperationState::AwaitingResponse {
560 return Err(state_error());
561 }
562 let result = self.advance_inner(bytes);
563 self.record(result)
564 }
565 fn advance_inner(&mut self, bytes: &[u8]) -> Result<Step, Error> {
566 if bytes.len() > self.options.exchange.max_response_bytes {
567 return Err(Error::new(ErrorKind::LimitExceeded).at(Phase::Conversation));
568 }
569 let response = ResponseApdu::parse(bytes)?;
570 self.response_bytes = self
571 .response_bytes
572 .checked_add(response.data().len())
573 .ok_or_else(|| Error::new(ErrorKind::LimitExceeded).at(Phase::Conversation))?;
574 if self.response_bytes > self.options.limits.max_total_response_bytes {
575 return Err(Error::new(ErrorKind::LimitExceeded).at(Phase::Conversation));
576 }
577 let result = self
578 .conversation
579 .as_mut()
580 .ok_or_else(state_error)?
581 .advance(response, self.options)?;
582 match result {
583 None => self.exchange(),
584 Some(response) => {
585 self.conversation = None;
586 self.drive(Some(response))
587 }
588 }
589 }
590 fn drive(&mut self, response: Option<ResponseData>) -> Result<Step, Error> {
591 match self
592 .machine
593 .as_mut()
594 .ok_or_else(state_error)?
595 .next(response)?
596 {
597 Action::Done(value) => {
598 self.output = Some(value);
599 self.machine = None;
600 self.conversation = None;
601 self.state = OperationState::Completed;
602 Ok(Step::Done)
603 }
604 Action::Command(command) => {
605 self.conversation = Some(ConversationState::new(command, self.options)?);
606 self.exchange()
607 }
608 }
609 }
610 fn exchange(&mut self) -> Result<Step, Error> {
611 if self.exchanges >= self.options.limits.max_exchanges {
612 return Err(Error::new(ErrorKind::LimitExceeded).at(Phase::Conversation));
613 }
614 self.exchanges += 1;
615 self.state = OperationState::AwaitingResponse;
616 Ok(Step::Exchange)
617 }
618 fn record(&mut self, result: Result<Step, Error>) -> Result<Step, Error> {
619 if let Err(error) = &result {
620 self.progress = self.machine.as_mut().and_then(|m| m.take_progress());
621 self.machine = None;
622 self.conversation = None;
623 self.error = Some(error.clone());
624 self.state = OperationState::Failed;
625 }
626 result
627 }
628}
629fn state_error() -> Error {
630 Error::new(ErrorKind::OperationStateError)
631}
632struct SingleCommand {
633 command: Option<LogicalCommand>,
634}
635impl Machine<ResponseData> for SingleCommand {
636 fn next(&mut self, response: Option<ResponseData>) -> Result<Action<ResponseData>, Error> {
637 match response {
638 Some(response) => Ok(Action::Done(response)),
639 None => Ok(Action::Command(
640 self.command.take().ok_or_else(state_error)?,
641 )),
642 }
643 }
644}
645pub fn conversation(
655 command: LogicalCommand,
656 options: OperationOptions,
657) -> Result<Operation<ResponseData>, Error> {
658 validate_command(&command, options)?;
659 Operation::from_machine(
660 SingleCommand {
661 command: Some(command),
662 },
663 options,
664 )
665}
666
667pub fn validate_command(command: &LogicalCommand, options: OperationOptions) -> Result<(), Error> {
674 ConversationState::new(command.clone(), options.validate()?).map(|_| ())
675}