Skip to content

Commit c9654de

Browse files
refactor(transport): probe real availability and simplify transport()
1 parent 61ad0af commit c9654de

10 files changed

Lines changed: 58 additions & 53 deletions

File tree

libwebauthn/src/lib.rs

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -215,36 +215,27 @@ impl PartialEq for PinNotSetUpdate {
215215
}
216216
}
217217

218-
/// Transports compiled into this build. Hybrid/caBLE is always included. Using it
219-
/// at runtime still needs a BLE adapter (see `transport::cable::is_available`).
220-
/// NFC appears only when an `nfc-backend-*` feature is enabled.
221-
pub fn available_transports() -> Vec<Transport> {
222-
[
223-
Transport::Usb,
224-
Transport::Ble,
225-
Transport::Hybrid,
226-
#[cfg(any(feature = "nfc-backend-pcsc", feature = "nfc-backend-libnfc"))]
227-
Transport::Nfc,
228-
]
229-
.into_iter()
230-
.collect()
218+
/// The transports usable right now. USB is always available. BLE and Hybrid
219+
/// (caBLE) need a Bluetooth adapter. NFC needs a reader and a compiled backend.
220+
pub async fn available_transports() -> Vec<Transport> {
221+
let mut transports = vec![Transport::Usb];
222+
// BLE and Hybrid (caBLE) both need a Bluetooth adapter.
223+
if transport::ble::is_available().await {
224+
transports.push(Transport::Ble);
225+
transports.push(Transport::Hybrid);
226+
}
227+
if transport::nfc::is_nfc_available() {
228+
transports.push(Transport::Nfc);
229+
}
230+
transports
231231
}
232232

233233
#[cfg(test)]
234234
mod tests {
235235
use super::*;
236236

237-
#[test]
238-
fn available_transports_reports_compiled_in() {
239-
let transports = available_transports();
240-
assert!(transports.contains(&Transport::Usb));
241-
assert!(transports.contains(&Transport::Ble));
242-
assert!(transports.contains(&Transport::Hybrid));
243-
244-
let nfc_compiled = cfg!(any(
245-
feature = "nfc-backend-pcsc",
246-
feature = "nfc-backend-libnfc"
247-
));
248-
assert_eq!(transports.contains(&Transport::Nfc), nfc_compiled);
237+
#[tokio::test]
238+
async fn available_transports_always_includes_usb() {
239+
assert!(available_transports().await.contains(&Transport::Usb));
249240
}
250241
}

libwebauthn/src/ops/webauthn/make_credential.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ use crate::{
3434
},
3535
},
3636
transport::AuthTokenData,
37+
Transport,
3738
};
3839

