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/**
66e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @fileoverview Implements an enroll handler using USB gnubbies.
75c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
85c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu'use strict';
95c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
116e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {!EnrollHelperRequest} request The enroll request.
125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @constructor
136e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @implements {RequestHandler}
145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)function UsbEnrollHandler(request) {
166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  /** @private {!EnrollHelperRequest} */
176e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  this.request_ = request;
185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
196e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  /** @private {Array.<Gnubby>} */
205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.waitingForTouchGnubbies_ = [];
215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {boolean} */
235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.closed_ = false;
245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {boolean} */
255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.notified_ = false;
265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
29116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * Default timeout value in case the caller never provides a valid timeout.
30116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * @const
31116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch */
326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)UsbEnrollHandler.DEFAULT_TIMEOUT_MILLIS = 30 * 1000;
33116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
34116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch/**
356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {RequestHandlerCallback} cb Called back with the result of the
366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) *     request, and an optional source for the result.
376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @return {boolean} Whether this handler could be run.
385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)UsbEnrollHandler.prototype.run = function(cb) {
40116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  var timeoutMillis =
416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      this.request_.timeoutSeconds ?
426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      this.request_.timeoutSeconds * 1000 :
436e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      UsbEnrollHandler.DEFAULT_TIMEOUT_MILLIS;
44116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  /** @private {Countdown} */
456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  this.timer_ = DEVICE_FACTORY_REGISTRY.getCountdownFactory().createTimer(
466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      timeoutMillis);
476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  this.enrollChallenges = this.request_.enrollChallenges;
486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  /** @private {RequestHandlerCallback} */
49116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  this.cb_ = cb;
50116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  this.signer_ = new MultipleGnubbySigner(
51116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      true /* forEnroll */,
52116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      this.signerCompleted_.bind(this),
53116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      this.signerFoundGnubby_.bind(this),
54116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      timeoutMillis,
556e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      this.request_.logMsgUrl);
566e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return this.signer_.doSign(this.request_.signData);
575c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
585c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
595c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** Closes this helper. */
606e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)UsbEnrollHandler.prototype.close = function() {
615c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.closed_ = true;
625c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  for (var i = 0; i < this.waitingForTouchGnubbies_.length; i++) {
635c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.waitingForTouchGnubbies_[i].closeWhenIdle();
645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.waitingForTouchGnubbies_ = [];
665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (this.signer_) {
675c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.signer_.close();
685c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.signer_ = null;
695c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
705c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
715c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
725c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
735c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Called when a MultipleGnubbySigner completes its sign request.
74116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * @param {boolean} anyPending Whether any gnubbies are pending.
755c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
765c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
776e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)UsbEnrollHandler.prototype.signerCompleted_ = function(anyPending) {
78116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (!this.anyGnubbiesFound_ || this.anyTimeout_ || anyPending ||
79116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      this.timer_.expired()) {
80116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    this.notifyError_(DeviceStatusCodes.TIMEOUT_STATUS);
815c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  } else {
825c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    // Do nothing: signerFoundGnubby will have been called with each succeeding
835c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    // gnubby.
845c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
855c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
865c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
875c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
885c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Called when a MultipleGnubbySigner finds a gnubby that can enroll.
89010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {MultipleSignerResult} signResult Signature results
90116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * @param {boolean} moreExpected Whether the signer expects to report
91116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch *     results from more gnubbies.
925c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
935c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
946e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)UsbEnrollHandler.prototype.signerFoundGnubby_ =
95116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    function(signResult, moreExpected) {
96116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (!signResult.code) {
97116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    // If the signer reports a gnubby can sign, report this immediately to the
986e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    // caller, as the gnubby is already enrolled. Map ok to WRONG_DATA, so the
996e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    // caller knows what to do.
1006e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    this.notifyError_(DeviceStatusCodes.WRONG_DATA_STATUS);
1011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  } else if (signResult.code == DeviceStatusCodes.WRONG_DATA_STATUS ||
1021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      signResult.code == DeviceStatusCodes.WRONG_LENGTH_STATUS) {
103116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    var gnubby = signResult['gnubby'];
1046e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    // A valid helper request contains at least one enroll challenge, so use
1056e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    // the app id hash from the first challenge.
1066e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    var appIdHash = this.request_.enrollChallenges[0].appIdHash;
1076e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    DEVICE_FACTORY_REGISTRY.getGnubbyFactory().notEnrolledPrerequisiteCheck(
1086e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        gnubby, appIdHash, this.gnubbyPrerequisitesChecked_.bind(this));
1095c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
1105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
1115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
1125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
1136e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * Called with the result of a gnubby prerequisite check.
1146e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {number} rc The result of the prerequisite check.
1156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {Gnubby=} opt_gnubby The gnubby whose prerequisites were checked.
1166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @private
1176e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) */
1186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)UsbEnrollHandler.prototype.gnubbyPrerequisitesChecked_ =
1196e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    function(rc, opt_gnubby) {
1206e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (rc || this.timer_.expired()) {
1216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    // Do nothing:
1226e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    // If the timer is expired, the signerCompleted_ callback will indicate
1236e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    // timeout to the caller.
1246e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    // If there's an error, this gnubby is ineligible, but there's nothing we
1256e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    // can do about that here.
1266e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    return;
1276e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
1286e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  // If the callback succeeded, the gnubby is not null.
1296e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  var gnubby = /** @type {Gnubby} */ (opt_gnubby);
1306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  this.anyGnubbiesFound_ = true;
1316e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  this.waitingForTouchGnubbies_.push(gnubby);
1326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  this.matchEnrollVersionToGnubby_(gnubby);
1336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)};
1346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)/**
1365c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Attempts to match the gnubby's U2F version with an appropriate enroll
1375c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * challenge.
1386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {Gnubby} gnubby Gnubby instance
1395c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
1405c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
1416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)UsbEnrollHandler.prototype.matchEnrollVersionToGnubby_ = function(gnubby) {
1425c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (!gnubby) {
1435c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    console.warn(UTIL_fmt('no gnubby, WTF?'));
1446e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    return;
1455c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
1465c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  gnubby.version(this.gnubbyVersioned_.bind(this, gnubby));
1475c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
1485c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
1495c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
1505c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Called with the result of a version command.
1516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {Gnubby} gnubby Gnubby instance
1525c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {number} rc result of version command.
1535c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {ArrayBuffer=} data version.
1545c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
1555c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
1566e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)UsbEnrollHandler.prototype.gnubbyVersioned_ = function(gnubby, rc, data) {
1575c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (rc) {
1585c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.removeWrongVersionGnubby_(gnubby);
1595c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    return;
1605c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
1615c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var version = UTIL_BytesToString(new Uint8Array(data || null));
1625c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.tryEnroll_(gnubby, version);
1635c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
1645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
1655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
1665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Drops the gnubby from the list of eligible gnubbies.
1676e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {Gnubby} gnubby Gnubby instance
1685c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
1695c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
1706e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)UsbEnrollHandler.prototype.removeWaitingGnubby_ = function(gnubby) {
1715c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  gnubby.closeWhenIdle();
1725c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var index = this.waitingForTouchGnubbies_.indexOf(gnubby);
1735c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (index >= 0) {
1745c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.waitingForTouchGnubbies_.splice(index, 1);
1755c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
1765c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
1775c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
1785c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
1795c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Drops the gnubby from the list of eligible gnubbies, as it has the wrong
1805c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * version.
1816e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {Gnubby} gnubby Gnubby instance
1825c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
1835c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
1846e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)UsbEnrollHandler.prototype.removeWrongVersionGnubby_ = function(gnubby) {
1855c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.removeWaitingGnubby_(gnubby);
186116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (!this.waitingForTouchGnubbies_.length) {
187116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    // Whoops, this was the last gnubby.
188116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    this.anyGnubbiesFound_ = false;
189116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if (this.timer_.expired()) {
190116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      this.notifyError_(DeviceStatusCodes.TIMEOUT_STATUS);
1916e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    } else if (this.signer_) {
1926e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      this.signer_.reScanDevices();
193116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    }
1945c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
1955c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
1965c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
1975c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
1985c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Attempts enrolling a particular gnubby with a challenge of the appropriate
1995c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * version.
2006e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {Gnubby} gnubby Gnubby instance
201010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {string} version Protocol version
2025c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
2035c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
2046e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)UsbEnrollHandler.prototype.tryEnroll_ = function(gnubby, version) {
2055c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var challenge = this.getChallengeOfVersion_(version);
2065c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (!challenge) {
2075c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.removeWrongVersionGnubby_(gnubby);
2085c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    return;
2095c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
2101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  var challengeValue = B64_decode(challenge['challengeHash']);
2111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  var appIdHash = challenge['appIdHash'];
2121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  var individualAttest =
2131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      DEVICE_FACTORY_REGISTRY.getIndividualAttestation().
2141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci          requestIndividualAttestation(appIdHash);
2151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  gnubby.enroll(challengeValue, B64_decode(appIdHash),
2161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      this.enrollCallback_.bind(this, gnubby, version), individualAttest);
2175c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
2185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
2195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
2205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Finds the (first) challenge of the given version in this helper's challenges.
221010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {string} version Protocol version
2225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @return {Object} challenge, if found, or null if not.
2235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
2245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
2256e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)UsbEnrollHandler.prototype.getChallengeOfVersion_ = function(version) {
2265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  for (var i = 0; i < this.enrollChallenges.length; i++) {
2275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    if (this.enrollChallenges[i]['version'] == version) {
2285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      return this.enrollChallenges[i];
2295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    }
2305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
2315c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  return null;
2325c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
2335c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
2345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
2355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Called with the result of an enroll request to a gnubby.
2366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {Gnubby} gnubby Gnubby instance
237010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {string} version Protocol version
238010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {number} code Status code
239010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {ArrayBuffer=} infoArray Returned data
2405c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
2415c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
2426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)UsbEnrollHandler.prototype.enrollCallback_ =
2435c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    function(gnubby, version, code, infoArray) {
2445c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (this.notified_) {
2455c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    // Enroll completed after previous success or failure. Disregard.
2465c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    return;
2475c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
2485c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  switch (code) {
2496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    case -GnubbyDevice.GONE:
2505c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        // Close this gnubby.
2515c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        this.removeWaitingGnubby_(gnubby);
2525c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        if (!this.waitingForTouchGnubbies_.length) {
253116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          // Last enroll attempt is complete and last gnubby is gone.
254116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          this.anyGnubbiesFound_ = false;
255116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          if (this.timer_.expired()) {
256116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch            this.notifyError_(DeviceStatusCodes.TIMEOUT_STATUS);
2576e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)          } else if (this.signer_) {
2586e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)            this.signer_.reScanDevices();
259116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          }
2605c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        }
2615c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      break;
2625c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
2635c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    case DeviceStatusCodes.WAIT_TOUCH_STATUS:
2645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    case DeviceStatusCodes.BUSY_STATUS:
2655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    case DeviceStatusCodes.TIMEOUT_STATUS:
2665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      if (this.timer_.expired()) {
267116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        // Record that at least one gnubby timed out, to return a timeout status
268116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        // from the complete callback if no other eligible gnubbies are found.
269116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        /** @private {boolean} */
270116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        this.anyTimeout_ = true;
2715c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        // Close this gnubby.
2725c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        this.removeWaitingGnubby_(gnubby);
273116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        if (!this.waitingForTouchGnubbies_.length) {
2745c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu          // Last enroll attempt is complete: return this error.
2755c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu          console.log(UTIL_fmt('timeout (' + code.toString(16) +
2765c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu              ') enrolling'));
277116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch          this.notifyError_(DeviceStatusCodes.TIMEOUT_STATUS);
2785c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        }
2795c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      } else {
2806e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        DEVICE_FACTORY_REGISTRY.getCountdownFactory().createTimer(
2816e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)            UsbEnrollHandler.ENUMERATE_DELAY_INTERVAL_MILLIS,
282116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch            this.tryEnroll_.bind(this, gnubby, version));
2835c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      }
2845c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      break;
2855c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
2865c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    case DeviceStatusCodes.OK_STATUS:
2875c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      var info = B64_encode(new Uint8Array(infoArray || []));
2885c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      this.notifySuccess_(version, info);
2895c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      break;
2905c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
2915c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    default:
2925c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      console.log(UTIL_fmt('Failed to enroll gnubby: ' + code));
293116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      this.notifyError_(code);
2945c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      break;
2955c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
2965c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
2975c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
2985c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
299116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * How long to delay between repeated enroll attempts, in milliseconds.
300116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * @const
301116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch */
3026e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)UsbEnrollHandler.ENUMERATE_DELAY_INTERVAL_MILLIS = 200;
303116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
304116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch/**
305116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * Notifies the callback with an error code.
306116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * @param {number} code The error code to report.
3075c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
3085c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
3096e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)UsbEnrollHandler.prototype.notifyError_ = function(code) {
3105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (this.notified_ || this.closed_)
3115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    return;
3125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.notified_ = true;
3135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.close();
314116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  var reply = {
315116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    'type': 'enroll_helper_reply',
316116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    'code': code
317116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  };
318116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  this.cb_(reply);
3195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
3205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
3215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
322010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {string} version Protocol version
323010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {string} info B64 encoded success data
3245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
3255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
3266e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)UsbEnrollHandler.prototype.notifySuccess_ = function(version, info) {
3275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (this.notified_ || this.closed_)
3285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    return;
3295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.notified_ = true;
3305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.close();
331116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  var reply = {
332116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    'type': 'enroll_helper_reply',
333116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    'code': DeviceStatusCodes.OK_STATUS,
334116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    'version': version,
335116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    'enrollData': info
336116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  };
337116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  this.cb_(reply);
3385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
339