Skip to main content

fido_authenticator/dispatch/
apdu.rs

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