Skip to content

Commit 6ddcd3f

Browse files
Add Error to CableUxUpdate
1 parent 7d2e6cf commit 6ddcd3f

File tree

9 files changed

+59
-52
lines changed

9 files changed

+59
-52
lines changed

libwebauthn/examples/webauthn_cable.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ async fn handle_updates(mut state_recv: Receiver<CableUxUpdate>) {
8080
CableUpdate::Connecting => println!("Connecting to the device..."),
8181
CableUpdate::Authenticating => println!("Authenticating with the device..."),
8282
CableUpdate::Connected => println!("Tunnel established successfully!"),
83-
CableUpdate::Failed => println!("Failed to establish tunnel."),
83+
CableUpdate::Error(err) => println!("Error during connection: {}", err),
8484
},
8585
}
8686
}

libwebauthn/src/transport/cable/advertisement.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use uuid::Uuid;
77
use crate::transport::ble::btleplug::{self, FidoDevice};
88
use crate::transport::cable::crypto::trial_decrypt_advert;
99
use crate::transport::error::TransportError;
10-
use crate::webauthn::error::Error;
1110

1211
const CABLE_UUID_FIDO: &str = "0000fff9-0000-1000-8000-00805f9b34fb";
1312
const CABLE_UUID_GOOGLE: &str = "0000fde2-0000-1000-8000-00805f9b34fb";
@@ -41,22 +40,22 @@ impl From<[u8; 16]> for DecryptedAdvert {
4140
#[instrument(skip_all, err)]
4241
pub(crate) async fn await_advertisement(
4342
eid_key: &[u8],
44-
) -> Result<(FidoDevice, DecryptedAdvert), Error> {
43+
) -> Result<(FidoDevice, DecryptedAdvert), TransportError> {
4544
let uuids = &[
4645
Uuid::parse_str(CABLE_UUID_FIDO).unwrap(),
4746
Uuid::parse_str(CABLE_UUID_GOOGLE).unwrap(), // Deprecated, but may still be in use.
4847
];
4948
let stream = btleplug::manager::start_discovery_for_service_data(uuids)
5049
.await
51-
.or(Err(Error::Transport(TransportError::TransportUnavailable)))?;
50+
.or(Err(TransportError::TransportUnavailable))?;
5251

5352
let mut stream = pin!(stream);
5453
while let Some((adapter, peripheral, data)) = stream.as_mut().next().await {
5554
debug!({ ?peripheral, ?data }, "Found device with service data");
5655

5756
let Some(device) = btleplug::manager::get_device(peripheral.clone())
5857
.await
59-
.or(Err(Error::Transport(TransportError::TransportUnavailable)))?
58+
.or(Err(TransportError::TransportUnavailable))?
6059
else {
6160
warn!(
6261
?peripheral,
@@ -82,11 +81,11 @@ pub(crate) async fn await_advertisement(
8281
adapter
8382
.stop_scan()
8483
.await
85-
.or(Err(Error::Transport(TransportError::TransportUnavailable)))?;
84+
.or(Err(TransportError::TransportUnavailable))?;
8685

8786
return Ok((device, advert));
8887
}
8988

9089
warn!("BLE advertisement discovery stream terminated");
91-
Err(Error::Transport(TransportError::TransportUnavailable))
90+
Err(TransportError::TransportUnavailable)
9291
}

libwebauthn/src/transport/cable/channel.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ impl CableChannel {
5757

5858
// If already terminated, return error immediately
5959
if *rx.borrow() == ConnectionState::Terminated {
60-
return Err(Error::Transport(TransportError::ConnectionFailed));
60+
return Err(Error::Transport(TransportError::ConnectionLost));
6161
}
6262

6363
// Wait for state change
@@ -72,7 +72,7 @@ impl CableChannel {
7272
}
7373

7474
// If the sender was dropped, consider it a failure
75-
Err(Error::Transport(TransportError::ConnectionFailed))
75+
Err(Error::Transport(TransportError::ConnectionLost))
7676
}
7777
}
7878

@@ -105,7 +105,7 @@ pub enum CableUpdate {
105105
/// Connected to the authenticator device via the tunnel server.
106106
Connected,
107107
/// The connection to the authenticator device has failed.
108-
Failed,
108+
Error(TransportError),
109109
}
110110

111111
impl From<UvUpdate> for CableUxUpdate {

libwebauthn/src/transport/cable/connection_stages.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ use super::tunnel::{self, CableTunnelConnectionType, TunnelNoiseState};
1212
use crate::proto::ctap2::cbor::{CborRequest, CborResponse};
1313
use crate::transport::ble::btleplug::FidoDevice;
1414
use crate::transport::error::TransportError;
15-
use crate::webauthn::error::Error;
1615
use std::sync::Arc;
1716

1817
#[derive(Debug)]
@@ -60,7 +59,7 @@ impl ConnectionInput {
6059
pub fn new_for_qr_code(
6160
qr_device: &CableQrCodeDevice,
6261
proximity_output: &ProximityCheckOutput,
63-
) -> Result<Self, Error> {
62+
) -> Result<Self, TransportError> {
6463
let tunnel_domain = decode_tunnel_domain_from_advert(&proximity_output.advert)?;
6564

6665
let routing_id_str = hex::encode(&proximity_output.advert.routing_id);
@@ -194,7 +193,7 @@ impl TunnelConnectionInput {
194193
#[async_trait]
195194
pub(crate) trait UxUpdateSender: Send + Sync {
196195
async fn send_update(&self, update: CableUxUpdate);
197-
async fn send_error(&self);
196+
async fn send_error(&self, error: TransportError);
198197
async fn set_connection_state(&self, state: ConnectionState);
199198
}
200199

@@ -221,10 +220,10 @@ impl UxUpdateSender for MpscUxUpdateSender {
221220
let _ = self.sender.send(update);
222221
}
223222

224-
async fn send_error(&self) {
223+
async fn send_error(&self, error: TransportError) {
225224
let _ = self
226225
.sender
227-
.send(CableUxUpdate::CableUpdate(CableUpdate::Failed));
226+
.send(CableUxUpdate::CableUpdate(CableUpdate::Error(error)));
228227
let _ = self.connection_state_tx.send(ConnectionState::Terminated);
229228
}
230229

@@ -237,7 +236,7 @@ impl UxUpdateSender for MpscUxUpdateSender {
237236
pub(crate) async fn proximity_check_stage(
238237
input: ProximityCheckInput,
239238
ux_sender: &dyn UxUpdateSender,
240-
) -> Result<ProximityCheckOutput, Error> {
239+
) -> Result<ProximityCheckOutput, TransportError> {
241240
debug!("Starting proximity check stage");
242241

243242
ux_sender
@@ -257,7 +256,7 @@ pub(crate) async fn proximity_check_stage(
257256
pub(crate) async fn connection_stage(
258257
input: ConnectionInput,
259258
ux_sender: &dyn UxUpdateSender,
260-
) -> Result<ConnectionOutput, Error> {
259+
) -> Result<ConnectionOutput, TransportError> {
261260
debug!(?input.tunnel_domain, "Starting connection stage");
262261

263262
ux_sender
@@ -278,7 +277,7 @@ pub(crate) async fn connection_stage(
278277
pub(crate) async fn handshake_stage(
279278
input: HandshakeInput,
280279
ux_sender: &dyn UxUpdateSender,
281-
) -> Result<HandshakeOutput, Error> {
280+
) -> Result<HandshakeOutput, TransportError> {
282281
debug!("Starting handshake stage");
283282

284283
ux_sender
@@ -312,10 +311,12 @@ fn derive_psk(secret: &[u8], advert_plaintext: &[u8]) -> [u8; 32] {
312311
psk
313312
}
314313

315-
pub(crate) fn decode_tunnel_domain_from_advert(advert: &DecryptedAdvert) -> Result<String, Error> {
314+
pub(crate) fn decode_tunnel_domain_from_advert(
315+
advert: &DecryptedAdvert,
316+
) -> Result<String, TransportError> {
316317
tunnel::decode_tunnel_server_domain(advert.encoded_tunnel_server_domain)
317318
.ok_or_else(|| {
318319
error!({ encoded = %advert.encoded_tunnel_server_domain }, "Failed to decode tunnel server domain");
319-
Error::Transport(TransportError::InvalidFraming)
320+
TransportError::InvalidFraming
320321
})
321322
}

libwebauthn/src/transport/cable/known_devices.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ impl CableKnownDevice {
162162
async fn connection(
163163
known_device: &CableKnownDevice,
164164
ux_sender: &super::connection_stages::MpscUxUpdateSender,
165-
) -> Result<HandshakeOutput, Error> {
165+
) -> Result<HandshakeOutput, TransportError> {
166166
let client_nonce = rand::random::<ClientNonce>();
167167

168168
// Stage 1: Connection (no proximity check needed for known devices)
@@ -201,9 +201,12 @@ impl<'d> Device<'d, Cable, CableChannel> for CableKnownDevice {
201201
let ux_sender =
202202
MpscUxUpdateSender::new(ux_update_sender_clone, connection_state_sender);
203203

204-
let Ok(handshake_output) = Self::connection(&known_device, &ux_sender).await else {
205-
ux_sender.send_error().await;
206-
return;
204+
let handshake_output = match Self::connection(&known_device, &ux_sender).await {
205+
Ok(handshake_output) => handshake_output,
206+
Err(e) => {
207+
ux_sender.send_error(e).await;
208+
return;
209+
}
207210
};
208211

209212
let tunnel_input = TunnelConnectionInput::from_handshake_output(

libwebauthn/src/transport/cable/qr_code_device.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use crate::proto::ctap2::cbor;
2525
use crate::transport::cable::digit_encode;
2626
use crate::transport::Device;
2727
use crate::webauthn::error::Error;
28+
use crate::webauthn::TransportError;
2829

2930
#[derive(Debug, Clone, Copy, Serialize, PartialEq)]
3031
pub enum QrCodeOperationHint {
@@ -162,7 +163,7 @@ impl CableQrCodeDevice {
162163
async fn connection(
163164
qr_device: &CableQrCodeDevice,
164165
ux_sender: &MpscUxUpdateSender,
165-
) -> Result<super::connection_stages::HandshakeOutput, Error> {
166+
) -> Result<super::connection_stages::HandshakeOutput, TransportError> {
166167
// Stage 1: Proximity check
167168
let proximity_input = ProximityCheckInput::new_for_qr_code(qr_device);
168169
let proximity_output = proximity_check_stage(proximity_input, ux_sender).await?;
@@ -206,9 +207,12 @@ impl<'d> Device<'d, Cable, CableChannel> for CableQrCodeDevice {
206207
let ux_sender =
207208
MpscUxUpdateSender::new(ux_update_sender_clone.clone(), connection_state_sender);
208209

209-
let Ok(handshake_output) = Self::connection(&qr_device, &ux_sender).await else {
210-
ux_sender.send_error().await;
211-
return;
210+
let handshake_output = match Self::connection(&qr_device, &ux_sender).await {
211+
Ok(handshake_output) => handshake_output,
212+
Err(e) => {
213+
ux_sender.send_error(e).await;
214+
return;
215+
}
212216
};
213217

214218
let tunnel_input = TunnelConnectionInput::from_handshake_output(

libwebauthn/src/transport/cable/tunnel.rs

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ impl std::fmt::Debug for CableTunnelConnectionType {
202202
pub(crate) async fn connect<'d>(
203203
tunnel_domain: &str,
204204
connection_type: &CableTunnelConnectionType,
205-
) -> Result<WebSocketStream<MaybeTlsStream<TcpStream>>, Error> {
205+
) -> Result<WebSocketStream<MaybeTlsStream<TcpStream>>, TransportError> {
206206
ensure_rustls_crypto_provider();
207207

208208
let connect_url = match connection_type {
@@ -221,22 +221,22 @@ pub(crate) async fn connect<'d>(
221221
debug!(?connect_url, "Connecting to tunnel server");
222222
let mut request = connect_url
223223
.into_client_request()
224-
.or(Err(Error::Transport(TransportError::InvalidEndpoint)))?;
224+
.or(Err(TransportError::InvalidEndpoint))?;
225225
request.headers_mut().insert(
226226
"Sec-WebSocket-Protocol",
227227
"fido.cable"
228228
.parse()
229-
.or(Err(Error::Transport(TransportError::InvalidEndpoint)))?,
229+
.or(Err(TransportError::InvalidEndpoint))?,
230230
);
231231

232232
if let CableTunnelConnectionType::KnownDevice { client_payload, .. } = connection_type {
233-
let client_payload = cbor::to_vec(client_payload)
234-
.or(Err(Error::Transport(TransportError::InvalidEndpoint)))?;
233+
let client_payload =
234+
cbor::to_vec(client_payload).or(Err(TransportError::InvalidEndpoint))?;
235235
request.headers_mut().insert(
236236
"X-caBLE-Client-Payload",
237237
hex::encode(&client_payload)
238238
.parse()
239-
.or(Err(Error::Transport(TransportError::InvalidEndpoint)))?,
239+
.or(Err(TransportError::InvalidEndpoint))?,
240240
);
241241
}
242242
trace!(?request);
@@ -245,14 +245,14 @@ pub(crate) async fn connect<'d>(
245245
Ok((ws_stream, response)) => (ws_stream, response),
246246
Err(e) => {
247247
error!(?e, "Failed to connect to tunnel server");
248-
return Err(Error::Transport(TransportError::ConnectionFailed));
248+
return Err(TransportError::ConnectionFailed);
249249
}
250250
};
251251
debug!(?response, "Connected to tunnel server");
252252

253253
if response.status() != StatusCode::SWITCHING_PROTOCOLS {
254254
error!(?response, "Failed to switch to websocket protocol");
255-
return Err(Error::Transport(TransportError::ConnectionFailed));
255+
return Err(TransportError::ConnectionFailed);
256256
}
257257
debug!("Tunnel server returned success");
258258

@@ -269,7 +269,7 @@ pub(crate) async fn do_handshake(
269269
ws_stream: &mut WebSocketStream<MaybeTlsStream<TcpStream>>,
270270
psk: [u8; 32],
271271
connection_type: &CableTunnelConnectionType,
272-
) -> Result<TunnelNoiseState, Error> {
272+
) -> Result<TunnelNoiseState, TransportError> {
273273
let noise_handshake = match connection_type {
274274
CableTunnelConnectionType::QrCode { private_key, .. } => {
275275
let local_private_key = private_key.to_owned().to_bytes();
@@ -294,7 +294,7 @@ pub(crate) async fn do_handshake(
294294
Ok(handshake) => handshake,
295295
Err(e) => {
296296
error!(?e, "Failed to build Noise handshake");
297-
return Err(Error::Transport(TransportError::ConnectionFailed));
297+
return Err(TransportError::ConnectionFailed);
298298
}
299299
};
300300

@@ -303,7 +303,7 @@ pub(crate) async fn do_handshake(
303303
Ok(msg_len) => msg_len,
304304
Err(e) => {
305305
error!(?e, "Failed to write initial handshake message");
306-
return Err(Error::Transport(TransportError::ConnectionFailed));
306+
return Err(TransportError::ConnectionFailed);
307307
}
308308
};
309309

@@ -315,7 +315,7 @@ pub(crate) async fn do_handshake(
315315

316316
if let Err(e) = ws_stream.send(Message::Binary(initial_msg.into())).await {
317317
error!(?e, "Failed to send initial handshake message");
318-
return Err(Error::Transport(TransportError::ConnectionFailed));
318+
return Err(TransportError::ConnectionFailed);
319319
}
320320
debug!("Sent initial handshake message");
321321

@@ -329,15 +329,15 @@ pub(crate) async fn do_handshake(
329329

330330
Some(Ok(msg)) => {
331331
error!(?msg, "Unexpected message type received");
332-
return Err(Error::Transport(TransportError::ConnectionFailed));
332+
return Err(TransportError::ConnectionFailed);
333333
}
334334
Some(Err(e)) => {
335335
error!(?e, "Failed to read handshake response");
336-
return Err(Error::Transport(TransportError::ConnectionFailed));
336+
return Err(TransportError::ConnectionFailed);
337337
}
338338
None => {
339339
error!("Connection was closed before handshake was complete");
340-
return Err(Error::Transport(TransportError::ConnectionFailed));
340+
return Err(TransportError::ConnectionFailed);
341341
}
342342
};
343343

@@ -350,7 +350,7 @@ pub(crate) async fn do_handshake(
350350
{ len = response.len() },
351351
"Peer handshake message is too short"
352352
);
353-
return Err(Error::Transport(TransportError::ConnectionFailed));
353+
return Err(TransportError::ConnectionFailed);
354354
}
355355

356356
let mut payload = [0u8; 1024];
@@ -365,7 +365,7 @@ pub(crate) async fn do_handshake(
365365

366366
if !noise_handshake.is_handshake_finished() {
367367
error!("Handshake did not complete");
368-
return Err(Error::Transport(TransportError::ConnectionFailed));
368+
return Err(TransportError::ConnectionFailed);
369369
}
370370

371371
Ok(TunnelNoiseState {

libwebauthn/src/transport/error.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#[derive(thiserror::Error, Debug, PartialEq)]
1+
#[derive(thiserror::Error, Debug, PartialEq, Clone)]
22
pub enum TransportError {
33
#[error("connection failed")]
44
ConnectionFailed,
@@ -23,3 +23,9 @@ pub enum TransportError {
2323
#[error("input/output error: {0}")]
2424
IoError(std::io::ErrorKind),
2525
}
26+
27+
impl From<snow::Error> for TransportError {
28+
fn from(_error: snow::Error) -> Self {
29+
TransportError::NegotiationFailed
30+
}
31+
}

libwebauthn/src/webauthn/error.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,6 @@ pub enum Error {
1111
Platform(#[from] PlatformError),
1212
}
1313

14-
impl From<snow::Error> for Error {
15-
fn from(_error: snow::Error) -> Self {
16-
Error::Transport(TransportError::NegotiationFailed)
17-
}
18-
}
19-
2014
impl From<CborError> for Error {
2115
fn from(error: CborError) -> Self {
2216
Error::Platform(PlatformError::CborError(error))

0 commit comments

Comments
 (0)