15c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu// Copyright 2014 The Chromium Authors. All rights reserved.
25c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu// Use of this source code is governed by a BSD-style license that can be
35c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu// found in the LICENSE file.
45c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
55c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
65c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @fileoverview Implements an enroll helper using USB gnubbies.
75c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
85c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu'use strict';
95c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
11010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {!GnubbyFactory} factory A factory for Gnubby instances
12010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {!Countdown} timer A timer for enroll timeout
135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {function(number, boolean)} errorCb Called when an enroll request
145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu *     fails with an error code and whether any gnubbies were found.
155c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {function(string, string)} successCb Called with the result of a
165c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu *     successful enroll request, along with the version of the gnubby that
175c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu *     provided it.
185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {(function(number, boolean)|undefined)} opt_progressCb Called with
195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu *     progress updates to the enroll request.
205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {string=} opt_logMsgUrl A URL to post log messages to.
215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @constructor
225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @implements {EnrollHelper}
235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liufunction UsbEnrollHelper(factory, timer, errorCb, successCb, opt_progressCb,
255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    opt_logMsgUrl) {
265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {!GnubbyFactory} */
275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.factory_ = factory;
285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {!Countdown} */
295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.timer_ = timer;
305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {function(number, boolean)} */
315c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.errorCb_ = errorCb;
325c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {function(string, string)} */
335c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.successCb_ = successCb;
345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {(function(number, boolean)|undefined)} */
355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.progressCb_ = opt_progressCb;
365c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {string|undefined} */
375c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.logMsgUrl_ = opt_logMsgUrl;
385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
395c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {Array.<SignHelperChallenge>} */
405c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.signChallenges_ = [];
415c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {boolean} */
425c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.signChallengesFinal_ = false;
435c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {Array.<usbGnubby>} */
445c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.waitingForTouchGnubbies_ = [];
455c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
465c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {boolean} */
475c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.closed_ = false;
485c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {boolean} */
495c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.notified_ = false;
505c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {number|undefined} */
515c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.lastProgressUpdate_ = undefined;
525c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {boolean} */
535c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.signerComplete_ = false;
545c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.getSomeGnubbies_();
555c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
565c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
575c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
585c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Attempts to enroll using the provided data.
595c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {Object} enrollChallenges a map of version string to enroll
605c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu *     challenges.
615c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {Array.<SignHelperChallenge>} signChallenges a list of sign
625c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu *     challenges for already enrolled gnubbies, to prevent double-enrolling a
635c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu *     device.
645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbEnrollHelper.prototype.doEnroll =
665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    function(enrollChallenges, signChallenges) {
675c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.enrollChallenges = enrollChallenges;
685c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.signChallengesFinal_ = true;
695c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (this.signer_) {
705c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.signer_.addEncodedChallenges(
715c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        signChallenges, this.signChallengesFinal_);
725c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  } else {
735c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.signChallenges_ = signChallenges;
745c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
755c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
765c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
775c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** Closes this helper. */
785c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbEnrollHelper.prototype.close = function() {
795c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.closed_ = true;
805c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  for (var i = 0; i < this.waitingForTouchGnubbies_.length; i++) {
815c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.waitingForTouchGnubbies_[i].closeWhenIdle();
825c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
835c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.waitingForTouchGnubbies_ = [];
845c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (this.signer_) {
855c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.signer_.close();
865c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.signer_ = null;
875c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
885c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
895c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
905c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
915c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Enumerates gnubbies, and begins processing challenges upon enumeration if
925c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * any gnubbies are found.
935c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
945c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
955c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbEnrollHelper.prototype.getSomeGnubbies_ = function() {
965c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.factory_.enumerate(this.enumerateCallback_.bind(this));
975c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
985c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
995c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
1005c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Called with the result of enumerating gnubbies.
1015c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {number} rc the result of the enumerate.
102010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {Array.<llGnubbyDeviceId>} indexes Device ids of enumerated gnubbies
1035c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
1045c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
1055c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbEnrollHelper.prototype.enumerateCallback_ = function(rc, indexes) {
1065c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (rc) {
1075c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    // Enumerate failure is rare enough that it might be worth reporting
1085c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    // directly, rather than trying again.
1095c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.errorCb_(rc, false);
1105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    return;
1115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
1125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (!indexes.length) {
1135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.maybeReEnumerateGnubbies_();
1145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    return;
1155c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
1165c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (this.timer_.expired()) {
1175c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.errorCb_(DeviceStatusCodes.TIMEOUT_STATUS, true);
1185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    return;
1195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
1205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.gotSomeGnubbies_(indexes);
1215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
1225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
1235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
1245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * If there's still time, re-enumerates devices and try with them. Otherwise
1255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * reports an error and, implicitly, stops the enroll operation.
1265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
1275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
1285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbEnrollHelper.prototype.maybeReEnumerateGnubbies_ = function() {
1295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var errorCode = DeviceStatusCodes.WRONG_DATA_STATUS;
1305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var anyGnubbies = false;
1315c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // If there's still time and we're still going, retry enumerating.
1325c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (!this.closed_ && !this.timer_.expired()) {
1335c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.notifyProgress_(errorCode, anyGnubbies);
1345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    var self = this;
1355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    // Use a delayed re-enumerate to prevent hammering the system unnecessarily.
1365c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    window.setTimeout(function() {
1375c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      if (self.timer_.expired()) {
1385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        self.notifyError_(errorCode, anyGnubbies);
1395c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      } else {
1405c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        self.getSomeGnubbies_();
1415c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      }
1425c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    }, 200);
1435c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  } else {
1445c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.notifyError_(errorCode, anyGnubbies);
1455c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
1465c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
1475c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
1485c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
1495c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Called with the result of enumerating gnubby indexes.
150010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {Array.<llGnubbyDeviceId>} indexes Device ids of enumerated gnubbies
1515c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
1525c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
1535c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbEnrollHelper.prototype.gotSomeGnubbies_ = function(indexes) {
1545c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.signer_ = new MultipleGnubbySigner(
1555c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      this.factory_,
1565c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      indexes,
1575c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      true /* forEnroll */,
1585c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      this.signerCompleted_.bind(this),
1595c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      this.signerFoundGnubby_.bind(this),
1605c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      this.timer_,
1615c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      this.logMsgUrl_);
1625c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (this.signChallengesFinal_) {
1635c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.signer_.addEncodedChallenges(
1645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        this.signChallenges_, this.signChallengesFinal_);
1655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.pendingSignChallenges_ = [];
1665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
1675c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
1685c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
1695c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
1705c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Called when a MultipleGnubbySigner completes its sign request.
1715c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {boolean} anySucceeded whether any sign attempt completed
1725c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu *     successfully.
1735c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {number=} errorCode an error code from a failing gnubby, if one was
1745c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu *     found.
1755c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
1765c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
1775c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbEnrollHelper.prototype.signerCompleted_ = function(anySucceeded, errorCode) {
1785c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.signerComplete_ = true;
1795c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // The signer is not created unless some gnubbies were enumerated, so
1805c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // anyGnubbies is mostly always true. The exception is when the last gnubby is
1815c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // removed, handled shortly.
1825c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var anyGnubbies = true;
1835c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (!anySucceeded) {
1845c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    if (errorCode == -llGnubby.GONE) {
1855c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      // If the last gnubby was removed, report as though no gnubbies were
1865c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      // found.
1875c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      this.maybeReEnumerateGnubbies_();
1885c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    } else {
1895c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      if (!errorCode) errorCode = DeviceStatusCodes.WRONG_DATA_STATUS;
1905c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      this.notifyError_(errorCode, anyGnubbies);
1915c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    }
1925c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  } else if (this.anyTimeout) {
1935c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    // Some previously succeeding gnubby timed out: return its error code.
1945c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.notifyError_(this.timeoutError, anyGnubbies);
1955c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  } else {
1965c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    // Do nothing: signerFoundGnubby will have been called with each succeeding
1975c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    // gnubby.
1985c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
1995c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
2005c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
2015c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
2025c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Called when a MultipleGnubbySigner finds a gnubby that can enroll.
203010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {number} code Status code
204010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {MultipleSignerResult} signResult Signature results
2055c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
2065c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
2075c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbEnrollHelper.prototype.signerFoundGnubby_ = function(code, signResult) {
2085c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var gnubby = signResult['gnubby'];
2095c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.waitingForTouchGnubbies_.push(gnubby);
2105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.notifyProgress_(DeviceStatusCodes.WAIT_TOUCH_STATUS, true);
2115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (code == DeviceStatusCodes.WRONG_DATA_STATUS) {
2125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    if (signResult['challenge']) {
2135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      // If the signer yielded a busy open, indicate waiting for touch
2145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      // immediately, rather than attempting enroll. This allows the UI to
2155c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      // update, since a busy open is a potentially long operation.
2165c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      this.notifyError_(DeviceStatusCodes.WAIT_TOUCH_STATUS, true);
2175c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    } else {
2185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      this.matchEnrollVersionToGnubby_(gnubby);
2195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    }
2205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
2215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
2225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
2235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
2245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Attempts to match the gnubby's U2F version with an appropriate enroll
2255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * challenge.
226010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {usbGnubby} gnubby Gnubby instance
2275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
2285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
2295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbEnrollHelper.prototype.matchEnrollVersionToGnubby_ = function(gnubby) {
2305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (!gnubby) {
2315c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    console.warn(UTIL_fmt('no gnubby, WTF?'));
2325c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
2335c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  gnubby.version(this.gnubbyVersioned_.bind(this, gnubby));
2345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
2355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
2365c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
2375c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Called with the result of a version command.
238010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {usbGnubby} gnubby Gnubby instance
2395c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {number} rc result of version command.
2405c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {ArrayBuffer=} data version.
2415c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
2425c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
2435c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbEnrollHelper.prototype.gnubbyVersioned_ = function(gnubby, rc, data) {
2445c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (rc) {
2455c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.removeWrongVersionGnubby_(gnubby);
2465c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    return;
2475c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
2485c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var version = UTIL_BytesToString(new Uint8Array(data || null));
2495c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.tryEnroll_(gnubby, version);
2505c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
2515c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
2525c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
2535c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Drops the gnubby from the list of eligible gnubbies.
254010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {usbGnubby} gnubby Gnubby instance
2555c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
2565c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
2575c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbEnrollHelper.prototype.removeWaitingGnubby_ = function(gnubby) {
2585c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  gnubby.closeWhenIdle();
2595c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var index = this.waitingForTouchGnubbies_.indexOf(gnubby);
2605c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (index >= 0) {
2615c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.waitingForTouchGnubbies_.splice(index, 1);
2625c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
2635c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
2645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
2655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
2665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Drops the gnubby from the list of eligible gnubbies, as it has the wrong
2675c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * version.
268010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {usbGnubby} gnubby Gnubby instance
2695c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
2705c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
2715c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbEnrollHelper.prototype.removeWrongVersionGnubby_ = function(gnubby) {
2725c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.removeWaitingGnubby_(gnubby);
2735c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (!this.waitingForTouchGnubbies_.length && this.signerComplete_) {
2745c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    // Whoops, this was the last gnubby: indicate there are none.
2755c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.notifyError_(DeviceStatusCodes.WRONG_DATA_STATUS, false);
2765c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
2775c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
2785c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
2795c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
2805c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Attempts enrolling a particular gnubby with a challenge of the appropriate
2815c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * version.
282010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {usbGnubby} gnubby Gnubby instance
283010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {string} version Protocol version
2845c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
2855c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
2865c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbEnrollHelper.prototype.tryEnroll_ = function(gnubby, version) {
2875c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var challenge = this.getChallengeOfVersion_(version);
2885c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (!challenge) {
2895c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.removeWrongVersionGnubby_(gnubby);
2905c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    return;
2915c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
2925c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var challengeChallenge = B64_decode(challenge['challenge']);
2935c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var appIdHash = B64_decode(challenge['appIdHash']);
2945c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  gnubby.enroll(challengeChallenge, appIdHash,
2955c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      this.enrollCallback_.bind(this, gnubby, version));
2965c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
2975c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
2985c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
2995c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Finds the (first) challenge of the given version in this helper's challenges.
300010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {string} version Protocol version
3015c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @return {Object} challenge, if found, or null if not.
3025c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
3035c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
3045c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbEnrollHelper.prototype.getChallengeOfVersion_ = function(version) {
3055c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  for (var i = 0; i < this.enrollChallenges.length; i++) {
3065c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    if (this.enrollChallenges[i]['version'] == version) {
3075c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      return this.enrollChallenges[i];
3085c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    }
3095c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
3105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  return null;
3115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
3125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
3135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
3145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Called with the result of an enroll request to a gnubby.
315010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {usbGnubby} gnubby Gnubby instance
316010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {string} version Protocol version
317010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {number} code Status code
318010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {ArrayBuffer=} infoArray Returned data
3195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
3205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
3215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbEnrollHelper.prototype.enrollCallback_ =
3225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    function(gnubby, version, code, infoArray) {
3235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (this.notified_) {
3245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    // Enroll completed after previous success or failure. Disregard.
3255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    return;
3265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
3275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  switch (code) {
3285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    case -llGnubby.GONE:
3295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        // Close this gnubby.
3305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        this.removeWaitingGnubby_(gnubby);
3315c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        if (!this.waitingForTouchGnubbies_.length) {
3325c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu          // Last enroll attempt is complete and last gnubby is gone: retry if
3335c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu          // possible.
3345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu          this.maybeReEnumerateGnubbies_();
3355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        }
3365c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      break;
3375c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
3385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    case DeviceStatusCodes.WAIT_TOUCH_STATUS:
3395c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    case DeviceStatusCodes.BUSY_STATUS:
3405c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    case DeviceStatusCodes.TIMEOUT_STATUS:
3415c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      if (this.timer_.expired()) {
3425c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        // Store any timeout error code, to be returned from the complete
3435c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        // callback if no other eligible gnubbies are found.
3445c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        this.anyTimeout = true;
3455c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        this.timeoutError = code;
3465c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        // Close this gnubby.
3475c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        this.removeWaitingGnubby_(gnubby);
3485c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        if (!this.waitingForTouchGnubbies_.length && !this.notified_) {
3495c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu          // Last enroll attempt is complete: return this error.
3505c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu          console.log(UTIL_fmt('timeout (' + code.toString(16) +
3515c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu              ') enrolling'));
3525c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu          this.notifyError_(code, true);
3535c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        }
3545c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      } else {
3555c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        // Notify caller of waiting for touch events.
3565c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        if (code == DeviceStatusCodes.WAIT_TOUCH_STATUS) {
3575c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu          this.notifyProgress_(code, true);
3585c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        }
3595c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        window.setTimeout(this.tryEnroll_.bind(this, gnubby, version), 200);
3605c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      }
3615c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      break;
3625c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
3635c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    case DeviceStatusCodes.OK_STATUS:
3645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      var info = B64_encode(new Uint8Array(infoArray || []));
3655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      this.notifySuccess_(version, info);
3665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      break;
3675c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
3685c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    default:
3695c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      console.log(UTIL_fmt('Failed to enroll gnubby: ' + code));
3705c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      this.notifyError_(code, true);
3715c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      break;
3725c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
3735c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
3745c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
3755c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
376010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {number} code Status code
377010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {boolean} anyGnubbies If any gnubbies were found
3785c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
3795c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
3805c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbEnrollHelper.prototype.notifyError_ = function(code, anyGnubbies) {
3815c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (this.notified_ || this.closed_)
3825c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    return;
3835c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.notified_ = true;
3845c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.close();
3855c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.errorCb_(code, anyGnubbies);
3865c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
3875c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
3885c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
389010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {string} version Protocol version
390010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {string} info B64 encoded success data
3915c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
3925c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
3935c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbEnrollHelper.prototype.notifySuccess_ = function(version, info) {
3945c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (this.notified_ || this.closed_)
3955c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    return;
3965c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.notified_ = true;
3975c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.close();
3985c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.successCb_(version, info);
3995c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
4005c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
4015c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
402010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {number} code Status code
403010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {boolean} anyGnubbies If any gnubbies were found
4045c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
4055c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
4065c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbEnrollHelper.prototype.notifyProgress_ = function(code, anyGnubbies) {
4075c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (this.lastProgressUpdate_ == code || this.notified_ || this.closed_)
4085c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    return;
4095c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.lastProgressUpdate_ = code;
4105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (this.progressCb_) this.progressCb_(code, anyGnubbies);
4115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
4125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
4135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
4145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {!GnubbyFactory} gnubbyFactory factory to create gnubbies.
4155c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @constructor
4165c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @implements {EnrollHelperFactory}
4175c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
4185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liufunction UsbEnrollHelperFactory(gnubbyFactory) {
4195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {!GnubbyFactory} */
4205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.gnubbyFactory_ = gnubbyFactory;
4215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
4225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
4235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
424010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {!Countdown} timer Timeout timer
4255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {function(number, boolean)} errorCb Called when an enroll request
4265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu *     fails with an error code and whether any gnubbies were found.
4275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {function(string, string)} successCb Called with the result of a
4285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu *     successful enroll request, along with the version of the gnubby that
4295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu *     provided it.
4305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {(function(number, boolean)|undefined)} opt_progressCb Called with
4315c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu *     progress updates to the enroll request.
4325c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {string=} opt_logMsgUrl A URL to post log messages to.
4335c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @return {UsbEnrollHelper} the newly created helper.
4345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
4355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbEnrollHelperFactory.prototype.createHelper =
4365c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    function(timer, errorCb, successCb, opt_progressCb, opt_logMsgUrl) {
4375c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var helper =
4385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      new UsbEnrollHelper(this.gnubbyFactory_, timer, errorCb, successCb,
4395c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu          opt_progressCb, opt_logMsgUrl);
4405c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  return helper;
4415c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
442