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 a sign helper using USB gnubbies. 75c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 85c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu'use strict'; 95c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liuvar CORRUPT_sign = false; 115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 13010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {!GnubbyFactory} factory Factory for gnubby instances 145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {Countdown} timer Timer after whose expiration the caller is no longer 155c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * interested in the result of a sign request. 165c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {function(number, boolean)} errorCb Called when a sign request fails 175c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * with an error code and whether any gnubbies were found. 1846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) * @param {function(SignHelperChallenge, string, string=)} successCb Called with 1946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) * the signature produced by a successful sign request. 205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {string=} opt_logMsgUrl A URL to post log messages to. 215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @constructor 225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @implements {SignHelper} 235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 2446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)function UsbSignHelper(factory, timer, errorCb, successCb, opt_logMsgUrl) { 255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {!GnubbyFactory} */ 265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.factory_ = factory; 275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {Countdown} */ 285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.timer_ = timer; 295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {function(number, boolean)} */ 305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.errorCb_ = errorCb; 3146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) /** @private {function(SignHelperChallenge, string, string=)} */ 325c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.successCb_ = successCb; 335c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {string|undefined} */ 345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.logMsgUrl_ = opt_logMsgUrl; 355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 365c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {Array.<SignHelperChallenge>} */ 375c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.pendingChallenges_ = []; 385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {Array.<usbGnubby>} */ 395c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.waitingForTouchGnubbies_ = []; 405c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 415c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {boolean} */ 425c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.notified_ = false; 435c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {boolean} */ 445c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.signerComplete_ = false; 455c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu} 465c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 475c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 485c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Attempts to sign the provided challenges. 49010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {Array.<SignHelperChallenge>} challenges Challenges to sign 505c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @return {boolean} whether this set of challenges was accepted. 515c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 525c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbSignHelper.prototype.doSign = function(challenges) { 535c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (!challenges.length) { 545c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // Fail a sign request with an empty set of challenges, and pretend to have 555c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // alerted the caller in case the enumerate is still pending. 565c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.notified_ = true; 575c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu return false; 585c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } else { 595c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.pendingChallenges_ = challenges; 605c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.getSomeGnubbies_(); 615c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu return true; 625c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 635c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Enumerates gnubbies, and begins processing challenges upon enumeration if 675c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * any gnubbies are found. 685c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private 695c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 705c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbSignHelper.prototype.getSomeGnubbies_ = function() { 715c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.factory_.enumerate(this.enumerateCallback.bind(this)); 725c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 735c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 745c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 755c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Called with the result of enumerating gnubbies. 765c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {number} rc the result of the enumerate. 77010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {Array.<llGnubbyDeviceId>} indexes Indexes of found gnubbies 785c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 795c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbSignHelper.prototype.enumerateCallback = function(rc, indexes) { 805c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (rc) { 815c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.notifyError_(rc, false); 825c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu return; 835c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 845c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (!indexes.length) { 855c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.notifyError_(DeviceStatusCodes.WRONG_DATA_STATUS, false); 865c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu return; 875c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 885c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (this.timer_.expired()) { 895c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.notifyError_(DeviceStatusCodes.TIMEOUT_STATUS, true); 905c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu return; 915c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 925c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.gotSomeGnubbies_(indexes); 935c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 945c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 955c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 965c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Called with the result of enumerating gnubby indexes. 97010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {Array.<llGnubbyDeviceId>} indexes Indexes of found gnubbies 985c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private 995c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 1005c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbSignHelper.prototype.gotSomeGnubbies_ = function(indexes) { 1015c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {MultipleGnubbySigner} */ 1025c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.signer_ = new MultipleGnubbySigner( 1035c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.factory_, 1045c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu indexes, 1055c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu false /* forEnroll */, 1065c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.signerCompleted_.bind(this), 1075c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.signerFoundGnubby_.bind(this), 1085c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.timer_, 1095c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.logMsgUrl_); 1105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.signer_.addEncodedChallenges(this.pendingChallenges_, true); 1115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 1125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 1135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 1145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Called when a MultipleGnubbySigner completes its sign request. 1155c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {boolean} anySucceeded whether any sign attempt completed 1165c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * successfully. 1175c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {number=} errorCode an error code from a failing gnubby, if one was 1185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * found. 1195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private 1205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 1215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbSignHelper.prototype.signerCompleted_ = function(anySucceeded, errorCode) { 1225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.signerComplete_ = true; 1235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // The signer is not created unless some gnubbies were enumerated, so 1245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // anyGnubbies is mostly always true. The exception is when the last gnubby is 1255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // removed, handled shortly. 1265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var anyGnubbies = true; 1275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (!anySucceeded) { 1285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (!errorCode) { 1295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu errorCode = DeviceStatusCodes.WRONG_DATA_STATUS; 1305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } else if (errorCode == -llGnubby.GONE) { 1315c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // If the last gnubby was removed, report as though no gnubbies were 1325c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // found. 1335c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu errorCode = DeviceStatusCodes.WRONG_DATA_STATUS; 1345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu anyGnubbies = false; 1355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 1365c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.notifyError_(errorCode, anyGnubbies); 1375c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } else if (this.anyTimeout_) { 1385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // Some previously succeeding gnubby timed out: return its error code. 1395c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.notifyError_(this.timeoutError_, anyGnubbies); 1405c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } else { 1415c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // Do nothing: signerFoundGnubby_ will have been called with each 1425c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // succeeding gnubby. 1435c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 1445c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 1455c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 1465c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 1475c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Called when a MultipleGnubbySigner finds a gnubby that has successfully 1485c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * signed, or can successfully sign, one of the challenges. 149010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {number} code Status code 150010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {MultipleSignerResult} signResult Signer result object 1515c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private 1525c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 1535c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbSignHelper.prototype.signerFoundGnubby_ = function(code, signResult) { 1545c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var gnubby = signResult['gnubby']; 1555c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var challenge = signResult['challenge']; 1565c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var info = new Uint8Array(signResult['info']); 1575c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (code == DeviceStatusCodes.OK_STATUS && info.length > 0 && info[0]) { 1585c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.notifySuccess_(gnubby, challenge, info); 1595c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } else { 1605c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.waitingForTouchGnubbies_.push(gnubby); 1615c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.retrySignIfNotTimedOut_(gnubby, challenge, code); 1625c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 1635c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 1645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 1655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 1665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Reports the result of a successful sign operation. 167010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {usbGnubby} gnubby Gnubby instance 168010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {SignHelperChallenge} challenge Challenge signed 169010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {Uint8Array} info Result data 1705c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private 1715c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 1725c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbSignHelper.prototype.notifySuccess_ = function(gnubby, challenge, info) { 1735c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (this.notified_) 1745c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu return; 1755c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.notified_ = true; 1765c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 1775c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu gnubby.closeWhenIdle(); 1785c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.close(); 1795c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 1805c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (CORRUPT_sign) { 1815c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu CORRUPT_sign = false; 1825c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu info[info.length - 1] = info[info.length - 1] ^ 0xff; 1835c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 1845c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var encodedChallenge = {}; 1855c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu encodedChallenge['challengeHash'] = B64_encode(challenge['challengeHash']); 1865c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu encodedChallenge['appIdHash'] = B64_encode(challenge['appIdHash']); 1875c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu encodedChallenge['keyHandle'] = B64_encode(challenge['keyHandle']); 1885c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.successCb_( 18946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) /** @type {SignHelperChallenge} */ (encodedChallenge), B64_encode(info), 19046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) 'USB'); 1915c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 1925c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 1935c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 1945c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Reports error to the caller. 1955c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {number} code error to report 196010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {boolean} anyGnubbies If any gnubbies were found 1975c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private 1985c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 1995c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbSignHelper.prototype.notifyError_ = function(code, anyGnubbies) { 2005c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (this.notified_) 2015c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu return; 2025c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.notified_ = true; 2035c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.close(); 2045c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.errorCb_(code, anyGnubbies); 2055c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 2065c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 2075c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 2085c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Retries signing a particular challenge on a gnubby. 209010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {usbGnubby} gnubby Gnubby instance 210010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {SignHelperChallenge} challenge Challenge to retry 2115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private 2125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 2135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbSignHelper.prototype.retrySign_ = function(gnubby, challenge) { 2145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var challengeHash = challenge['challengeHash']; 2155c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var appIdHash = challenge['appIdHash']; 2165c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var keyHandle = challenge['keyHandle']; 2175c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu gnubby.sign(challengeHash, appIdHash, keyHandle, 2185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.signCallback_.bind(this, gnubby, challenge)); 2195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 2205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 2215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 2225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Called when a gnubby completes a sign request. 223010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {usbGnubby} gnubby Gnubby instance 224010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {SignHelperChallenge} challenge Challenge to retry 225010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {number} code Previous status code 2265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private 2275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 2285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbSignHelper.prototype.retrySignIfNotTimedOut_ = 2295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu function(gnubby, challenge, code) { 2305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (this.timer_.expired()) { 2315c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // Store any timeout error code, to be returned from the complete 2325c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // callback if no other eligible gnubbies are found. 2335c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {boolean} */ 2345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.anyTimeout_ = true; 2355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {number} */ 2365c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.timeoutError_ = code; 2375c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.removePreviouslyEligibleGnubby_(gnubby, code); 2385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } else { 2395c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu window.setTimeout(this.retrySign_.bind(this, gnubby, challenge), 200); 2405c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 2415c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 2425c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 2435c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 2445c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Removes a gnubby that was waiting for touch from the list, with the given 2455c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * error code. If this is the last gnubby, notifies the caller of the error. 246010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {usbGnubby} gnubby Gnubby instance 247010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {number} code Previous status code 2485c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private 2495c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 2505c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbSignHelper.prototype.removePreviouslyEligibleGnubby_ = 2515c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu function(gnubby, code) { 2525c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // Close this gnubby. 2535c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu gnubby.closeWhenIdle(); 2545c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var index = this.waitingForTouchGnubbies_.indexOf(gnubby); 2555c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (index >= 0) { 2565c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.waitingForTouchGnubbies_.splice(index, 1); 2575c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 2585c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (!this.waitingForTouchGnubbies_.length && this.signerComplete_ && 2595c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu !this.notified_) { 2605c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // Last sign attempt is complete: return this error. 2615c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu console.log(UTIL_fmt('timeout or error (' + code.toString(16) + 2625c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu ') signing')); 2635c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // If the last device is gone, report as if no gnubbies were found. 2645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (code == -llGnubby.GONE) { 2655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.notifyError_(DeviceStatusCodes.WRONG_DATA_STATUS, false); 2665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu return; 2675c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 2685c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.notifyError_(code, true); 2695c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 2705c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 2715c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 2725c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 2735c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Called when a gnubby completes a sign request. 274010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {usbGnubby} gnubby Gnubby instance 275010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {SignHelperChallenge} challenge Challenge signed 276010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {number} code Status code 277010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {ArrayBuffer=} infoArray Result data 2785c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private 2795c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 2805c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbSignHelper.prototype.signCallback_ = 2815c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu function(gnubby, challenge, code, infoArray) { 2825c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (this.notified_) { 2835c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // Individual sign completed after previous success or failure. Disregard. 2845c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu return; 2855c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 2865c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var info = new Uint8Array(infoArray || []); 2875c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (code == DeviceStatusCodes.OK_STATUS && info.length > 0 && info[0]) { 2885c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.notifySuccess_(gnubby, challenge, info); 2895c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } else if (code == DeviceStatusCodes.OK_STATUS || 2905c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu code == DeviceStatusCodes.WAIT_TOUCH_STATUS || 2915c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu code == DeviceStatusCodes.BUSY_STATUS) { 2925c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.retrySignIfNotTimedOut_(gnubby, challenge, code); 2935c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } else { 2945c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu console.log(UTIL_fmt('got error ' + code.toString(16) + ' signing')); 2955c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.removePreviouslyEligibleGnubby_(gnubby, code); 2965c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 2975c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 2985c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 2995c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 3005c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Closes the MultipleGnubbySigner, if any. 3015c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 3025c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbSignHelper.prototype.close = function() { 3035c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (this.signer_) { 3045c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.signer_.close(); 3055c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.signer_ = null; 3065c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 3075c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu for (var i = 0; i < this.waitingForTouchGnubbies_.length; i++) { 3085c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.waitingForTouchGnubbies_[i].closeWhenIdle(); 3095c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 3105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.waitingForTouchGnubbies_ = []; 3115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 3125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 3135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 3145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {!GnubbyFactory} gnubbyFactory Factory to create gnubbies. 3155c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @constructor 3165c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @implements {SignHelperFactory} 3175c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 3185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liufunction UsbSignHelperFactory(gnubbyFactory) { 3195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {!GnubbyFactory} */ 3205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.gnubbyFactory_ = gnubbyFactory; 3215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu} 3225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 3235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 3245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {Countdown} timer Timer after whose expiration the caller is no longer 3255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * interested in the result of a sign request. 3265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {function(number, boolean)} errorCb Called when a sign request fails 3275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * with an error code and whether any gnubbies were found. 3285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {function(SignHelperChallenge, string)} successCb Called with the 3295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * signature produced by a successful sign request. 3305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {string=} opt_logMsgUrl A URL to post log messages to. 3315c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @return {UsbSignHelper} the newly created helper. 3325c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 3335c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuUsbSignHelperFactory.prototype.createHelper = 33446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) function(timer, errorCb, successCb, opt_logMsgUrl) { 3355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var helper = 3365c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu new UsbSignHelper(this.gnubbyFactory_, timer, errorCb, successCb, 33746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles) opt_logMsgUrl); 3385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu return helper; 3395c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 340