Skip to content

Commit 155ff1c

Browse files
fix(cable): send shutdown on close and linger for late linking data
close() now asks the connection loop to send a Shutdown control frame over the encrypted channel and waits for it to terminate before the channel is dropped. After a CTAP response the loop lingers briefly to capture a late linking Update before sending Shutdown and tearing down.
1 parent d593d6b commit 155ff1c

6 files changed

Lines changed: 384 additions & 8 deletions

File tree

libwebauthn/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ reqwest = { version = "0.12", default-features = false, features = [
118118
[dev-dependencies]
119119
tracing-subscriber = { version = "0.3.3", features = ["env-filter"] }
120120
qrcode = "0.14.1"
121+
# test-util enables paused time for deterministic timeout/linger tests
122+
tokio = { version = "1.45", features = ["test-util"] }
121123
# For turning on logging in unittests
122124
test-log = { version = "0.2" }
123125

libwebauthn/src/transport/cable/channel.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ pub struct CableChannel {
4747
pub(crate) ux_update_sender: broadcast::Sender<CableUxUpdate>,
4848
pub(crate) connection_state_receiver: watch::Receiver<ConnectionState>,
4949
pub(crate) persistent_token_store: Option<Arc<dyn PersistentTokenStore>>,
50+
pub(crate) close_sender: Option<mpsc::Sender<()>>,
5051
}
5152

5253
impl CableChannel {
@@ -137,7 +138,14 @@ impl Channel for CableChannel {
137138
}
138139

139140
async fn close(&mut self) {
140-
// TODO Send CableTunnelMessageType#Shutdown and drop the connection
141+
// Signal the loop to send Shutdown, then wait for it to flush and terminate.
142+
if let Some(close_sender) = self.close_sender.take() {
143+
let _ = close_sender.send(()).await;
144+
}
145+
let mut connection_state = self.connection_state_receiver.clone();
146+
let _ = connection_state
147+
.wait_for(|state| *state == ConnectionState::Terminated)
148+
.await;
141149
}
142150

143151
async fn apdu_send(&mut self, _request: &ApduRequest, _timeout: Duration) -> Result<(), Error> {

libwebauthn/src/transport/cable/connection_stages.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ pub(crate) struct TunnelConnectionInput {
198198
pub noise_state: TunnelNoiseState,
199199
pub cbor_tx_recv: mpsc::Receiver<CborRequest>,
200200
pub cbor_rx_send: mpsc::Sender<CborResponse>,
201+
pub close_rx: mpsc::Receiver<()>,
201202
}
202203

203204
impl TunnelConnectionInput {
@@ -206,6 +207,7 @@ impl TunnelConnectionInput {
206207
known_device_store: Option<Arc<dyn CableKnownDeviceInfoStore>>,
207208
cbor_tx_recv: mpsc::Receiver<CborRequest>,
208209
cbor_rx_send: mpsc::Sender<CborResponse>,
210+
close_rx: mpsc::Receiver<()>,
209211
) -> Self {
210212
Self {
211213
connection_type: handshake_output.connection_type,
@@ -215,6 +217,7 @@ impl TunnelConnectionInput {
215217
noise_state: handshake_output.noise_state,
216218
cbor_tx_recv,
217219
cbor_rx_send,
220+
close_rx,
218221
}
219222
}
220223
}

libwebauthn/src/transport/cable/known_devices.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ impl<'d> Device<'d, Cable, CableChannel> for CableKnownDevice {
198198
let (ux_update_sender, _) = broadcast::channel(16);
199199
let (cbor_tx_send, cbor_tx_recv) = mpsc::channel(16);
200200
let (cbor_rx_send, cbor_rx_recv) = mpsc::channel(16);
201+
let (close_sender, close_rx) = mpsc::channel(1);
201202
let (connection_state_sender, connection_state_receiver) =
202203
watch::channel(ConnectionState::Connecting);
203204

@@ -225,6 +226,7 @@ impl<'d> Device<'d, Cable, CableChannel> for CableKnownDevice {
225226
Some(known_device.store),
226227
cbor_tx_recv,
227228
cbor_rx_send,
229+
close_rx,
228230
);
229231

230232
match protocol::connection(tunnel_input).await {
@@ -247,6 +249,7 @@ impl<'d> Device<'d, Cable, CableChannel> for CableKnownDevice {
247249
ux_update_sender,
248250
connection_state_receiver,
249251
persistent_token_store: settings.persistent_token_store,
252+
close_sender: Some(close_sender),
250253
})
251254
}
252255
}

0 commit comments

Comments
 (0)