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 |