libwebauthn 0.5.0

FIDO2 (WebAuthn) and FIDO U2F platform library for Linux written in Rust
Documentation
use async_trait::async_trait;
use std::{collections::VecDeque, fmt::Display, time::Duration};
use tokio::sync::broadcast;

use crate::{
    proto::{
        ctap1::apdu::{ApduRequest, ApduResponse},
        ctap2::cbor::{CborRequest, CborResponse},
    },
    transport::{
        device::SupportedProtocols, AuthTokenData, Channel, ChannelStatus, Ctap2AuthTokenStore,
    },
    webauthn::Error,
    UvUpdate,
};

pub struct MockChannel {
    expected_requests: VecDeque<CborRequest>,
    responses: VecDeque<CborResponse>,
    auth_token_data: Option<AuthTokenData>,
    ux_update_sender: broadcast::Sender<UvUpdate>,
}

impl Default for MockChannel {
    fn default() -> Self {
        Self::new()
    }
}

impl MockChannel {
    pub fn new() -> Self {
        let (ux_update_sender, _) = broadcast::channel(16);
        Self {
            expected_requests: VecDeque::new(),
            responses: VecDeque::new(),
            auth_token_data: None,
            ux_update_sender,
        }
    }

    pub fn push_command_pair(&mut self, expected_request: CborRequest, response: CborResponse) {
        self.expected_requests.push_front(expected_request);
        self.responses.push_front(response);
    }
}

impl Ctap2AuthTokenStore for MockChannel {
    fn store_auth_data(&mut self, auth_token_data: AuthTokenData) {
        self.auth_token_data = Some(auth_token_data);
    }

    fn get_auth_data(&self) -> Option<&AuthTokenData> {
        self.auth_token_data.as_ref()
    }

    fn clear_uv_auth_token_store(&mut self) {
        self.auth_token_data = None;
    }
}

impl Display for MockChannel {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "TestChannel")
    }
}

#[async_trait]
impl Channel for MockChannel {
    type UxUpdate = UvUpdate;

    fn get_ux_update_sender(&self) -> &broadcast::Sender<Self::UxUpdate> {
        &self.ux_update_sender
    }

    async fn supported_protocols(&self) -> Result<SupportedProtocols, Error> {
        Ok(SupportedProtocols {
            u2f: false,
            fido2: true,
        })
    }
    async fn status(&self) -> ChannelStatus {
        unimplemented!();
    }
    async fn close(&mut self) {
        unimplemented!();
    }

    async fn apdu_send(&mut self, _request: &ApduRequest, _timeout: Duration) -> Result<(), Error> {
        unimplemented!();
    }
    async fn apdu_recv(&mut self, _timeout: Duration) -> Result<ApduResponse, Error> {
        unimplemented!();
    }

    async fn cbor_send(&mut self, request: &CborRequest, _timeout: Duration) -> Result<(), Error> {
        let expected = self
            .expected_requests
            .pop_back()
            .expect("No expected request found, but one was sent");
        assert_eq!(
            &expected,
            request,
            "{} items still in the queue. Left: Expected, Right: Received",
            self.expected_requests.len()
        );
        Ok(())
    }
    async fn cbor_recv(&mut self, _timeout: Duration) -> Result<CborResponse, Error> {
        let response = self
            .responses
            .pop_back()
            .expect("No response found, but one was requested");
        Ok(response)
    }
}