Skip to content

Commit 5ccd1d7

Browse files
QR code device channel and known devices
1 parent d1aa3fb commit 5ccd1d7

File tree

10 files changed

+754
-257
lines changed

10 files changed

+754
-257
lines changed

Cargo.lock

Lines changed: 167 additions & 85 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

libwebauthn/examples/webauthn_cable.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::sync::Arc;
44
use std::time::Duration;
55

66
use libwebauthn::pin::PinRequestReason;
7-
use libwebauthn::transport::cable::channel::CableUxUpdate;
7+
use libwebauthn::transport::cable::channel::{CableUpdate, CableUxUpdate};
88
use libwebauthn::transport::cable::known_devices::{
99
CableKnownDevice, ClientPayloadHint, EphemeralDeviceInfoStore,
1010
};
@@ -75,6 +75,13 @@ async fn handle_updates(mut state_recv: Receiver<CableUxUpdate>) {
7575
}
7676
}
7777
},
78+
CableUxUpdate::CableUpdate(cable_update) => match cable_update {
79+
CableUpdate::ProximityCheck => println!("Proximity check in progress..."),
80+
CableUpdate::Connecting => println!("Connecting to the device..."),
81+
CableUpdate::Authenticating => println!("Authenticating with the device..."),
82+
CableUpdate::Connected => println!("Tunnel established successfully!"),
83+
CableUpdate::Failed => println!("Failed to establish tunnel."),
84+
},
7885
}
7986
}
8087
}

libwebauthn/src/transport/cable/advertisement.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use ::btleplug::api::Central;
22
use futures::StreamExt;
33
use std::pin::pin;
4-
use tracing::{debug, trace, warn};
4+
use tracing::{debug, instrument, trace, warn};
55
use uuid::Uuid;
66

77
use crate::transport::ble::btleplug::{self, FidoDevice};
@@ -15,7 +15,7 @@ const CABLE_UUID_GOOGLE: &str = "0000fde2-0000-1000-8000-00805f9b34fb";
1515
#[derive(Debug)]
1616
pub(crate) struct DecryptedAdvert {
1717
pub plaintext: [u8; 16],
18-
pub nonce: [u8; 10],
18+
pub _nonce: [u8; 10],
1919
pub routing_id: [u8; 3],
2020
pub encoded_tunnel_server_domain: u16,
2121
}
@@ -31,13 +31,14 @@ impl From<[u8; 16]> for DecryptedAdvert {
3131
plaintext_fixed.copy_from_slice(&plaintext[..16]);
3232
Self {
3333
plaintext: plaintext_fixed,
34-
nonce,
34+
_nonce: nonce,
3535
routing_id,
3636
encoded_tunnel_server_domain,
3737
}
3838
}
3939
}
4040

