Skip to main content

authenticator_ctap2_2021/
manager.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use 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        // Start a new work queue thread.
82        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                        // This must not block, otherwise we can't cancel.
94                        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                        // This must not block, otherwise we can't cancel.
114                        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                        // Cancelling must block so that we don't start a new
126                        // polling thread before the old one has shut down.
127                        sm.cancel();
128                    }
129                    Ok(QueueAction::RegisterCtap2 { .. }) => {
130                        // TODO(MS): What to do here? Error out? Silently ignore?
131                        unimplemented!();
132                    }
133                    Ok(QueueAction::SignCtap2 { .. }) => {
134                        // TODO(MS): What to do here? Error out? Silently ignore?
135                        unimplemented!();
136                    }
137                    Ok(QueueAction::Reset { .. }) | Ok(QueueAction::SetPin { .. }) => {
138                        unimplemented!();
139                    }
140                    // Explicitly list the actions we ignore so that extension of
141                    // Queue action flags during compilation that we need to handle
142                    // these cases.
143                    Ok(QueueAction::InfoCtap2 { .. }) => { /* continue */ }
144                    Err(Timeout) => { /* continue */ }
145                    Err(RecvTimeoutError::Disconnected) => {
146                        break;
147                    } // _ => { /* continue */ }
148                }
149            }
150
151            // Cancel any ongoing activity.
152            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        // Start a new work queue thread.
296        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                        // This must not block, otherwise we can't cancel.
308                        sm.register(timeout, make_credentials, status, callback);
309                    }
310
311                    Ok(QueueAction::SignCtap2 {
312                        timeout,
313                        get_assertion,
314                        status,
315                        callback,
316                    }) => {
317                        // This must not block, otherwise we can't cancel.
318                        sm.sign(timeout, get_assertion, status, callback);
319                    }
320
321                    Ok(QueueAction::Cancel) => {
322                        // Cancelling must block so that we don't start a new
323                        // polling thread before the old one has shut down.
324                        sm.cancel();
325                    }
326
327                    Ok(QueueAction::Reset {
328                        timeout,
329                        status,
330                        callback,
331                    }) => {
332                        // Reset the token: Delete all keypairs, reset PIN
333                        sm.reset(timeout, status, callback);
334                    }
335
336                    Ok(QueueAction::SetPin {
337                        timeout,
338                        new_pin,
339                        status,
340                        callback,
341                    }) => {
342                        // This must not block, otherwise we can't cancel.
343                        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                        // TODO(MS): Remove QueueAction::RegisterCtap1 once U2FManager is deleted.
353                        //           The repackaging from CTAP1 to CTAP2 happens in self.register()
354                        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                        // TODO(MS): Remove QueueAction::SignCtap1 once U2FManager is deleted.
367                        //           The repackaging from CTAP1 to CTAP2 happens in self.sign()
368                        unimplemented!()
369                    }
370                    Ok(QueueAction::InfoCtap2 {
371                        timeout,
372                        status,
373                        callback,
374                    }) => {
375                        sm.info(timeout, status, callback);
376                    }
377                    // Explicitly list the actions we ignore so that extension of
378                    // Queue action flags during compilation that we need to handle
379                    // these cases.
380                    Err(Timeout) => { /* continue */ }
381                    Err(RecvTimeoutError::Disconnected) => {
382                        break;
383                    }
384                }
385            }
386
387            // Cancel any ongoing activity.
388            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                    // pin_auth will be filled in Statemachine, once we have a device
429                )
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![], // TODO(MS): Implement excludeList. See spec.
448                    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}