Skip to content

Commit 26aa766

Browse files
author
Martin Sirringhaus
committed
Detect if no devices are connected and send a StatusUpdate accordingly.
1 parent be6526c commit 26aa766

19 files changed

+160
-37
lines changed

examples/ctap2.rs

+3
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

+15-6
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ use authenticator::{
1515
};
1616
use getopts::Options;
1717
use sha2::{Digest, Sha256};
18+
use std::io::Write;
1819
use std::sync::mpsc::{channel, RecvError};
1920
use std::{env, io, thread};
20-
use std::io::Write;
2121

2222
fn print_usage(program: &str, opts: Options) {
2323
println!("------------------------------------------------------------------------");
@@ -84,6 +84,9 @@ fn register_user(manager: &mut AuthenticatorService, username: &str, timeout_ms:
8484
Ok(StatusUpdate::InteractiveManagement(..)) => {
8585
panic!("STATUS: This can't happen when doing non-interactive usage");
8686
}
87+
Ok(StatusUpdate::NoDevicesFound) => {
88+
println!("STATUS: No device found. Please connect one!");
89+
}
8790
Ok(StatusUpdate::SelectDeviceNotice) => {
8891
println!("STATUS: Please select a device by touching one of them.");
8992
}
@@ -216,10 +219,7 @@ fn main() {
216219
"timeout in seconds",
217220
"SEC",
218221
);
219-
opts.optflag(
220-
"s",
221-
"skip_reg",
222-
"Skip registration");
222+
opts.optflag("s", "skip_reg", "Skip registration");
223223

224224
opts.optflag("h", "help", "print this help menu");
225225
let matches = match opts.parse(&args[1..]) {
@@ -273,6 +273,9 @@ fn main() {
273273
Ok(StatusUpdate::InteractiveManagement(..)) => {
274274
panic!("STATUS: This can't happen when doing non-interactive usage");
275275
}
276+
Ok(StatusUpdate::NoDevicesFound) => {
277+
println!("STATUS: No device found. Please connect one!");
278+
}
276279
Ok(StatusUpdate::SelectDeviceNotice) => {
277280
println!("STATUS: Please select a device by touching one of them.");
278281
}
@@ -368,7 +371,13 @@ fn main() {
368371
println!("Found credentials:");
369372
println!(
370373
"{:?}",
371-
assertion_object.assertion.user.clone().unwrap().name.unwrap() // Unwrapping here, as these shouldn't fail
374+
assertion_object
375+
.assertion
376+
.user
377+
.clone()
378+
.unwrap()
379+
.name
380+
.unwrap() // Unwrapping here, as these shouldn't fail
372381
);
373382
println!("-----------------------------------------------------------------");
374383
println!("Done.");

examples/interactive_management.rs

+3
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/reset.rs

+3
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

+3
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

+3
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ fn main() {
8282
Ok(StatusUpdate::InteractiveManagement(..)) => {
8383
panic!("STATUS: This can't happen when doing non-interactive usage");
8484
}
85+
Ok(StatusUpdate::NoDevicesFound) => {
86+
println!("STATUS: No device found. Please connect one!");
87+
}
8588
Ok(StatusUpdate::SelectDeviceNotice) => {
8689
println!("STATUS: Please select a device by touching one of them.");
8790
}

src/ctap2/commands/mod.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
use crate::crypto::{CryptoError, PinUvAuthParam, PinUvAuthToken};
2-
use crate::ctap2::commands::client_pin::{
3-
GetPinRetries, GetUvRetries, PinError,
4-
};
2+
use crate::ctap2::commands::client_pin::{GetPinRetries, GetUvRetries, PinError};
53
use crate::ctap2::commands::get_info::AuthenticatorInfo;
64
use crate::ctap2::server::UserVerificationRequirement;
75
use crate::errors::AuthenticatorError;

src/status_update.rs

+2
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ pub enum StatusUpdate {
107107
InteractiveManagement(InteractiveUpdate),
108108
/// Sent when a token returns multiple results for a getAssertion request
109109
SelectResultNotice(Sender<Option<usize>>, Vec<PublicKeyCredentialUserEntity>),
110+
/// Inform user that no devices are plugged in
111+
NoDevicesFound,
110112
}
111113

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

src/transport/device_selector.rs

+91-6
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

@@ -45,7 +47,7 @@ pub struct DeviceSelector {
4547
}
4648

4749
impl DeviceSelector {
48-
pub fn run() -> Self {
50+
pub fn run(status: Sender<crate::StatusUpdate>) -> Self {
4951
let (selector_send, selector_rec) = channel();
5052
// let new_device_callback = Arc::new(new_device_cb);
5153
let runloop = RunLoop::new(move |alive| {
@@ -77,6 +79,9 @@ impl DeviceSelector {
7779
break; // We are done here. The selected device continues without us.
7880
}
7981
DeviceSelectorEvent::DevicesAdded(ids) => {
82+
if ids.is_empty() && waiting_for_response.is_empty() && tokens.is_empty() {
83+
send_status(&status, StatusUpdate::NoDevicesFound);
84+
}
8085
for id in ids {
8186
debug!("Device added event: {:?}", id);
8287
waiting_for_response.insert(id);
@@ -99,9 +104,15 @@ impl DeviceSelector {
99104
tokens.retain(|dev_id, _| dev_id != id);
100105
if tokens.is_empty() {
101106
blinking = false;
107+
if waiting_for_response.is_empty() {
108+
send_status(&status, StatusUpdate::NoDevicesFound);
109+
}
102110
continue;
103111
}
104112
}
113+
if waiting_for_response.is_empty() && tokens.is_empty() {
114+
send_status(&status, StatusUpdate::NoDevicesFound);
115+
}
105116
// We are already blinking, so no need to run the code below this match
106117
// that figures out if we should blink or not. In fact, currently, we do
107118
// NOT want to run this code again, because if you have 2 blinking tokens
@@ -115,6 +126,9 @@ impl DeviceSelector {
115126
DeviceSelectorEvent::NotAToken(ref id) => {
116127
debug!("Device not a token event: {:?}", id);
117128
waiting_for_response.remove(id);
129+
if waiting_for_response.is_empty() && tokens.is_empty() {
130+
send_status(&status, StatusUpdate::NoDevicesFound);
131+
}
118132
}
119133
DeviceSelectorEvent::ImAToken((id, tx)) => {
120134
let _ = waiting_for_response.remove(&id);
@@ -187,6 +201,7 @@ pub mod tests {
187201
transport::FidoDevice,
188202
u2ftypes::U2FDeviceInfo,
189203
};
204+
use std::sync::mpsc::TryRecvError;
190205

191206
fn gen_info(id: String) -> U2FDeviceInfo {
192207
U2FDeviceInfo {
@@ -267,9 +282,10 @@ pub mod tests {
267282
Device::new("device selector 4").unwrap(),
268283
];
269284

285+
let (tx, rx) = channel();
270286
// Make those actual tokens. The rest is interpreted as non-u2f-devices
271287
make_device_with_pin(&mut devices[2]);
272-
let selector = DeviceSelector::run();
288+
let selector = DeviceSelector::run(tx);
273289

274290
// Adding all
275291
add_devices(devices.iter(), &selector);
@@ -278,6 +294,7 @@ pub mod tests {
278294
send_no_token(d, &selector);
279295
}
280296
});
297+
assert_matches!(rx.try_recv(), Err(TryRecvError::Empty));
281298

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

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

295-
let mut selector = DeviceSelector::run();
312+
let (tx, rx) = channel();
313+
let mut selector = DeviceSelector::run(tx);
296314

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

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

@@ -323,7 +346,8 @@ pub mod tests {
323346
make_device_with_pin(&mut devices[4]);
324347
make_device_with_pin(&mut devices[5]);
325348

326-
let selector = DeviceSelector::run();
349+
let (tx, rx) = channel();
350+
let selector = DeviceSelector::run(tx);
327351

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

360385
#[test]
@@ -375,7 +400,8 @@ pub mod tests {
375400
make_device_simple_u2f(&mut devices[4]);
376401
make_device_simple_u2f(&mut devices[5]);
377402

378-
let selector = DeviceSelector::run();
403+
let (tx, rx) = channel();
404+
let selector = DeviceSelector::run(tx);
379405

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

422449
#[test]
@@ -437,7 +464,8 @@ pub mod tests {
437464
make_device_with_pin(&mut devices[4]);
438465
make_device_with_pin(&mut devices[5]);
439466

440-
let selector = DeviceSelector::run();
467+
let (tx, rx) = channel();
468+
let selector = DeviceSelector::run(tx);
441469

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

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

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

src/transport/freebsd/transaction.rs

+1-1
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| {

src/transport/linux/transaction.rs

+1-1
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| {

src/transport/macos/transaction.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ impl Transaction {
4646
{
4747
let (tx, rx) = channel();
4848
let timeout = (timeout as f64) / 1000.0;
49-
let device_selector = DeviceSelector::run();
49+
let device_selector = DeviceSelector::run(status.clone());
5050
let selector_sender = device_selector.clone_sender();
5151
let builder = thread::Builder::new();
5252
let thread = builder

0 commit comments

Comments
 (0)