41+
#[instrument(skip_all, err)]
4142
pub(crate) async fn await_advertisement(
4243
eid_key: &[u8],
4344
) -> Result<(FidoDevice, DecryptedAdvert), Error> {

libwebauthn/src/transport/cable/channel.rs

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::fmt::{Display, Formatter};
22
use std::time::Duration;
33

44
use async_trait::async_trait;
5-
use tokio::sync::mpsc;
5+
use tokio::sync::{mpsc, watch};
66
use tokio::{task, time};
77
use tracing::error;
88

@@ -21,6 +21,16 @@ use crate::UvUpdate;
2121
use super::known_devices::CableKnownDevice;
2222
use super::qr_code_device::CableQrCodeDevice;
2323

24+
#[derive(Debug, Clone, PartialEq)]
25+
pub enum ConnectionState {
26+
/// Connection is being established (proximity check, connecting, authenticating)
27+
Connecting,
28+
/// Connection is fully established and ready for operations
29+
Connected,
30+
/// Connection has terminated
31+
Terminated,
32+
}
33+
2434
#[derive(Debug)]
2535
pub enum CableChannelDevice<'d> {
2636
QrCode(&'d CableQrCodeDevice),
@@ -34,12 +44,42 @@ pub struct CableChannel {
3444

3545
/// The noise state used for encryption over the WebSocket stream.
3646
// pub(crate) noise_state: TransportState,
37-
38-
/// The device that this channel is connected to.
3947
pub(crate) handle_connection: task::JoinHandle<()>,
4048
pub(crate) cbor_sender: mpsc::Sender<CborRequest>,
4149
pub(crate) cbor_receiver: mpsc::Receiver<CborResponse>,
4250
pub(crate) tx: mpsc::Sender<CableUxUpdate>,
51+
/// Watch receiver for connection state
52+
pub(crate) connection_state_rx: watch::Receiver<ConnectionState>,
53+
}
54+
55+
impl CableChannel {
56+
async fn wait_for_connection(&self) -> Result<(), Error> {
57+
let mut rx = self.connection_state_rx.clone();
58+
59+
// If already connected, return immediately
60+
if *rx.borrow() == ConnectionState::Connected {
61+
return Ok(());
62+
}
63+
64+
// If already terminated, return error immediately
65+
if *rx.borrow() == ConnectionState::Terminated {
66+
return Err(Error::Transport(TransportError::ConnectionFailed));
67+
}
68+
69+
// Wait for state change
70+
while rx.changed().await.is_ok() {
71+
match *rx.borrow() {
72+
ConnectionState::Connected => return Ok(()),
73+
ConnectionState::Terminated => {
74+
return Err(Error::Transport(TransportError::ConnectionFailed))
75+
}
76+
ConnectionState::Connecting => continue,
77+
}
78+
}
79+
80+
// If the sender was dropped, consider it a failure
81+
Err(Error::Transport(TransportError::ConnectionFailed))
82+
}
4383
}
4484

4585
impl Display for CableChannel {
@@ -57,6 +97,21 @@ impl Drop for CableChannel {
5797
#[derive(Debug)]
5898
pub enum CableUxUpdate {
5999
UvUpdate(UvUpdate),
100+
CableUpdate(CableUpdate),
101+
}
102+
103+
#[derive(Debug)]
104+
pub enum CableUpdate {
105+
/// Waiting for proximity check user interaction (eg. scan a QR code, or confirm on the device).
106+
ProximityCheck,
107+
/// Connecting to the tunnel server.
108+
Connecting,
109+
/// Connected to the tunnel server, authenticating the channel.
110+
Authenticating,
111+
/// Connected to the authenticator device via the tunnel server.
112+
Connected,
113+
/// The connection to the authenticator device has failed.
114+
Failed,
60115
}
61116

62117
impl From<UvUpdate> for CableUxUpdate {
@@ -95,6 +150,10 @@ impl<'d> Channel for CableChannel {
95150
}
96151

97152
async fn cbor_send(&mut self, request: &CborRequest, timeout: Duration) -> Result<(), Error> {
153+
// First, wait for connection to be established (no timeout for handshake)
154+
self.wait_for_connection().await?;
155+
156+
// Now apply timeout only to the actual CBOR operation
98157
match time::timeout(timeout, self.cbor_sender.send(request.clone())).await {
99158
Ok(Ok(_)) => Ok(()),
100159
Ok(Err(error)) => {
@@ -109,6 +168,10 @@ impl<'d> Channel for CableChannel {
109168
}
110169

111170
async fn cbor_recv(&mut self, timeout: Duration) -> Result<CborResponse, Error> {
171+
// First, wait for connection to be established (no timeout for handshake)
172+
self.wait_for_connection().await?;
173+
174+
// Now apply timeout only to the actual CBOR operation
112175
match time::timeout(timeout, self.cbor_receiver.recv()).await {
113176
Ok(Some(response)) => Ok(response),
114177
Ok(None) => Err(Error::Transport(TransportError::TransportUnavailable)),

0 commit comments

Comments
 (0)