ykoath_protocol/
calculate_all.rs

1use 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        // https://github.com/tokio-rs/tracing/issues/2796
37        #[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}