|
1 | | -use std::time::Duration; |
| 1 | +use std::{collections::HashMap, time::Duration}; |
2 | 2 |
|
3 | 3 | use async_stream::stream; |
4 | 4 | use base64::{self, engine::general_purpose::URL_SAFE_NO_PAD, Engine}; |
5 | 5 | use futures_lite::Stream; |
6 | 6 | use libwebauthn::{ |
7 | 7 | ops::webauthn::GetAssertionResponse, |
8 | 8 | proto::CtapError, |
9 | | - transport::{hid::HidDevice, Device}, |
| 9 | + transport::{ |
| 10 | + hid::{channel::HidChannelHandle, HidDevice}, |
| 11 | + Device, |
| 12 | + }, |
10 | 13 | webauthn::{Error as WebAuthnError, WebAuthn}, |
11 | 14 | UxUpdate, |
12 | 15 | }; |
@@ -71,59 +74,76 @@ impl InProcessUsbHandler { |
71 | 74 | } |
72 | 75 | } |
73 | 76 | UsbStateInternal::SelectingDevice(hid_devices) => { |
| 77 | + let expected_answers = hid_devices.len(); |
74 | 78 | let (blinking_tx, mut blinking_rx) = |
75 | | - tokio::sync::mpsc::channel::<Option<HidDevice>>(hid_devices.len()); |
76 | | - let mut expected_answers = hid_devices.len(); |
77 | | - for mut device in hid_devices { |
| 79 | + tokio::sync::mpsc::channel::<Option<usize>>(expected_answers); |
| 80 | + let mut channel_map = HashMap::new(); |
| 81 | + let (setup_tx, mut setup_rx) = |
| 82 | + tokio::sync::mpsc::channel::<(usize, HidDevice, HidChannelHandle)>( |
| 83 | + expected_answers, |
| 84 | + ); |
| 85 | + for (idx, mut device) in hid_devices.into_iter().enumerate() { |
| 86 | + let stx = setup_tx.clone(); |
78 | 87 | let tx = blinking_tx.clone(); |
79 | 88 | tokio::spawn(async move { |
| 89 | + let dev = device.clone(); |
| 90 | + |
80 | 91 | let res = match device.channel().await { |
81 | | - Ok((ref mut channel, _)) => channel |
82 | | - .blink_and_wait_for_user_presence(Duration::from_secs(300)) |
83 | | - .await |
84 | | - .map_err(|err| { |
85 | | - format!( |
| 92 | + Ok((ref mut channel, _)) => { |
| 93 | + let cancel_handle = channel.get_handle(); |
| 94 | + stx.send((idx, dev, cancel_handle)).await.unwrap(); |
| 95 | + drop(stx); |
| 96 | + |
| 97 | + let was_selected = channel |
| 98 | + .blink_and_wait_for_user_presence(Duration::from_secs(300)) |
| 99 | + .await; |
| 100 | + match was_selected { |
| 101 | + Ok(true) => Ok(Some(idx)), |
| 102 | + Ok(false) => Ok(None), |
| 103 | + Err(err) => Err(format!( |
86 | 104 | "Failed to send wink request to authenticator: {:?}", |
87 | 105 | err |
88 | | - ) |
89 | | - }) |
90 | | - .and_then(|blinking| { |
91 | | - if blinking { |
92 | | - Ok(()) |
93 | | - } else { |
94 | | - Err("Authenticator was not able to blink".to_string()) |
95 | | - } |
96 | | - }), |
| 106 | + )), |
| 107 | + } |
| 108 | + } |
97 | 109 | Err(err) => Err(format!( |
98 | 110 | "Failed to create channel for USB authenticator: {:?}", |
99 | 111 | err |
100 | 112 | )), |
101 | 113 | } |
102 | 114 | .inspect_err(|err| tracing::warn!(err)) |
103 | | - .ok(); |
104 | | - |
105 | | - if let Err(err) = tx.send(res.map(|_| device)).await { |
| 115 | + .unwrap_or_default(); // In case of error, we also send `None` |
| 116 | + if let Err(err) = tx.send(res).await { |
106 | 117 | tracing::error!( |
107 | 118 | "Failed to send notification of wink response: {:?}", |
108 | 119 | err, |
109 | 120 | ); |
110 | 121 | } |
111 | 122 | }); |
112 | 123 | } |
| 124 | + drop(setup_tx); |
| 125 | + // Receiving all cancel handles |
| 126 | + while let Some((idx, device, handle)) = setup_rx.recv().await { |
| 127 | + channel_map.insert(idx, (device, handle)); |
| 128 | + } |
| 129 | + |
| 130 | + tracing::info!("Waiting for user interaction"); |
| 131 | + drop(blinking_tx); |
113 | 132 | let mut state = UsbStateInternal::Idle; |
114 | 133 | while let Some(msg) = blinking_rx.recv().await { |
115 | | - expected_answers -= 1; |
116 | 134 | match msg { |
117 | | - Some(device) => { |
| 135 | + Some(idx) => { |
| 136 | + let (device, _handle) = channel_map.remove(&idx).unwrap(); |
| 137 | + tracing::info!("User selected device {device:?}."); |
| 138 | + for (_key, (device, handle)) in channel_map.into_iter() { |
| 139 | + tracing::info!("Cancelling device {device:?}."); |
| 140 | + handle.cancel_ongoing_operation().await; |
| 141 | + } |
118 | 142 | state = UsbStateInternal::Connected(device); |
119 | 143 | break; |
120 | 144 | } |
121 | 145 | None => { |
122 | | - if expected_answers == 0 { |
123 | | - break; |
124 | | - } else { |
125 | | - continue; |
126 | | - } |
| 146 | + continue; |
127 | 147 | } |
128 | 148 | } |
129 | 149 | } |
|
0 commit comments