ykoath_protocol/
calculate_all.rs1use crate::escape_ascii::EscapeAscii;
2use crate::{Error, YubiKey};
3use std::fmt;
4use std::iter;
5
6#[derive(Clone, Copy)]
7pub struct Response<'a> {
8 pub name: &'a [u8],
9 pub inner: Inner<'a>,
10}
11
12impl fmt::Debug for Response<'_> {
13 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
14 f.debug_struct("Response")
15 .field("name", &EscapeAscii(self.name))
16 .field("inner", &self.inner)
17 .finish()
18 }
19}
20
21#[derive(Clone, Copy, Debug)]
22pub enum Inner<'a> {
23 Response(crate::calculate::Response<'a>),
24 Hotp,
25 Touch,
26}
27
28impl YubiKey {
29 #[tracing::instrument(err, ret, skip(buf))]
30 pub fn calculate_all<'a>(
31 &self,
32 truncate: bool,
33 challenge: &[u8],
34 buf: &'a mut Vec<u8>,
35 ) -> Result<Vec<Response<'a>>, Error> {
36 #[allow(clippy::redundant_locals)]
38 let buf = buf;
39 buf.clear();
40 buf.extend_from_slice(&[
41 0x00,
42 0xa4,
43 0x00,
44 #[allow(clippy::bool_to_int_with_if)]
45 if truncate { 0x01 } else { 0x00 },
46 ]);
47 buf.push(0x00);
48 Self::push(buf, 0x74, challenge)?;
49 let mut response = self.transmit(buf)?;
50 iter::from_fn(|| {
51 if response.is_empty() {
52 None
53 } else {
54 Some(Self::pop(&mut response, &[0x71]).and_then(|(_, name)| {
55 let (tag, response) = Self::pop(
56 &mut response,
57 &[if truncate { 0x76 } else { 0x75 }, 0x77, 0x7c],
58 )?;
59 let inner = match tag {
60 0x75 | 0x76 => {
61 let digits = *response.first().ok_or(Error::InsufficientData)?;
62 let response = &response[1..];
63 Ok(Inner::Response(crate::calculate::Response {
64 digits,
65 response,
66 }))
67 }
68 0x77 => Ok(Inner::Hotp),
69 0x7c => Ok(Inner::Touch),
70 _ => Err(Error::UnexpectedValue(tag)),
71 }?;
72 Ok(Response { name, inner })
73 }))
74 }
75 })
76 .collect()
77 }
78}