| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * @fileoverview Handles web page requests for gnubby enrollment. | 6 * @fileoverview Handles web page requests for gnubby enrollment. |
| 7 */ | 7 */ |
| 8 | 8 |
| 9 'use strict'; | 9 'use strict'; |
| 10 | 10 |
| (...skipping 197 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 208 /** | 208 /** |
| 209 * @typedef {{ | 209 * @typedef {{ |
| 210 * version: (string|undefined), | 210 * version: (string|undefined), |
| 211 * challenge: string, | 211 * challenge: string, |
| 212 * appId: string | 212 * appId: string |
| 213 * }} | 213 * }} |
| 214 */ | 214 */ |
| 215 var EnrollChallenge; | 215 var EnrollChallenge; |
| 216 | 216 |
| 217 /** | 217 /** |
| 218 * @param {Array.<EnrollChallenge>} enrollChallenges The enroll challenges to | 218 * @param {Array<EnrollChallenge>} enrollChallenges The enroll challenges to |
| 219 * validate. | 219 * validate. |
| 220 * @param {boolean} appIdRequired Whether the appId property is required on | 220 * @param {boolean} appIdRequired Whether the appId property is required on |
| 221 * each challenge. | 221 * each challenge. |
| 222 * @return {boolean} Whether the given array of challenges is a valid enroll | 222 * @return {boolean} Whether the given array of challenges is a valid enroll |
| 223 * challenges array. | 223 * challenges array. |
| 224 */ | 224 */ |
| 225 function isValidEnrollChallengeArray(enrollChallenges, appIdRequired) { | 225 function isValidEnrollChallengeArray(enrollChallenges, appIdRequired) { |
| 226 var seenVersions = {}; | 226 var seenVersions = {}; |
| 227 for (var i = 0; i < enrollChallenges.length; i++) { | 227 for (var i = 0; i < enrollChallenges.length; i++) { |
| 228 var enrollChallenge = enrollChallenges[i]; | 228 var enrollChallenge = enrollChallenges[i]; |
| (...skipping 17 matching lines...) Expand all Loading... |
| 246 // The challenge is required. | 246 // The challenge is required. |
| 247 return false; | 247 return false; |
| 248 } | 248 } |
| 249 } | 249 } |
| 250 return true; | 250 return true; |
| 251 } | 251 } |
| 252 | 252 |
| 253 /** | 253 /** |
| 254 * Finds the enroll challenge of the given version in the enroll challlenge | 254 * Finds the enroll challenge of the given version in the enroll challlenge |
| 255 * array. | 255 * array. |
| 256 * @param {Array.<EnrollChallenge>} enrollChallenges The enroll challenges to | 256 * @param {Array<EnrollChallenge>} enrollChallenges The enroll challenges to |
| 257 * search. | 257 * search. |
| 258 * @param {string} version Version to search for. | 258 * @param {string} version Version to search for. |
| 259 * @return {?EnrollChallenge} The enroll challenge with the given versions, or | 259 * @return {?EnrollChallenge} The enroll challenge with the given versions, or |
| 260 * null if it isn't found. | 260 * null if it isn't found. |
| 261 */ | 261 */ |
| 262 function findEnrollChallengeOfVersion(enrollChallenges, version) { | 262 function findEnrollChallengeOfVersion(enrollChallenges, version) { |
| 263 for (var i = 0; i < enrollChallenges.length; i++) { | 263 for (var i = 0; i < enrollChallenges.length; i++) { |
| 264 if (enrollChallenges[i]['version'] == version) { | 264 if (enrollChallenges[i]['version'] == version) { |
| 265 return enrollChallenges[i]; | 265 return enrollChallenges[i]; |
| 266 } | 266 } |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 300 /** | 300 /** |
| 301 * Gets the expanded sign challenges from an enroll request, potentially by | 301 * Gets the expanded sign challenges from an enroll request, potentially by |
| 302 * modifying the request to contain a challenge value where one was omitted. | 302 * modifying the request to contain a challenge value where one was omitted. |
| 303 * (For enrolling, the server isn't interested in the value of a signature, | 303 * (For enrolling, the server isn't interested in the value of a signature, |
| 304 * only whether the presented key handle is already enrolled.) | 304 * only whether the presented key handle is already enrolled.) |
| 305 * @param {Object} request The request. | 305 * @param {Object} request The request. |
| 306 * @param {string} signChallengesName The name of the sign challenges value in | 306 * @param {string} signChallengesName The name of the sign challenges value in |
| 307 * the request. | 307 * the request. |
| 308 * @param {string=} opt_registeredKeysName The name of the registered keys | 308 * @param {string=} opt_registeredKeysName The name of the registered keys |
| 309 * value in the request. | 309 * value in the request. |
| 310 * @return {Array.<SignChallenge>} | 310 * @return {Array<SignChallenge>} |
| 311 */ | 311 */ |
| 312 function getSignRequestsFromEnrollRequest(request, signChallengesName, | 312 function getSignRequestsFromEnrollRequest(request, signChallengesName, |
| 313 opt_registeredKeysName) { | 313 opt_registeredKeysName) { |
| 314 var signChallenges; | 314 var signChallenges; |
| 315 if (opt_registeredKeysName && | 315 if (opt_registeredKeysName && |
| 316 request.hasOwnProperty(opt_registeredKeysName)) { | 316 request.hasOwnProperty(opt_registeredKeysName)) { |
| 317 signChallenges = request[opt_registeredKeysName]; | 317 signChallenges = request[opt_registeredKeysName]; |
| 318 } else { | 318 } else { |
| 319 signChallenges = request[signChallengesName]; | 319 signChallenges = request[signChallengesName]; |
| 320 } | 320 } |
| (...skipping 28 matching lines...) Expand all Loading... |
| 349 /** @private {function(U2fError)} */ | 349 /** @private {function(U2fError)} */ |
| 350 this.errorCb_ = errorCb; | 350 this.errorCb_ = errorCb; |
| 351 /** @private {function(string, string, (string|undefined))} */ | 351 /** @private {function(string, string, (string|undefined))} */ |
| 352 this.successCb_ = successCb; | 352 this.successCb_ = successCb; |
| 353 /** @private {string|undefined} */ | 353 /** @private {string|undefined} */ |
| 354 this.logMsgUrl_ = opt_logMsgUrl; | 354 this.logMsgUrl_ = opt_logMsgUrl; |
| 355 | 355 |
| 356 /** @private {boolean} */ | 356 /** @private {boolean} */ |
| 357 this.done_ = false; | 357 this.done_ = false; |
| 358 | 358 |
| 359 /** @private {Object.<string, string>} */ | 359 /** @private {Object<string, string>} */ |
| 360 this.browserData_ = {}; | 360 this.browserData_ = {}; |
| 361 /** @private {Array.<EnrollHelperChallenge>} */ | 361 /** @private {Array<EnrollHelperChallenge>} */ |
| 362 this.encodedEnrollChallenges_ = []; | 362 this.encodedEnrollChallenges_ = []; |
| 363 /** @private {Array.<SignHelperChallenge>} */ | 363 /** @private {Array<SignHelperChallenge>} */ |
| 364 this.encodedSignChallenges_ = []; | 364 this.encodedSignChallenges_ = []; |
| 365 // Allow http appIds for http origins. (Broken, but the caller deserves | 365 // Allow http appIds for http origins. (Broken, but the caller deserves |
| 366 // what they get.) | 366 // what they get.) |
| 367 /** @private {boolean} */ | 367 /** @private {boolean} */ |
| 368 this.allowHttp_ = | 368 this.allowHttp_ = |
| 369 this.sender_.origin ? this.sender_.origin.indexOf('http://') == 0 : false; | 369 this.sender_.origin ? this.sender_.origin.indexOf('http://') == 0 : false; |
| 370 /** @private {Closeable} */ | 370 /** @private {Closeable} */ |
| 371 this.handler_ = null; | 371 this.handler_ = null; |
| 372 } | 372 } |
| 373 | 373 |
| 374 /** | 374 /** |
| 375 * Default timeout value in case the caller never provides a valid timeout. | 375 * Default timeout value in case the caller never provides a valid timeout. |
| 376 */ | 376 */ |
| 377 Enroller.DEFAULT_TIMEOUT_MILLIS = 30 * 1000; | 377 Enroller.DEFAULT_TIMEOUT_MILLIS = 30 * 1000; |
| 378 | 378 |
| 379 /** | 379 /** |
| 380 * Performs an enroll request with the given enroll and sign challenges. | 380 * Performs an enroll request with the given enroll and sign challenges. |
| 381 * @param {Array.<EnrollChallenge>} enrollChallenges A set of enroll challenges. | 381 * @param {Array<EnrollChallenge>} enrollChallenges A set of enroll challenges. |
| 382 * @param {Array.<SignChallenge>} signChallenges A set of sign challenges for | 382 * @param {Array<SignChallenge>} signChallenges A set of sign challenges for |
| 383 * existing enrollments for this user and appId. | 383 * existing enrollments for this user and appId. |
| 384 * @param {string=} opt_appId The app id for the entire request. | 384 * @param {string=} opt_appId The app id for the entire request. |
| 385 */ | 385 */ |
| 386 Enroller.prototype.doEnroll = function(enrollChallenges, signChallenges, | 386 Enroller.prototype.doEnroll = function(enrollChallenges, signChallenges, |
| 387 opt_appId) { | 387 opt_appId) { |
| 388 /** @private {Array.<EnrollChallenge>} */ | 388 /** @private {Array<EnrollChallenge>} */ |
| 389 this.enrollChallenges_ = enrollChallenges; | 389 this.enrollChallenges_ = enrollChallenges; |
| 390 /** @private {Array.<SignChallenge>} */ | 390 /** @private {Array<SignChallenge>} */ |
| 391 this.signChallenges_ = signChallenges; | 391 this.signChallenges_ = signChallenges; |
| 392 /** @private {(string|undefined)} */ | 392 /** @private {(string|undefined)} */ |
| 393 this.appId_ = opt_appId; | 393 this.appId_ = opt_appId; |
| 394 var self = this; | 394 var self = this; |
| 395 getTabIdWhenPossible(this.sender_).then(function() { | 395 getTabIdWhenPossible(this.sender_).then(function() { |
| 396 if (self.done_) return; | 396 if (self.done_) return; |
| 397 self.approveOrigin_(); | 397 self.approveOrigin_(); |
| 398 }, function() { | 398 }, function() { |
| 399 self.close(); | 399 self.close(); |
| 400 self.notifyError_({errorCode: ErrorCodes.BAD_REQUEST}); | 400 self.notifyError_({errorCode: ErrorCodes.BAD_REQUEST}); |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 524 if (!appId) { | 524 if (!appId) { |
| 525 // Sanity check. (Other code should fail if it's not set.) | 525 // Sanity check. (Other code should fail if it's not set.) |
| 526 console.warn(UTIL_fmt('No appId?')); | 526 console.warn(UTIL_fmt('No appId?')); |
| 527 } | 527 } |
| 528 encodedChallenge['appIdHash'] = B64_encode(sha256HashOfString(appId)); | 528 encodedChallenge['appIdHash'] = B64_encode(sha256HashOfString(appId)); |
| 529 return /** @type {EnrollHelperChallenge} */ (encodedChallenge); | 529 return /** @type {EnrollHelperChallenge} */ (encodedChallenge); |
| 530 }; | 530 }; |
| 531 | 531 |
| 532 /** | 532 /** |
| 533 * Encodes the given enroll challenges using this enroller's state. | 533 * Encodes the given enroll challenges using this enroller's state. |
| 534 * @param {Array.<EnrollChallenge>} enrollChallenges The enroll challenges. | 534 * @param {Array<EnrollChallenge>} enrollChallenges The enroll challenges. |
| 535 * @param {string=} opt_appId The app id for the entire request. | 535 * @param {string=} opt_appId The app id for the entire request. |
| 536 * @return {!Array.<EnrollHelperChallenge>} The encoded enroll challenges. | 536 * @return {!Array<EnrollHelperChallenge>} The encoded enroll challenges. |
| 537 * @private | 537 * @private |
| 538 */ | 538 */ |
| 539 Enroller.prototype.encodeEnrollChallenges_ = function(enrollChallenges, | 539 Enroller.prototype.encodeEnrollChallenges_ = function(enrollChallenges, |
| 540 opt_appId) { | 540 opt_appId) { |
| 541 var challenges = []; | 541 var challenges = []; |
| 542 for (var i = 0; i < enrollChallenges.length; i++) { | 542 for (var i = 0; i < enrollChallenges.length; i++) { |
| 543 var enrollChallenge = enrollChallenges[i]; | 543 var enrollChallenge = enrollChallenges[i]; |
| 544 var version = enrollChallenge.version; | 544 var version = enrollChallenge.version; |
| 545 if (!version) { | 545 if (!version) { |
| 546 // Version is implicitly V1 if not specified. | 546 // Version is implicitly V1 if not specified. |
| (...skipping 22 matching lines...) Expand all Loading... |
| 569 challenges.push( | 569 challenges.push( |
| 570 Enroller.encodeEnrollChallenge_(enrollChallenge, opt_appId)); | 570 Enroller.encodeEnrollChallenge_(enrollChallenge, opt_appId)); |
| 571 } | 571 } |
| 572 } | 572 } |
| 573 return challenges; | 573 return challenges; |
| 574 }; | 574 }; |
| 575 | 575 |
| 576 /** | 576 /** |
| 577 * Checks the app ids associated with this enroll request, and calls a callback | 577 * Checks the app ids associated with this enroll request, and calls a callback |
| 578 * with the result of the check. | 578 * with the result of the check. |
| 579 * @param {!Array.<string>} enrollAppIds The app ids in the enroll challenge | 579 * @param {!Array<string>} enrollAppIds The app ids in the enroll challenge |
| 580 * portion of the enroll request. | 580 * portion of the enroll request. |
| 581 * @param {function(boolean)} cb Called with the result of the check. | 581 * @param {function(boolean)} cb Called with the result of the check. |
| 582 * @private | 582 * @private |
| 583 */ | 583 */ |
| 584 Enroller.prototype.checkAppIds_ = function(enrollAppIds, cb) { | 584 Enroller.prototype.checkAppIds_ = function(enrollAppIds, cb) { |
| 585 var appIds = | 585 var appIds = |
| 586 UTIL_unionArrays(enrollAppIds, getDistinctAppIds(this.signChallenges_)); | 586 UTIL_unionArrays(enrollAppIds, getDistinctAppIds(this.signChallenges_)); |
| 587 FACTORY_REGISTRY.getOriginChecker() | 587 FACTORY_REGISTRY.getOriginChecker() |
| 588 .canClaimAppIds(this.sender_.origin, appIds) | 588 .canClaimAppIds(this.sender_.origin, appIds) |
| 589 .then(this.originChecked_.bind(this, appIds, cb)); | 589 .then(this.originChecked_.bind(this, appIds, cb)); |
| 590 }; | 590 }; |
| 591 | 591 |
| 592 /** | 592 /** |
| 593 * Called with the result of checking the origin. When the origin is allowed | 593 * Called with the result of checking the origin. When the origin is allowed |
| 594 * to claim the app ids, begins checking whether the app ids also list the | 594 * to claim the app ids, begins checking whether the app ids also list the |
| 595 * origin. | 595 * origin. |
| 596 * @param {!Array.<string>} appIds The app ids. | 596 * @param {!Array<string>} appIds The app ids. |
| 597 * @param {function(boolean)} cb Called with the result of the check. | 597 * @param {function(boolean)} cb Called with the result of the check. |
| 598 * @param {boolean} result Whether the origin could claim the app ids. | 598 * @param {boolean} result Whether the origin could claim the app ids. |
| 599 * @private | 599 * @private |
| 600 */ | 600 */ |
| 601 Enroller.prototype.originChecked_ = function(appIds, cb, result) { | 601 Enroller.prototype.originChecked_ = function(appIds, cb, result) { |
| 602 if (!result) { | 602 if (!result) { |
| 603 this.notifyError_({errorCode: ErrorCodes.BAD_REQUEST}); | 603 this.notifyError_({errorCode: ErrorCodes.BAD_REQUEST}); |
| 604 return; | 604 return; |
| 605 } | 605 } |
| 606 /** @private {!AppIdChecker} */ | 606 /** @private {!AppIdChecker} */ |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 670 // For U2F_V2, the challenge sent to the gnubby is modified to be the hash | 670 // For U2F_V2, the challenge sent to the gnubby is modified to be the hash |
| 671 // of the browser data. Include the browser data. | 671 // of the browser data. Include the browser data. |
| 672 browserData = this.browserData_[reply.version]; | 672 browserData = this.browserData_[reply.version]; |
| 673 } | 673 } |
| 674 | 674 |
| 675 this.notifySuccess_(/** @type {string} */ (reply.version), | 675 this.notifySuccess_(/** @type {string} */ (reply.version), |
| 676 /** @type {string} */ (reply.enrollData), | 676 /** @type {string} */ (reply.enrollData), |
| 677 browserData); | 677 browserData); |
| 678 } | 678 } |
| 679 }; | 679 }; |
| OLD | NEW |