1use crate::authenticatorservice::AuthenticatorTransport;
6use crate::authenticatorservice::{RegisterArgs, RegisterArgsCtap1, SignArgs};
7use crate::consts::PARAMETER_SIZE;
8use crate::crypto::COSEAlgorithm;
9use crate::ctap2::client_data::{CollectedClientData, WebauthnType};
10use crate::ctap2::commands::get_assertion::{GetAssertion, GetAssertionOptions};
11use crate::ctap2::commands::make_credentials::MakeCredentials;
12use crate::ctap2::commands::make_credentials::MakeCredentialsOptions;
13use crate::ctap2::server::{
14 PublicKeyCredentialParameters, RelyingParty, RelyingPartyWrapper, RpIdHash,
15};
16use crate::errors::*;
17use crate::statecallback::StateCallback;
18use crate::statemachine::{StateMachine, StateMachineCtap2};
19use crate::{Pin, SignFlags};
20use runloop::RunLoop;
21use std::io;
22use std::sync::mpsc::{channel, RecvTimeoutError, Sender};
23use std::time::Duration;
24
25enum QueueAction {
26 RegisterCtap1 {
27 timeout: u64,
28 ctap_args: RegisterArgsCtap1,
29 status: Sender<crate::StatusUpdate>,
30 callback: StateCallback<crate::Result<crate::RegisterResult>>,
31 },
32 RegisterCtap2 {
33 timeout: u64,
34 make_credentials: MakeCredentials,
35 status: Sender<crate::StatusUpdate>,
36 callback: StateCallback<crate::Result<crate::RegisterResult>>,
37 },
38 SignCtap1 {
39 flags: crate::SignFlags,
40 timeout: u64,
41 challenge: Vec<u8>,
42 app_ids: Vec<crate::AppId>,
43 key_handles: Vec<crate::KeyHandle>,
44 status: Sender<crate::StatusUpdate>,
45 callback: StateCallback<crate::Result<crate::SignResult>>,
46 },
47 SignCtap2 {
48 timeout: u64,
49 get_assertion: GetAssertion,
50 status: Sender<crate::StatusUpdate>,
51 callback: StateCallback<crate::Result<crate::SignResult>>,
52 },
53 Cancel,
54 Reset {
55 timeout: u64,
56 status: Sender<crate::StatusUpdate>,
57 callback: StateCallback<crate::Result<crate::ResetResult>>,
58 },
59 SetPin {
60 timeout: u64,
61 new_pin: Pin,
62 status: Sender<crate::StatusUpdate>,
63 callback: StateCallback<crate::Result<crate::ResetResult>>,
64 },
65 InfoCtap2 {
66 timeout: u64,
67 status: Sender<crate::StatusUpdate>,
68 callback: StateCallback<crate::Result<crate::InfoResult>>,
69 },
70}
71
72pub struct U2FManager {
73 queue: RunLoop,
74 tx: Sender<QueueAction>,
75}
76
77impl U2FManager {
78 pub fn new() -> io::Result<Self> {
79 let (tx, rx) = channel();
80
81 let queue = RunLoop::new(move |alive| {
83 let mut sm = StateMachine::new();
84
85 while alive() {
86 match rx.recv_timeout(Duration::from_millis(50)) {
87 Ok(QueueAction::RegisterCtap1 {
88 timeout,
89 ctap_args,
90 status,
91 callback,
92 }) => {
93 sm.register(
95 ctap_args.flags,
96 timeout,
97 ctap_args.challenge,
98 ctap_args.application,
99 ctap_args.key_handles,
100 status,
101 callback,
102 );
103 }
104 Ok(QueueAction::SignCtap1 {
105 flags,
106 timeout,
107 challenge,
108 app_ids,
109 key_handles,
110 status,
111 callback,
112 }) => {
113 sm.sign(
115 flags,
116 timeout,
117 challenge,
118 app_ids,
119 key_handles,
120 status,
121 callback,
122 );
123 }
124 Ok(QueueAction::Cancel) => {
125 sm.cancel();
128 }
129 Ok(QueueAction::RegisterCtap2 { .. }) => {
130 unimplemented!();
132 }
133 Ok(QueueAction::SignCtap2 { .. }) => {
134 unimplemented!();
136 }
137 Ok(QueueAction::Reset { .. }) | Ok(QueueAction::SetPin { .. }) => {
138 unimplemented!();
139 }
140 Ok(QueueAction::InfoCtap2 { .. }) => { }
144 Err(Timeout) => { }
145 Err(RecvTimeoutError::Disconnected) => {
146 break;
147 } }
149 }
150
151 sm.cancel();
153 })?;
154
155 Ok(Self { queue, tx })
156 }
157}
158
159impl AuthenticatorTransport for U2FManager {
160 fn register(
161 &mut self,
162 timeout: u64,
163 ctap_args: RegisterArgs,
164 status: Sender<crate::StatusUpdate>,
165 callback: StateCallback<crate::Result<crate::RegisterResult>>,
166 ) -> crate::Result<()> {
167 let args = match ctap_args {
168 RegisterArgs::CTAP1(args) => args,
169 RegisterArgs::CTAP2(_) => {
170 return Err(AuthenticatorError::VersionMismatch("U2FManager", 1));
171 }
172 };
173 if args.challenge.len() != PARAMETER_SIZE || args.application.len() != PARAMETER_SIZE {
174 return Err(AuthenticatorError::InvalidRelyingPartyInput);
175 }
176
177 for key_handle in &args.key_handles {
178 if key_handle.credential.len() > 256 {
179 return Err(AuthenticatorError::InvalidRelyingPartyInput);
180 }
181 }
182
183 let action = QueueAction::RegisterCtap1 {
184 timeout,
185 ctap_args: args,
186 status,
187 callback,
188 };
189 Ok(self.tx.send(action)?)
190 }
191
192 fn sign(
193 &mut self,
194 timeout: u64,
195 ctap_args: SignArgs,
196 status: Sender<crate::StatusUpdate>,
197 callback: StateCallback<crate::Result<crate::SignResult>>,
198 ) -> crate::Result<()> {
199 let args = match ctap_args {
200 SignArgs::CTAP1(args) => args,
201 SignArgs::CTAP2(_) => {
202 return Err(AuthenticatorError::VersionMismatch("U2FManager", 1));
203 }
204 };
205
206 if args.challenge.len() != PARAMETER_SIZE {
207 return Err(AuthenticatorError::InvalidRelyingPartyInput);
208 }
209
210 if args.app_ids.is_empty() {
211 return Err(AuthenticatorError::InvalidRelyingPartyInput);
212 }
213
214 for app_id in &args.app_ids {
215 if app_id.len() != PARAMETER_SIZE {
216 return Err(AuthenticatorError::InvalidRelyingPartyInput);
217 }
218 }
219
220 for key_handle in &args.key_handles {
221 if key_handle.credential.len() > 256 {
222 return Err(AuthenticatorError::InvalidRelyingPartyInput);
223 }
224 }
225
226 let action = QueueAction::SignCtap1 {
227 flags: args.flags,
228 timeout,
229 challenge: args.challenge,
230 app_ids: args.app_ids,
231 key_handles: args.key_handles,
232 status,
233 callback,
234 };
235 Ok(self.tx.send(action)?)
236 }
237
238 fn cancel(&mut self) -> crate::Result<()> {
239 Ok(self.tx.send(QueueAction::Cancel)?)
240 }
241
242 fn reset(
243 &mut self,
244 timeout: u64,
245 status: Sender<crate::StatusUpdate>,
246 callback: StateCallback<crate::Result<crate::ResetResult>>,
247 ) -> crate::Result<()> {
248 Ok(self.tx.send(QueueAction::Reset {
249 timeout,
250 status,
251 callback,
252 })?)
253 }
254
255 fn set_pin(
256 &mut self,
257 timeout: u64,
258 new_pin: Pin,
259 status: Sender<crate::StatusUpdate>,
260 callback: StateCallback<crate::Result<crate::ResetResult>>,
261 ) -> crate::Result<()> {
262 Ok(self.tx.send(QueueAction::SetPin {
263 timeout,
264 new_pin,
265 status,
266 callback,
267 })?)
268 }
269
270 fn info(
271 &mut self,
272 timeout: u64,
273 status: Sender<crate::StatusUpdate>,
274 callback: StateCallback<crate::Result<crate::InfoResult>>,
275 ) -> crate::Result<()> {
276 unimplemented!();
277 }
278}
279
280impl Drop for U2FManager {
281 fn drop(&mut self) {
282 self.queue.cancel();
283 }
284}
285
286pub struct Manager {
287 queue: RunLoop,
288 tx: Sender<QueueAction>,
289}
290
291impl Manager {
292 pub fn new() -> io::Result<Self> {
293 let (tx, rx) = channel();
294
295 let queue = RunLoop::new(move |alive| {
297 let mut sm = StateMachineCtap2::new();
298
299 while alive() {
300 match rx.recv_timeout(Duration::from_millis(50)) {
301 Ok(QueueAction::RegisterCtap2 {
302 timeout,
303 make_credentials,
304 status,
305 callback,
306 }) => {
307 sm.register(timeout, make_credentials, status, callback);
309 }
310
311 Ok(QueueAction::SignCtap2 {
312 timeout,
313 get_assertion,
314 status,
315 callback,
316 }) => {
317 sm.sign(timeout, get_assertion, status, callback);
319 }
320
321 Ok(QueueAction::Cancel) => {
322 sm.cancel();
325 }
326
327 Ok(QueueAction::Reset {
328 timeout,
329 status,
330 callback,
331 }) => {
332 sm.reset(timeout, status, callback);
334 }
335
336 Ok(QueueAction::SetPin {
337 timeout,
338 new_pin,
339 status,
340 callback,
341 }) => {
342 sm.set_pin(timeout, new_pin, status, callback);
344 }
345
346 Ok(QueueAction::RegisterCtap1 {
347 timeout: _,
348 ctap_args: _,
349 status: _,
350 callback: _,
351 }) => {
352 unimplemented!();
355 }
356
357 Ok(QueueAction::SignCtap1 {
358 timeout: _,
359 callback: _,
360 flags: _,
361 challenge: _,
362 app_ids: _,
363 key_handles: _,
364 status: _,
365 }) => {
366 unimplemented!()
369 }
370 Ok(QueueAction::InfoCtap2 {
371 timeout,
372 status,
373 callback,
374 }) => {
375 sm.info(timeout, status, callback);
376 }
377 Err(Timeout) => { }
381 Err(RecvTimeoutError::Disconnected) => {
382 break;
383 }
384 }
385 }
386
387 sm.cancel();
389 })?;
390
391 Ok(Self { queue, tx })
392 }
393}
394
395impl Drop for Manager {
396 fn drop(&mut self) {
397 self.queue.cancel();
398 }
399}
400
401impl AuthenticatorTransport for Manager {
402 fn register(
403 &mut self,
404 timeout: u64,
405 ctap_args: RegisterArgs,
406 status: Sender<crate::StatusUpdate>,
407 callback: StateCallback<crate::Result<crate::RegisterResult>>,
408 ) -> Result<(), AuthenticatorError> {
409 let make_credentials = match ctap_args {
410 RegisterArgs::CTAP2(args) => {
411 let client_data = CollectedClientData {
412 webauthn_type: WebauthnType::Create,
413 challenge: args.challenge.into(),
414 origin: args.origin,
415 cross_origin: false,
416 token_binding: None,
417 };
418
419 MakeCredentials::new(
420 client_data,
421 RelyingPartyWrapper::Data(args.relying_party),
422 Some(args.user),
423 args.pub_cred_params,
424 args.exclude_list,
425 args.options,
426 args.extensions,
427 args.pin,
428 )
430 }
431 RegisterArgs::CTAP1(args) => {
432 let client_data = CollectedClientData {
433 webauthn_type: WebauthnType::Create,
434 challenge: args.challenge.into(),
435 origin: String::new(),
436 cross_origin: false,
437 token_binding: None,
438 };
439
440 MakeCredentials::new(
441 client_data,
442 RelyingPartyWrapper::Hash(RpIdHash::from(&args.application)?),
443 None,
444 vec![PublicKeyCredentialParameters {
445 alg: COSEAlgorithm::ES256,
446 }],
447 vec![], MakeCredentialsOptions {
449 resident_key: None,
450 user_verification: None,
451 },
452 Default::default(),
453 None,
454 )
455 }
456 };
457
458 let action = QueueAction::RegisterCtap2 {
459 timeout,
460 make_credentials,
461 status,
462 callback,
463 };
464 Ok(self.tx.send(action)?)
465 }
466
467 fn sign(
468 &mut self,
469 timeout: u64,
470 ctap_args: SignArgs,
471 status: Sender<crate::StatusUpdate>,
472 callback: StateCallback<crate::Result<crate::SignResult>>,
473 ) -> crate::Result<()> {
474 match ctap_args {
475 SignArgs::CTAP1(args) => {
476 if args.challenge.len() != PARAMETER_SIZE {
477 return Err(AuthenticatorError::InvalidRelyingPartyInput);
478 }
479
480 if args.app_ids.is_empty() {
481 return Err(AuthenticatorError::InvalidRelyingPartyInput);
482 }
483
484 let client_data = CollectedClientData {
485 webauthn_type: WebauthnType::Get,
486 challenge: args.challenge.into(),
487 origin: String::new(),
488 cross_origin: false,
489 token_binding: None,
490 };
491 let options = if args.flags == SignFlags::empty() {
492 GetAssertionOptions::default()
493 } else {
494 GetAssertionOptions {
495 user_verification: Some(
496 args.flags.contains(SignFlags::REQUIRE_USER_VERIFICATION),
497 ),
498 ..GetAssertionOptions::default()
499 }
500 };
501
502 for app_id in &args.app_ids {
503 if app_id.len() != PARAMETER_SIZE {
504 return Err(AuthenticatorError::InvalidRelyingPartyInput);
505 }
506 for key_handle in &args.key_handles {
507 if key_handle.credential.len() > 256 {
508 return Err(AuthenticatorError::InvalidRelyingPartyInput);
509 }
510 let rp = RelyingPartyWrapper::Hash(RpIdHash::from(&app_id)?);
511
512 let allow_list = vec![key_handle.into()];
513
514 let get_assertion = GetAssertion::new(
515 client_data.clone(),
516 rp,
517 allow_list,
518 options,
519 Default::default(),
520 None,
521 );
522
523 let action = QueueAction::SignCtap2 {
524 timeout,
525 get_assertion,
526 status: status.clone(),
527 callback: callback.clone(),
528 };
529 self.tx.send(action)?;
530 }
531 }
532 }
533
534 SignArgs::CTAP2(args) => {
535 let client_data = CollectedClientData {
536 webauthn_type: WebauthnType::Get,
537 challenge: args.challenge.into(),
538 origin: args.origin,
539 cross_origin: false,
540 token_binding: None,
541 };
542
543 let get_assertion = GetAssertion::new(
544 client_data.clone(),
545 RelyingPartyWrapper::Data(RelyingParty {
546 id: args.relying_party_id,
547 name: None,
548 icon: None,
549 }),
550 args.allow_list,
551 args.options,
552 args.extensions,
553 args.pin,
554 );
555
556 let action = QueueAction::SignCtap2 {
557 timeout,
558 get_assertion,
559 status,
560 callback,
561 };
562 self.tx.send(action)?;
563 }
564 };
565 Ok(())
566 }
567
568 fn cancel(&mut self) -> Result<(), AuthenticatorError> {
569 Ok(self.tx.send(QueueAction::Cancel)?)
570 }
571
572 fn reset(
573 &mut self,
574 timeout: u64,
575 status: Sender<crate::StatusUpdate>,
576 callback: StateCallback<crate::Result<crate::ResetResult>>,
577 ) -> Result<(), AuthenticatorError> {
578 Ok(self.tx.send(QueueAction::Reset {
579 timeout,
580 status,
581 callback,
582 })?)
583 }
584
585 fn set_pin(
586 &mut self,
587 timeout: u64,
588 new_pin: Pin,
589 status: Sender<crate::StatusUpdate>,
590 callback: StateCallback<crate::Result<crate::ResetResult>>,
591 ) -> crate::Result<()> {
592 Ok(self.tx.send(QueueAction::SetPin {
593 timeout,
594 new_pin,
595 status,
596 callback,
597 })?)
598 }
599
600 fn info(
601 &mut self,
602 timeout: u64,
603 status: Sender<crate::StatusUpdate>,
604 callback: StateCallback<crate::Result<crate::InfoResult>>,
605 ) -> crate::Result<()> {
606 debug!("Queuing InfoCtap2 Action");
607 Ok(self.tx.send(QueueAction::InfoCtap2 {
608 timeout,
609 status,
610 callback,
611 })?)
612 }
613}