| Index: chrome/browser/resources/cryptotoken/multiplesigner.js
|
| diff --git a/chrome/browser/resources/cryptotoken/multiplesigner.js b/chrome/browser/resources/cryptotoken/multiplesigner.js
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..86d5e1a8d95e623fad4a23667fcc631ec2551b42
|
| --- /dev/null
|
| +++ b/chrome/browser/resources/cryptotoken/multiplesigner.js
|
| @@ -0,0 +1,276 @@
|
| +// 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 A multiple gnubby signer wraps the process of opening a number
|
| + * of gnubbies, signing each challenge in an array of challenges until a
|
| + * success condition is satisfied, and yielding each succeeding gnubby.
|
| + *
|
| + * @author juanlang@google.com (Juan Lang)
|
| + */
|
| +'use strict';
|
| +
|
| +/**
|
| + * Creates a new sign handler with an array of gnubby indexes.
|
| + * @param {!GnubbyFactory} factory Used to create and open the gnubbies.
|
| + * @param {Array.<llGnubbyDeviceId>} gnubbyIndexes Which gnubbies to open.
|
| + * @param {boolean} forEnroll Whether this signer is signing for an attempted
|
| + * enroll operation.
|
| + * @param {function(boolean, (number|undefined))} completedCb Called when this
|
| + * signer completes sign attempts, i.e. no further results should be
|
| + * expected.
|
| + * @param {function(number, MultipleSignerResult)} gnubbyFoundCb Called with
|
| + * each gnubby/challenge that yields a successful result.
|
| + * @param {Countdown=} opt_timer An advisory timer, beyond whose expiration the
|
| + * signer will not attempt any new operations, assuming the caller is no
|
| + * longer interested in the outcome.
|
| + * @param {string=} opt_logMsgUrl A URL to post log messages to.
|
| + * @constructor
|
| + */
|
| +function MultipleGnubbySigner(factory, gnubbyIndexes, forEnroll, completedCb,
|
| + gnubbyFoundCb, opt_timer, opt_logMsgUrl) {
|
| + /** @private {!GnubbyFactory} */
|
| + this.factory_ = factory;
|
| + /** @private {Array.<llGnubbyDeviceId>} */
|
| + this.gnubbyIndexes_ = gnubbyIndexes;
|
| + /** @private {boolean} */
|
| + this.forEnroll_ = forEnroll;
|
| + /** @private {function(boolean, (number|undefined))} */
|
| + this.completedCb_ = completedCb;
|
| + /** @private {function(number, MultipleSignerResult)} */
|
| + this.gnubbyFoundCb_ = gnubbyFoundCb;
|
| + /** @private {Countdown|undefined} */
|
| + this.timer_ = opt_timer;
|
| + /** @private {string|undefined} */
|
| + this.logMsgUrl_ = opt_logMsgUrl;
|
| +
|
| + /** @private {Array.<SignHelperChallenge>} */
|
| + this.challenges_ = [];
|
| + /** @private {boolean} */
|
| + this.challengesFinal_ = false;
|
| +
|
| + // Create a signer for each gnubby.
|
| + /** @private {boolean} */
|
| + this.anySucceeded_ = false;
|
| + /** @private {number} */
|
| + this.numComplete_ = 0;
|
| + /** @private {Array.<SingleGnubbySigner>} */
|
| + this.signers_ = [];
|
| + /** @private {Array.<boolean>} */
|
| + this.stillGoing_ = [];
|
| + /** @private {Array.<number>} */
|
| + this.errorStatus_ = [];
|
| + for (var i = 0; i < gnubbyIndexes.length; i++) {
|
| + this.addGnubby(gnubbyIndexes[i]);
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * Attempts to open this signer's gnubbies, if they're not already open.
|
| + * (This is implicitly done by addChallenges.)
|
| + */
|
| +MultipleGnubbySigner.prototype.open = function() {
|
| + for (var i = 0; i < this.signers_.length; i++) {
|
| + this.signers_[i].open();
|
| + }
|
| +};
|
| +
|
| +/**
|
| + * Closes this signer's gnubbies, if any are open.
|
| + */
|
| +MultipleGnubbySigner.prototype.close = function() {
|
| + for (var i = 0; i < this.signers_.length; i++) {
|
| + this.signers_[i].close();
|
| + }
|
| +};
|
| +
|
| +/**
|
| + * Adds challenges to the set of challenges being tried by this signer.
|
| + * The challenges are an array of challenge objects, where each challenge
|
| + * object's values are base64-encoded.
|
| + * If the signer is currently idle, begins signing the new challenges.
|
| + *
|
| + * @param {Array} challenges
|
| + * @param {boolean} finalChallenges
|
| + * @return {boolean} whether the challenges were successfully added.
|
| + * @public
|
| + */
|
| +MultipleGnubbySigner.prototype.addEncodedChallenges =
|
| + function(challenges, finalChallenges) {
|
| + var decodedChallenges = [];
|
| + if (challenges) {
|
| + for (var i = 0; i < challenges.length; i++) {
|
| + var decodedChallenge = {};
|
| + var challenge = challenges[i];
|
| + decodedChallenge['challengeHash'] =
|
| + B64_decode(challenge['challengeHash']);
|
| + decodedChallenge['appIdHash'] = B64_decode(challenge['appIdHash']);
|
| + decodedChallenge['keyHandle'] = B64_decode(challenge['keyHandle']);
|
| + if (challenge['version']) {
|
| + decodedChallenge['version'] = challenge['version'];
|
| + }
|
| + decodedChallenges.push(decodedChallenge);
|
| + }
|
| + }
|
| + return this.addChallenges(decodedChallenges, finalChallenges);
|
| +};
|
| +
|
| +/**
|
| + * Adds challenges to the set of challenges being tried by this signer.
|
| + * If the signer is currently idle, begins signing the new challenges.
|
| + *
|
| + * @param {Array.<SignHelperChallenge>} challenges
|
| + * @param {boolean} finalChallenges
|
| + * @return {boolean} whether the challenges were successfully added.
|
| + * @public
|
| + */
|
| +MultipleGnubbySigner.prototype.addChallenges =
|
| + function(challenges, finalChallenges) {
|
| + if (this.challengesFinal_) {
|
| + // Can't add new challenges once they're finalized.
|
| + return false;
|
| + }
|
| +
|
| + if (challenges) {
|
| + for (var i = 0; i < challenges.length; i++) {
|
| + this.challenges_.push(challenges[i]);
|
| + }
|
| + }
|
| + this.challengesFinal_ = finalChallenges;
|
| +
|
| + for (var i = 0; i < this.signers_.length; i++) {
|
| + this.stillGoing_[i] =
|
| + this.signers_[i].addChallenges(challenges, finalChallenges);
|
| + this.errorStatus_[i] = 0;
|
| + }
|
| + return true;
|
| +};
|
| +
|
| +/**
|
| + * Adds a new gnubby to this signer's list of gnubbies. (Only possible while
|
| + * this signer is still signing: without this restriction, the morePossible
|
| + * indication in the callbacks could become violated.) If this signer has
|
| + * challenges to sign, begins signing on the new gnubby with them.
|
| + * @param {llGnubbyDeviceId} gnubbyIndex The index of the gnubby to add.
|
| + * @return {boolean} Whether the gnubby was added successfully.
|
| + * @public
|
| + */
|
| +MultipleGnubbySigner.prototype.addGnubby = function(gnubbyIndex) {
|
| + if (this.numComplete_ && this.numComplete_ == this.signers_.length)
|
| + return false;
|
| +
|
| + var index = this.signers_.length;
|
| + this.signers_.push(
|
| + new SingleGnubbySigner(
|
| + this.factory_,
|
| + gnubbyIndex,
|
| + this.forEnroll_,
|
| + this.signFailedCallback_.bind(this, index),
|
| + this.signSucceededCallback_.bind(this, index),
|
| + this.timer_ ? this.timer_.clone() : null,
|
| + this.logMsgUrl_));
|
| + this.stillGoing_.push(false);
|
| +
|
| + if (this.challenges_.length) {
|
| + this.stillGoing_[index] =
|
| + this.signers_[index].addChallenges(this.challenges_,
|
| + this.challengesFinal_);
|
| + }
|
| + return true;
|
| +};
|
| +
|
| +/**
|
| + * Called by a SingleGnubbySigner upon failure, i.e. unsuccessful completion of
|
| + * all its sign operations.
|
| + * @param {number} index the index of the gnubby whose result this is
|
| + * @param {number} code the result code of the sign operation
|
| + * @private
|
| + */
|
| +MultipleGnubbySigner.prototype.signFailedCallback_ = function(index, code) {
|
| + console.log(
|
| + UTIL_fmt('failure. gnubby ' + index + ' got code ' + code.toString(16)));
|
| + if (!this.stillGoing_[index]) {
|
| + console.log(UTIL_fmt('gnubby ' + index + ' no longer running!'));
|
| + // Shouldn't ever happen? Disregard.
|
| + return;
|
| + }
|
| + this.stillGoing_[index] = false;
|
| + this.errorStatus_[index] = code;
|
| + this.numComplete_++;
|
| + var morePossible = this.numComplete_ < this.signers_.length;
|
| + if (!morePossible)
|
| + this.notifyComplete_();
|
| +};
|
| +
|
| +/**
|
| + * Called by a SingleGnubbySigner upon success.
|
| + * @param {number} index the index of the gnubby whose result this is
|
| + * @param {usbGnubby} gnubby the underlying gnubby that succeded.
|
| + * @param {number} code the result code of the sign operation
|
| + * @param {SingleSignerResult=} signResult
|
| + * @private
|
| + */
|
| +MultipleGnubbySigner.prototype.signSucceededCallback_ =
|
| + function(index, gnubby, code, signResult) {
|
| + console.log(UTIL_fmt('success! gnubby ' + index + ' got code ' +
|
| + code.toString(16)));
|
| + if (!this.stillGoing_[index]) {
|
| + console.log(UTIL_fmt('gnubby ' + index + ' no longer running!'));
|
| + // Shouldn't ever happen? Disregard.
|
| + return;
|
| + }
|
| + this.anySucceeded_ = true;
|
| + this.stillGoing_[index] = false;
|
| + this.notifySuccess_(code, gnubby, index, signResult);
|
| + this.numComplete_++;
|
| + var morePossible = this.numComplete_ < this.signers_.length;
|
| + if (!morePossible)
|
| + this.notifyComplete_();
|
| +};
|
| +
|
| +/**
|
| + * @private
|
| + */
|
| +MultipleGnubbySigner.prototype.notifyComplete_ = function() {
|
| + // See if any of the signers failed with a strange error. If so, report a
|
| + // single error to the caller, partly as a diagnostic aid and partly to
|
| + // distinguish real failures from wrong data.
|
| + var funnyBusiness;
|
| + for (var i = 0; i < this.errorStatus_.length; i++) {
|
| + if (this.errorStatus_[i] &&
|
| + this.errorStatus_[i] != DeviceStatusCodes.WRONG_DATA_STATUS &&
|
| + this.errorStatus_[i] != DeviceStatusCodes.WAIT_TOUCH_STATUS) {
|
| + funnyBusiness = this.errorStatus_[i];
|
| + break;
|
| + }
|
| + }
|
| + if (funnyBusiness) {
|
| + console.warn(UTIL_fmt('all done (success: ' + this.anySucceeded_ + ', ' +
|
| + 'funny error = ' + funnyBusiness + ')'));
|
| + } else {
|
| + console.log(UTIL_fmt('all done (success: ' + this.anySucceeded_ + ')'));
|
| + }
|
| + this.completedCb_(this.anySucceeded_, funnyBusiness);
|
| +};
|
| +
|
| +/**
|
| + * @param {number} code
|
| + * @param {usbGnubby} gnubby
|
| + * @param {number} gnubbyIndex
|
| + * @param {SingleSignerResult=} singleSignerResult
|
| + * @private
|
| + */
|
| +MultipleGnubbySigner.prototype.notifySuccess_ =
|
| + function(code, gnubby, gnubbyIndex, singleSignerResult) {
|
| + console.log(UTIL_fmt('success (' + code.toString(16) + ')'));
|
| + var signResult = {
|
| + 'gnubby': gnubby,
|
| + 'gnubbyIndex': gnubbyIndex
|
| + };
|
| + if (singleSignerResult && singleSignerResult['challenge'])
|
| + signResult['challenge'] = singleSignerResult['challenge'];
|
| + if (singleSignerResult && singleSignerResult['info'])
|
| + signResult['info'] = singleSignerResult['info'];
|
| + this.gnubbyFoundCb_(code, signResult);
|
| +};
|
|
|