| Index: chrome/browser/resources/cryptotoken/enroller.js
|
| diff --git a/chrome/browser/resources/cryptotoken/enroller.js b/chrome/browser/resources/cryptotoken/enroller.js
|
| index ecc809d502d39ef97ac2d291b25037436845f0db..00f1b79282d0fe6858e8a275894d462494bdb724 100644
|
| --- a/chrome/browser/resources/cryptotoken/enroller.js
|
| +++ b/chrome/browser/resources/cryptotoken/enroller.js
|
| @@ -83,13 +83,14 @@ function handleU2fEnrollRequest(sender, request, sendResponse) {
|
| closeable =
|
| validateAndBeginEnrollRequest(
|
| sender, request, 'registerRequests', 'signRequests',
|
| - sendErrorResponse, sendSuccessResponse);
|
| + sendErrorResponse, sendSuccessResponse, 'registeredKeys');
|
| return closeable;
|
| }
|
|
|
| /**
|
| * Validates an enroll request using the given parameters, and, if valid, begins
|
| - * handling the enroll request.
|
| + * handling the enroll request. (The enroll request may be modified as a result
|
| + * of handling it.)
|
| * @param {MessageSender} sender The sender of the message.
|
| * @param {Object} request The web page's enroll request.
|
| * @param {string} enrollChallengesName The name of the enroll challenges value
|
| @@ -99,11 +100,14 @@ function handleU2fEnrollRequest(sender, request, sendResponse) {
|
| * @param {function(ErrorCodes)} errorCb Error callback.
|
| * @param {function(string, string, (string|undefined))} successCb Success
|
| * callback.
|
| + * @param {string=} opt_registeredKeysName The name of the registered keys
|
| + * value in the request.
|
| * @return {Closeable} Request handler that should be closed when the browser
|
| * message channel is closed.
|
| */
|
| function validateAndBeginEnrollRequest(sender, request,
|
| - enrollChallengesName, signChallengesName, errorCb, successCb) {
|
| + enrollChallengesName, signChallengesName, errorCb, successCb,
|
| + opt_registeredKeysName) {
|
| var origin = getOriginFromUrl(/** @type {string} */ (sender.url));
|
| if (!origin) {
|
| errorCb(ErrorCodes.BAD_REQUEST);
|
| @@ -111,20 +115,31 @@ function validateAndBeginEnrollRequest(sender, request,
|
| }
|
|
|
| if (!isValidEnrollRequest(request, enrollChallengesName,
|
| - signChallengesName)) {
|
| + signChallengesName, opt_registeredKeysName)) {
|
| errorCb(ErrorCodes.BAD_REQUEST);
|
| return null;
|
| }
|
|
|
| var enrollChallenges = request[enrollChallengesName];
|
| - var signChallenges = request[signChallengesName];
|
| + var signChallenges;
|
| + if (opt_registeredKeysName &&
|
| + request.hasOwnProperty(opt_registeredKeysName)) {
|
| + // Convert registered keys to sign challenges by adding a challenge value.
|
| + signChallenges = request[opt_registeredKeysName];
|
| + for (var i = 0; i < signChallenges.length; i++) {
|
| + // The actual value doesn't matter, as long as it's a string.
|
| + signChallenges[i]['challenge'] = '';
|
| + }
|
| + } else {
|
| + signChallenges = request[signChallengesName];
|
| + }
|
| var logMsgUrl = request['logMsgUrl'];
|
|
|
| var timer = createTimerForRequest(
|
| FACTORY_REGISTRY.getCountdownFactory(), request);
|
| var enroller = new Enroller(timer, origin, errorCb, successCb,
|
| sender.tlsChannelId, logMsgUrl);
|
| - enroller.doEnroll(enrollChallenges, signChallenges);
|
| + enroller.doEnroll(enrollChallenges, signChallenges, request['appId']);
|
| return /** @type {Closeable} */ (enroller);
|
| }
|
|
|
| @@ -135,22 +150,32 @@ function validateAndBeginEnrollRequest(sender, request,
|
| * in the request.
|
| * @param {string} signChallengesName The name of the sign challenges value in
|
| * the request.
|
| + * @param {string=} opt_registeredKeysName The name of the registered keys
|
| + * value in the request.
|
| * @return {boolean} Whether the request appears valid.
|
| */
|
| function isValidEnrollRequest(request, enrollChallengesName,
|
| - signChallengesName) {
|
| + signChallengesName, opt_registeredKeysName) {
|
| if (!request.hasOwnProperty(enrollChallengesName))
|
| return false;
|
| var enrollChallenges = request[enrollChallengesName];
|
| if (!enrollChallenges.length)
|
| return false;
|
| - if (!isValidEnrollChallengeArray(enrollChallenges))
|
| + var hasAppId = request.hasOwnProperty('appId');
|
| + if (!isValidEnrollChallengeArray(enrollChallenges, !hasAppId))
|
| return false;
|
| var signChallenges = request[signChallengesName];
|
| // A missing sign challenge array is ok, in the case the user is not already
|
| // enrolled.
|
| - if (signChallenges && !isValidSignChallengeArray(signChallenges))
|
| + if (signChallenges && !isValidSignChallengeArray(signChallenges, !hasAppId))
|
| return false;
|
| + if (opt_registeredKeysName) {
|
| + var registeredKeys = request[opt_registeredKeysName];
|
| + if (registeredKeys &&
|
| + !isValidRegisteredKeyArray(registeredKeys, !hasAppId)) {
|
| + return false;
|
| + }
|
| + }
|
| return true;
|
| }
|
|
|
| @@ -166,10 +191,12 @@ var EnrollChallenge;
|
| /**
|
| * @param {Array.<EnrollChallenge>} enrollChallenges The enroll challenges to
|
| * validate.
|
| + * @param {boolean} appIdRequired Whether the appId property is required on
|
| + * each challenge.
|
| * @return {boolean} Whether the given array of challenges is a valid enroll
|
| * challenges array.
|
| */
|
| -function isValidEnrollChallengeArray(enrollChallenges) {
|
| +function isValidEnrollChallengeArray(enrollChallenges, appIdRequired) {
|
| var seenVersions = {};
|
| for (var i = 0; i < enrollChallenges.length; i++) {
|
| var enrollChallenge = enrollChallenges[i];
|
| @@ -186,7 +213,7 @@ function isValidEnrollChallengeArray(enrollChallenges) {
|
| return false;
|
| }
|
| seenVersions[version] = version;
|
| - if (!enrollChallenge['appId']) {
|
| + if (appIdRequired && !enrollChallenge['appId']) {
|
| return false;
|
| }
|
| if (!enrollChallenge['challenge']) {
|
| @@ -300,10 +327,13 @@ Enroller.DEFAULT_TIMEOUT_MILLIS = 30 * 1000;
|
| * @param {Array.<EnrollChallenge>} enrollChallenges A set of enroll challenges.
|
| * @param {Array.<SignChallenge>} signChallenges A set of sign challenges for
|
| * existing enrollments for this user and appId.
|
| + * @param {string=} opt_appId The app id for the entire request.
|
| */
|
| -Enroller.prototype.doEnroll = function(enrollChallenges, signChallenges) {
|
| - var encodedEnrollChallenges = this.encodeEnrollChallenges_(enrollChallenges);
|
| - var encodedSignChallenges = encodeSignChallenges(signChallenges);
|
| +Enroller.prototype.doEnroll = function(enrollChallenges, signChallenges,
|
| + opt_appId) {
|
| + var encodedEnrollChallenges =
|
| + this.encodeEnrollChallenges_(enrollChallenges, opt_appId);
|
| + var encodedSignChallenges = encodeSignChallenges(signChallenges, opt_appId);
|
| var request = {
|
| type: 'enroll_helper_request',
|
| enrollChallenges: encodedEnrollChallenges,
|
| @@ -317,8 +347,19 @@ Enroller.prototype.doEnroll = function(enrollChallenges, signChallenges) {
|
|
|
| // Begin fetching/checking the app ids.
|
| var enrollAppIds = [];
|
| + if (opt_appId) {
|
| + enrollAppIds.push(opt_appId);
|
| + }
|
| for (var i = 0; i < enrollChallenges.length; i++) {
|
| - enrollAppIds.push(enrollChallenges[i]['appId']);
|
| + if (enrollChallenges[i].hasOwnProperty('appId')) {
|
| + enrollAppIds.push(enrollChallenges[i]['appId']);
|
| + }
|
| + }
|
| + // Sanity check
|
| + if (!enrollAppIds.length) {
|
| + console.warn(UTIL_fmt('empty enroll app ids?'));
|
| + this.notifyError_(ErrorCodes.BAD_REQUEST);
|
| + return;
|
| }
|
| var self = this;
|
| this.checkAppIds_(enrollAppIds, signChallenges, function(result) {
|
| @@ -341,10 +382,11 @@ Enroller.prototype.doEnroll = function(enrollChallenges, signChallenges) {
|
| /**
|
| * Encodes the enroll challenge as an enroll helper challenge.
|
| * @param {EnrollChallenge} enrollChallenge The enroll challenge to encode.
|
| + * @param {string=} opt_appId The app id for the entire request.
|
| * @return {EnrollHelperChallenge} The encoded challenge.
|
| * @private
|
| */
|
| -Enroller.encodeEnrollChallenge_ = function(enrollChallenge) {
|
| +Enroller.encodeEnrollChallenge_ = function(enrollChallenge, opt_appId) {
|
| var encodedChallenge = {};
|
| var version;
|
| if (enrollChallenge['version']) {
|
| @@ -354,19 +396,30 @@ Enroller.encodeEnrollChallenge_ = function(enrollChallenge) {
|
| version = 'U2F_V1';
|
| }
|
| encodedChallenge['version'] = version;
|
| - encodedChallenge['challenge'] = enrollChallenge['challenge'];
|
| - encodedChallenge['appIdHash'] =
|
| - B64_encode(sha256HashOfString(enrollChallenge['appId']));
|
| + encodedChallenge['challengeHash'] = enrollChallenge['challenge'];
|
| + var appId;
|
| + if (enrollChallenge['appId']) {
|
| + appId = enrollChallenge['appId'];
|
| + } else {
|
| + appId = opt_appId;
|
| + }
|
| + if (!appId) {
|
| + // Sanity check. (Other code should fail if it's not set.)
|
| + console.warn(UTIL_fmt('No appId?'));
|
| + }
|
| + encodedChallenge['appIdHash'] = B64_encode(sha256HashOfString(appId));
|
| return /** @type {EnrollHelperChallenge} */ (encodedChallenge);
|
| };
|
|
|
| /**
|
| * Encodes the given enroll challenges using this enroller's state.
|
| * @param {Array.<EnrollChallenge>} enrollChallenges The enroll challenges.
|
| + * @param {string=} opt_appId The app id for the entire request.
|
| * @return {!Array.<EnrollHelperChallenge>} The encoded enroll challenges.
|
| * @private
|
| */
|
| -Enroller.prototype.encodeEnrollChallenges_ = function(enrollChallenges) {
|
| +Enroller.prototype.encodeEnrollChallenges_ = function(enrollChallenges,
|
| + opt_appId) {
|
| var challenges = [];
|
| for (var i = 0; i < enrollChallenges.length; i++) {
|
| var enrollChallenge = enrollChallenges[i];
|
| @@ -393,9 +446,10 @@ Enroller.prototype.encodeEnrollChallenges_ = function(enrollChallenges) {
|
| this.browserData_[version] =
|
| B64_encode(UTIL_StringToBytes(browserData));
|
| challenges.push(Enroller.encodeEnrollChallenge_(
|
| - /** @type {EnrollChallenge} */ (modifiedChallenge)));
|
| + /** @type {EnrollChallenge} */ (modifiedChallenge), opt_appId));
|
| } else {
|
| - challenges.push(Enroller.encodeEnrollChallenge_(enrollChallenge));
|
| + challenges.push(
|
| + Enroller.encodeEnrollChallenge_(enrollChallenge, opt_appId));
|
| }
|
| }
|
| return challenges;
|
|
|