| 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 sign requests. | 6 * @fileoverview Handles web page requests for gnubby sign requests. |
| 7 * | 7 * |
| 8 */ | 8 */ |
| 9 | 9 |
| 10 'use strict'; | 10 'use strict'; |
| 11 | 11 |
| 12 var gnubbySignRequestQueue; | 12 var gnubbySignRequestQueue; |
| 13 | 13 |
| 14 function initRequestQueue() { | 14 function initRequestQueue() { |
| 15 gnubbySignRequestQueue = new OriginKeyedRequestQueue( | 15 gnubbySignRequestQueue = |
| 16 FACTORY_REGISTRY.getSystemTimer()); | 16 new OriginKeyedRequestQueue(FACTORY_REGISTRY.getSystemTimer()); |
| 17 } | 17 } |
| 18 | 18 |
| 19 /** | 19 /** |
| 20 * Handles a U2F sign request. | 20 * Handles a U2F sign request. |
| 21 * @param {MessageSender} messageSender The message sender. | 21 * @param {MessageSender} messageSender The message sender. |
| 22 * @param {Object} request The web page's sign request. | 22 * @param {Object} request The web page's sign request. |
| 23 * @param {Function} sendResponse Called back with the result of the sign. | 23 * @param {Function} sendResponse Called back with the result of the sign. |
| 24 * @return {Closeable} Request handler that should be closed when the browser | 24 * @return {Closeable} Request handler that should be closed when the browser |
| 25 * message channel is closed. | 25 * message channel is closed. |
| 26 */ | 26 */ |
| 27 function handleU2fSignRequest(messageSender, request, sendResponse) { | 27 function handleU2fSignRequest(messageSender, request, sendResponse) { |
| 28 var sentResponse = false; | 28 var sentResponse = false; |
| 29 var queuedSignRequest; | 29 var queuedSignRequest; |
| 30 | 30 |
| 31 function sendErrorResponse(error) { | 31 function sendErrorResponse(error) { |
| 32 sendResponseOnce(sentResponse, queuedSignRequest, | 32 sendResponseOnce( |
| 33 sentResponse, queuedSignRequest, |
| 33 makeU2fErrorResponse(request, error.errorCode, error.errorMessage), | 34 makeU2fErrorResponse(request, error.errorCode, error.errorMessage), |
| 34 sendResponse); | 35 sendResponse); |
| 35 } | 36 } |
| 36 | 37 |
| 37 function sendSuccessResponse(challenge, info, browserData) { | 38 function sendSuccessResponse(challenge, info, browserData) { |
| 38 var responseData = makeU2fSignResponseDataFromChallenge(challenge); | 39 var responseData = makeU2fSignResponseDataFromChallenge(challenge); |
| 39 addSignatureAndBrowserDataToResponseData(responseData, info, browserData, | 40 addSignatureAndBrowserDataToResponseData( |
| 40 'clientData'); | 41 responseData, info, browserData, 'clientData'); |
| 41 var response = makeU2fSuccessResponse(request, responseData); | 42 var response = makeU2fSuccessResponse(request, responseData); |
| 42 sendResponseOnce(sentResponse, queuedSignRequest, response, sendResponse); | 43 sendResponseOnce(sentResponse, queuedSignRequest, response, sendResponse); |
| 43 } | 44 } |
| 44 | 45 |
| 45 var sender = createSenderFromMessageSender(messageSender); | 46 var sender = createSenderFromMessageSender(messageSender); |
| 46 if (!sender) { | 47 if (!sender) { |
| 47 sendErrorResponse({errorCode: ErrorCodes.BAD_REQUEST}); | 48 sendErrorResponse({errorCode: ErrorCodes.BAD_REQUEST}); |
| 48 return null; | 49 return null; |
| 49 } | 50 } |
| 50 if (sender.origin.indexOf('http://') == 0 && !HTTP_ORIGINS_ALLOWED) { | 51 if (sender.origin.indexOf('http://') == 0 && !HTTP_ORIGINS_ALLOWED) { |
| 51 sendErrorResponse({errorCode: ErrorCodes.BAD_REQUEST}); | 52 sendErrorResponse({errorCode: ErrorCodes.BAD_REQUEST}); |
| 52 return null; | 53 return null; |
| 53 } | 54 } |
| 54 | 55 |
| 55 queuedSignRequest = | 56 queuedSignRequest = validateAndEnqueueSignRequest( |
| 56 validateAndEnqueueSignRequest( | 57 sender, request, sendErrorResponse, sendSuccessResponse); |
| 57 sender, request, sendErrorResponse, sendSuccessResponse); | |
| 58 return queuedSignRequest; | 58 return queuedSignRequest; |
| 59 } | 59 } |
| 60 | 60 |
| 61 /** | 61 /** |
| 62 * Creates a base U2F responseData object from the server challenge. | 62 * Creates a base U2F responseData object from the server challenge. |
| 63 * @param {SignChallenge} challenge The server challenge. | 63 * @param {SignChallenge} challenge The server challenge. |
| 64 * @return {Object} The responseData object. | 64 * @return {Object} The responseData object. |
| 65 */ | 65 */ |
| 66 function makeU2fSignResponseDataFromChallenge(challenge) { | 66 function makeU2fSignResponseDataFromChallenge(challenge) { |
| 67 var responseData = { | 67 var responseData = {'keyHandle': challenge['keyHandle']}; |
| 68 'keyHandle': challenge['keyHandle'] | |
| 69 }; | |
| 70 return responseData; | 68 return responseData; |
| 71 } | 69 } |
| 72 | 70 |
| 73 /** | 71 /** |
| 74 * Adds the browser data and signature values to a responseData object. | 72 * Adds the browser data and signature values to a responseData object. |
| 75 * @param {Object} responseData The "base" responseData object. | 73 * @param {Object} responseData The "base" responseData object. |
| 76 * @param {string} signatureData The signature data. | 74 * @param {string} signatureData The signature data. |
| 77 * @param {string} browserData The browser data generated from the challenge. | 75 * @param {string} browserData The browser data generated from the challenge. |
| 78 * @param {string} browserDataName The name of the browser data key in the | 76 * @param {string} browserDataName The name of the browser data key in the |
| 79 * responseData object. | 77 * responseData object. |
| 80 */ | 78 */ |
| 81 function addSignatureAndBrowserDataToResponseData(responseData, signatureData, | 79 function addSignatureAndBrowserDataToResponseData( |
| 82 browserData, browserDataName) { | 80 responseData, signatureData, browserData, browserDataName) { |
| 83 responseData[browserDataName] = B64_encode(UTIL_StringToBytes(browserData)); | 81 responseData[browserDataName] = B64_encode(UTIL_StringToBytes(browserData)); |
| 84 responseData['signatureData'] = signatureData; | 82 responseData['signatureData'] = signatureData; |
| 85 } | 83 } |
| 86 | 84 |
| 87 /** | 85 /** |
| 88 * Validates a sign request using the given sign challenges name, and, if valid, | 86 * Validates a sign request using the given sign challenges name, and, if valid, |
| 89 * enqueues the sign request for eventual processing. | 87 * enqueues the sign request for eventual processing. |
| 90 * @param {WebRequestSender} sender The sender of the message. | 88 * @param {WebRequestSender} sender The sender of the message. |
| 91 * @param {Object} request The web page's sign request. | 89 * @param {Object} request The web page's sign request. |
| 92 * @param {function(U2fError)} errorCb Error callback. | 90 * @param {function(U2fError)} errorCb Error callback. |
| 93 * @param {function(SignChallenge, string, string)} successCb Success callback. | 91 * @param {function(SignChallenge, string, string)} successCb Success callback. |
| 94 * @return {Closeable} Request handler that should be closed when the browser | 92 * @return {Closeable} Request handler that should be closed when the browser |
| 95 * message channel is closed. | 93 * message channel is closed. |
| 96 */ | 94 */ |
| 97 function validateAndEnqueueSignRequest(sender, request, errorCb, successCb) { | 95 function validateAndEnqueueSignRequest(sender, request, errorCb, successCb) { |
| 98 function timeout() { | 96 function timeout() { |
| 99 errorCb({errorCode: ErrorCodes.TIMEOUT}); | 97 errorCb({errorCode: ErrorCodes.TIMEOUT}); |
| 100 } | 98 } |
| 101 | 99 |
| 102 if (!isValidSignRequest(request)) { | 100 if (!isValidSignRequest(request)) { |
| 103 errorCb({errorCode: ErrorCodes.BAD_REQUEST}); | 101 errorCb({errorCode: ErrorCodes.BAD_REQUEST}); |
| 104 return null; | 102 return null; |
| 105 } | 103 } |
| 106 | 104 |
| 107 // The typecast is necessary because getSignChallenges can return undefined. | 105 // The typecast is necessary because getSignChallenges can return undefined. |
| 108 // On the other hand, a valid sign request can't contain an undefined sign | 106 // On the other hand, a valid sign request can't contain an undefined sign |
| 109 // challenge list, so the typecast is safe. | 107 // challenge list, so the typecast is safe. |
| 110 var signChallenges = /** @type {!Array<SignChallenge>} */ ( | 108 var signChallenges = |
| 111 getSignChallenges(request)); | 109 /** @type {!Array<SignChallenge>} */ (getSignChallenges(request)); |
| 112 var appId; | 110 var appId; |
| 113 if (request['appId']) { | 111 if (request['appId']) { |
| 114 appId = request['appId']; | 112 appId = request['appId']; |
| 115 } else if (signChallenges.length) { | 113 } else if (signChallenges.length) { |
| 116 appId = signChallenges[0]['appId']; | 114 appId = signChallenges[0]['appId']; |
| 117 } | 115 } |
| 118 // Sanity check | 116 // Sanity check |
| 119 if (!appId) { | 117 if (!appId) { |
| 120 console.warn(UTIL_fmt('empty sign appId?')); | 118 console.warn(UTIL_fmt('empty sign appId?')); |
| 121 errorCb({errorCode: ErrorCodes.BAD_REQUEST}); | 119 errorCb({errorCode: ErrorCodes.BAD_REQUEST}); |
| 122 return null; | 120 return null; |
| 123 } | 121 } |
| 124 var timeoutValueSeconds = getTimeoutValueFromRequest(request); | 122 var timeoutValueSeconds = getTimeoutValueFromRequest(request); |
| 125 // Attenuate watchdog timeout value less than the signer's timeout, so the | 123 // Attenuate watchdog timeout value less than the signer's timeout, so the |
| 126 // watchdog only fires after the signer could reasonably have called back, | 124 // watchdog only fires after the signer could reasonably have called back, |
| 127 // not before. | 125 // not before. |
| 128 timeoutValueSeconds = attenuateTimeoutInSeconds(timeoutValueSeconds, | 126 timeoutValueSeconds = attenuateTimeoutInSeconds( |
| 129 MINIMUM_TIMEOUT_ATTENUATION_SECONDS / 2); | 127 timeoutValueSeconds, MINIMUM_TIMEOUT_ATTENUATION_SECONDS / 2); |
| 130 var watchdog = new WatchdogRequestHandler(timeoutValueSeconds, timeout); | 128 var watchdog = new WatchdogRequestHandler(timeoutValueSeconds, timeout); |
| 131 var wrappedErrorCb = watchdog.wrapCallback(errorCb); | 129 var wrappedErrorCb = watchdog.wrapCallback(errorCb); |
| 132 var wrappedSuccessCb = watchdog.wrapCallback(successCb); | 130 var wrappedSuccessCb = watchdog.wrapCallback(successCb); |
| 133 | 131 |
| 134 var timer = createAttenuatedTimer( | 132 var timer = createAttenuatedTimer( |
| 135 FACTORY_REGISTRY.getCountdownFactory(), timeoutValueSeconds); | 133 FACTORY_REGISTRY.getCountdownFactory(), timeoutValueSeconds); |
| 136 var logMsgUrl = request['logMsgUrl']; | 134 var logMsgUrl = request['logMsgUrl']; |
| 137 | 135 |
| 138 // Queue sign requests from the same origin, to protect against simultaneous | 136 // Queue sign requests from the same origin, to protect against simultaneous |
| 139 // sign-out on many tabs resulting in repeated sign-in requests. | 137 // sign-out on many tabs resulting in repeated sign-in requests. |
| 140 var queuedSignRequest = new QueuedSignRequest(signChallenges, | 138 var queuedSignRequest = new QueuedSignRequest( |
| 141 timer, sender, wrappedErrorCb, wrappedSuccessCb, request['challenge'], | 139 signChallenges, timer, sender, wrappedErrorCb, wrappedSuccessCb, |
| 142 appId, logMsgUrl); | 140 request['challenge'], appId, logMsgUrl); |
| 143 if (!gnubbySignRequestQueue) { | 141 if (!gnubbySignRequestQueue) { |
| 144 initRequestQueue(); | 142 initRequestQueue(); |
| 145 } | 143 } |
| 146 var requestToken = gnubbySignRequestQueue.queueRequest(appId, sender.origin, | 144 var requestToken = gnubbySignRequestQueue.queueRequest( |
| 147 queuedSignRequest.begin.bind(queuedSignRequest), timer); | 145 appId, sender.origin, queuedSignRequest.begin.bind(queuedSignRequest), |
| 146 timer); |
| 148 queuedSignRequest.setToken(requestToken); | 147 queuedSignRequest.setToken(requestToken); |
| 149 | 148 |
| 150 watchdog.setCloseable(queuedSignRequest); | 149 watchdog.setCloseable(queuedSignRequest); |
| 151 return watchdog; | 150 return watchdog; |
| 152 } | 151 } |
| 153 | 152 |
| 154 /** | 153 /** |
| 155 * Returns whether the request appears to be a valid sign request. | 154 * Returns whether the request appears to be a valid sign request. |
| 156 * @param {Object} request The request. | 155 * @param {Object} request The request. |
| 157 * @return {boolean} Whether the request appears valid. | 156 * @return {boolean} Whether the request appears valid. |
| 158 */ | 157 */ |
| 159 function isValidSignRequest(request) { | 158 function isValidSignRequest(request) { |
| 160 var signChallenges = getSignChallenges(request); | 159 var signChallenges = getSignChallenges(request); |
| 161 if (!signChallenges) { | 160 if (!signChallenges) { |
| 162 return false; | 161 return false; |
| 163 } | 162 } |
| 164 var hasDefaultChallenge = request.hasOwnProperty('challenge'); | 163 var hasDefaultChallenge = request.hasOwnProperty('challenge'); |
| 165 var hasAppId = request.hasOwnProperty('appId'); | 164 var hasAppId = request.hasOwnProperty('appId'); |
| 166 // If the sign challenge array is empty, the global appId is required. | 165 // If the sign challenge array is empty, the global appId is required. |
| 167 if (!hasAppId && (!signChallenges || !signChallenges.length)) { | 166 if (!hasAppId && (!signChallenges || !signChallenges.length)) { |
| 168 return false; | 167 return false; |
| 169 } | 168 } |
| 170 return isValidSignChallengeArray(signChallenges, !hasDefaultChallenge, | 169 return isValidSignChallengeArray( |
| 171 !hasAppId); | 170 signChallenges, !hasDefaultChallenge, !hasAppId); |
| 172 } | 171 } |
| 173 | 172 |
| 174 /** | 173 /** |
| 175 * Adapter class representing a queued sign request. | 174 * Adapter class representing a queued sign request. |
| 176 * @param {!Array<SignChallenge>} signChallenges The sign challenges. | 175 * @param {!Array<SignChallenge>} signChallenges The sign challenges. |
| 177 * @param {Countdown} timer Timeout timer | 176 * @param {Countdown} timer Timeout timer |
| 178 * @param {WebRequestSender} sender Message sender. | 177 * @param {WebRequestSender} sender Message sender. |
| 179 * @param {function(U2fError)} errorCb Error callback | 178 * @param {function(U2fError)} errorCb Error callback |
| 180 * @param {function(SignChallenge, string, string)} successCb Success callback | 179 * @param {function(SignChallenge, string, string)} successCb Success callback |
| 181 * @param {string|undefined} opt_defaultChallenge A default sign challenge | 180 * @param {string|undefined} opt_defaultChallenge A default sign challenge |
| 182 * value, if a request does not provide one. | 181 * value, if a request does not provide one. |
| 183 * @param {string|undefined} opt_appId The app id for the entire request. | 182 * @param {string|undefined} opt_appId The app id for the entire request. |
| 184 * @param {string|undefined} opt_logMsgUrl Url to post log messages to | 183 * @param {string|undefined} opt_logMsgUrl Url to post log messages to |
| 185 * @constructor | 184 * @constructor |
| 186 * @implements {Closeable} | 185 * @implements {Closeable} |
| 187 */ | 186 */ |
| 188 function QueuedSignRequest(signChallenges, timer, sender, errorCb, | 187 function QueuedSignRequest( |
| 189 successCb, opt_defaultChallenge, opt_appId, opt_logMsgUrl) { | 188 signChallenges, timer, sender, errorCb, successCb, opt_defaultChallenge, |
| 189 opt_appId, opt_logMsgUrl) { |
| 190 /** @private {!Array<SignChallenge>} */ | 190 /** @private {!Array<SignChallenge>} */ |
| 191 this.signChallenges_ = signChallenges; | 191 this.signChallenges_ = signChallenges; |
| 192 /** @private {Countdown} */ | 192 /** @private {Countdown} */ |
| 193 this.timer_ = timer.clone(this.close.bind(this)); | 193 this.timer_ = timer.clone(this.close.bind(this)); |
| 194 /** @private {WebRequestSender} */ | 194 /** @private {WebRequestSender} */ |
| 195 this.sender_ = sender; | 195 this.sender_ = sender; |
| 196 /** @private {function(U2fError)} */ | 196 /** @private {function(U2fError)} */ |
| 197 this.errorCb_ = errorCb; | 197 this.errorCb_ = errorCb; |
| 198 /** @private {function(SignChallenge, string, string)} */ | 198 /** @private {function(SignChallenge, string, string)} */ |
| 199 this.successCb_ = successCb; | 199 this.successCb_ = successCb; |
| 200 /** @private {string|undefined} */ | 200 /** @private {string|undefined} */ |
| 201 this.defaultChallenge_ = opt_defaultChallenge; | 201 this.defaultChallenge_ = opt_defaultChallenge; |
| 202 /** @private {string|undefined} */ | 202 /** @private {string|undefined} */ |
| 203 this.appId_ = opt_appId; | 203 this.appId_ = opt_appId; |
| 204 /** @private {string|undefined} */ | 204 /** @private {string|undefined} */ |
| 205 this.logMsgUrl_ = opt_logMsgUrl; | 205 this.logMsgUrl_ = opt_logMsgUrl; |
| 206 /** @private {boolean} */ | 206 /** @private {boolean} */ |
| 207 this.begun_ = false; | 207 this.begun_ = false; |
| 208 /** @private {boolean} */ | 208 /** @private {boolean} */ |
| 209 this.closed_ = false; | 209 this.closed_ = false; |
| 210 } | 210 } |
| 211 | 211 |
| 212 /** Closes this sign request. */ | 212 /** Closes this sign request. */ |
| 213 QueuedSignRequest.prototype.close = function() { | 213 QueuedSignRequest.prototype.close = function() { |
| 214 if (this.closed_) return; | 214 if (this.closed_) |
| 215 return; |
| 215 var hadBegunSigning = false; | 216 var hadBegunSigning = false; |
| 216 if (this.begun_ && this.signer_) { | 217 if (this.begun_ && this.signer_) { |
| 217 this.signer_.close(); | 218 this.signer_.close(); |
| 218 hadBegunSigning = true; | 219 hadBegunSigning = true; |
| 219 } | 220 } |
| 220 if (this.token_) { | 221 if (this.token_) { |
| 221 if (hadBegunSigning) { | 222 if (hadBegunSigning) { |
| 222 console.log(UTIL_fmt('closing in-progress request')); | 223 console.log(UTIL_fmt('closing in-progress request')); |
| 223 } else { | 224 } else { |
| 224 console.log(UTIL_fmt('closing timed-out request before processing')); | 225 console.log(UTIL_fmt('closing timed-out request before processing')); |
| (...skipping 17 matching lines...) Expand all Loading... |
| 242 */ | 243 */ |
| 243 QueuedSignRequest.prototype.begin = function(token) { | 244 QueuedSignRequest.prototype.begin = function(token) { |
| 244 if (this.timer_.expired()) { | 245 if (this.timer_.expired()) { |
| 245 console.log(UTIL_fmt('Queued request begun after timeout')); | 246 console.log(UTIL_fmt('Queued request begun after timeout')); |
| 246 this.close(); | 247 this.close(); |
| 247 this.errorCb_({errorCode: ErrorCodes.TIMEOUT}); | 248 this.errorCb_({errorCode: ErrorCodes.TIMEOUT}); |
| 248 return; | 249 return; |
| 249 } | 250 } |
| 250 this.begun_ = true; | 251 this.begun_ = true; |
| 251 this.setToken(token); | 252 this.setToken(token); |
| 252 this.signer_ = new Signer(this.timer_, this.sender_, | 253 this.signer_ = new Signer( |
| 253 this.signerFailed_.bind(this), this.signerSucceeded_.bind(this), | 254 this.timer_, this.sender_, this.signerFailed_.bind(this), |
| 254 this.logMsgUrl_); | 255 this.signerSucceeded_.bind(this), this.logMsgUrl_); |
| 255 if (!this.signer_.setChallenges(this.signChallenges_, this.defaultChallenge_, | 256 if (!this.signer_.setChallenges( |
| 256 this.appId_)) { | 257 this.signChallenges_, this.defaultChallenge_, this.appId_)) { |
| 257 token.complete(); | 258 token.complete(); |
| 258 this.errorCb_({errorCode: ErrorCodes.BAD_REQUEST}); | 259 this.errorCb_({errorCode: ErrorCodes.BAD_REQUEST}); |
| 259 } | 260 } |
| 260 // Signer now has responsibility for maintaining timeout. | 261 // Signer now has responsibility for maintaining timeout. |
| 261 this.timer_.clearTimeout(); | 262 this.timer_.clearTimeout(); |
| 262 }; | 263 }; |
| 263 | 264 |
| 264 /** | 265 /** |
| 265 * Called when this request's signer fails. | 266 * Called when this request's signer fails. |
| 266 * @param {U2fError} error The failure reported by the signer. | 267 * @param {U2fError} error The failure reported by the signer. |
| 267 * @private | 268 * @private |
| 268 */ | 269 */ |
| 269 QueuedSignRequest.prototype.signerFailed_ = function(error) { | 270 QueuedSignRequest.prototype.signerFailed_ = function(error) { |
| 270 this.token_.complete(); | 271 this.token_.complete(); |
| 271 this.errorCb_(error); | 272 this.errorCb_(error); |
| 272 }; | 273 }; |
| 273 | 274 |
| 274 /** | 275 /** |
| 275 * Called when this request's signer succeeds. | 276 * Called when this request's signer succeeds. |
| 276 * @param {SignChallenge} challenge The challenge that was signed. | 277 * @param {SignChallenge} challenge The challenge that was signed. |
| 277 * @param {string} info The sign result. | 278 * @param {string} info The sign result. |
| 278 * @param {string} browserData Browser data JSON | 279 * @param {string} browserData Browser data JSON |
| 279 * @private | 280 * @private |
| 280 */ | 281 */ |
| 281 QueuedSignRequest.prototype.signerSucceeded_ = | 282 QueuedSignRequest.prototype.signerSucceeded_ = function( |
| 282 function(challenge, info, browserData) { | 283 challenge, info, browserData) { |
| 283 this.token_.complete(); | 284 this.token_.complete(); |
| 284 this.successCb_(challenge, info, browserData); | 285 this.successCb_(challenge, info, browserData); |
| 285 }; | 286 }; |
| 286 | 287 |
| 287 /** | 288 /** |
| 288 * Creates an object to track signing with a gnubby. | 289 * Creates an object to track signing with a gnubby. |
| 289 * @param {Countdown} timer Timer for sign request. | 290 * @param {Countdown} timer Timer for sign request. |
| 290 * @param {WebRequestSender} sender The message sender. | 291 * @param {WebRequestSender} sender The message sender. |
| 291 * @param {function(U2fError)} errorCb Called when the sign operation fails. | 292 * @param {function(U2fError)} errorCb Called when the sign operation fails. |
| 292 * @param {function(SignChallenge, string, string)} successCb Called when the | 293 * @param {function(SignChallenge, string, string)} successCb Called when the |
| (...skipping 18 matching lines...) Expand all Loading... |
| 311 /** @private {boolean} */ | 312 /** @private {boolean} */ |
| 312 this.done_ = false; | 313 this.done_ = false; |
| 313 | 314 |
| 314 /** @private {Object<string, string>} */ | 315 /** @private {Object<string, string>} */ |
| 315 this.browserData_ = {}; | 316 this.browserData_ = {}; |
| 316 /** @private {Object<string, SignChallenge>} */ | 317 /** @private {Object<string, SignChallenge>} */ |
| 317 this.serverChallenges_ = {}; | 318 this.serverChallenges_ = {}; |
| 318 // Allow http appIds for http origins. (Broken, but the caller deserves | 319 // Allow http appIds for http origins. (Broken, but the caller deserves |
| 319 // what they get.) | 320 // what they get.) |
| 320 /** @private {boolean} */ | 321 /** @private {boolean} */ |
| 321 this.allowHttp_ = this.sender_.origin ? | 322 this.allowHttp_ = |
| 322 this.sender_.origin.indexOf('http://') == 0 : false; | 323 this.sender_.origin ? this.sender_.origin.indexOf('http://') == 0 : false; |
| 323 /** @private {Closeable} */ | 324 /** @private {Closeable} */ |
| 324 this.handler_ = null; | 325 this.handler_ = null; |
| 325 } | 326 } |
| 326 | 327 |
| 327 /** | 328 /** |
| 328 * Sets the challenges to be signed. | 329 * Sets the challenges to be signed. |
| 329 * @param {Array<SignChallenge>} signChallenges The challenges to set. | 330 * @param {Array<SignChallenge>} signChallenges The challenges to set. |
| 330 * @param {string=} opt_defaultChallenge A default sign challenge | 331 * @param {string=} opt_defaultChallenge A default sign challenge |
| 331 * value, if a request does not provide one. | 332 * value, if a request does not provide one. |
| 332 * @param {string=} opt_appId The app id for the entire request. | 333 * @param {string=} opt_appId The app id for the entire request. |
| 333 * @return {boolean} Whether the challenges could be set. | 334 * @return {boolean} Whether the challenges could be set. |
| 334 */ | 335 */ |
| 335 Signer.prototype.setChallenges = function(signChallenges, opt_defaultChallenge, | 336 Signer.prototype.setChallenges = function( |
| 336 opt_appId) { | 337 signChallenges, opt_defaultChallenge, opt_appId) { |
| 337 if (this.challengesSet_ || this.done_) | 338 if (this.challengesSet_ || this.done_) |
| 338 return false; | 339 return false; |
| 339 if (this.timer_.expired()) { | 340 if (this.timer_.expired()) { |
| 340 this.notifyError_({errorCode: ErrorCodes.TIMEOUT}); | 341 this.notifyError_({errorCode: ErrorCodes.TIMEOUT}); |
| 341 return true; | 342 return true; |
| 342 } | 343 } |
| 343 /** @private {Array<SignChallenge>} */ | 344 /** @private {Array<SignChallenge>} */ |
| 344 this.signChallenges_ = signChallenges; | 345 this.signChallenges_ = signChallenges; |
| 345 /** @private {string|undefined} */ | 346 /** @private {string|undefined} */ |
| 346 this.defaultChallenge_ = opt_defaultChallenge; | 347 this.defaultChallenge_ = opt_defaultChallenge; |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 378 /** | 379 /** |
| 379 * Called with the result of checking the origin. When the origin is allowed | 380 * Called with the result of checking the origin. When the origin is allowed |
| 380 * to claim the app ids, begins checking whether the app ids also list the | 381 * to claim the app ids, begins checking whether the app ids also list the |
| 381 * origin. | 382 * origin. |
| 382 * @param {!Array<string>} appIds The app ids. | 383 * @param {!Array<string>} appIds The app ids. |
| 383 * @param {boolean} result Whether the origin could claim the app ids. | 384 * @param {boolean} result Whether the origin could claim the app ids. |
| 384 * @private | 385 * @private |
| 385 */ | 386 */ |
| 386 Signer.prototype.originChecked_ = function(appIds, result) { | 387 Signer.prototype.originChecked_ = function(appIds, result) { |
| 387 if (!result) { | 388 if (!result) { |
| 388 var error = { | 389 var error = {errorCode: ErrorCodes.BAD_REQUEST, errorMessage: 'bad appId'}; |
| 389 errorCode: ErrorCodes.BAD_REQUEST, | |
| 390 errorMessage: 'bad appId' | |
| 391 }; | |
| 392 this.notifyError_(error); | 390 this.notifyError_(error); |
| 393 return; | 391 return; |
| 394 } | 392 } |
| 395 var appIdChecker = FACTORY_REGISTRY.getAppIdCheckerFactory().create(); | 393 var appIdChecker = FACTORY_REGISTRY.getAppIdCheckerFactory().create(); |
| 396 appIdChecker. | 394 appIdChecker |
| 397 checkAppIds( | 395 .checkAppIds( |
| 398 this.timer_.clone(), this.sender_.origin, | 396 this.timer_.clone(), this.sender_.origin, |
| 399 /** @type {!Array<string>} */ (appIds), this.allowHttp_, | 397 /** @type {!Array<string>} */ (appIds), this.allowHttp_, |
| 400 this.logMsgUrl_) | 398 this.logMsgUrl_) |
| 401 .then(this.appIdChecked_.bind(this)); | 399 .then(this.appIdChecked_.bind(this)); |
| 402 }; | 400 }; |
| 403 | 401 |
| 404 /** | 402 /** |
| 405 * Called with the result of checking app ids. When the app ids are valid, | 403 * Called with the result of checking app ids. When the app ids are valid, |
| 406 * adds the sign challenges to those being signed. | 404 * adds the sign challenges to those being signed. |
| 407 * @param {boolean} result Whether the app ids are valid. | 405 * @param {boolean} result Whether the app ids are valid. |
| 408 * @private | 406 * @private |
| 409 */ | 407 */ |
| 410 Signer.prototype.appIdChecked_ = function(result) { | 408 Signer.prototype.appIdChecked_ = function(result) { |
| 411 if (!result) { | 409 if (!result) { |
| 412 var error = { | 410 var error = {errorCode: ErrorCodes.BAD_REQUEST, errorMessage: 'bad appId'}; |
| 413 errorCode: ErrorCodes.BAD_REQUEST, | |
| 414 errorMessage: 'bad appId' | |
| 415 }; | |
| 416 this.notifyError_(error); | 411 this.notifyError_(error); |
| 417 return; | 412 return; |
| 418 } | 413 } |
| 419 if (!this.doSign_()) { | 414 if (!this.doSign_()) { |
| 420 this.notifyError_({errorCode: ErrorCodes.BAD_REQUEST}); | 415 this.notifyError_({errorCode: ErrorCodes.BAD_REQUEST}); |
| 421 return; | 416 return; |
| 422 } | 417 } |
| 423 }; | 418 }; |
| 424 | 419 |
| 425 /** | 420 /** |
| (...skipping 10 matching lines...) Expand all Loading... |
| 436 serverChallenge = challenge['challenge']; | 431 serverChallenge = challenge['challenge']; |
| 437 } else { | 432 } else { |
| 438 serverChallenge = this.defaultChallenge_; | 433 serverChallenge = this.defaultChallenge_; |
| 439 } | 434 } |
| 440 if (!serverChallenge) { | 435 if (!serverChallenge) { |
| 441 console.warn(UTIL_fmt('challenge missing')); | 436 console.warn(UTIL_fmt('challenge missing')); |
| 442 return false; | 437 return false; |
| 443 } | 438 } |
| 444 var keyHandle = challenge['keyHandle']; | 439 var keyHandle = challenge['keyHandle']; |
| 445 | 440 |
| 446 var browserData = | 441 var browserData = makeSignBrowserData( |
| 447 makeSignBrowserData(serverChallenge, this.sender_.origin, | 442 serverChallenge, this.sender_.origin, this.sender_.tlsChannelId); |
| 448 this.sender_.tlsChannelId); | |
| 449 this.browserData_[keyHandle] = browserData; | 443 this.browserData_[keyHandle] = browserData; |
| 450 this.serverChallenges_[keyHandle] = challenge; | 444 this.serverChallenges_[keyHandle] = challenge; |
| 451 } | 445 } |
| 452 | 446 |
| 453 var encodedChallenges = encodeSignChallenges(this.signChallenges_, | 447 var encodedChallenges = encodeSignChallenges( |
| 454 this.defaultChallenge_, this.appId_, this.getChallengeHash_.bind(this)); | 448 this.signChallenges_, this.defaultChallenge_, this.appId_, |
| 449 this.getChallengeHash_.bind(this)); |
| 455 | 450 |
| 456 var timeoutSeconds = this.timer_.millisecondsUntilExpired() / 1000.0; | 451 var timeoutSeconds = this.timer_.millisecondsUntilExpired() / 1000.0; |
| 457 var request = makeSignHelperRequest(encodedChallenges, timeoutSeconds, | 452 var request = |
| 458 this.logMsgUrl_); | 453 makeSignHelperRequest(encodedChallenges, timeoutSeconds, this.logMsgUrl_); |
| 459 this.handler_ = | 454 this.handler_ = FACTORY_REGISTRY.getRequestHelper().getHandler( |
| 460 FACTORY_REGISTRY.getRequestHelper() | 455 /** @type {HelperRequest} */ (request)); |
| 461 .getHandler(/** @type {HelperRequest} */ (request)); | |
| 462 if (!this.handler_) | 456 if (!this.handler_) |
| 463 return false; | 457 return false; |
| 464 return this.handler_.run(this.helperComplete_.bind(this)); | 458 return this.handler_.run(this.helperComplete_.bind(this)); |
| 465 }; | 459 }; |
| 466 | 460 |
| 467 /** | 461 /** |
| 468 * @param {string} keyHandle The key handle used with the challenge. | 462 * @param {string} keyHandle The key handle used with the challenge. |
| 469 * @param {string} challenge The challenge. | 463 * @param {string} challenge The challenge. |
| 470 * @return {string} The hashed challenge associated with the key | 464 * @return {string} The hashed challenge associated with the key |
| 471 * handle/challenge pair. | 465 * handle/challenge pair. |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 534 */ | 528 */ |
| 535 Signer.prototype.helperComplete_ = function(helperReply, opt_source) { | 529 Signer.prototype.helperComplete_ = function(helperReply, opt_source) { |
| 536 if (helperReply.type != 'sign_helper_reply') { | 530 if (helperReply.type != 'sign_helper_reply') { |
| 537 this.notifyError_({errorCode: ErrorCodes.OTHER_ERROR}); | 531 this.notifyError_({errorCode: ErrorCodes.OTHER_ERROR}); |
| 538 return; | 532 return; |
| 539 } | 533 } |
| 540 var reply = /** @type {SignHelperReply} */ (helperReply); | 534 var reply = /** @type {SignHelperReply} */ (helperReply); |
| 541 | 535 |
| 542 if (reply.code) { | 536 if (reply.code) { |
| 543 var reportedError = mapDeviceStatusCodeToU2fError(reply.code); | 537 var reportedError = mapDeviceStatusCodeToU2fError(reply.code); |
| 544 console.log(UTIL_fmt('helper reported ' + reply.code.toString(16) + | 538 console.log(UTIL_fmt( |
| 545 ', returning ' + reportedError.errorCode)); | 539 'helper reported ' + reply.code.toString(16) + ', returning ' + |
| 540 reportedError.errorCode)); |
| 546 this.notifyError_(reportedError); | 541 this.notifyError_(reportedError); |
| 547 } else { | 542 } else { |
| 548 if (this.logMsgUrl_ && opt_source) { | 543 if (this.logMsgUrl_ && opt_source) { |
| 549 var logMsg = 'signed&source=' + opt_source; | 544 var logMsg = 'signed&source=' + opt_source; |
| 550 logMessage(logMsg, this.logMsgUrl_); | 545 logMessage(logMsg, this.logMsgUrl_); |
| 551 } | 546 } |
| 552 | 547 |
| 553 var key = reply.responseData['keyHandle']; | 548 var key = reply.responseData['keyHandle']; |
| 554 var browserData = this.browserData_[key]; | 549 var browserData = this.browserData_[key]; |
| 555 // Notify with server-provided challenge, not the encoded one: the | 550 // Notify with server-provided challenge, not the encoded one: the |
| 556 // server-provided challenge contains additional fields it relies on. | 551 // server-provided challenge contains additional fields it relies on. |
| 557 var serverChallenge = this.serverChallenges_[key]; | 552 var serverChallenge = this.serverChallenges_[key]; |
| 558 this.notifySuccess_(serverChallenge, reply.responseData.signatureData, | 553 this.notifySuccess_( |
| 559 browserData); | 554 serverChallenge, reply.responseData.signatureData, browserData); |
| 560 } | 555 } |
| 561 }; | 556 }; |
| OLD | NEW |