1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
//! Low-level transport abstraction layer for communication with FIDO tokens.
//!
//! See [crate::ctap2] for a higher-level abstraction over this API.
mod any;
pub mod iso7816;
#[cfg(any(all(doc, not(doctest)), feature = "vendor-solokey"))]
pub(crate) mod solokey;
#[cfg(any(doc, feature = "bluetooth", feature = "usb"))]
pub(crate) mod types;
#[cfg(any(all(doc, not(doctest)), feature = "vendor-yubikey"))]
pub(crate) mod yubikey;
pub use crate::transport::any::{AnyToken, AnyTransport};
use async_trait::async_trait;
use futures::stream::BoxStream;
use std::fmt;
use webauthn_rs_proto::AuthenticatorTransport;
use crate::{ctap2::*, error::WebauthnCError, ui::UiCallback};
#[cfg(any(doc, feature = "bluetooth", feature = "usb"))]
pub(crate) const TYPE_INIT: u8 = 0x80;
#[derive(Debug)]
pub enum TokenEvent<T: Token> {
Added(T),
Removed(T::Id),
EnumerationComplete,
}
/// Represents a transport layer protocol for [Token].
///
/// If you don't care which transport your application uses, use [AnyTransport]
/// to automatically use all available transports on the platform.
#[async_trait]
pub trait Transport<'b>: Sized + fmt::Debug + Send {
/// The type of [Token] returned by this [Transport].
type Token: Token + 'b;
/// Watches for token connection and disconnection on this [Transport].
///
/// Initially, this send synthetic [`TokenEvent::Added`] for all
/// currently-connected tokens, followed by
/// [`TokenEvent::EnumerationComplete`].
async fn watch(&self) -> Result<BoxStream<TokenEvent<Self::Token>>, WebauthnCError>;
/// Gets all currently-connected devices associated with this [Transport].
///
/// This method does not work for Bluetooth devices. Use
/// [`watch()`][] instead.
///
/// [`watch()`]: Transport::watch
async fn tokens(&self) -> Result<Vec<Self::Token>, WebauthnCError>;
}
/// Represents a connection to a single FIDO token over a [Transport].
///
/// This is a low level interface to FIDO tokens, passing raw messages.
/// [crate::ctap2] provides a higher level abstraction.
#[async_trait]
pub trait Token: Sized + fmt::Debug + Sync + Send {
type Id: Sized + fmt::Debug + Sync + Send;
fn has_button(&self) -> bool {
true
}
/// Gets the transport layer used for communication with this token.
fn get_transport(&self) -> AuthenticatorTransport;
/// Transmit a CBOR message to a token, and deserialises the response.
async fn transmit<'a, C, R, U>(&mut self, cmd: C, ui: &U) -> Result<R, WebauthnCError>
where
C: CBORCommand<Response = R>,
R: CBORResponse,
U: UiCallback,
{
let cbor = cmd.cbor().map_err(|_| WebauthnCError::Cbor)?;
trace!(">>> {}", hex::encode(&cbor));
let resp = self.transmit_raw(&cbor, ui).await?;
trace!("<<< {}", hex::encode(&resp));
R::try_from(resp.as_slice()).map_err(|_| {
//error!("error: {:?}", e);
WebauthnCError::Cbor
})
}
/// Transmits a command on the underlying transport.
///
/// `cbor` is a CBOR-encoded command.
///
/// Interfaces need to check for and return any transport-layer-specific
/// error code [WebauthnCError::Ctap], but don't need to worry about
/// deserialising CBOR.
async fn transmit_raw<U>(&mut self, cbor: &[u8], ui: &U) -> Result<Vec<u8>, WebauthnCError>
where
U: UiCallback;
/// Cancels a pending request.
async fn cancel(&mut self) -> Result<(), WebauthnCError>;
/// Initializes the [Token]
async fn init(&mut self) -> Result<(), WebauthnCError>;
/// Closes the [Token]
async fn close(&mut self) -> Result<(), WebauthnCError>;
}