passkey_types/u2f/
commands.rs1use super::{ResponseStatusWords, authenticate::AuthenticationRequest, register::RegisterRequest};
2
3#[derive(Debug)]
10pub enum Command {
11 Register,
13 Authenticate,
15 Version,
17 Unsuported(u8),
19}
20
21impl From<Command> for u8 {
22 fn from(src: Command) -> Self {
23 match src {
24 Command::Register => 0x01,
25 Command::Authenticate => 0x02,
26 Command::Version => 0x03,
27 Command::Unsuported(cmd) => cmd,
28 }
29 }
30}
31
32impl From<u8> for Command {
33 fn from(src: u8) -> Self {
34 match src {
35 0x01 => Command::Register,
36 0x02 => Command::Authenticate,
37 0x03 => Command::Version,
38 cmd => Command::Unsuported(cmd),
39 }
40 }
41}
42
43#[derive(Debug)]
45pub enum RequestPayload {
46 Register(RegisterRequest),
48 Authenticate(AuthenticationRequest),
50 Version,
52}
53
54#[derive(Debug)]
56pub struct Request {
57 pub cla: u8,
59 pub ins: Command,
61 pub p1: u8,
63 pub data_len: usize,
65 pub data: RequestPayload,
67}
68
69const REQUEST_HEADER_LEN: usize = 6;
70
71impl TryFrom<&[u8]> for Request {
72 type Error = ResponseStatusWords;
73
74 #[expect(clippy::as_conversions)]
75 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
76 if value.len() < REQUEST_HEADER_LEN {
77 return Err(ResponseStatusWords::WrongLength);
78 }
79
80 let cla = value[0];
81 if cla != 0 {
82 return Err(ResponseStatusWords::WrongData);
83 }
84 let ins = Command::from(value[1]);
85 let p1 = value[2];
86 let data_start = REQUEST_HEADER_LEN + 1;
87 let data_len = u32::from_be_bytes(value[3..data_start].try_into().unwrap()) as usize;
91 let data_end = data_start + data_len;
92 let payload = &value[data_start..data_end];
93
94 let data = match ins {
95 Command::Register => RequestPayload::Register(
96 payload
97 .try_into()
98 .map_err(|_| ResponseStatusWords::WrongLength)?,
100 ),
101 Command::Authenticate => RequestPayload::Authenticate(
102 AuthenticationRequest::try_from(payload, p1)
103 .map_err(|_| ResponseStatusWords::WrongLength)?,
104 ),
105 Command::Version => RequestPayload::Version,
106 Command::Unsuported(_) => return Err(ResponseStatusWords::InsNotSupported),
107 };
108
109 Ok(Request {
110 cla,
111 ins,
112 p1,
113 data_len,
114 data,
115 })
116 }
117}