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 23 matching lines...) Expand all Loading... |
34 sendErrorResponse({errorCode: ErrorCodes.OTHER_ERROR}); | 34 sendErrorResponse({errorCode: ErrorCodes.OTHER_ERROR}); |
35 return; | 35 return; |
36 } | 36 } |
37 var responseData = | 37 var responseData = |
38 makeEnrollResponseData(enrollChallenge, u2fVersion, | 38 makeEnrollResponseData(enrollChallenge, u2fVersion, |
39 'enrollData', info, 'browserData', browserData); | 39 'enrollData', info, 'browserData', browserData); |
40 var response = makeWebSuccessResponse(request, responseData); | 40 var response = makeWebSuccessResponse(request, responseData); |
41 sendResponseOnce(sentResponse, closeable, response, sendResponse); | 41 sendResponseOnce(sentResponse, closeable, response, sendResponse); |
42 } | 42 } |
43 | 43 |
| 44 function timeout() { |
| 45 sendErrorResponse({errorCode: ErrorCodes.TIMEOUT}); |
| 46 } |
| 47 |
44 var sender = createSenderFromMessageSender(messageSender); | 48 var sender = createSenderFromMessageSender(messageSender); |
45 if (!sender) { | 49 if (!sender) { |
46 sendErrorResponse({errorCode: ErrorCodes.BAD_REQUEST}); | 50 sendErrorResponse({errorCode: ErrorCodes.BAD_REQUEST}); |
47 return null; | 51 return null; |
48 } | 52 } |
49 | 53 |
50 var enroller = | 54 if (!isValidEnrollRequest(request, 'enrollChallenges', 'signData')) { |
51 validateEnrollRequest( | 55 sendErrorResponse({errorCode: ErrorCodes.BAD_REQUEST}); |
52 sender, request, 'enrollChallenges', 'signData', | 56 return null; |
53 sendErrorResponse, sendSuccessResponse); | |
54 if (enroller) { | |
55 var registerRequests = request['enrollChallenges']; | |
56 var signRequests = getSignRequestsFromEnrollRequest(request, 'signData'); | |
57 closeable = /** @type {Closeable} */ (enroller); | |
58 enroller.doEnroll(registerRequests, signRequests, request['appId']); | |
59 } | 57 } |
| 58 |
| 59 var timeoutValueSeconds = getTimeoutValueFromRequest(request); |
| 60 // Attenuate watchdog timeout value less than the enroller's timeout, so the |
| 61 // watchdog only fires after the enroller could reasonably have called back, |
| 62 // not before. |
| 63 var watchdogTimeoutValueSeconds = attenuateTimeoutInSeconds( |
| 64 timeoutValueSeconds, MINIMUM_TIMEOUT_ATTENUATION_SECONDS / 2); |
| 65 var watchdog = new WatchdogRequestHandler(watchdogTimeoutValueSeconds, |
| 66 timeout); |
| 67 var wrappedErrorCb = watchdog.wrapCallback(sendErrorResponse); |
| 68 var wrappedSuccessCb = watchdog.wrapCallback(sendSuccessResponse); |
| 69 |
| 70 var timer = createAttenuatedTimer( |
| 71 FACTORY_REGISTRY.getCountdownFactory(), timeoutValueSeconds); |
| 72 var logMsgUrl = request['logMsgUrl']; |
| 73 var enroller = new Enroller(timer, sender, wrappedErrorCb, wrappedSuccessCb, |
| 74 logMsgUrl); |
| 75 watchdog.setCloseable(/** @type {!Closeable} */ (enroller)); |
| 76 closeable = watchdog; |
| 77 |
| 78 var registerRequests = request['enrollChallenges']; |
| 79 var signRequests = getSignRequestsFromEnrollRequest(request, 'signData'); |
| 80 enroller.doEnroll(registerRequests, signRequests, request['appId']); |
| 81 |
60 return closeable; | 82 return closeable; |
61 } | 83 } |
62 | 84 |
63 /** | 85 /** |
64 * Handles a U2F enroll request. | 86 * Handles a U2F enroll request. |
65 * @param {MessageSender} messageSender The message sender. | 87 * @param {MessageSender} messageSender The message sender. |
66 * @param {Object} request The web page's enroll request. | 88 * @param {Object} request The web page's enroll request. |
67 * @param {Function} sendResponse Called back with the result of the enroll. | 89 * @param {Function} sendResponse Called back with the result of the enroll. |
68 * @return {Closeable} A handler object to be closed when the browser channel | 90 * @return {Closeable} A handler object to be closed when the browser channel |
69 * closes. | 91 * closes. |
(...skipping 16 matching lines...) Expand all Loading... |
86 sendErrorResponse({errorCode: ErrorCodes.OTHER_ERROR}); | 108 sendErrorResponse({errorCode: ErrorCodes.OTHER_ERROR}); |
87 return; | 109 return; |
88 } | 110 } |
89 var responseData = | 111 var responseData = |
90 makeEnrollResponseData(enrollChallenge, u2fVersion, | 112 makeEnrollResponseData(enrollChallenge, u2fVersion, |
91 'registrationData', info, 'clientData', browserData); | 113 'registrationData', info, 'clientData', browserData); |
92 var response = makeU2fSuccessResponse(request, responseData); | 114 var response = makeU2fSuccessResponse(request, responseData); |
93 sendResponseOnce(sentResponse, closeable, response, sendResponse); | 115 sendResponseOnce(sentResponse, closeable, response, sendResponse); |
94 } | 116 } |
95 | 117 |
| 118 function timeout() { |
| 119 sendErrorResponse({errorCode: ErrorCodes.TIMEOUT}); |
| 120 } |
| 121 |
96 var sender = createSenderFromMessageSender(messageSender); | 122 var sender = createSenderFromMessageSender(messageSender); |
97 if (!sender) { | 123 if (!sender) { |
98 sendErrorResponse({errorCode: ErrorCodes.BAD_REQUEST}); | 124 sendErrorResponse({errorCode: ErrorCodes.BAD_REQUEST}); |
99 return null; | 125 return null; |
100 } | 126 } |
101 | 127 |
102 var enroller = | 128 if (!isValidEnrollRequest(request, 'registerRequests', 'signRequests', |
103 validateEnrollRequest( | 129 'registeredKeys')) { |
104 sender, request, 'registerRequests', 'signRequests', | 130 sendErrorResponse({errorCode: ErrorCodes.BAD_REQUEST}); |
105 sendErrorResponse, sendSuccessResponse, 'registeredKeys'); | 131 return null; |
106 if (enroller) { | |
107 var registerRequests = request['registerRequests']; | |
108 var signRequests = getSignRequestsFromEnrollRequest(request, | |
109 'signRequests', 'registeredKeys'); | |
110 closeable = /** @type {Closeable} */ (enroller); | |
111 enroller.doEnroll(registerRequests, signRequests, request['appId']); | |
112 } | 132 } |
| 133 |
| 134 var timeoutValueSeconds = getTimeoutValueFromRequest(request); |
| 135 // Attenuate watchdog timeout value less than the enroller's timeout, so the |
| 136 // watchdog only fires after the enroller could reasonably have called back, |
| 137 // not before. |
| 138 var watchdogTimeoutValueSeconds = attenuateTimeoutInSeconds( |
| 139 timeoutValueSeconds, MINIMUM_TIMEOUT_ATTENUATION_SECONDS / 2); |
| 140 var watchdog = new WatchdogRequestHandler(watchdogTimeoutValueSeconds, |
| 141 timeout); |
| 142 var wrappedErrorCb = watchdog.wrapCallback(sendErrorResponse); |
| 143 var wrappedSuccessCb = watchdog.wrapCallback(sendSuccessResponse); |
| 144 |
| 145 var timer = createAttenuatedTimer( |
| 146 FACTORY_REGISTRY.getCountdownFactory(), timeoutValueSeconds); |
| 147 var logMsgUrl = request['logMsgUrl']; |
| 148 var enroller = new Enroller(timer, sender, sendErrorResponse, |
| 149 sendSuccessResponse, logMsgUrl); |
| 150 watchdog.setCloseable(/** @type {!Closeable} */ (enroller)); |
| 151 closeable = watchdog; |
| 152 |
| 153 var registerRequests = request['registerRequests']; |
| 154 var signRequests = getSignRequestsFromEnrollRequest(request, |
| 155 'signRequests', 'registeredKeys'); |
| 156 enroller.doEnroll(registerRequests, signRequests, request['appId']); |
| 157 |
113 return closeable; | 158 return closeable; |
114 } | 159 } |
115 | 160 |
116 /** | 161 /** |
117 * Validates an enroll request using the given parameters. | |
118 * @param {WebRequestSender} sender The sender of the message. | |
119 * @param {Object} request The web page's enroll request. | |
120 * @param {string} enrollChallengesName The name of the enroll challenges value | |
121 * in the request. | |
122 * @param {string} signChallengesName The name of the sign challenges value in | |
123 * the request. | |
124 * @param {function(U2fError)} errorCb Error callback. | |
125 * @param {function(string, string, (string|undefined))} successCb Success | |
126 * callback. | |
127 * @param {string=} opt_registeredKeysName The name of the registered keys | |
128 * value in the request. | |
129 * @return {Enroller} Enroller object representing the request, if the request | |
130 * is valid, or null if the request is invalid. | |
131 */ | |
132 function validateEnrollRequest(sender, request, | |
133 enrollChallengesName, signChallengesName, errorCb, successCb, | |
134 opt_registeredKeysName) { | |
135 if (!isValidEnrollRequest(request, enrollChallengesName, | |
136 signChallengesName, opt_registeredKeysName)) { | |
137 errorCb({errorCode: ErrorCodes.BAD_REQUEST}); | |
138 return null; | |
139 } | |
140 | |
141 var timeoutValueSeconds = getTimeoutValueFromRequest(request); | |
142 var timer = createAttenuatedTimer( | |
143 FACTORY_REGISTRY.getCountdownFactory(), timeoutValueSeconds); | |
144 var logMsgUrl = request['logMsgUrl']; | |
145 var enroller = new Enroller(timer, sender, errorCb, successCb, logMsgUrl); | |
146 return enroller; | |
147 } | |
148 | |
149 /** | |
150 * Returns whether the request appears to be a valid enroll request. | 162 * Returns whether the request appears to be a valid enroll request. |
151 * @param {Object} request The request. | 163 * @param {Object} request The request. |
152 * @param {string} enrollChallengesName The name of the enroll challenges value | 164 * @param {string} enrollChallengesName The name of the enroll challenges value |
153 * in the request. | 165 * in the request. |
154 * @param {string} signChallengesName The name of the sign challenges value in | 166 * @param {string} signChallengesName The name of the sign challenges value in |
155 * the request. | 167 * the request. |
156 * @param {string=} opt_registeredKeysName The name of the registered keys | 168 * @param {string=} opt_registeredKeysName The name of the registered keys |
157 * value in the request. | 169 * value in the request. |
158 * @return {boolean} Whether the request appears valid. | 170 * @return {boolean} Whether the request appears valid. |
159 */ | 171 */ |
(...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
386 * to the request to the handler if/when the user has done so. | 398 * to the request to the handler if/when the user has done so. |
387 * @private | 399 * @private |
388 */ | 400 */ |
389 Enroller.prototype.approveOrigin_ = function() { | 401 Enroller.prototype.approveOrigin_ = function() { |
390 var self = this; | 402 var self = this; |
391 FACTORY_REGISTRY.getApprovedOrigins() | 403 FACTORY_REGISTRY.getApprovedOrigins() |
392 .isApprovedOrigin(this.sender_.origin, this.sender_.tabId) | 404 .isApprovedOrigin(this.sender_.origin, this.sender_.tabId) |
393 .then(function(result) { | 405 .then(function(result) { |
394 if (self.done_) return; | 406 if (self.done_) return; |
395 if (!result) { | 407 if (!result) { |
396 // Origin not approved: fail the result. | 408 // Origin not approved: rather than give an explicit indication to |
397 self.notifyError_({errorCode: ErrorCodes.BAD_REQUEST}); | 409 // the web page, let a timeout occur. |
| 410 if (self.timer_.expired()) { |
| 411 self.notifyTimeout_(); |
| 412 return; |
| 413 } |
| 414 var newTimer = self.timer_.clone(self.notifyTimeout_.bind(self)); |
| 415 self.timer_.clearTimeout(); |
| 416 self.timer_ = newTimer; |
398 return; | 417 return; |
399 } | 418 } |
400 self.sendEnrollRequestToHelper_(); | 419 self.sendEnrollRequestToHelper_(); |
401 }); | 420 }); |
402 }; | 421 }; |
403 | 422 |
404 /** | 423 /** |
| 424 * Notifies the caller of a timeout error. |
| 425 * @private |
| 426 */ |
| 427 Enroller.prototype.notifyTimeout_ = function() { |
| 428 this.notifyError_({errorCode: ErrorCodes.TIMEOUT}); |
| 429 }; |
| 430 |
| 431 /** |
405 * Performs an enroll request with this instance's enroll and sign challenges, | 432 * Performs an enroll request with this instance's enroll and sign challenges, |
406 * by encoding them into a helper request and passing the resulting request to | 433 * by encoding them into a helper request and passing the resulting request to |
407 * the factory registry's helper. | 434 * the factory registry's helper. |
408 * @private | 435 * @private |
409 */ | 436 */ |
410 Enroller.prototype.sendEnrollRequestToHelper_ = function() { | 437 Enroller.prototype.sendEnrollRequestToHelper_ = function() { |
411 var encodedEnrollChallenges = | 438 var encodedEnrollChallenges = |
412 this.encodeEnrollChallenges_(this.enrollChallenges_, this.appId_); | 439 this.encodeEnrollChallenges_(this.enrollChallenges_, this.appId_); |
413 // If the request didn't contain a sign challenge, provide one. The value | 440 // If the request didn't contain a sign challenge, provide one. The value |
414 // doesn't matter. | 441 // doesn't matter. |
(...skipping 220 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
635 // For U2F_V2, the challenge sent to the gnubby is modified to be the hash | 662 // For U2F_V2, the challenge sent to the gnubby is modified to be the hash |
636 // of the browser data. Include the browser data. | 663 // of the browser data. Include the browser data. |
637 browserData = this.browserData_[reply.version]; | 664 browserData = this.browserData_[reply.version]; |
638 } | 665 } |
639 | 666 |
640 this.notifySuccess_(/** @type {string} */ (reply.version), | 667 this.notifySuccess_(/** @type {string} */ (reply.version), |
641 /** @type {string} */ (reply.enrollData), | 668 /** @type {string} */ (reply.enrollData), |
642 browserData); | 669 browserData); |
643 } | 670 } |
644 }; | 671 }; |
OLD | NEW |