ykoath_protocol/
calculate.rs1use crate::escape_ascii::EscapeAscii;
2use crate::{Error, YubiKey};
3use std::fmt::{self, Display};
4
5#[derive(Clone, Copy, Debug)]
6pub struct Response<'a> {
7 pub digits: u8,
8 pub response: &'a [u8],
9}
10
11impl Response<'_> {
12 #[must_use]
13 pub fn code(&self) -> Code {
14 let mut response = 0_u32.to_be_bytes();
15 let len = response.len();
16 response[len.saturating_sub(self.response.len())..]
17 .copy_from_slice(&self.response[self.response.len().saturating_sub(len)..]);
18 Code {
19 digits: self.digits,
20 response: u32::from_be_bytes(response),
21 }
22 }
23}
24
25#[derive(Clone, Copy)]
26pub struct Code {
27 digits: u8,
28 response: u32,
29}
30
31impl Display for Code {
32 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33 write!(
35 f,
36 "{:01$}",
37 (self.response & 0x7fff_ffff) % 10_u32.pow(u32::from(self.digits)),
38 usize::from(self.digits),
39 )
40 }
41}
42
43impl YubiKey {
44 #[tracing::instrument(err, fields(name = ?EscapeAscii(name)), ret, skip(name, buf))]
45 pub fn calculate<'a>(
46 &self,
47 truncate: bool,
48 name: &[u8],
49 challenge: &[u8],
50 buf: &'a mut Vec<u8>,
51 ) -> Result<Response<'a>, Error> {
52 #[allow(clippy::redundant_locals)]
54 let buf = buf;
55 buf.clear();
56 buf.extend_from_slice(&[
57 0x00,
58 0xa2,
59 0x00,
60 #[allow(clippy::bool_to_int_with_if)]
61 if truncate { 0x01 } else { 0x00 },
62 ]);
63 buf.push(0x00);
64 Self::push(buf, 0x71, name)?;
65 Self::push(buf, 0x74, challenge)?;
66 let mut response = self.transmit(buf)?;
67 let (_, response) = Self::pop(&mut response, &[if truncate { 0x76 } else { 0x75 }])?;
68 Ok(Response {
69 digits: *response.first().ok_or(Error::InsufficientData)?,
70 response: &response[1..],
71 })
72 }
73}