fido_authenticator/dispatch/
apdu.rs

1use apdu_app::Interface;
2use ctap_types::{serde::error::Error as SerdeError, Error};
3use iso7816::{command::CommandView, Data, Status};
4
5use crate::{Authenticator, TrussedRequirements, UserPresence};
6
7pub enum CtapMappingError {
8    InvalidCommand(u8),
9    ParsingError(SerdeError),
10}
11
12impl From<CtapMappingError> for Error {
13    fn from(mapping_error: CtapMappingError) -> Error {
14        match mapping_error {
15            CtapMappingError::InvalidCommand(_cmd) => Error::InvalidCommand,
16            CtapMappingError::ParsingError(cbor_error) => match cbor_error {
17                SerdeError::SerdeMissingField => Error::MissingParameter,
18                _ => Error::InvalidCbor,
19            },
20        }
21    }
22}
23
24impl<UP, T, const R: usize> apdu_app::App<R> for Authenticator<UP, T>
25where
26    UP: UserPresence,
27    T: TrussedRequirements,
28{
29    fn select(
30        &mut self,
31        interface: Interface,
32        _: CommandView<'_>,
33        reply: &mut Data<R>,
34    ) -> apdu_app::Result {
35        // FIDO-over-CCID does not seem to officially be a thing; we don't support it.
36        // If we would, need to review the following cases catering to semi-documented U2F legacy.
37        if interface != Interface::Contactless {
38            return Err(Status::ConditionsOfUseNotSatisfied);
39        }
40
41        reply.extend_from_slice(b"U2F_V2").unwrap();
42        Ok(())
43    }
44
45    fn deselect(&mut self) {}
46
47    fn call(
48        &mut self,
49        interface: Interface,
50        apdu: CommandView<'_>,
51        response: &mut Data<R>,
52    ) -> apdu_app::Result {
53        // FIDO-over-CCID does not seem to officially be a thing; we don't support it.
54        // If we would, need to review the following cases catering to semi-documented U2F legacy.
55        if interface != Interface::Contactless {
56            return Err(Status::ConditionsOfUseNotSatisfied);
57        }
58
59        let instruction: u8 = apdu.instruction().into();
60
61        // Officially, only NFCCTAP_MSG (0x10) should occur, which is our FidoCommand::Cbor:
62        // <https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#nfc-ctap-msg>
63        //
64        // However, for U2F legacy support (presumably very widespread), after registration
65        // "3. Client sends a command for an operation (register / authenticate)"
66        // <https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-nfc-protocol-v1.2-ps-20170411.html>
67
68        match instruction {
69            // U2F instruction codes
70            // NB(nickray): I don't think 0x00 is a valid case.
71            0x00..=0x02 => super::try_handle_ctap1(self, apdu, response)?, //self.call_authenticator_u2f(apdu, response),
72
73            _ => {
74                match ctaphid_app::Command::try_from(instruction) {
75                    // 0x10
76                    Ok(ctaphid_app::Command::Cbor) => {
77                        super::handle_ctap2(self, apdu.data(), response)
78                    }
79                    Ok(ctaphid_app::Command::Msg) => super::try_handle_ctap1(self, apdu, response)?,
80                    Ok(ctaphid_app::Command::Deselect) => apdu_app::App::<R>::deselect(self),
81                    _ => {
82                        info!("Unsupported ins for fido app {:02x}", instruction);
83                        return Err(iso7816::Status::InstructionNotSupportedOrInvalid);
84                    }
85                }
86            }
87        };
88        Ok(())
89    }
90}