1use crate::Bytes;
6
7pub const NO_ERROR: u16 = 0x9000;
8
9pub use iso7816::Status as Error;
11
12pub mod authenticate {
13 use super::{Bytes, ControlByte};
14
15 #[derive(Clone, Debug, Eq, PartialEq)]
16 pub struct Request<'a> {
17 pub control_byte: ControlByte,
18 pub challenge: &'a [u8; 32],
19 pub app_id: &'a [u8; 32],
20 pub key_handle: &'a [u8],
21 }
22
23 #[derive(Clone, Debug, Eq, PartialEq)]
24 pub struct Response {
25 pub user_presence: u8,
26 pub count: u32,
27 pub signature: Bytes<72>,
28 }
29}
30
31pub mod register {
32 use super::Bytes;
33
34 #[derive(Clone, Debug, Eq, PartialEq)]
35 pub struct Request<'a> {
36 pub challenge: &'a [u8; 32],
37 pub app_id: &'a [u8; 32],
38 }
39
40 #[derive(Clone, Debug, Eq, PartialEq)]
41 pub struct Response {
42 pub header_byte: u8,
43 pub public_key: Bytes<65>,
44 pub key_handle: Bytes<255>,
45 pub attestation_certificate: Bytes<1024>,
46 pub signature: Bytes<72>,
47 }
48
49 impl Response {
50 pub fn new(
51 header_byte: u8,
52 public_key: &cosey::EcdhEsHkdf256PublicKey,
53 key_handle: Bytes<255>,
54 signature: Bytes<72>,
55 attestation_certificate: Bytes<1024>,
56 ) -> Self {
57 let mut public_key_bytes = Bytes::new();
58 public_key_bytes.push(0x04).unwrap();
59 public_key_bytes.extend_from_slice(&public_key.x).unwrap();
60 public_key_bytes.extend_from_slice(&public_key.y).unwrap();
61
62 Self {
63 header_byte,
64 public_key: public_key_bytes,
65 key_handle,
66 attestation_certificate,
67 signature,
68 }
69 }
70 }
71}
72
73#[repr(u8)]
74#[derive(Copy, Clone, Debug, Eq, PartialEq)]
75#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
76pub enum ControlByte {
77 CheckOnly = 0x07,
83 EnforceUserPresenceAndSign = 0x03,
84 DontEnforceUserPresenceAndSign = 0x08,
85}
86
87impl TryFrom<u8> for ControlByte {
88 type Error = Error;
89
90 fn try_from(byte: u8) -> Result<ControlByte> {
91 match byte {
92 0x07 => Ok(ControlByte::CheckOnly),
93 0x03 => Ok(ControlByte::EnforceUserPresenceAndSign),
94 0x08 => Ok(ControlByte::DontEnforceUserPresenceAndSign),
95 _ => Err(Error::IncorrectDataParameter),
96 }
97 }
98}
99
100pub type Result<T> = core::result::Result<T, Error>;
101
102pub type Register<'a> = register::Request<'a>;
104pub type Authenticate<'a> = authenticate::Request<'a>;
106
107pub type RegisterResponse = register::Response;
109pub type AuthenticateResponse = authenticate::Response;
111
112#[derive(Clone, Debug, Eq, PartialEq)]
113#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
114#[allow(clippy::large_enum_variant)]
115pub enum Request<'a> {
117 Register(register::Request<'a>),
118 Authenticate(authenticate::Request<'a>),
119 Version,
120}
121
122#[derive(Clone, Debug, Eq, PartialEq)]
123#[allow(clippy::large_enum_variant)]
124pub enum Response {
126 Register(register::Response),
127 Authenticate(authenticate::Response),
128 Version([u8; 6]),
129}
130
131impl Response {
132 #[allow(clippy::result_unit_err)]
133 #[inline(never)]
134 pub fn serialize(&self, buf: &mut heapless::VecView<u8>) -> core::result::Result<(), ()> {
135 match self {
136 Response::Register(reg) => {
137 buf.push(reg.header_byte).map_err(drop)?;
138 buf.extend_from_slice(®.public_key).map_err(drop)?;
139 buf.push(reg.key_handle.len() as u8).map_err(drop)?;
140 buf.extend_from_slice(®.key_handle).map_err(drop)?;
141 buf.extend_from_slice(®.attestation_certificate)
142 .map_err(drop)?;
143 buf.extend_from_slice(®.signature).map_err(drop)
144 }
145 Response::Authenticate(auth) => {
146 buf.push(auth.user_presence).map_err(drop)?;
147 buf.extend_from_slice(&auth.count.to_be_bytes())
148 .map_err(drop)?;
149 buf.extend_from_slice(&auth.signature).map_err(drop)
150 }
151 Response::Version(version) => buf.extend_from_slice(version).map_err(drop),
152 }
153 }
154}
155
156impl<'a, const S: usize> TryFrom<&'a iso7816::Command<S>> for Request<'a> {
157 type Error = Error;
158 fn try_from(apdu: &'a iso7816::Command<S>) -> Result<Request<'a>> {
159 apdu.as_view().try_into()
160 }
161}
162
163impl<'a> TryFrom<iso7816::command::CommandView<'a>> for Request<'a> {
164 type Error = Error;
165 #[inline(never)]
166 fn try_from(apdu: iso7816::command::CommandView<'a>) -> Result<Request<'a>> {
167 let cla = apdu.class().into_inner();
168 let ins = match apdu.instruction() {
169 iso7816::Instruction::Unknown(ins) => ins,
170 _ins => 0,
171 };
172 let p1 = apdu.p1;
173 let _p2 = apdu.p2;
174
175 if cla != 0 {
176 return Err(Error::ClassNotSupported);
177 }
178
179 if ins == 0x3 {
180 return Ok(Request::Version);
183 };
184
185 let request = apdu.data();
186
187 match ins {
188 0x1 => {
190 if request.len() != 64 {
191 return Err(Error::IncorrectDataParameter);
192 }
193 Ok(Request::Register(Register {
194 challenge: (&request[..32]).try_into().unwrap(),
195 app_id: (&request[32..]).try_into().unwrap(),
196 }))
197 }
198
199 0x2 => {
201 let control_byte = ControlByte::try_from(p1)?;
202 if request.len() < 65 {
203 return Err(Error::IncorrectDataParameter);
204 }
205 let key_handle_length = request[64] as usize;
206 if request.len() != 65 + key_handle_length {
207 return Err(Error::IncorrectDataParameter);
208 }
209 Ok(Request::Authenticate(Authenticate {
210 control_byte,
211 challenge: (&request[..32]).try_into().unwrap(),
212 app_id: (&request[32..64]).try_into().unwrap(),
213 key_handle: &request[65..],
214 }))
215 }
216
217 0x3 => Ok(Request::Version),
219
220 _ => Err(Error::InstructionNotSupportedOrInvalid),
221 }
222 }
223}
224
225pub trait Authenticator {
230 fn register(&mut self, request: ®ister::Request<'_>) -> Result<register::Response>;
232 fn authenticate(
234 &mut self,
235 request: &authenticate::Request<'_>,
236 ) -> Result<authenticate::Response>;
237 fn version() -> [u8; 6] {
239 *b"U2F_V2"
240 }
241
242 #[inline(never)]
243 fn call_ctap1(&mut self, request: &Request<'_>) -> Result<Response> {
244 match request {
245 Request::Register(reg) => {
246 debug_now!("CTAP1.REG");
247 Ok(Response::Register(self.register(reg)?))
248 }
249 Request::Authenticate(auth) => {
250 debug_now!("CTAP1.AUTH");
251 Ok(Response::Authenticate(self.authenticate(auth)?))
252 }
253 Request::Version => Ok(Response::Version(Self::version())),
254 }
255 }
256}
257
258impl<A: Authenticator> crate::Rpc<Error, Request<'_>, Response> for A {
259 fn call(&mut self, request: &Request<'_>) -> Result<Response> {
261 self.call_ctap1(request)
262 }
263}
264
265#[cfg(test)]
266mod tests {
267 use super::*;
268 use heapless::Vec;
269 use hex_literal::hex;
270 use iso7816::command::{
271 class::Class, instruction::Instruction, Command, CommandBuilder, ExpectedLen,
272 };
273
274 fn command(ins: u8, p1: u8, p2: u8, data: &[u8]) -> Command<1024> {
278 let builder = CommandBuilder::new(
279 Class::from_byte(0).unwrap(),
280 Instruction::from(ins),
281 p1,
282 p2,
283 data,
284 ExpectedLen::Max,
285 );
286 let mut apdu = Vec::<_, 1024>::new();
287 builder.serialize_into(&mut apdu).unwrap();
288 Command::try_from(&apdu).unwrap()
289 }
290
291 #[test]
292 fn test_register_request() {
293 let mut input = [0; 64];
294 input[..32].copy_from_slice(&hex!(
296 "4142d21c00d94ffb9d504ada8f99b721f4b191ae4e37ca0140f696b6983cfacb"
297 ));
298 input[32..].copy_from_slice(&hex!(
300 "f0e6a6a97042a4f1f1c87f5f7d44315b2d852c2df5c7991cc66241bf7072d1c4"
301 ));
302
303 let command = command(1, 0, 0, &input);
304 let request = Request::try_from(&command).unwrap();
305 let Request::Register(request) = request else {
306 panic!("expected register request, got: {:?}", request);
307 };
308 assert_eq!(request.challenge, &input[..32]);
309 assert_eq!(request.app_id, &input[32..]);
310 }
311
312 #[test]
313 fn test_register_response() {
314 let public_key = hex!("b174bc49c7ca254b70d2e5c207cee9cf174820ebd77ea3c65508c26da51b657c1cc6b952f8621697936482da0a6d3d3826a59095daf6cd7c03e2e60385d2f6d9");
315 let public_key = cosey::EcdhEsHkdf256PublicKey {
316 x: Bytes::try_from(&public_key[..32]).unwrap(),
317 y: Bytes::try_from(&public_key[32..]).unwrap(),
318 };
319 let key_handle = hex!("2a552dfdb7477ed65fd84133f86196010b2215b57da75d315b7b9e8fe2e3925a6019551bab61d16591659cbaf00b4950f7abfe6660e2e006f76868b772d70c25");
320 let key_handle = Bytes::try_from(&key_handle).unwrap();
321 let signature = hex!("304502201471899bcc3987e62e8202c9b39c33c19033f7340352dba80fcab017db9230e402210082677d673d891933ade6f617e5dbde2e247e70423fd5ad7804a6d3d3961ef871");
322 let signature = Bytes::try_from(&signature).unwrap();
323 let attestation_certificate = hex!("3082013c3081e4a003020102020a47901280001155957352300a06082a8648ce3d0403023017311530130603550403130c476e756262792050696c6f74301e170d3132303831343138323933325a170d3133303831343138323933325a3031312f302d0603550403132650696c6f74476e756262792d302e342e312d34373930313238303030313135353935373335323059301306072a8648ce3d020106082a8648ce3d030107034200048d617e65c9508e64bcc5673ac82a6799da3c1446682c258c463fffdf58dfd2fa3e6c378b53d795c4a4dffb4199edd7862f23abaf0203b4b8911ba0569994e101300a06082a8648ce3d0403020347003044022060cdb6061e9c22262d1aac1d96d8c70829b2366531dda268832cb836bcd30dfa0220631b1459f09e6330055722c8d89b7f48883b9089b88d60d1d9795902b30410df");
324 let attestation_certificate = Bytes::try_from(&attestation_certificate).unwrap();
325 let response = register::Response::new(
326 0x05,
327 &public_key,
328 key_handle,
329 signature,
330 attestation_certificate,
331 );
332 let mut output = Vec::<_, 1024>::new();
333 Response::Register(response).serialize(&mut output).unwrap();
334 assert_eq!(
335 output.as_slice(),
336 &hex!("0504b174bc49c7ca254b70d2e5c207cee9cf174820ebd77ea3c65508c26da51b657c1cc6b952f8621697936482da0a6d3d3826a59095daf6cd7c03e2e60385d2f6d9402a552dfdb7477ed65fd84133f86196010b2215b57da75d315b7b9e8fe2e3925a6019551bab61d16591659cbaf00b4950f7abfe6660e2e006f76868b772d70c253082013c3081e4a003020102020a47901280001155957352300a06082a8648ce3d0403023017311530130603550403130c476e756262792050696c6f74301e170d3132303831343138323933325a170d3133303831343138323933325a3031312f302d0603550403132650696c6f74476e756262792d302e342e312d34373930313238303030313135353935373335323059301306072a8648ce3d020106082a8648ce3d030107034200048d617e65c9508e64bcc5673ac82a6799da3c1446682c258c463fffdf58dfd2fa3e6c378b53d795c4a4dffb4199edd7862f23abaf0203b4b8911ba0569994e101300a06082a8648ce3d0403020347003044022060cdb6061e9c22262d1aac1d96d8c70829b2366531dda268832cb836bcd30dfa0220631b1459f09e6330055722c8d89b7f48883b9089b88d60d1d9795902b30410df304502201471899bcc3987e62e8202c9b39c33c19033f7340352dba80fcab017db9230e402210082677d673d891933ade6f617e5dbde2e247e70423fd5ad7804a6d3d3961ef871"),
337 );
338 }
339
340 #[test]
341 fn test_authenticate_request() {
342 let challenge = &hex!("ccd6ee2e47baef244d49a222db496bad0ef5b6f93aa7cc4d30c4821b3b9dbc57");
343 let application = &hex!("4b0be934baebb5d12d26011b69227fa5e86df94e7d94aa2949a89f2d493992ca");
344 let key_handle = &hex!("2a552dfdb7477ed65fd84133f86196010b2215b57da75d315b7b9e8fe2e3925a6019551bab61d16591659cbaf00b4950f7abfe6660e2e006f76868b772d70c25");
345 let mut input = Vec::<_, 1024>::new();
346 input.extend_from_slice(challenge).unwrap();
347 input.extend_from_slice(application).unwrap();
348 input.push(u8::try_from(key_handle.len()).unwrap()).unwrap();
349 input.extend_from_slice(key_handle).unwrap();
350
351 let control_bytes = [
352 (0x07, ControlByte::CheckOnly),
353 (0x03, ControlByte::EnforceUserPresenceAndSign),
354 (0x08, ControlByte::DontEnforceUserPresenceAndSign),
355 ];
356
357 for (byte, variant) in control_bytes {
358 let command = command(2, byte, 0, &input);
359 let request = Request::try_from(&command).unwrap();
360 let Request::Authenticate(request) = request else {
361 panic!("expected authenticate request, got: {:?}", request);
362 };
363 assert_eq!(request.control_byte, variant);
364 assert_eq!(request.challenge, challenge);
365 assert_eq!(request.app_id, application);
366 assert_eq!(request.key_handle, key_handle);
367 }
368 }
369
370 #[test]
371 fn test_authenticate_response() {
372 let signature = &hex!("304402204b5f0cd17534cedd8c34ee09570ef542a353df4436030ce43d406de870b847780220267bb998fac9b7266eb60e7cb0b5eabdfd5ba9614f53c7b22272ec10047a923f");
373 let signature = Bytes::try_from(signature).unwrap();
374 let response = authenticate::Response {
375 user_presence: 1,
376 count: 1,
377 signature,
378 };
379 let mut output = Vec::<_, 1024>::new();
380 Response::Authenticate(response)
381 .serialize(&mut output)
382 .unwrap();
383 assert_eq!(
384 output.as_slice(),
385 &hex!("0100000001304402204b5f0cd17534cedd8c34ee09570ef542a353df4436030ce43d406de870b847780220267bb998fac9b7266eb60e7cb0b5eabdfd5ba9614f53c7b22272ec10047a923f"),
386 );
387 }
388
389 #[test]
390 fn test_version_request() {
391 let command = command(3, 0, 0, &[]);
392 let request = Request::try_from(&command).unwrap();
393 assert_eq!(request, Request::Version);
394 }
395
396 #[test]
397 fn test_version_response() {
398 let response = Response::Version(*b"U2F_V2");
399 let mut output = Vec::<_, 1024>::new();
400 response.serialize(&mut output).unwrap();
401 assert_eq!(output.as_slice(), b"U2F_V2");
402 }
403}