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 A single gnubby signer wraps the process of opening a gnubby, 75c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * signing each challenge in an array of challenges until a success condition 85c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * is satisfied, and finally yielding the gnubby upon success. 96e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * 105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu'use strict'; 135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 15116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * @typedef {{ 16116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * code: number, 176e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * gnubby: (Gnubby|undefined), 18116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * challenge: (SignHelperChallenge|undefined), 19116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * info: (ArrayBuffer|undefined) 20116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * }} 21116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch */ 22116680a4aac90f2aa7413d9095a592090648e557Ben Murdochvar SingleSignerResult; 23116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 24116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch/** 255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Creates a new sign handler with a gnubby. This handler will perform a sign 265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * operation using each challenge in an array of challenges until its success 275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * condition is satisified, or an error or timeout occurs. The success condition 285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * is defined differently depending whether this signer is used for enrolling 295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * or for signing: 305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * 315c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * For enroll, success is defined as each challenge yielding wrong data. This 325c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * means this gnubby is not currently enrolled for any of the appIds in any 335c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * challenge. 345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * 35116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * For sign, success is defined as any challenge yielding ok. 365c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * 376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * The complete callback is called only when the signer reaches success or 386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * failure, i.e. when there is no need for this signer to continue trying new 396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * challenges. 405c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * 416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {GnubbyDeviceId} gnubbyId Which gnubby to open. 425c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {boolean} forEnroll Whether this signer is signing for an attempted 435c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * enroll operation. 44116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * @param {function(SingleSignerResult)} 45116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * completeCb Called when this signer completes, i.e. no further results are 46116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * possible. 47116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * @param {Countdown} timer An advisory timer, beyond whose expiration the 485c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * signer will not attempt any new operations, assuming the caller is no 495c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * longer interested in the outcome. 505c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {string=} opt_logMsgUrl A URL to post log messages to. 515c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @constructor 525c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 536e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)function SingleGnubbySigner(gnubbyId, forEnroll, completeCb, timer, 54116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch opt_logMsgUrl) { 556e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) /** @private {GnubbyDeviceId} */ 56116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch this.gnubbyId_ = gnubbyId; 575c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {SingleGnubbySigner.State} */ 585c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.state_ = SingleGnubbySigner.State.INIT; 595c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {boolean} */ 605c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.forEnroll_ = forEnroll; 61116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch /** @private {function(SingleSignerResult)} */ 62116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch this.completeCb_ = completeCb; 63116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch /** @private {Countdown} */ 64116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch this.timer_ = timer; 655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {string|undefined} */ 665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.logMsgUrl_ = opt_logMsgUrl; 675c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 685c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {!Array.<!SignHelperChallenge>} */ 695c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.challenges_ = []; 705c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {number} */ 715c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.challengeIndex_ = 0; 725c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** @private {boolean} */ 73116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch this.challengesSet_ = false; 745c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci /** @private {!Object.<string, number>} */ 761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.cachedError_ = []; 775c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu} 785c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 795c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** @enum {number} */ 805c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuSingleGnubbySigner.State = { 815c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** Initial state. */ 825c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu INIT: 0, 835c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** The signer is attempting to open a gnubby. */ 845c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu OPENING: 1, 855c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** The signer's gnubby opened, but is busy. */ 865c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu BUSY: 2, 875c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** The signer has an open gnubby, but no challenges to sign. */ 885c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu IDLE: 3, 895c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** The signer is currently signing a challenge. */ 905c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu SIGNING: 4, 91116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch /** The signer got a final outcome. */ 92116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch COMPLETE: 5, 935c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** The signer is closing its gnubby. */ 94116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch CLOSING: 6, 955c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu /** The signer is closed. */ 96116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch CLOSED: 7 97116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}; 98116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 99116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch/** 1006e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @return {GnubbyDeviceId} This device id of the gnubby for this signer. 101116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch */ 102116680a4aac90f2aa7413d9095a592090648e557Ben MurdochSingleGnubbySigner.prototype.getDeviceId = function() { 103116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return this.gnubbyId_; 1045c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 1055c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 1065c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 1075c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Attempts to open this signer's gnubby, if it's not already open. 1085c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * (This is implicitly done by addChallenges.) 1095c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 1105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuSingleGnubbySigner.prototype.open = function() { 1115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (this.state_ == SingleGnubbySigner.State.INIT) { 1125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.state_ = SingleGnubbySigner.State.OPENING; 1136e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) DEVICE_FACTORY_REGISTRY.getGnubbyFactory().openGnubby( 1146e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) this.gnubbyId_, 1156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) this.forEnroll_, 1166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) this.openCallback_.bind(this), 1176e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) this.logMsgUrl_); 1185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 1195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 1205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 1215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 1225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Closes this signer's gnubby, if it's held. 1235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 1245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuSingleGnubbySigner.prototype.close = function() { 1255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (!this.gnubby_) return; 1265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.state_ = SingleGnubbySigner.State.CLOSING; 1275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.gnubby_.closeWhenIdle(this.closed_.bind(this)); 1285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 1295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 1305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 1315c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Called when this signer's gnubby is closed. 1325c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private 1335c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 1345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuSingleGnubbySigner.prototype.closed_ = function() { 1355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.gnubby_ = null; 1365c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.state_ = SingleGnubbySigner.State.CLOSED; 1375c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 1385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 1395c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 140116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * Begins signing the given challenges. 141116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * @param {Array.<SignHelperChallenge>} challenges The challenges to sign. 1425c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @return {boolean} Whether the challenges were accepted. 1435c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 144116680a4aac90f2aa7413d9095a592090648e557Ben MurdochSingleGnubbySigner.prototype.doSign = function(challenges) { 145116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (this.challengesSet_) { 146116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // Can't add new challenges once they've been set. 1475c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu return false; 1485c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 1495c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 1505c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (challenges) { 1515c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu console.log(this.gnubby_); 1525c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu console.log(UTIL_fmt('adding ' + challenges.length + ' challenges')); 1535c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu for (var i = 0; i < challenges.length; i++) { 1545c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.challenges_.push(challenges[i]); 1555c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 1565c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 157116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch this.challengesSet_ = true; 1585c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 1595c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu switch (this.state_) { 1605c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu case SingleGnubbySigner.State.INIT: 1615c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.open(); 1625c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu break; 1635c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu case SingleGnubbySigner.State.OPENING: 164116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // The open has already commenced, so accept the challenges, but don't do 165116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // anything. 1665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu break; 1675c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu case SingleGnubbySigner.State.IDLE: 1685c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (this.challengeIndex_ < challenges.length) { 169116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // Challenges set: start signing. 1705c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.doSign_(this.challengeIndex_); 171116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } else { 172116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // An empty list of challenges can be set during enroll, when the user 173116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // has no existing enrolled gnubbies. It's unexpected during sign, but 174116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // returning WRONG_DATA satisfies the caller in either case. 1755c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var self = this; 1765c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu window.setTimeout(function() { 1775c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu self.goToError_(DeviceStatusCodes.WRONG_DATA_STATUS); 1785c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu }, 0); 1795c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 1805c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu break; 1815c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu case SingleGnubbySigner.State.SIGNING: 1825c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // Already signing, so don't kick off a new sign, but accept the added 1835c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // challenges. 1845c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu break; 1855c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu default: 1865c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu return false; 1875c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 1885c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu return true; 1895c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 1905c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 1915c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 1925c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * How long to delay retrying a failed open. 1935c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 1945c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuSingleGnubbySigner.OPEN_DELAY_MILLIS = 200; 1955c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 1965c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 197116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * How long to delay retrying a sign requiring touch. 198116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch */ 199116680a4aac90f2aa7413d9095a592090648e557Ben MurdochSingleGnubbySigner.SIGN_DELAY_MILLIS = 200; 200116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch 201116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch/** 2025c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {number} rc The result of the open operation. 2036e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) * @param {Gnubby=} gnubby The opened gnubby, if open was successful (or busy). 2045c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private 2055c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 2065c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuSingleGnubbySigner.prototype.openCallback_ = function(rc, gnubby) { 2075c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (this.state_ != SingleGnubbySigner.State.OPENING && 2085c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.state_ != SingleGnubbySigner.State.BUSY) { 2095c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // Open completed after close, perhaps? Ignore. 2105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu return; 2115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 2125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 2135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu switch (rc) { 2145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu case DeviceStatusCodes.OK_STATUS: 2155c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (!gnubby) { 2165c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu console.warn(UTIL_fmt('open succeeded but gnubby is null, WTF?')); 2175c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } else { 2185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.gnubby_ = gnubby; 2195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.gnubby_.version(this.versionCallback_.bind(this)); 2205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 2215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu break; 2225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu case DeviceStatusCodes.BUSY_STATUS: 2235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.gnubby_ = gnubby; 2245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.state_ = SingleGnubbySigner.State.BUSY; 2255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // If there's still time, retry the open. 2265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (!this.timer_ || !this.timer_.expired()) { 2275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var self = this; 2285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu window.setTimeout(function() { 2295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (self.gnubby_) { 2306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) DEVICE_FACTORY_REGISTRY.getGnubbyFactory().openGnubby( 2316e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) self.gnubbyId_, 2326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) self.forEnroll_, 2336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) self.openCallback_.bind(self), 2346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) self.logMsgUrl_); 2355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 2365c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu }, SingleGnubbySigner.OPEN_DELAY_MILLIS); 2375c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } else { 2385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.goToError_(DeviceStatusCodes.BUSY_STATUS); 2395c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 2405c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu break; 2415c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu default: 242010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) // TODO: This won't be confused with success, but should it be 2435c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // part of the same namespace as the other error codes, which are 2445c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // always in DeviceStatusCodes.*? 2451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.goToError_(rc, true); 2465c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 2475c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 2485c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 2495c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 2505c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Called with the result of a version command. 2515c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {number} rc Result of version command. 2525c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {ArrayBuffer=} opt_data Version. 2535c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private 2545c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 2555c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuSingleGnubbySigner.prototype.versionCallback_ = function(rc, opt_data) { 2565c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (rc) { 2571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.goToError_(rc, true); 2585c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu return; 2595c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 2605c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.state_ = SingleGnubbySigner.State.IDLE; 2615c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.version_ = UTIL_BytesToString(new Uint8Array(opt_data || [])); 2625c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.doSign_(this.challengeIndex_); 2635c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 2645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 2655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 266010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {number} challengeIndex Index of challenge to sign 2675c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private 2685c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 2695c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuSingleGnubbySigner.prototype.doSign_ = function(challengeIndex) { 270116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (!this.gnubby_) { 271116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // Already closed? Nothing to do. 272116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return; 273116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } 2745c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (this.timer_ && this.timer_.expired()) { 275116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // If the timer is expired, that means we never got a success response. 276116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // We could have gotten wrong data on a partial set of challenges, but this 277116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // means we don't yet know the final outcome. In any event, we don't yet 278116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // know the final outcome: return timeout. 279116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch this.goToError_(DeviceStatusCodes.TIMEOUT_STATUS); 280116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch return; 281116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch } 282116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch if (!this.challengesSet_) { 283116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch this.state_ = SingleGnubbySigner.State.IDLE; 2845c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu return; 2855c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 2865c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 2875c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.state_ = SingleGnubbySigner.State.SIGNING; 2885c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 2895c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (challengeIndex >= this.challenges_.length) { 2905c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.signCallback_(challengeIndex, DeviceStatusCodes.WRONG_DATA_STATUS); 2915c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu return; 2925c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 2935c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 2945c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var challenge = this.challenges_[challengeIndex]; 2955c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var challengeHash = challenge.challengeHash; 2965c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var appIdHash = challenge.appIdHash; 2975c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var keyHandle = challenge.keyHandle; 2981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (this.cachedError_.hasOwnProperty(keyHandle)) { 2995c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // Cache hit: return wrong data again. 3001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.signCallback_(challengeIndex, this.cachedError_[keyHandle]); 3015c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } else if (challenge.version && challenge.version != this.version_) { 3025c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // Sign challenge for a different version of gnubby: return wrong data. 3035c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.signCallback_(challengeIndex, DeviceStatusCodes.WRONG_DATA_STATUS); 3045c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } else { 305116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch var nowink = false; 3065c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.gnubby_.sign(challengeHash, appIdHash, keyHandle, 3075c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.signCallback_.bind(this, challengeIndex), 308010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) nowink); 3095c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 3105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 3115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 3125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 3135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Called with the result of a single sign operation. 3145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {number} challengeIndex the index of the challenge just attempted 3155c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {number} code the result of the sign operation 316010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {ArrayBuffer=} opt_info Optional result data 3175c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private 3185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 3195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuSingleGnubbySigner.prototype.signCallback_ = 3205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu function(challengeIndex, code, opt_info) { 321116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch console.log(UTIL_fmt('gnubby ' + JSON.stringify(this.gnubbyId_) + 3225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu ', challenge ' + challengeIndex + ' yielded ' + code.toString(16))); 3235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (this.state_ != SingleGnubbySigner.State.SIGNING) { 3245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu console.log(UTIL_fmt('already done!')); 3255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // We're done, the caller's no longer interested. 3265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu return; 3275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 3285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 3291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // Cache wrong data or wrong length results, re-asking the gnubby to sign it 3301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci // won't produce different results. 3311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (code == DeviceStatusCodes.WRONG_DATA_STATUS || 3321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci code == DeviceStatusCodes.WRONG_LENGTH_STATUS) { 3335c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (challengeIndex < this.challenges_.length) { 3345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu var challenge = this.challenges_[challengeIndex]; 3351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci if (!this.cachedError_.hasOwnProperty(challenge.keyHandle)) { 3361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.cachedError_[challenge.keyHandle] = code; 3375c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 3385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 3395c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 3405c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 341116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch var self = this; 3425c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu switch (code) { 3435c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu case DeviceStatusCodes.GONE_STATUS: 3445c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.goToError_(code); 3455c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu break; 3465c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 3475c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu case DeviceStatusCodes.TIMEOUT_STATUS: 348010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) // TODO: On a TIMEOUT_STATUS, sync first, then retry. 3495c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu case DeviceStatusCodes.BUSY_STATUS: 3505c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.doSign_(this.challengeIndex_); 3515c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu break; 3525c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 3535c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu case DeviceStatusCodes.OK_STATUS: 3545c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (this.forEnroll_) { 3555c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.goToError_(code); 3565c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } else { 3575c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.goToSuccess_(code, this.challenges_[challengeIndex], opt_info); 3585c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 3595c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu break; 3605c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 3615c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu case DeviceStatusCodes.WAIT_TOUCH_STATUS: 362116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch window.setTimeout(function() { 363116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch self.doSign_(self.challengeIndex_); 364116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch }, SingleGnubbySigner.SIGN_DELAY_MILLIS); 3655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu break; 3665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 3675c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu case DeviceStatusCodes.WRONG_DATA_STATUS: 3681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci case DeviceStatusCodes.WRONG_LENGTH_STATUS: 3695c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (this.challengeIndex_ < this.challenges_.length - 1) { 3705c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.doSign_(++this.challengeIndex_); 3715c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } else if (this.forEnroll_) { 372116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch this.goToSuccess_(code); 3735c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } else { 3745c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.goToError_(code); 3755c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 3765c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu break; 3775c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 3785c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu default: 3795c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (this.forEnroll_) { 3801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.goToError_(code, true); 3815c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } else if (this.challengeIndex_ < this.challenges_.length - 1) { 3825c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.doSign_(++this.challengeIndex_); 3835c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } else { 3841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci this.goToError_(code, true); 3855c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 3865c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 3875c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 3885c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 3895c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 3905c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Switches to the error state, and notifies caller. 391010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {number} code Error code 3921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci * @param {boolean=} opt_warn Whether to warn in the console about the error. 3935c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private 3945c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 3951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciSingleGnubbySigner.prototype.goToError_ = function(code, opt_warn) { 396116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch this.state_ = SingleGnubbySigner.State.COMPLETE; 3971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci var logFn = opt_warn ? console.warn.bind(console) : console.log.bind(console); 3981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci logFn(UTIL_fmt('failed (' + code.toString(16) + ')')); 3995c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // Since this gnubby can no longer produce a useful result, go ahead and 4005c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu // close it. 4015c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.close(); 402116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch var result = { code: code }; 403116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch this.completeCb_(result); 4045c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 4055c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu 4065c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** 4075c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Switches to the success state, and notifies caller. 408010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {number} code Status code 409010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {SignHelperChallenge=} opt_challenge The challenge signed 410010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {ArrayBuffer=} opt_info Optional result data 4115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private 4125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */ 4135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuSingleGnubbySigner.prototype.goToSuccess_ = 4145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu function(code, opt_challenge, opt_info) { 415116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch this.state_ = SingleGnubbySigner.State.COMPLETE; 4165c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu console.log(UTIL_fmt('success (' + code.toString(16) + ')')); 417116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch var result = { code: code, gnubby: this.gnubby_ }; 4185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (opt_challenge || opt_info) { 4195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (opt_challenge) { 420116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch result['challenge'] = opt_challenge; 4215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 4225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu if (opt_info) { 423116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch result['info'] = opt_info; 4245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 4255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu } 426116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch this.completeCb_(result); 427116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch // this.gnubby_ is now owned by completeCb_. 4285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu this.gnubby_ = null; 4295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}; 430