Skip to content

Commit b2f12ee

Browse files
Martin Sirringhausmsirringhaus
authored andcommitted
Detect if no devices are connected and send a StatusUpdate accordingly.
1 parent b5beeee commit b2f12ee

19 files changed

+153
-28
lines changed

examples/ctap2.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ fn main() {
8484
let (status_tx, status_rx) = channel::<StatusUpdate>();
8585
thread::spawn(move || loop {
8686
match status_rx.recv() {
87+
Ok(StatusUpdate::NoDevicesFound) => {
88+
println!("STATUS: No device found. Please connect one!");
89+
}
8790
Ok(StatusUpdate::InteractiveManagement(..)) => {
8891
panic!("STATUS: This can't happen when doing non-interactive usage");
8992
}

examples/ctap2_discoverable_creds.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ fn register_user(
9898
Ok(StatusUpdate::InteractiveManagement(..)) => {
9999
panic!("STATUS: This can't happen when doing non-interactive usage");
100100
}
101+
Ok(StatusUpdate::NoDevicesFound) => {
102+
println!("STATUS: No device found. Please connect one!");
103+
}
101104
Ok(StatusUpdate::SelectDeviceNotice) => {
102105
println!("STATUS: Please select a device by touching one of them.");
103106
}
@@ -355,6 +358,9 @@ fn main() {
355358
Ok(StatusUpdate::InteractiveManagement(..)) => {
356359
panic!("STATUS: This can't happen when doing non-interactive usage");
357360
}
361+
Ok(StatusUpdate::NoDevicesFound) => {
362+
println!("STATUS: No device found. Please connect one!");
363+
}
358364
Ok(StatusUpdate::SelectDeviceNotice) => {
359365
println!("STATUS: Please select a device by touching one of them.");
360366
}

examples/interactive_management.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,9 @@ fn interactive_status_callback(status_rx: Receiver<StatusUpdate>) {
680680
);
681681
continue;
682682
}
683+
Ok(StatusUpdate::NoDevicesFound) => {
684+
println!("STATUS: No device found. Please connect one!");
685+
}
683686
Ok(StatusUpdate::SelectDeviceNotice) => {
684687
println!("STATUS: Please select a device by touching one of them.");
685688
}

examples/prf.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ fn main() {
100100
Ok(StatusUpdate::InteractiveManagement(..)) => {
101101
panic!("STATUS: This can't happen when doing non-interactive usage");
102102
}
103+
Ok(StatusUpdate::NoDevicesFound) => {
104+
println!("STATUS: No device found. Please connect one!");
105+
}
103106
Ok(StatusUpdate::SelectDeviceNotice) => {
104107
println!("STATUS: Please select a device by touching one of them.");
105108
}

examples/reset.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ fn main() {
9393

9494
loop {
9595
match status_rx.recv() {
96+
Ok(StatusUpdate::NoDevicesFound) => {
97+
println!("STATUS: No device found. Please connect one!");
98+
}
9699
Ok(StatusUpdate::SelectDeviceNotice) => {
97100
println!("ERROR: Please unplug all other tokens that should not be reset!");
98101
// Needed to give the tokens enough time to start blinking

examples/set_pin.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ fn main() {
6868
Ok(StatusUpdate::InteractiveManagement(..)) => {
6969
panic!("STATUS: This can't happen when doing non-interactive usage");
7070
}
71+
Ok(StatusUpdate::NoDevicesFound) => {
72+
println!("STATUS: No device found. Please connect one!");
73+
}
7174
Ok(StatusUpdate::SelectDeviceNotice) => {
7275
println!("STATUS: Please select a device by touching one of them.");
7376
}

examples/test_exclude_list.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ fn main() {
8080
Ok(StatusUpdate::InteractiveManagement(..)) => {
8181
panic!("STATUS: This can't happen when doing non-interactive usage");
8282
}
83+
Ok(StatusUpdate::NoDevicesFound) => {
84+
println!("STATUS: No device found. Please connect one!");
85+
}
8386
Ok(StatusUpdate::SelectDeviceNotice) => {
8487
println!("STATUS: Please select a device by touching one of them.");
8588
}

src/status_update.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ pub enum StatusUpdate {
111111
/// After MakeCredential, supply the user with the large blob key and let
112112
/// them calculate the payload, to send back to us.
113113
LargeBlobData(Sender<LargeBlobArrayElement>, Vec<u8>),
114+
/// Inform user that no devices are plugged in
115+
NoDevicesFound,
114116
}
115117

116118
pub(crate) fn send_status(status: &Sender<StatusUpdate>, msg: StatusUpdate) {

src/transport/device_selector.rs

Lines changed: 91 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
use crate::status_update::send_status;
12
use crate::transport::hid::HIDDevice;
3+
use crate::StatusUpdate;
24

35
pub use crate::transport::platform::device::Device;
46

@@ -43,7 +45,7 @@ pub struct DeviceSelector {
4345
}
4446

4547
impl DeviceSelector {
46-
pub fn run() -> Self {
48+
pub fn run(status: Sender<crate::StatusUpdate>) -> Self {
4749
let (selector_send, selector_rec) = channel();
4850
// let new_device_callback = Arc::new(new_device_cb);
4951
let runloop = RunLoop::new(move |alive| {
@@ -75,6 +77,9 @@ impl DeviceSelector {
7577
break; // We are done here. The selected device continues without us.
7678
}
7779
DeviceSelectorEvent::DevicesAdded(ids) => {
80+
if ids.is_empty() && waiting_for_response.is_empty() && tokens.is_empty() {
81+
send_status(&status, StatusUpdate::NoDevicesFound);
82+
}
7883
for id in ids {
7984
debug!("Device added event: {:?}", id);
8085
waiting_for_response.insert(id);
@@ -97,9 +102,15 @@ impl DeviceSelector {
97102
tokens.retain(|dev_id, _| dev_id != id);
98103
if tokens.is_empty() {
99104
blinking = false;
105+
if waiting_for_response.is_empty() {
106+
send_status(&status, StatusUpdate::NoDevicesFound);
107+
}
100108
continue;
101109
}
102110
}
111+
if waiting_for_response.is_empty() && tokens.is_empty() {
112+
send_status(&status, StatusUpdate::NoDevicesFound);
113+
}
103114
// We are already blinking, so no need to run the code below this match
104115
// that figures out if we should blink or not. In fact, currently, we do
105116
// NOT want to run this code again, because if you have 2 blinking tokens
@@ -113,6 +124,9 @@ impl DeviceSelector {
113124
DeviceSelectorEvent::NotAToken(ref id) => {
114125
debug!("Device not a token event: {:?}", id);
115126
waiting_for_response.remove(id);
127+
if waiting_for_response.is_empty() && tokens.is_empty() {
128+
send_status(&status, StatusUpdate::NoDevicesFound);
129+
}
116130
}
117131
DeviceSelectorEvent::ImAToken((id, tx)) => {
118132
let _ = waiting_for_response.remove(&id);
@@ -185,6 +199,7 @@ pub mod tests {
185199
transport::FidoDevice,
186200
u2ftypes::U2FDeviceInfo,
187201
};
202+
use std::sync::mpsc::TryRecvError;
188203

189204
pub(crate) fn gen_info(id: String) -> U2FDeviceInfo {
190205
U2FDeviceInfo {
@@ -268,9 +283,10 @@ pub mod tests {
268283
Device::new("device selector 4").unwrap(),
269284
];
270285

286+
let (tx, rx) = channel();
271287
// Make those actual tokens. The rest is interpreted as non-u2f-devices
272288
make_device_with_pin(&mut devices[2]);
273-
let selector = DeviceSelector::run();
289+
let selector = DeviceSelector::run(tx);
274290

275291
// Adding all
276292
add_devices(devices.iter(), &selector);
@@ -279,6 +295,7 @@ pub mod tests {
279295
send_no_token(d, &selector);
280296
}
281297
});
298+
assert_matches!(rx.try_recv(), Err(TryRecvError::Empty));
282299

283300
send_i_am_token(&devices[2], &selector);
284301

@@ -293,18 +310,24 @@ pub mod tests {
293310
fn test_device_selector_stop() {
294311
let device = Device::new("device selector 1").unwrap();
295312

296-
let mut selector = DeviceSelector::run();
313+
let (tx, rx) = channel();
314+
let mut selector = DeviceSelector::run(tx);
297315

298316
// Adding all
299317
selector
300318
.clone_sender()
301319
.send(DeviceSelectorEvent::DevicesAdded(vec![device.id()]))
302320
.unwrap();
321+
assert_matches!(rx.try_recv(), Err(TryRecvError::Empty));
303322

304323
selector
305324
.clone_sender()
306325
.send(DeviceSelectorEvent::NotAToken(device.id()))
307326
.unwrap();
327+
assert_matches!(
328+
rx.recv_timeout(Duration::from_millis(500)),
329+
Ok(StatusUpdate::NoDevicesFound)
330+
);
308331
selector.stop();
309332
}
310333

@@ -324,7 +347,8 @@ pub mod tests {
324347
make_device_with_pin(&mut devices[4]);
325348
make_device_with_pin(&mut devices[5]);
326349

327-
let selector = DeviceSelector::run();
350+
let (tx, rx) = channel();
351+
let selector = DeviceSelector::run(tx);
328352

329353
// Adding all, except the last one (we simulate that this one is not yet plugged in)
330354
add_devices(devices.iter().take(5), &selector);
@@ -356,6 +380,7 @@ pub mod tests {
356380
devices[5].receiver.as_ref().unwrap().recv().unwrap(),
357381
DeviceCommand::Blink
358382
);
383+
assert_matches!(rx.try_recv(), Err(TryRecvError::Empty));
359384
}
360385

361386
#[test]
@@ -376,7 +401,8 @@ pub mod tests {
376401
make_device_simple_u2f(&mut devices[4]);
377402
make_device_simple_u2f(&mut devices[5]);
378403

379-
let selector = DeviceSelector::run();
404+
let (tx, rx) = channel();
405+
let selector = DeviceSelector::run(tx);
380406

381407
// Adding all, except the last one (we simulate that this one is not yet plugged in)
382408
add_devices(devices.iter().take(5), &selector);
@@ -418,6 +444,7 @@ pub mod tests {
418444
devices[6].receiver.as_ref().unwrap().recv().unwrap(),
419445
DeviceCommand::Blink
420446
);
447+
assert_matches!(rx.try_recv(), Err(TryRecvError::Empty));
421448
}
422449

423450
#[test]
@@ -438,7 +465,8 @@ pub mod tests {
438465
make_device_with_pin(&mut devices[4]);
439466
make_device_with_pin(&mut devices[5]);
440467

441-
let selector = DeviceSelector::run();
468+
let (tx, rx) = channel();
469+
let selector = DeviceSelector::run(tx);
442470

443471
// Adding all, except the last one (we simulate that this one is not yet plugged in)
444472
add_devices(devices.iter(), &selector);
@@ -457,11 +485,16 @@ pub mod tests {
457485
DeviceCommand::Blink
458486
);
459487
}
488+
assert_matches!(rx.try_recv(), Err(TryRecvError::Empty));
460489

461490
// Remove all tokens
462491
for idx in [2, 4, 5] {
463492
remove_device(&devices[idx], &selector);
464493
}
494+
assert_matches!(
495+
rx.recv_timeout(Duration::from_millis(500)),
496+
Ok(StatusUpdate::NoDevicesFound)
497+
);
465498

466499
// Adding one again
467500
send_i_am_token(&devices[4], &selector);
@@ -472,4 +505,56 @@ pub mod tests {
472505
DeviceCommand::Continue
473506
);
474507
}
508+
509+
#[test]
510+
fn test_device_selector_no_devices() {
511+
let mut devices = vec![
512+
Device::new("device selector 1").unwrap(),
513+
Device::new("device selector 2").unwrap(),
514+
Device::new("device selector 3").unwrap(),
515+
Device::new("device selector 4").unwrap(),
516+
];
517+
518+
let (tx, rx) = channel();
519+
// Make those actual tokens. The rest is interpreted as non-u2f-devices
520+
make_device_with_pin(&mut devices[2]);
521+
make_device_with_pin(&mut devices[3]);
522+
let selector = DeviceSelector::run(tx);
523+
524+
// Adding no devices first (none are plugged in when we start)
525+
add_devices(std::iter::empty(), &selector);
526+
assert_matches!(
527+
rx.recv_timeout(Duration::from_millis(500)),
528+
Ok(StatusUpdate::NoDevicesFound)
529+
);
530+
531+
// Adding the devices
532+
add_devices(devices.iter(), &selector);
533+
devices.iter_mut().for_each(|d| {
534+
if !d.is_u2f() {
535+
send_no_token(d, &selector);
536+
}
537+
});
538+
assert_matches!(rx.try_recv(), Err(TryRecvError::Empty));
539+
540+
send_i_am_token(&devices[2], &selector);
541+
send_i_am_token(&devices[3], &selector);
542+
543+
assert_eq!(
544+
devices[2].receiver.as_ref().unwrap().recv().unwrap(),
545+
DeviceCommand::Blink
546+
);
547+
assert_eq!(
548+
devices[3].receiver.as_ref().unwrap().recv().unwrap(),
549+
DeviceCommand::Blink
550+
);
551+
552+
// Removing all blinking devices
553+
remove_device(&devices[2], &selector);
554+
remove_device(&devices[3], &selector);
555+
assert_matches!(
556+
rx.recv_timeout(Duration::from_millis(500)),
557+
Ok(StatusUpdate::NoDevicesFound)
558+
);
559+
}
475560
}

src/transport/freebsd/transaction.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ impl Transaction {
3535
+ 'static,
3636
T: 'static,
3737
{
38-
let device_selector = DeviceSelector::run();
38+
let device_selector = DeviceSelector::run(status.clone());
3939
let selector_sender = device_selector.clone_sender();
4040
let thread = RunLoop::new_with_timeout(
4141
move |alive| {

0 commit comments

Comments
 (0)