| Index: chrome/browser/resources/cryptotoken/signer.js
|
| diff --git a/chrome/browser/resources/cryptotoken/signer.js b/chrome/browser/resources/cryptotoken/signer.js
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..bd255598c1ea2326e933bf784bdf19209ab08794
|
| --- /dev/null
|
| +++ b/chrome/browser/resources/cryptotoken/signer.js
|
| @@ -0,0 +1,626 @@
|
| +// Copyright (c) 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +/**
|
| + * @fileoverview Handles web page requests for gnubby sign requests.
|
| + *
|
| + * @author juanlang@google.com (Juan Lang)
|
| + */
|
| +
|
| +'use strict';
|
| +
|
| +var signRequestQueue = new OriginKeyedRequestQueue();
|
| +
|
| +/**
|
| + * Handles a sign request.
|
| + * @param {!SignHelperFactory} factory Factory to create a sign helper.
|
| + * @param {MessageSender} sender The sender of the message.
|
| + * @param {Object} request The web page's sign request.
|
| + * @param {boolean} enforceAppIdValid Whether to enforce that the app id in the
|
| + * request matches the sender's origin.
|
| + * @param {Function} sendResponse Called back with the result of the sign.
|
| + * @param {boolean} toleratesMultipleResponses Whether the sendResponse
|
| + * callback can be called more than once, e.g. for progress updates.
|
| + * @return {Closeable}
|
| + */
|
| +function handleSignRequest(factory, sender, request, enforceAppIdValid,
|
| + sendResponse, toleratesMultipleResponses) {
|
| + var sentResponse = false;
|
| + function sendResponseOnce(r) {
|
| + if (queuedSignRequest) {
|
| + queuedSignRequest.close();
|
| + queuedSignRequest = null;
|
| + }
|
| + if (!sentResponse) {
|
| + sentResponse = true;
|
| + try {
|
| + // If the page has gone away or the connection has otherwise gone,
|
| + // sendResponse fails.
|
| + sendResponse(r);
|
| + } catch (exception) {
|
| + console.warn('sendResponse failed: ' + exception);
|
| + }
|
| + } else {
|
| + console.warn(UTIL_fmt('Tried to reply more than once! Juan, FIX ME'));
|
| + }
|
| + }
|
| +
|
| + function sendErrorResponse(code) {
|
| + var response = formatWebPageResponse(GnubbyMsgTypes.SIGN_WEB_REPLY, code);
|
| + sendResponseOnce(response);
|
| + }
|
| +
|
| + function sendSuccessResponse(challenge, info, browserData) {
|
| + var responseData = {};
|
| + for (var k in challenge) {
|
| + responseData[k] = challenge[k];
|
| + }
|
| + responseData['browserData'] = B64_encode(UTIL_StringToBytes(browserData));
|
| + responseData['signatureData'] = info;
|
| + var response = formatWebPageResponse(GnubbyMsgTypes.SIGN_WEB_REPLY,
|
| + GnubbyCodeTypes.OK, responseData);
|
| + sendResponseOnce(response);
|
| + }
|
| +
|
| + function sendNotification(code) {
|
| + console.log(UTIL_fmt('notification, code=' + code));
|
| + // Can the callback handle progress updates? If so, send one.
|
| + if (toleratesMultipleResponses) {
|
| + var response = formatWebPageResponse(
|
| + GnubbyMsgTypes.SIGN_WEB_NOTIFICATION, code);
|
| + if (request['requestId']) {
|
| + response['requestId'] = request['requestId'];
|
| + }
|
| + sendResponse(response);
|
| + }
|
| + }
|
| +
|
| + var origin = getOriginFromUrl(/** @type {string} */ (sender.url));
|
| + if (!origin) {
|
| + sendErrorResponse(GnubbyCodeTypes.BAD_REQUEST);
|
| + return null;
|
| + }
|
| + // More closure type inference fail.
|
| + var nonNullOrigin = /** @type {string} */ (origin);
|
| +
|
| + if (!isValidSignRequest(request)) {
|
| + sendErrorResponse(GnubbyCodeTypes.BAD_REQUEST);
|
| + return null;
|
| + }
|
| +
|
| + var signData = request['signData'];
|
| + // A valid sign data has at least one challenge, so get the first appId from
|
| + // the first challenge.
|
| + var firstAppId = signData[0]['appId'];
|
| + var timeoutMillis = Signer.DEFAULT_TIMEOUT_MILLIS;
|
| + if (request['timeout']) {
|
| + // Request timeout is in seconds.
|
| + timeoutMillis = request['timeout'] * 1000;
|
| + }
|
| + var timer = new CountdownTimer(timeoutMillis);
|
| + var logMsgUrl = request['logMsgUrl'];
|
| +
|
| + // Queue sign requests from the same origin, to protect against simultaneous
|
| + // sign-out on many tabs resulting in repeated sign-in requests.
|
| + var queuedSignRequest = new QueuedSignRequest(signData, factory, timer,
|
| + nonNullOrigin, enforceAppIdValid, sendErrorResponse, sendSuccessResponse,
|
| + sendNotification, sender.tlsChannelId, logMsgUrl);
|
| + var requestToken = signRequestQueue.queueRequest(firstAppId, nonNullOrigin,
|
| + queuedSignRequest.begin.bind(queuedSignRequest), timer);
|
| + queuedSignRequest.setToken(requestToken);
|
| + return queuedSignRequest;
|
| +}
|
| +
|
| +/**
|
| + * Returns whether the request appears to be a valid sign request.
|
| + * @param {Object} request the request.
|
| + * @return {boolean} whether the request appears valid.
|
| + */
|
| +function isValidSignRequest(request) {
|
| + if (!request.hasOwnProperty('signData'))
|
| + return false;
|
| + var signData = request['signData'];
|
| + // If a sign request contains an empty array of challenges, it could never
|
| + // be fulfilled. Fail.
|
| + if (!signData.length)
|
| + return false;
|
| + return isValidSignData(signData);
|
| +}
|
| +
|
| +/**
|
| + * Adapter class representing a queued sign request.
|
| + * @param {!SignData} signData
|
| + * @param {!SignHelperFactory} factory
|
| + * @param {Countdown} timer
|
| + * @param {string} origin
|
| + * @param {boolean} enforceAppIdValid
|
| + * @param {function(number)} errorCb
|
| + * @param {function(SignChallenge, string, string)} successCb
|
| + * @param {(function(number)|undefined)} opt_progressCb
|
| + * @param {string|undefined} opt_tlsChannelId
|
| + * @param {string|undefined} opt_logMsgUrl
|
| + * @constructor
|
| + * @implements {Closeable}
|
| + */
|
| +function QueuedSignRequest(signData, factory, timer, origin, enforceAppIdValid,
|
| + errorCb, successCb, opt_progressCb, opt_tlsChannelId, opt_logMsgUrl) {
|
| + /** @private {!SignData} */
|
| + this.signData_ = signData;
|
| + /** @private {!SignHelperFactory} */
|
| + this.factory_ = factory;
|
| + /** @private {Countdown} */
|
| + this.timer_ = timer;
|
| + /** @private {string} */
|
| + this.origin_ = origin;
|
| + /** @private {boolean} */
|
| + this.enforceAppIdValid_ = enforceAppIdValid;
|
| + /** @private {function(number)} */
|
| + this.errorCb_ = errorCb;
|
| + /** @private {function(SignChallenge, string, string)} */
|
| + this.successCb_ = successCb;
|
| + /** @private {(function(number)|undefined)} */
|
| + this.progressCb_ = opt_progressCb;
|
| + /** @private {string|undefined} */
|
| + this.tlsChannelId_ = opt_tlsChannelId;
|
| + /** @private {string|undefined} */
|
| + this.logMsgUrl_ = opt_logMsgUrl;
|
| + /** @private {boolean} */
|
| + this.begun_ = false;
|
| + /** @private {boolean} */
|
| + this.closed_ = false;
|
| +}
|
| +
|
| +/** Closes this sign request. */
|
| +QueuedSignRequest.prototype.close = function() {
|
| + if (this.closed_) return;
|
| + if (this.begun_ && this.signer_) {
|
| + this.signer_.close();
|
| + }
|
| + if (this.token_) {
|
| + this.token_.complete();
|
| + }
|
| + this.closed_ = true;
|
| +};
|
| +
|
| +/**
|
| + * @param {QueuedRequestToken} token Token for this sign request.
|
| + */
|
| +QueuedSignRequest.prototype.setToken = function(token) {
|
| + /** @private {QueuedRequestToken} */
|
| + this.token_ = token;
|
| +};
|
| +
|
| +/**
|
| + * Called when this sign request may begin work.
|
| + * @param {QueuedRequestToken} token Token for this sign request.
|
| + */
|
| +QueuedSignRequest.prototype.begin = function(token) {
|
| + this.begun_ = true;
|
| + this.setToken(token);
|
| + this.signer_ = new Signer(this.factory_, this.timer_, this.origin_,
|
| + this.enforceAppIdValid_, this.signerFailed_.bind(this),
|
| + this.signerSucceeded_.bind(this), this.progressCb_,
|
| + this.tlsChannelId_, this.logMsgUrl_);
|
| + if (!this.signer_.setChallenges(this.signData_)) {
|
| + token.complete();
|
| + this.errorCb_(GnubbyCodeTypes.BAD_REQUEST);
|
| + }
|
| +};
|
| +
|
| +/**
|
| + * Called when this request's signer fails.
|
| + * @param {number} code The failure code reported by the signer.
|
| + * @private
|
| + */
|
| +QueuedSignRequest.prototype.signerFailed_ = function(code) {
|
| + this.token_.complete();
|
| + this.errorCb_(code);
|
| +};
|
| +
|
| +/**
|
| + * Called when this request's signer succeeds.
|
| + * @param {SignChallenge} challenge The challenge that was signed.
|
| + * @param {string} info The sign result.
|
| + * @param {string} browserData
|
| + * @private
|
| + */
|
| +QueuedSignRequest.prototype.signerSucceeded_ =
|
| + function(challenge, info, browserData) {
|
| + this.token_.complete();
|
| + this.successCb_(challenge, info, browserData);
|
| +};
|
| +
|
| +/**
|
| + * Creates an object to track signing with a gnubby.
|
| + * @param {!SignHelperFactory} helperFactory Factory to create a sign helper.
|
| + * @param {Countdown} timer Timer for sign request.
|
| + * @param {string} origin The origin making the request.
|
| + * @param {boolean} enforceAppIdValid Whether to enforce that the appId in the
|
| + * request matches the sender's origin.
|
| + * @param {function(number)} errorCb Called when the sign operation fails.
|
| + * @param {function(SignChallenge, string, string)} successCb Called when the
|
| + * sign operation succeeds.
|
| + * @param {(function(number)|undefined)} opt_progressCb Called with progress
|
| + * updates to the sign request.
|
| + * @param {string=} opt_tlsChannelId the TLS channel ID, if any, of the origin
|
| + * making the request.
|
| + * @param {string=} opt_logMsgUrl The url to post log messages to.
|
| + * @constructor
|
| + */
|
| +function Signer(helperFactory, timer, origin, enforceAppIdValid,
|
| + errorCb, successCb, opt_progressCb, opt_tlsChannelId, opt_logMsgUrl) {
|
| + /** @private {Countdown} */
|
| + this.timer_ = timer;
|
| + /** @private {string} */
|
| + this.origin_ = origin;
|
| + /** @private {boolean} */
|
| + this.enforceAppIdValid_ = enforceAppIdValid;
|
| + /** @private {function(number)} */
|
| + this.errorCb_ = errorCb;
|
| + /** @private {function(SignChallenge, string, string)} */
|
| + this.successCb_ = successCb;
|
| + /** @private {(function(number)|undefined)} */
|
| + this.progressCb_ = opt_progressCb;
|
| + /** @private {string|undefined} */
|
| + this.tlsChannelId_ = opt_tlsChannelId;
|
| + /** @private {string|undefined} */
|
| + this.logMsgUrl_ = opt_logMsgUrl;
|
| +
|
| + /** @private {boolean} */
|
| + this.challengesSet_ = false;
|
| + /** @private {Array.<SignHelperChallenge>} */
|
| + this.pendingChallenges_ = [];
|
| + /** @private {boolean} */
|
| + this.done_ = false;
|
| +
|
| + /** @private {Object.<string, string>} */
|
| + this.browserData_ = {};
|
| + /** @private {Object.<string, SignChallenge>} */
|
| + this.serverChallenges_ = {};
|
| + // Allow http appIds for http origins. (Broken, but the caller deserves
|
| + // what they get.)
|
| + /** @private {boolean} */
|
| + this.allowHttp_ = this.origin_ ? this.origin_.indexOf('http://') == 0 : false;
|
| +
|
| + // Protect against helper failure with a watchdog.
|
| + this.createWatchdog_(timer);
|
| + /** @private {SignHelper} */
|
| + this.helper_ = helperFactory.createHelper(
|
| + timer, this.helperError_.bind(this), this.helperSuccess_.bind(this),
|
| + this.helperProgress_.bind(this), this.logMsgUrl_);
|
| +}
|
| +
|
| +/**
|
| + * Creates a timer with an expiry greater than the expiration time of the given
|
| + * timer.
|
| + * @param {Countdown} timer
|
| + * @private
|
| + */
|
| +Signer.prototype.createWatchdog_ = function(timer) {
|
| + var millis = timer.millisecondsUntilExpired();
|
| + millis += CountdownTimer.TIMER_INTERVAL_MILLIS;
|
| + /** @private {Countdown|undefined} */
|
| + this.watchdogTimer_ = new CountdownTimer(millis, this.timeout_.bind(this));
|
| +};
|
| +
|
| +/**
|
| + * Default timeout value in case the caller never provides a valid timeout.
|
| + */
|
| +Signer.DEFAULT_TIMEOUT_MILLIS = 30 * 1000;
|
| +
|
| +/**
|
| + * Sets the challenges to be signed.
|
| + * @param {SignData} signData The challenges to set.
|
| + * @return {boolean} Whether the challenges could be set.
|
| + */
|
| +Signer.prototype.setChallenges = function(signData) {
|
| + if (this.challengesSet_ || this.done_)
|
| + return false;
|
| + /** @private {SignData} */
|
| + this.signData_ = signData;
|
| + /** @private {boolean} */
|
| + this.challengesSet_ = true;
|
| +
|
| + // If app id enforcing isn't in effect, go ahead and start the helper with
|
| + // all of the incoming challenges.
|
| + var success = true;
|
| + if (!this.enforceAppIdValid_) {
|
| + success = this.addChallenges(signData, true /* finalChallenges */);
|
| + }
|
| +
|
| + this.checkAppIds_();
|
| + return success;
|
| +};
|
| +
|
| +/**
|
| + * Adds new challenges to the challenges being signed.
|
| + * @param {SignData} signData Challenges to add.
|
| + * @param {boolean} finalChallenges Whether these are the final challenges.
|
| + * @return {boolean} Whether the challenge could be added.
|
| + */
|
| +Signer.prototype.addChallenges = function(signData, finalChallenges) {
|
| + var newChallenges = this.encodeSignChallenges_(signData);
|
| + for (var i = 0; i < newChallenges.length; i++) {
|
| + this.pendingChallenges_.push(newChallenges[i]);
|
| + }
|
| + if (!finalChallenges) {
|
| + return true;
|
| + }
|
| + return this.helper_.doSign(this.pendingChallenges_);
|
| +};
|
| +
|
| +/**
|
| + * Creates challenges for helper from challenges.
|
| + * @param {Array.<SignChallenge>} challenges Challenges to add.
|
| + * @return {Array.<SignHelperChallenge>}
|
| + * @private
|
| + */
|
| +Signer.prototype.encodeSignChallenges_ = function(challenges) {
|
| + var newChallenges = [];
|
| + for (var i = 0; i < challenges.length; i++) {
|
| + var incomingChallenge = challenges[i];
|
| + var serverChallenge = incomingChallenge['challenge'];
|
| + var appId = incomingChallenge['appId'];
|
| + var encodedKeyHandle = incomingChallenge['keyHandle'];
|
| + var version = incomingChallenge['version'];
|
| +
|
| + var browserData =
|
| + makeSignBrowserData(serverChallenge, this.origin_, this.tlsChannelId_);
|
| + var encodedChallenge = makeChallenge(browserData, appId, encodedKeyHandle,
|
| + version);
|
| +
|
| + var key = encodedKeyHandle + encodedChallenge['challengeHash'];
|
| + this.browserData_[key] = browserData;
|
| + this.serverChallenges_[key] = incomingChallenge;
|
| +
|
| + newChallenges.push(encodedChallenge);
|
| + }
|
| + return newChallenges;
|
| +};
|
| +
|
| +/**
|
| + * Checks the app ids of incoming requests, and, when this signer is enforcing
|
| + * that app ids are valid, adds successful challenges to those being signed.
|
| + * @private
|
| + */
|
| +Signer.prototype.checkAppIds_ = function() {
|
| + // Check the incoming challenges' app ids.
|
| + /** @private {Array.<[string, Array.<Request>]>} */
|
| + this.orderedRequests_ = requestsByAppId(this.signData_);
|
| + if (!this.orderedRequests_.length) {
|
| + // Safety check: if the challenges are somehow empty, the helper will never
|
| + // be fed any data, so the request could never be satisfied. You lose.
|
| + this.notifyError_(GnubbyCodeTypes.BAD_REQUEST);
|
| + return;
|
| + }
|
| + /** @private {number} */
|
| + this.fetchedAppIds_ = 0;
|
| + /** @private {number} */
|
| + this.validAppIds_ = 0;
|
| + for (var i = 0, appIdRequestsPair; i < this.orderedRequests_.length; i++) {
|
| + var appIdRequestsPair = this.orderedRequests_[i];
|
| + var appId = appIdRequestsPair[0];
|
| + var requests = appIdRequestsPair[1];
|
| + if (appId == this.origin_) {
|
| + // Trivially allowed.
|
| + this.fetchedAppIds_++;
|
| + this.validAppIds_++;
|
| + // Only add challenges if in enforcing mode, i.e. they weren't added
|
| + // earlier.
|
| + if (this.enforceAppIdValid_) {
|
| + this.addChallenges(requests,
|
| + this.fetchedAppIds_ == this.orderedRequests_.length);
|
| + }
|
| + } else {
|
| + var start = new Date();
|
| + fetchAllowedOriginsForAppId(appId, this.allowHttp_,
|
| + this.fetchedAllowedOriginsForAppId_.bind(this, appId, start,
|
| + requests));
|
| + }
|
| + }
|
| +};
|
| +
|
| +/**
|
| + * Called with the result of an app id fetch.
|
| + * @param {string} appId the app id that was fetched.
|
| + * @param {Date} start the time the fetch request started.
|
| + * @param {Array.<SignChallenge>} challenges Challenges for this app id.
|
| + * @param {number} rc The HTTP response code for the app id fetch.
|
| + * @param {!Array.<string>} allowedOrigins The origins allowed for this app id.
|
| + * @private
|
| + */
|
| +Signer.prototype.fetchedAllowedOriginsForAppId_ = function(appId, start,
|
| + challenges, rc, allowedOrigins) {
|
| + var end = new Date();
|
| + logFetchAppIdResult(appId, end - start, allowedOrigins, this.logMsgUrl_);
|
| + if (rc != 200 && !(rc >= 400 && rc < 500)) {
|
| + if (this.timer_.expired()) {
|
| + // Act as though the helper timed out.
|
| + this.helperError_(DeviceStatusCodes.TIMEOUT_STATUS, false);
|
| + } else {
|
| + start = new Date();
|
| + fetchAllowedOriginsForAppId(appId, this.allowHttp_,
|
| + this.fetchedAllowedOriginsForAppId_.bind(this, appId, start,
|
| + challenges));
|
| + }
|
| + return;
|
| + }
|
| + this.fetchedAppIds_++;
|
| + var finalChallenges = (this.fetchedAppIds_ == this.orderedRequests_.length);
|
| + if (isValidAppIdForOrigin(appId, this.origin_, allowedOrigins)) {
|
| + this.validAppIds_++;
|
| + // Only add challenges if in enforcing mode, i.e. they weren't added
|
| + // earlier.
|
| + if (this.enforceAppIdValid_) {
|
| + this.addChallenges(challenges, finalChallenges);
|
| + }
|
| + } else {
|
| + logInvalidOriginForAppId(this.origin_, appId, this.logMsgUrl_);
|
| + // If in enforcing mode and this is the final request, sign the valid
|
| + // challenges.
|
| + if (this.enforceAppIdValid_ && finalChallenges) {
|
| + if (!this.helper_.doSign(this.pendingChallenges_)) {
|
| + this.notifyError_(GnubbyCodeTypes.BAD_REQUEST);
|
| + return;
|
| + }
|
| + }
|
| + }
|
| + if (this.enforceAppIdValid_ && finalChallenges && !this.validAppIds_) {
|
| + // If all app ids are invalid, notify the caller, otherwise implicitly
|
| + // allow the helper to report whether any of the valid challenges succeeded.
|
| + this.notifyError_(GnubbyCodeTypes.BAD_APP_ID);
|
| + }
|
| +};
|
| +
|
| +/**
|
| + * Called when the timeout expires on this signer.
|
| + * @private
|
| + */
|
| +Signer.prototype.timeout_ = function() {
|
| + this.watchdogTimer_ = undefined;
|
| + // The web page gets grumpy if it doesn't get WAIT_TOUCH within a reasonable
|
| + // time.
|
| + this.notifyError_(GnubbyCodeTypes.WAIT_TOUCH);
|
| +};
|
| +
|
| +/** Closes this signer. */
|
| +Signer.prototype.close = function() {
|
| + if (this.helper_) this.helper_.close();
|
| +};
|
| +
|
| +/**
|
| + * Notifies the caller of error with the given error code.
|
| + * @param {number} code
|
| + * @private
|
| + */
|
| +Signer.prototype.notifyError_ = function(code) {
|
| + if (this.done_)
|
| + return;
|
| + this.close();
|
| + this.done_ = true;
|
| + this.errorCb_(code);
|
| +};
|
| +
|
| +/**
|
| + * Notifies the caller of success.
|
| + * @param {SignChallenge} challenge The challenge that was signed.
|
| + * @param {string} info The sign result.
|
| + * @param {string} browserData
|
| + * @private
|
| + */
|
| +Signer.prototype.notifySuccess_ = function(challenge, info, browserData) {
|
| + if (this.done_)
|
| + return;
|
| + this.close();
|
| + this.done_ = true;
|
| + this.successCb_(challenge, info, browserData);
|
| +};
|
| +
|
| +/**
|
| + * Notifies the caller of progress with the error code.
|
| + * @param {number} code
|
| + * @private
|
| + */
|
| +Signer.prototype.notifyProgress_ = function(code) {
|
| + if (this.done_)
|
| + return;
|
| + if (code != this.lastProgressUpdate_) {
|
| + this.lastProgressUpdate_ = code;
|
| + // If there is no progress callback, treat it like an error and clean up.
|
| + if (this.progressCb_) {
|
| + this.progressCb_(code);
|
| + } else {
|
| + this.notifyError_(code);
|
| + }
|
| + }
|
| +};
|
| +
|
| +/**
|
| + * Maps a sign helper's error code namespace to the page's error code namespace.
|
| + * @param {number} code Error code from DeviceStatusCodes namespace.
|
| + * @param {boolean} anyGnubbies Whether any gnubbies were found.
|
| + * @return {number} A GnubbyCodeTypes error code.
|
| + * @private
|
| + */
|
| +Signer.mapError_ = function(code, anyGnubbies) {
|
| + var reportedError;
|
| + switch (code) {
|
| + case DeviceStatusCodes.WRONG_DATA_STATUS:
|
| + reportedError = anyGnubbies ? GnubbyCodeTypes.NONE_PLUGGED_ENROLLED :
|
| + GnubbyCodeTypes.NO_GNUBBIES;
|
| + break;
|
| +
|
| + case DeviceStatusCodes.OK_STATUS:
|
| + // If the error callback is called with OK, it means the signature was
|
| + // empty, which we treat the same as...
|
| + case DeviceStatusCodes.WAIT_TOUCH_STATUS:
|
| + reportedError = GnubbyCodeTypes.WAIT_TOUCH;
|
| + break;
|
| +
|
| + case DeviceStatusCodes.BUSY_STATUS:
|
| + reportedError = GnubbyCodeTypes.BUSY;
|
| + break;
|
| +
|
| + default:
|
| + reportedError = GnubbyCodeTypes.UNKNOWN_ERROR;
|
| + break;
|
| + }
|
| + return reportedError;
|
| +};
|
| +
|
| +/**
|
| + * Called by the helper upon error.
|
| + * @param {number} code
|
| + * @param {boolean} anyGnubbies
|
| + * @private
|
| + */
|
| +Signer.prototype.helperError_ = function(code, anyGnubbies) {
|
| + this.clearTimeout_();
|
| + var reportedError = Signer.mapError_(code, anyGnubbies);
|
| + console.log(UTIL_fmt('helper reported ' + code.toString(16) +
|
| + ', returning ' + reportedError));
|
| + this.notifyError_(reportedError);
|
| +};
|
| +
|
| +/**
|
| + * Called by helper upon success.
|
| + * @param {SignHelperChallenge} challenge The challenge that was signed.
|
| + * @param {string} info The sign result.
|
| + * @private
|
| + */
|
| +Signer.prototype.helperSuccess_ = function(challenge, info) {
|
| + // Got a good reply, kill timer.
|
| + this.clearTimeout_();
|
| +
|
| + var key = challenge['keyHandle'] + challenge['challengeHash'];
|
| + var browserData = this.browserData_[key];
|
| + // Notify with server-provided challenge, not the encoded one: the
|
| + // server-provided challenge contains additional fields it relies on.
|
| + var serverChallenge = this.serverChallenges_[key];
|
| + this.notifySuccess_(serverChallenge, info, browserData);
|
| +};
|
| +
|
| +/**
|
| + * Called by helper to notify progress.
|
| + * @param {number} code
|
| + * @param {boolean} anyGnubbies
|
| + * @private
|
| + */
|
| +Signer.prototype.helperProgress_ = function(code, anyGnubbies) {
|
| + var reportedError = Signer.mapError_(code, anyGnubbies);
|
| + console.log(UTIL_fmt('helper notified ' + code.toString(16) +
|
| + ', returning ' + reportedError));
|
| + this.notifyProgress_(reportedError);
|
| +};
|
| +
|
| +/**
|
| + * Clears the timeout for this signer.
|
| + * @private
|
| + */
|
| +Signer.prototype.clearTimeout_ = function() {
|
| + if (this.watchdogTimer_) {
|
| + this.watchdogTimer_.clearTimeout();
|
| + this.watchdogTimer_ = undefined;
|
| + }
|
| +};
|
|
|