Skip to content

Commit d3a0d09

Browse files
committed
Add StatusUpdate::SelectResultNotice
1 parent 5d20800 commit d3a0d09

File tree

10 files changed

+140
-73
lines changed

10 files changed

+140
-73
lines changed

examples/ctap2.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@ fn main() {
133133
Ok(StatusUpdate::PinUvError(e)) => {
134134
panic!("Unexpected error: {:?}", e)
135135
}
136+
Ok(StatusUpdate::SelectResultNotice(_, _)) => {
137+
panic!("Unexpected select device notice")
138+
}
136139
Err(RecvError) => {
137140
println!("STATUS: end");
138141
return;

examples/ctap2_discoverable_creds.rs

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ use authenticator::{
1616
use getopts::Options;
1717
use sha2::{Digest, Sha256};
1818
use std::sync::mpsc::{channel, RecvError};
19-
use std::{env, thread};
19+
use std::{env, io, thread};
20+
use std::io::Write;
2021

2122
fn print_usage(program: &str, opts: Options) {
2223
println!("------------------------------------------------------------------------");
@@ -28,6 +29,37 @@ fn print_usage(program: &str, opts: Options) {
2829
print!("{}", opts.usage(&brief));
2930
}
3031

32+
fn ask_user_choice(choices: &[PublicKeyCredentialUserEntity]) -> Option<usize> {
33+
for (idx, op) in choices.iter().enumerate() {
34+
println!("({idx}) \"{}\"", op.name.as_ref().unwrap());
35+
}
36+
println!("({}) Cancel", choices.len());
37+
38+
let mut input = String::new();
39+
loop {
40+
input.clear();
41+
print!("Your choice: ");
42+
io::stdout()
43+
.lock()
44+
.flush()
45+
.expect("Failed to flush stdout!");
46+
io::stdin()
47+
.read_line(&mut input)
48+
.expect("error: unable to read user input");
49+
if let Ok(idx) = input.trim().parse::<usize>() {
50+
if idx < choices.len() {
51+
// Add a newline in case of success for better separation of in/output
52+
println!();
53+
return Some(idx);
54+
} else if idx == choices.len() {
55+
println!();
56+
return None;
57+
}
58+
println!("invalid input");
59+
}
60+
}
61+
}
62+
3163
fn register_user(manager: &mut AuthenticatorService, username: &str, timeout_ms: u64) {
3264
println!();
3365
println!("*********************************************************************");
@@ -98,6 +130,9 @@ fn register_user(manager: &mut AuthenticatorService, username: &str, timeout_ms:
98130
Ok(StatusUpdate::PinUvError(e)) => {
99131
panic!("Unexpected error: {:?}", e)
100132
}
133+
Ok(StatusUpdate::SelectResultNotice(_, _)) => {
134+
panic!("Unexpected select result notice")
135+
}
101136
Err(RecvError) => {
102137
println!("STATUS: end");
103138
return;
@@ -181,6 +216,10 @@ fn main() {
181216
"timeout in seconds",
182217
"SEC",
183218
);
219+
opts.optflag(
220+
"s",
221+
"skip_reg",
222+
"Skip registration");
184223

185224
opts.optflag("h", "help", "print this help menu");
186225
let matches = match opts.parse(&args[1..]) {
@@ -208,8 +247,10 @@ fn main() {
208247
}
209248
};
210249

211-
for username in &["A. User", "A. Nother", "Dr. Who"] {
212-
register_user(&mut manager, username, timeout_ms)
250+
if !matches.opt_present("skip_reg") {
251+
for username in &["A. User", "A. Nother", "Dr. Who"] {
252+
register_user(&mut manager, username, timeout_ms)
253+
}
213254
}
214255

215256
println!();
@@ -278,6 +319,11 @@ fn main() {
278319
Ok(StatusUpdate::PinUvError(e)) => {
279320
panic!("Unexpected error: {:?}", e)
280321
}
322+
Ok(StatusUpdate::SelectResultNotice(index_sender, users)) => {
323+
println!("Multiple signatures returned. Select one or cancel.");
324+
let idx = ask_user_choice(&users);
325+
index_sender.send(idx).expect("Failed to send choice");
326+
}
281327
Err(RecvError) => {
282328
println!("STATUS: end");
283329
return;
@@ -322,11 +368,7 @@ fn main() {
322368
println!("Found credentials:");
323369
println!(
324370
"{:?}",
325-
assertion_object
326-
.assertions
327-
.iter()
328-
.map(|x| x.user.clone().unwrap().name.unwrap()) // Unwrapping here, as these shouldn't fail
329-
.collect::<Vec<_>>()
371+
assertion_object.assertion.user.clone().unwrap().name.unwrap() // Unwrapping here, as these shouldn't fail
330372
);
331373
println!("-----------------------------------------------------------------");
332374
println!("Done.");

examples/interactive_management.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,9 @@ fn interactive_status_callback(status_rx: Receiver<StatusUpdate>) {
727727
Ok(StatusUpdate::PinUvError(e)) => {
728728
panic!("Unexpected error: {:?}", e)
729729
}
730+
Ok(StatusUpdate::SelectResultNotice(_, _)) => {
731+
panic!("Unexpected select device notice")
732+
}
730733
Err(RecvError) => {
731734
println!("STATUS: end");
732735
return;

examples/set_pin.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ fn main() {
114114
Ok(StatusUpdate::PinUvError(e)) => {
115115
panic!("Unexpected error: {:?}", e)
116116
}
117+
Ok(StatusUpdate::SelectResultNotice(_, _)) => {
118+
panic!("Unexpected select device notice")
119+
}
117120
Err(RecvError) => {
118121
println!("STATUS: end");
119122
return;

examples/test_exclude_list.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ fn main() {
128128
Ok(StatusUpdate::PinUvError(e)) => {
129129
panic!("Unexpected error: {:?}", e)
130130
}
131+
Ok(StatusUpdate::SelectResultNotice(_, _)) => {
132+
panic!("Unexpected select device notice")
133+
}
131134
Err(RecvError) => {
132135
println!("STATUS: end");
133136
return;

src/ctap2/commands/get_assertion.rs

Lines changed: 47 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -195,14 +195,10 @@ impl GetAssertion {
195195
// Handle extensions whose outputs are not encoded in the authenticator data.
196196
// 1. appId
197197
if let Some(app_id) = &self.extensions.app_id {
198-
result.extensions.app_id = result
199-
.assertions
200-
.first()
201-
.map(|assertion| {
202-
assertion.auth_data.rp_id_hash
203-
== RelyingPartyWrapper::from(app_id.as_str()).hash()
204-
})
205-
.or(Some(false));
198+
result.extensions.app_id = Some(
199+
result.assertion.auth_data.rp_id_hash
200+
== RelyingPartyWrapper::from(app_id.as_str()).hash(),
201+
);
206202
}
207203
}
208204
}
@@ -307,7 +303,7 @@ impl Serialize for GetAssertion {
307303
}
308304

309305
impl RequestCtap1 for GetAssertion {
310-
type Output = GetAssertionResult;
306+
type Output = Vec<GetAssertionResult>;
311307
type AdditionalInfo = PublicKeyCredentialDescriptor;
312308

313309
fn ctap1_format(&self) -> Result<(Vec<u8>, Self::AdditionalInfo), HIDError> {
@@ -358,24 +354,27 @@ impl RequestCtap1 for GetAssertion {
358354
return Err(Retryable::Error(HIDError::ApduStatus(err)));
359355
}
360356

361-
let mut output = GetAssertionResult::from_ctap1(input, &self.rp.hash(), add_info)
357+
let mut result = GetAssertionResult::from_ctap1(input, &self.rp.hash(), add_info)
362358
.map_err(|e| Retryable::Error(HIDError::Command(e)))?;
363-
self.finalize_result(&mut output);
364-
Ok(output)
359+
self.finalize_result(&mut result);
360+
// Although there's only one result, we return a vector for consistency with CTAP2.
361+
Ok(vec![result])
365362
}
366363

367364
fn send_to_virtual_device<Dev: VirtualFidoDevice>(
368365
&self,
369366
dev: &mut Dev,
370367
) -> Result<Self::Output, HIDError> {
371-
let mut output = dev.get_assertion(self)?;
372-
self.finalize_result(&mut output);
373-
Ok(output)
368+
let mut results = dev.get_assertion(self)?;
369+
for result in results.iter_mut() {
370+
self.finalize_result(result);
371+
}
372+
Ok(results)
374373
}
375374
}
376375

377376
impl RequestCtap2 for GetAssertion {
378-
type Output = GetAssertionResult;
377+
type Output = Vec<GetAssertionResult>;
379378

380379
fn command(&self) -> Command {
381380
Command::GetAssertion
@@ -411,22 +410,27 @@ impl RequestCtap2 for GetAssertion {
411410
let assertion: GetAssertionResponse =
412411
from_slice(&input[1..]).map_err(CommandError::Deserializing)?;
413412
let number_of_credentials = assertion.number_of_credentials.unwrap_or(1);
414-
let mut assertions = Vec::with_capacity(number_of_credentials);
415-
assertions.push(assertion.into());
413+
414+
let mut results = Vec::with_capacity(number_of_credentials);
415+
results.push(GetAssertionResult {
416+
assertion: assertion.into(),
417+
extensions: Default::default(),
418+
});
416419

417420
let msg = GetNextAssertion;
418421
// We already have one, so skipping 0
419422
for _ in 1..number_of_credentials {
420-
let new_cred = dev.send_cbor(&msg)?;
421-
assertions.push(new_cred.into());
423+
let assertion = dev.send_cbor(&msg)?;
424+
results.push(GetAssertionResult {
425+
assertion: assertion.into(),
426+
extensions: Default::default(),
427+
});
422428
}
423429

424-
let mut output = GetAssertionResult {
425-
assertions,
426-
extensions: Default::default(),
427-
};
428-
self.finalize_result(&mut output);
429-
Ok(output)
430+
for result in results.iter_mut() {
431+
self.finalize_result(result);
432+
}
433+
Ok(results)
430434
} else {
431435
let data: Value = from_slice(&input[1..]).map_err(CommandError::Deserializing)?;
432436
Err(CommandError::StatusCode(status, Some(data)).into())
@@ -437,9 +441,11 @@ impl RequestCtap2 for GetAssertion {
437441
&self,
438442
dev: &mut Dev,
439443
) -> Result<Self::Output, HIDError> {
440-
let mut output = dev.get_assertion(self)?;
441-
self.finalize_result(&mut output);
442-
Ok(output)
444+
let mut results = dev.get_assertion(self)?;
445+
for result in results.iter_mut() {
446+
self.finalize_result(result);
447+
}
448+
Ok(results)
443449
}
444450
}
445451

@@ -465,7 +471,7 @@ impl From<GetAssertionResponse> for Assertion {
465471

466472
#[derive(Debug, PartialEq, Eq)]
467473
pub struct GetAssertionResult {
468-
pub assertions: Vec<Assertion>,
474+
pub assertion: Assertion,
469475
pub extensions: AuthenticationExtensionsClientOutputs,
470476
}
471477

@@ -501,23 +507,10 @@ impl GetAssertionResult {
501507
};
502508

503509
Ok(GetAssertionResult {
504-
assertions: vec![assertion],
510+
assertion,
505511
extensions: Default::default(),
506512
})
507513
}
508-
509-
pub fn u2f_sign_data(&self) -> Vec<u8> {
510-
if let Some(first) = self.assertions.first() {
511-
let mut res = Vec::new();
512-
res.push(first.auth_data.flags.bits());
513-
res.extend(first.auth_data.counter.to_be_bytes());
514-
res.extend(&first.signature);
515-
res
516-
// first.signature.clone()
517-
} else {
518-
Vec::new()
519-
}
520-
}
521514
}
522515

523516
pub struct GetAssertionResponse {
@@ -791,10 +784,10 @@ pub mod test {
791784
auth_data: expected_auth_data,
792785
};
793786

794-
let expected = GetAssertionResult {
795-
assertions: vec![expected_assertion],
787+
let expected = vec![GetAssertionResult {
788+
assertion: expected_assertion,
796789
extensions: Default::default(),
797-
};
790+
}];
798791
let response = device.send_cbor(&assertion).unwrap();
799792
assert_eq!(response, expected);
800793
}
@@ -926,10 +919,10 @@ pub mod test {
926919
auth_data: expected_auth_data,
927920
};
928921

929-
let expected = GetAssertionResult {
930-
assertions: vec![expected_assertion],
922+
let expected = vec![GetAssertionResult {
923+
assertion: expected_assertion,
931924
extensions: Default::default(),
932-
};
925+
}];
933926
assert_eq!(response, expected);
934927
}
935928

@@ -1070,10 +1063,10 @@ pub mod test {
10701063
auth_data: expected_auth_data,
10711064
};
10721065

1073-
let expected = GetAssertionResult {
1074-
assertions: vec![expected_assertion],
1066+
let expected = vec![GetAssertionResult {
1067+
assertion: expected_assertion,
10751068
extensions: Default::default(),
1076-
};
1069+
}];
10771070
assert_eq!(response, expected);
10781071
}
10791072

@@ -1338,7 +1331,7 @@ pub mod test {
13381331
let resp = GetAssertionResult::from_ctap1(&sample, &rp_hash, &add_info)
13391332
.expect("could not handle response");
13401333
assert_eq!(
1341-
resp.assertions[0].auth_data.flags,
1334+
resp.assertion.auth_data.flags,
13421335
AuthenticatorDataFlags::USER_PRESENT | AuthenticatorDataFlags::RESERVED_1
13431336
);
13441337
}

src/ctap2/mod.rs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -680,15 +680,34 @@ pub fn sign<Dev: FidoDevice>(
680680
debug!("{get_assertion:?} using {pin_uv_auth_result:?}");
681681
debug!("------------------------------------------------------------------");
682682
send_status(&status, crate::StatusUpdate::PresenceRequired);
683-
let resp = dev.send_msg_cancellable(&get_assertion, alive);
684-
match resp {
685-
Ok(result) => {
686-
callback.call(Ok(result));
687-
return true;
688-
}
683+
let mut results = match dev.send_msg_cancellable(&get_assertion, alive) {
684+
Ok(results) => results,
689685
Err(e) => {
690686
handle_errors!(e, status, callback, pin_uv_auth_result, skip_uv);
691687
}
688+
};
689+
if results.len() == 1 {
690+
callback.call(Ok(results.swap_remove(0)));
691+
return true;
692+
}
693+
let (tx, rx) = channel();
694+
let user_entities = results
695+
.iter()
696+
.filter_map(|x| x.assertion.user.clone())
697+
.collect();
698+
send_status(
699+
&status,
700+
crate::StatusUpdate::SelectResultNotice(tx, user_entities),
701+
);
702+
match rx.recv() {
703+
Ok(Some(index)) if index < results.len() => {
704+
callback.call(Ok(results.swap_remove(index)));
705+
return true;
706+
}
707+
_ => {
708+
callback.call(Err(AuthenticatorError::CancelledByUser));
709+
return true;
710+
}
692711
}
693712
}
694713
false

0 commit comments

Comments
 (0)