3940
use super::timeout::DEFAULT_TIMEOUT;
@@ -48,7 +49,7 @@ pub struct MakeCredentialResponse {
4849
pub large_blob_key: Option<Vec<u8>>,
4950
pub unsigned_extensions_output: MakeCredentialsResponseUnsignedExtensions,
5051
/// Transport the credential was created over, stamped by the channel.
51-
pub transport: Option<crate::Transport>,
52+
pub transport: Option<Transport>,
5253
/// Transports the authenticator advertised in getInfo (0x09), if any.
5354
pub authenticator_transports: Option<Vec<String>>,
5455
}
@@ -67,7 +68,7 @@ struct AttestationObject<'a> {
6768
/// Maps the active transport to AuthenticatorTransport tokens for the registration
6869
/// `transports` member. The list is deduplicated and lexicographically sorted per
6970
/// WebAuthn L3 §5.2.1.1, and is empty when the transport is unknown.
70-
fn registration_transports(transport: Option<crate::Transport>) -> Vec<String> {
71+
fn registration_transports(transport: Option<Transport>) -> Vec<String> {
7172
let mut tokens: Vec<String> = transport
7273
.into_iter()
7374
.map(Ctap2Transport::from)
@@ -1537,18 +1538,18 @@ mod tests {
15371538
let request = create_test_request();
15381539

15391540
for (transport, token) in [
1540-
(crate::Transport::Usb, "usb"),
1541-
(crate::Transport::Ble, "ble"),
1542-
(crate::Transport::Nfc, "nfc"),
1543-
(crate::Transport::Hybrid, "hybrid"),
1541+
(Transport::Usb, "usb"),
1542+
(Transport::Ble, "ble"),
1543+
(Transport::Nfc, "nfc"),
1544+
(Transport::Hybrid, "hybrid"),
15441545
] {
15451546
response.transport = Some(transport);
15461547
let model = response.to_idl_model(&request).unwrap();
15471548
assert_eq!(model.response.transports, vec![token.to_string()]);
15481549
}
15491550

15501551
// The token reaches the JSON wire format too.
1551-
response.transport = Some(crate::Transport::Nfc);
1552+
response.transport = Some(Transport::Nfc);
15521553
let json = response
15531554
.to_json_string(
15541555
&request,
@@ -1573,7 +1574,7 @@ mod tests {
15731574
let request = create_test_request();
15741575

15751576
// Reported out of order with a duplicate; the ceremony transport (ble) folds in.
1576-
response.transport = Some(crate::Transport::Ble);
1577+
response.transport = Some(Transport::Ble);
15771578
response.authenticator_transports = Some(vec![
15781579
"usb".to_string(),
15791580
"nfc".to_string(),
@@ -1586,7 +1587,7 @@ mod tests {
15861587
);
15871588

15881589
// A ceremony transport already in the reported list is not duplicated.
1589-
response.transport = Some(crate::Transport::Usb);
1590+
response.transport = Some(Transport::Usb);
15901591
response.authenticator_transports = Some(vec!["usb".to_string(), "nfc".to_string()]);
15911592
let model = response.to_idl_model(&request).unwrap();
15921593
assert_eq!(
@@ -1600,7 +1601,7 @@ mod tests {
16001601
assert_eq!(model.response.transports, vec!["usb".to_string()]);
16011602

16021603
// Unknown tokens pass through, folded with the ceremony transport.
1603-
response.transport = Some(crate::Transport::Ble);
1604+
response.transport = Some(Transport::Ble);
16041605
response.authenticator_transports =
16051606
Some(vec!["smart-card".to_string(), "custom".to_string()]);
16061607
let model = response.to_idl_model(&request).unwrap();

libwebauthn/src/proto/ctap2/model.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::proto::ctap1::Ctap1Transport;
2+
use crate::Transport;
23
use crate::{
34
ops::webauthn::idl::create::PublicKeyCredentialUserEntity, pin::PinUvAuthProtocol,
45
webauthn::Error,
@@ -167,13 +168,13 @@ impl From<&Ctap1Transport> for Ctap2Transport {
167168
}
168169
}
169170

170-
impl From<crate::Transport> for Ctap2Transport {
171-
fn from(transport: crate::Transport) -> Ctap2Transport {
171+
impl From<Transport> for Ctap2Transport {
172+
fn from(transport: Transport) -> Ctap2Transport {
172173
match transport {
173-
crate::Transport::Usb => Ctap2Transport::Usb,
174-
crate::Transport::Ble => Ctap2Transport::Ble,
175-
crate::Transport::Nfc => Ctap2Transport::Nfc,
176-
crate::Transport::Hybrid => Ctap2Transport::Hybrid,
174+
Transport::Usb => Ctap2Transport::Usb,
175+
Transport::Ble => Ctap2Transport::Ble,
176+
Transport::Nfc => Ctap2Transport::Nfc,
177+
Transport::Hybrid => Ctap2Transport::Hybrid,
177178
}
178179
}
179180
}

libwebauthn/src/transport/ble/channel.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use crate::transport::channel::{
1515
use crate::transport::device::SupportedProtocols;
1616
use crate::transport::error::TransportError;
1717
use crate::webauthn::error::Error;
18+
use crate::Transport;
1819
use crate::UvUpdate;
1920

2021
use super::btleplug::manager::SupportedRevisions;
@@ -81,8 +82,8 @@ impl Display for BleChannel<'_> {
8182
impl<'a> Channel for BleChannel<'a> {
8283
type UxUpdate = UvUpdate;
8384

84-
fn transport(&self) -> Option<crate::Transport> {
85-
Some(crate::Transport::Ble)
85+
fn transport(&self) -> Transport {
86+
Transport::Ble
8687
}
8788

8889
async fn supported_protocols(&self) -> Result<SupportedProtocols, Error> {

libwebauthn/src/transport/cable/channel.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use crate::transport::{
1818
channel::ChannelStatus, device::SupportedProtocols, Channel, Ctap2AuthTokenStore,
1919
};
2020
use crate::webauthn::error::Error;
21+
use crate::Transport;
2122
use crate::UvUpdate;
2223

2324
use super::known_devices::CableKnownDevice;
@@ -125,8 +126,8 @@ impl From<UvUpdate> for CableUxUpdate {
125126
impl Channel for CableChannel {
126127
type UxUpdate = CableUxUpdate;
127128

128-
fn transport(&self) -> Option<crate::Transport> {
129-
Some(crate::Transport::Hybrid)
129+
fn transport(&self) -> Transport {
130+
Transport::Hybrid
130131
}
131132

132133
async fn supported_protocols(&self) -> Result<SupportedProtocols, Error> {

libwebauthn/src/transport/channel.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use crate::proto::{
1111
ctap2::cbor::{CborRequest, CborResponse},
1212
};
1313
use crate::webauthn::error::Error;
14+
use crate::Transport;
1415
use crate::UvUpdate;
1516

1617
use async_trait::async_trait;
@@ -65,9 +66,9 @@ pub trait Channel: Send + Sync + Display + Ctap2AuthTokenStore {
6566
async fn close(&mut self);
6667

6768
/// The transport this channel speaks over, used to populate the registration
68-
/// response `transports` member. `None` means unknown.
69-
fn transport(&self) -> Option<crate::Transport> {
70-
None
69+
/// response `transports` member.
70+
fn transport(&self) -> Transport {
71+
Transport::Usb
7172
}
7273

7374
async fn apdu_send(&mut self, request: &ApduRequest, timeout: Duration) -> Result<(), Error>;

libwebauthn/src/transport/hid/channel.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ use crate::transport::hid::framing::{
3434
HidCommand, HidMessage, HidMessageParser, HidMessageParserState,
3535
};
3636
use crate::webauthn::error::{Error, PlatformError};
37+
use crate::Transport;
3738
use crate::UvUpdate;
3839

3940
use super::device::get_hidapi;
@@ -505,8 +506,8 @@ impl Display for HidChannel<'_> {
505506
impl Channel for HidChannel<'_> {
506507
type UxUpdate = UvUpdate;
507508

508-
fn transport(&self) -> Option<crate::Transport> {
509-
Some(crate::Transport::Usb)
509+
fn transport(&self) -> Transport {
510+
Transport::Usb
510511
}
511512

512513
async fn supported_protocols(&self) -> Result<SupportedProtocols, Error> {

libwebauthn/src/transport/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ pub mod hid;
2323
pub mod mock;
2424
#[cfg(any(feature = "nfc-backend-pcsc", feature = "nfc-backend-libnfc"))]
2525
pub mod nfc;
26+
// No NFC backend compiled: a stub so callers need not gate on the feature.
27+
#[cfg(not(any(feature = "nfc-backend-pcsc", feature = "nfc-backend-libnfc")))]
28+
pub mod nfc {
29+
pub fn is_nfc_available() -> bool {
30+
false
31+
}
32+
}
2633

2734
mod channel;
2835
#[allow(clippy::module_inception)]

libwebauthn/src/transport/nfc/channel.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use crate::transport::channel::{
2121
use crate::transport::device::SupportedProtocols;
2222
use crate::transport::error::TransportError;
2323
use crate::webauthn::Error;
24+
use crate::Transport;
2425
use crate::UvUpdate;
2526

2627
use super::commands::{command_ctap_msg, command_get_response};
@@ -259,8 +260,8 @@ where
259260
{
260261
type UxUpdate = UvUpdate;
261262

262-
fn transport(&self) -> Option<crate::Transport> {
263-
Some(crate::Transport::Nfc)
263+
fn transport(&self) -> Transport {
264+
Transport::Nfc
264265
}
265266

266267
async fn supported_protocols(&self) -> Result<SupportedProtocols, Error> {

libwebauthn/src/webauthn.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ where
141141
FidoProtocol::FIDO2 => make_credential_fido2(self, op).await,
142142
FidoProtocol::U2F => make_credential_u2f(self, op).await,
143143
}?;
144-
response.transport = self.transport();
144+
response.transport = Some(self.transport());
145145
Ok(response)
146146
}
147147

0 commit comments

Comments
 (0)