signer.js revision 116680a4aac90f2aa7413d9095a592090648e557
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 Handles web page requests for gnubby sign requests.
75c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
85c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
95c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu'use strict';
105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liuvar signRequestQueue = new OriginKeyedRequestQueue();
125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Handles a sign request.
155c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {!SignHelperFactory} factory Factory to create a sign helper.
165c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {MessageSender} sender The sender of the message.
175c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {Object} request The web page's sign request.
185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {Function} sendResponse Called back with the result of the sign.
19010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @return {Closeable} Request handler that should be closed when the browser
20010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) *     message channel is closed.
215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
22116680a4aac90f2aa7413d9095a592090648e557Ben Murdochfunction handleSignRequest(factory, sender, request, sendResponse) {
235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var sentResponse = false;
245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  function sendResponseOnce(r) {
255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    if (queuedSignRequest) {
265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      queuedSignRequest.close();
275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      queuedSignRequest = null;
285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    }
295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    if (!sentResponse) {
305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      sentResponse = true;
315c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      try {
325c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        // If the page has gone away or the connection has otherwise gone,
335c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        // sendResponse fails.
345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        sendResponse(r);
355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      } catch (exception) {
365c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        console.warn('sendResponse failed: ' + exception);
375c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      }
385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    } else {
395c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      console.warn(UTIL_fmt('Tried to reply more than once! Juan, FIX ME'));
405c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    }
415c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
425c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
435c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  function sendErrorResponse(code) {
445c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    var response = formatWebPageResponse(GnubbyMsgTypes.SIGN_WEB_REPLY, code);
455c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    sendResponseOnce(response);
465c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
475c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
485c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  function sendSuccessResponse(challenge, info, browserData) {
495c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    var responseData = {};
505c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    for (var k in challenge) {
515c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      responseData[k] = challenge[k];
525c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    }
535c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    responseData['browserData'] = B64_encode(UTIL_StringToBytes(browserData));
545c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    responseData['signatureData'] = info;
555c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    var response = formatWebPageResponse(GnubbyMsgTypes.SIGN_WEB_REPLY,
565c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        GnubbyCodeTypes.OK, responseData);
575c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    sendResponseOnce(response);
585c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
595c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
605c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var origin = getOriginFromUrl(/** @type {string} */ (sender.url));
615c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (!origin) {
625c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    sendErrorResponse(GnubbyCodeTypes.BAD_REQUEST);
635c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    return null;
645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // More closure type inference fail.
665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var nonNullOrigin = /** @type {string} */ (origin);
675c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
685c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (!isValidSignRequest(request)) {
695c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    sendErrorResponse(GnubbyCodeTypes.BAD_REQUEST);
705c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    return null;
715c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
725c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
735c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var signData = request['signData'];
745c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // A valid sign data has at least one challenge, so get the first appId from
755c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // the first challenge.
765c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var firstAppId = signData[0]['appId'];
775c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var timeoutMillis = Signer.DEFAULT_TIMEOUT_MILLIS;
785c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (request['timeout']) {
795c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    // Request timeout is in seconds.
805c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    timeoutMillis = request['timeout'] * 1000;
815c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
825c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var timer = new CountdownTimer(timeoutMillis);
835c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var logMsgUrl = request['logMsgUrl'];
845c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
855c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // Queue sign requests from the same origin, to protect against simultaneous
865c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // sign-out on many tabs resulting in repeated sign-in requests.
875c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var queuedSignRequest = new QueuedSignRequest(signData, factory, timer,
8846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      nonNullOrigin, sendErrorResponse, sendSuccessResponse,
8946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      sender.tlsChannelId, logMsgUrl);
905c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var requestToken = signRequestQueue.queueRequest(firstAppId, nonNullOrigin,
915c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      queuedSignRequest.begin.bind(queuedSignRequest), timer);
925c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  queuedSignRequest.setToken(requestToken);
935c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  return queuedSignRequest;
945c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
955c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
965c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
975c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Returns whether the request appears to be a valid sign request.
985c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {Object} request the request.
995c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @return {boolean} whether the request appears valid.
1005c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
1015c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liufunction isValidSignRequest(request) {
1025c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (!request.hasOwnProperty('signData'))
1035c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    return false;
1045c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var signData = request['signData'];
1055c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // If a sign request contains an empty array of challenges, it could never
1065c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // be fulfilled. Fail.
1075c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (!signData.length)
1085c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    return false;
1095c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  return isValidSignData(signData);
1105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
1115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
1125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
1135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Adapter class representing a queued sign request.
114010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {!SignData} signData Signature data
115010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {!SignHelperFactory} factory Factory for SignHelper instances
116010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {Countdown} timer Timeout timer
117010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {string} origin Signature origin
118010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {function(number)} errorCb Error callback
119010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {function(SignChallenge, string, string)} successCb Success callback
120010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {string|undefined} opt_tlsChannelId TLS Channel Id
121010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {string|undefined} opt_logMsgUrl Url to post log messages to
1225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @constructor
1235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @implements {Closeable}
1245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
12546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)function QueuedSignRequest(signData, factory, timer, origin, errorCb, successCb,
12646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    opt_tlsChannelId, opt_logMsgUrl) {
1275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {!SignData} */
1285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.signData_ = signData;
1295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {!SignHelperFactory} */
1305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.factory_ = factory;
1315c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {Countdown} */
1325c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.timer_ = timer;
1335c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {string} */
1345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.origin_ = origin;
1355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {function(number)} */
1365c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.errorCb_ = errorCb;
1375c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {function(SignChallenge, string, string)} */
1385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.successCb_ = successCb;
1395c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {string|undefined} */
1405c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.tlsChannelId_ = opt_tlsChannelId;
1415c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {string|undefined} */
1425c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.logMsgUrl_ = opt_logMsgUrl;
1435c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {boolean} */
1445c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.begun_ = false;
1455c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {boolean} */
1465c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.closed_ = false;
1475c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
1485c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
1495c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** Closes this sign request. */
1505c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuQueuedSignRequest.prototype.close = function() {
1515c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (this.closed_) return;
1525c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (this.begun_ && this.signer_) {
1535c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.signer_.close();
1545c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
1555c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (this.token_) {
1565c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.token_.complete();
1575c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
1585c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.closed_ = true;
1595c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
1605c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
1615c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
1625c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {QueuedRequestToken} token Token for this sign request.
1635c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
1645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuQueuedSignRequest.prototype.setToken = function(token) {
1655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {QueuedRequestToken} */
1665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.token_ = token;
1675c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
1685c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
1695c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
1705c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Called when this sign request may begin work.
1715c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {QueuedRequestToken} token Token for this sign request.
1725c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
1735c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuQueuedSignRequest.prototype.begin = function(token) {
1745c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.begun_ = true;
1755c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.setToken(token);
1765c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.signer_ = new Signer(this.factory_, this.timer_, this.origin_,
17746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      this.signerFailed_.bind(this), this.signerSucceeded_.bind(this),
1785c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      this.tlsChannelId_, this.logMsgUrl_);
1795c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (!this.signer_.setChallenges(this.signData_)) {
1805c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    token.complete();
1815c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.errorCb_(GnubbyCodeTypes.BAD_REQUEST);
1825c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
1835c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
1845c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
1855c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
1865c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Called when this request's signer fails.
1875c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {number} code The failure code reported by the signer.
1885c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
1895c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
1905c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuQueuedSignRequest.prototype.signerFailed_ = function(code) {
1915c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.token_.complete();
1925c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.errorCb_(code);
1935c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
1945c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
1955c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
1965c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Called when this request's signer succeeds.
1975c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {SignChallenge} challenge The challenge that was signed.
1985c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {string} info The sign result.
199010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {string} browserData Browser data JSON
2005c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
2015c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
2025c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuQueuedSignRequest.prototype.signerSucceeded_ =
2035c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    function(challenge, info, browserData) {
2045c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.token_.complete();
2055c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.successCb_(challenge, info, browserData);
2065c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
2075c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
2085c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
2095c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Creates an object to track signing with a gnubby.
2105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {!SignHelperFactory} helperFactory Factory to create a sign helper.
2115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {Countdown} timer Timer for sign request.
2125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {string} origin The origin making the request.
2135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {function(number)} errorCb Called when the sign operation fails.
2145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {function(SignChallenge, string, string)} successCb Called when the
2155c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu *     sign operation succeeds.
2165c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {string=} opt_tlsChannelId the TLS channel ID, if any, of the origin
2175c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu *     making the request.
2185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {string=} opt_logMsgUrl The url to post log messages to.
2195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @constructor
2205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
22146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)function Signer(helperFactory, timer, origin, errorCb, successCb,
22246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    opt_tlsChannelId, opt_logMsgUrl) {
2235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {Countdown} */
2245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.timer_ = timer;
2255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {string} */
2265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.origin_ = origin;
2275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {function(number)} */
2285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.errorCb_ = errorCb;
2295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {function(SignChallenge, string, string)} */
2305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.successCb_ = successCb;
2315c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {string|undefined} */
2325c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.tlsChannelId_ = opt_tlsChannelId;
2335c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {string|undefined} */
2345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.logMsgUrl_ = opt_logMsgUrl;
2355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
2365c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {boolean} */
2375c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.challengesSet_ = false;
2385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {boolean} */
2395c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.done_ = false;
2405c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
2415c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {Object.<string, string>} */
2425c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.browserData_ = {};
2435c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {Object.<string, SignChallenge>} */
2445c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.serverChallenges_ = {};
2455c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // Allow http appIds for http origins. (Broken, but the caller deserves
2465c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // what they get.)
2475c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {boolean} */
2485c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.allowHttp_ = this.origin_ ? this.origin_.indexOf('http://') == 0 : false;
2495c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
2505c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // Protect against helper failure with a watchdog.
2515c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.createWatchdog_(timer);
2525c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {SignHelper} */
253116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  this.helper_ = helperFactory.createHelper();
2545c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
2555c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
2565c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
2575c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Creates a timer with an expiry greater than the expiration time of the given
2585c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * timer.
259010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {Countdown} timer Timeout timer
2605c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
2615c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
2625c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuSigner.prototype.createWatchdog_ = function(timer) {
2635c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var millis = timer.millisecondsUntilExpired();
2645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  millis += CountdownTimer.TIMER_INTERVAL_MILLIS;
2655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {Countdown|undefined} */
2665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.watchdogTimer_ = new CountdownTimer(millis, this.timeout_.bind(this));
2675c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
2685c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
2695c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
2705c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Default timeout value in case the caller never provides a valid timeout.
2715c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
2725c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuSigner.DEFAULT_TIMEOUT_MILLIS = 30 * 1000;
2735c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
2745c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
2755c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Sets the challenges to be signed.
2765c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {SignData} signData The challenges to set.
2775c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @return {boolean} Whether the challenges could be set.
2785c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
2795c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuSigner.prototype.setChallenges = function(signData) {
2805c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (this.challengesSet_ || this.done_)
2815c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    return false;
2825c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {SignData} */
2835c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.signData_ = signData;
2845c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  /** @private {boolean} */
2855c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.challengesSet_ = true;
2865c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
2875c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.checkAppIds_();
28846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  return true;
2895c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
2905c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
2915c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
292116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * Checks the app ids of incoming requests.
293116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * @private
2945c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
295116680a4aac90f2aa7413d9095a592090648e557Ben MurdochSigner.prototype.checkAppIds_ = function() {
296116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  var appIds = getDistinctAppIds(this.signData_);
297116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (!appIds || !appIds.length) {
298116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    this.notifyError_(GnubbyCodeTypes.BAD_REQUEST);
299116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return;
3005c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
301116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  /** @private {!AppIdChecker} */
302116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  this.appIdChecker_ = new AppIdChecker(this.timer_.clone(), this.origin_,
303116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      /** @type {!Array.<string>} */ (appIds), this.allowHttp_,
304116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      this.logMsgUrl_);
305116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  this.appIdChecker_.doCheck(this.appIdChecked_.bind(this));
306116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch};
307116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
308116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch/**
309116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * Called with the result of checking app ids.  When the app ids are valid,
310116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * adds the sign challenges to those being signed.
311116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * @param {boolean} result Whether the app ids are valid.
312116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * @private
313116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch */
314116680a4aac90f2aa7413d9095a592090648e557Ben MurdochSigner.prototype.appIdChecked_ = function(result) {
315116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (!result) {
316116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    this.notifyError_(GnubbyCodeTypes.BAD_APP_ID);
317116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return;
318116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }
319116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (!this.doSign_()) {
320116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    this.notifyError_(GnubbyCodeTypes.BAD_REQUEST);
321116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return;
3225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
3235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
3245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
3255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
326116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * Begins signing this signer's challenges.
327116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * @return {boolean} Whether the challenge could be added.
3285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
3295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
330116680a4aac90f2aa7413d9095a592090648e557Ben MurdochSigner.prototype.doSign_ = function() {
331116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  var encodedChallenges = [];
332116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  for (var i = 0; i < this.signData_.length; i++) {
333116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    var incomingChallenge = this.signData_[i];
3345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    var serverChallenge = incomingChallenge['challenge'];
3355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    var appId = incomingChallenge['appId'];
3365c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    var encodedKeyHandle = incomingChallenge['keyHandle'];
3375c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    var version = incomingChallenge['version'];
3385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
3395c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    var browserData =
3405c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        makeSignBrowserData(serverChallenge, this.origin_, this.tlsChannelId_);
3415c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    var encodedChallenge = makeChallenge(browserData, appId, encodedKeyHandle,
3425c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        version);
3435c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
3445c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    var key = encodedKeyHandle + encodedChallenge['challengeHash'];
3455c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.browserData_[key] = browserData;
3465c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.serverChallenges_[key] = incomingChallenge;
3475c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
348116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    encodedChallenges.push(encodedChallenge);
3495c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
350116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  var timeoutSeconds = this.timer_.millisecondsUntilExpired() / 1000.0;
351116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  var request = makeSignHelperRequest(encodedChallenges, timeoutSeconds,
352116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      this.logMsgUrl_);
353116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return this.helper_.doSign(request, this.helperComplete_.bind(this));
3545c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
3555c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
3565c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
3575c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Called when the timeout expires on this signer.
3585c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
3595c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
3605c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuSigner.prototype.timeout_ = function() {
3615c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.watchdogTimer_ = undefined;
3625c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // The web page gets grumpy if it doesn't get WAIT_TOUCH within a reasonable
3635c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // time.
3645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.notifyError_(GnubbyCodeTypes.WAIT_TOUCH);
3655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
3665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
3675c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/** Closes this signer. */
3685c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuSigner.prototype.close = function() {
369116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (this.appIdChecker_) {
370116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    this.appIdChecker_.close();
371116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }
372116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (this.helper_) {
373116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    this.helper_.close();
374116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }
3755c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
3765c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
3775c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
3785c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Notifies the caller of error with the given error code.
379010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {number} code Error code
3805c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
3815c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
3825c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuSigner.prototype.notifyError_ = function(code) {
3835c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (this.done_)
3845c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    return;
3855c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.close();
3865c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.done_ = true;
3875c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.errorCb_(code);
3885c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
3895c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
3905c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
3915c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Notifies the caller of success.
3925c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {SignChallenge} challenge The challenge that was signed.
3935c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {string} info The sign result.
394010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) * @param {string} browserData Browser data JSON
3955c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
3965c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
3975c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuSigner.prototype.notifySuccess_ = function(challenge, info, browserData) {
3985c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (this.done_)
3995c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    return;
4005c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.close();
4015c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.done_ = true;
4025c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.successCb_(challenge, info, browserData);
4035c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
4045c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
4055c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
4065c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Maps a sign helper's error code namespace to the page's error code namespace.
4075c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @param {number} code Error code from DeviceStatusCodes namespace.
4085c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @return {number} A GnubbyCodeTypes error code.
4095c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
4105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
411116680a4aac90f2aa7413d9095a592090648e557Ben MurdochSigner.mapError_ = function(code) {
4125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  var reportedError;
4135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  switch (code) {
4145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    case DeviceStatusCodes.WRONG_DATA_STATUS:
415116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      reportedError = GnubbyCodeTypes.NONE_PLUGGED_ENROLLED;
4165c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      break;
4175c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
418116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    case DeviceStatusCodes.TIMEOUT_STATUS:
4195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    case DeviceStatusCodes.WAIT_TOUCH_STATUS:
4205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      reportedError = GnubbyCodeTypes.WAIT_TOUCH;
4215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      break;
4225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
4235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    default:
4245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      reportedError = GnubbyCodeTypes.UNKNOWN_ERROR;
4255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      break;
4265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
4275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  return reportedError;
4285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
4295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
4305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
431116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * Called by the helper upon completion.
432116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * @param {SignHelperReply} reply The result of the sign request.
433116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch * @param {string=} opt_source The source of the sign result.
4345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
4355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
436116680a4aac90f2aa7413d9095a592090648e557Ben MurdochSigner.prototype.helperComplete_ = function(reply, opt_source) {
4375c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  this.clearTimeout_();
4385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
439116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (reply.code) {
440116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    var reportedError = Signer.mapError_(reply.code);
441116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    console.log(UTIL_fmt('helper reported ' + reply.code.toString(16) +
442116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        ', returning ' + reportedError));
443116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    this.notifyError_(reportedError);
444116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  } else {
445116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if (this.logMsgUrl_ && opt_source) {
446116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      var logMsg = 'signed&source=' + opt_source;
447116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      logMessage(logMsg, this.logMsgUrl_);
448116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    }
4495c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
450116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    var key =
451116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        reply.responseData['keyHandle'] + reply.responseData['challengeHash'];
452116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    var browserData = this.browserData_[key];
453116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    // Notify with server-provided challenge, not the encoded one: the
454116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    // server-provided challenge contains additional fields it relies on.
455116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    var serverChallenge = this.serverChallenges_[key];
456116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    this.notifySuccess_(serverChallenge, reply.responseData.signatureData,
457116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        browserData);
45846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  }
4595c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
4605c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
4615c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu/**
4625c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * Clears the timeout for this signer.
4635c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu * @private
4645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu */
4655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuSigner.prototype.clearTimeout_ = function() {
4665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (this.watchdogTimer_) {
4675c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.watchdogTimer_.clearTimeout();
4685c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    this.watchdogTimer_ = undefined;
4695c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
4705c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu};
471