libwebauthn 0.7.0

FIDO2 (WebAuthn) and FIDO U2F platform library for Linux written in Rust
Documentation
//! High-level FIDO U2F (CTAP1) client API for registering and authenticating
//! against an authenticator device. The [`U2F`] trait is blanket-implemented for
//! any [`Channel`] and offers three async operations: protocol negotiation,
//! registration, and signing.
//!
//! [`RegisterRequest`] and [`SignRequest`] describe the inputs, while the
//! corresponding response types carry the results back to the caller. The trait
//! handles the full lifecycle of a U2F exchange, from negotiating the device
//! protocol through running the request with error handling.

use async_trait::async_trait;
use tracing::{instrument, warn};

use crate::fido::FidoProtocol;
use crate::ops::u2f::{RegisterRequest, SignRequest};
use crate::ops::u2f::{RegisterResponse, SignResponse};
use crate::proto::ctap1::Ctap1;
use crate::transport::{error::TransportError, Channel};
use crate::webauthn::error::Error;

#[async_trait]
pub trait U2F {
    async fn u2f_negotiate_protocol(&mut self) -> Result<FidoProtocol, Error>;
    async fn u2f_register(&mut self, op: &RegisterRequest) -> Result<RegisterResponse, Error>;
    async fn u2f_sign(&mut self, op: &SignRequest) -> Result<SignResponse, Error>;
}

#[async_trait]
impl<C> U2F for C
where
    C: Channel,
{
    #[instrument(skip_all)]
    async fn u2f_negotiate_protocol(&mut self) -> Result<FidoProtocol, Error> {
        let supported = self.supported_protocols().await?;
        if !supported.u2f && !supported.fido2 {
            warn!("Negotiation failed: channel doesn't support U2F nor FIDO2");
            return Err(Error::Transport(TransportError::NegotiationFailed));
        }
        // Ensure CTAP1 version is reported correctly.
        self.ctap1_version().await?;
        let selected = FidoProtocol::U2F;
        Ok(selected)
    }

    #[instrument(skip_all, fields(dev = %self))]
    async fn u2f_register(&mut self, op: &RegisterRequest) -> Result<RegisterResponse, Error> {
        let protocol = self.u2f_negotiate_protocol().await?;
        match protocol {
            FidoProtocol::U2F => self.ctap1_register(op).await,
            _ => Err(Error::Transport(TransportError::NegotiationFailed)),
        }
    }

    #[instrument(skip_all, fields(dev = %self))]
    async fn u2f_sign(&mut self, op: &SignRequest) -> Result<SignResponse, Error> {
        let protocol = self.u2f_negotiate_protocol().await?;
        match protocol {
            FidoProtocol::U2F => self.ctap1_sign(op).await,
            _ => Err(Error::Transport(TransportError::NegotiationFailed)),
        }
    }
}