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 |