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 Implements an enroll handler using USB gnubbies. | 6 * @fileoverview Implements an enroll handler using USB gnubbies. |
7 */ | 7 */ |
8 'use strict'; | 8 'use strict'; |
9 | 9 |
10 /** | 10 /** |
(...skipping 19 matching lines...) Expand all Loading... |
30 * @const | 30 * @const |
31 */ | 31 */ |
32 UsbEnrollHandler.DEFAULT_TIMEOUT_MILLIS = 30 * 1000; | 32 UsbEnrollHandler.DEFAULT_TIMEOUT_MILLIS = 30 * 1000; |
33 | 33 |
34 /** | 34 /** |
35 * @param {RequestHandlerCallback} cb Called back with the result of the | 35 * @param {RequestHandlerCallback} cb Called back with the result of the |
36 * request, and an optional source for the result. | 36 * request, and an optional source for the result. |
37 * @return {boolean} Whether this handler could be run. | 37 * @return {boolean} Whether this handler could be run. |
38 */ | 38 */ |
39 UsbEnrollHandler.prototype.run = function(cb) { | 39 UsbEnrollHandler.prototype.run = function(cb) { |
40 var timeoutMillis = | 40 var timeoutMillis = this.request_.timeoutSeconds ? |
41 this.request_.timeoutSeconds ? | |
42 this.request_.timeoutSeconds * 1000 : | 41 this.request_.timeoutSeconds * 1000 : |
43 UsbEnrollHandler.DEFAULT_TIMEOUT_MILLIS; | 42 UsbEnrollHandler.DEFAULT_TIMEOUT_MILLIS; |
44 /** @private {Countdown} */ | 43 /** @private {Countdown} */ |
45 this.timer_ = DEVICE_FACTORY_REGISTRY.getCountdownFactory().createTimer( | 44 this.timer_ = |
46 timeoutMillis); | 45 DEVICE_FACTORY_REGISTRY.getCountdownFactory().createTimer(timeoutMillis); |
47 this.enrollChallenges = this.request_.enrollChallenges; | 46 this.enrollChallenges = this.request_.enrollChallenges; |
48 /** @private {RequestHandlerCallback} */ | 47 /** @private {RequestHandlerCallback} */ |
49 this.cb_ = cb; | 48 this.cb_ = cb; |
50 this.signer_ = new MultipleGnubbySigner( | 49 this.signer_ = new MultipleGnubbySigner( |
51 true /* forEnroll */, | 50 true /* forEnroll */, this.signerCompleted_.bind(this), |
52 this.signerCompleted_.bind(this), | 51 this.signerFoundGnubby_.bind(this), timeoutMillis, |
53 this.signerFoundGnubby_.bind(this), | |
54 timeoutMillis, | |
55 this.request_.logMsgUrl); | 52 this.request_.logMsgUrl); |
56 return this.signer_.doSign(this.request_.signData); | 53 return this.signer_.doSign(this.request_.signData); |
57 }; | 54 }; |
58 | 55 |
59 /** Closes this helper. */ | 56 /** Closes this helper. */ |
60 UsbEnrollHandler.prototype.close = function() { | 57 UsbEnrollHandler.prototype.close = function() { |
61 this.closed_ = true; | 58 this.closed_ = true; |
62 for (var i = 0; i < this.waitingForTouchGnubbies_.length; i++) { | 59 for (var i = 0; i < this.waitingForTouchGnubbies_.length; i++) { |
63 this.waitingForTouchGnubbies_[i].closeWhenIdle(); | 60 this.waitingForTouchGnubbies_[i].closeWhenIdle(); |
64 } | 61 } |
(...skipping 19 matching lines...) Expand all Loading... |
84 } | 81 } |
85 }; | 82 }; |
86 | 83 |
87 /** | 84 /** |
88 * Called when a MultipleGnubbySigner finds a gnubby that can enroll. | 85 * Called when a MultipleGnubbySigner finds a gnubby that can enroll. |
89 * @param {MultipleSignerResult} signResult Signature results | 86 * @param {MultipleSignerResult} signResult Signature results |
90 * @param {boolean} moreExpected Whether the signer expects to report | 87 * @param {boolean} moreExpected Whether the signer expects to report |
91 * results from more gnubbies. | 88 * results from more gnubbies. |
92 * @private | 89 * @private |
93 */ | 90 */ |
94 UsbEnrollHandler.prototype.signerFoundGnubby_ = | 91 UsbEnrollHandler.prototype.signerFoundGnubby_ = function( |
95 function(signResult, moreExpected) { | 92 signResult, moreExpected) { |
96 if (!signResult.code) { | 93 if (!signResult.code) { |
97 // If the signer reports a gnubby can sign, report this immediately to the | 94 // If the signer reports a gnubby can sign, report this immediately to the |
98 // caller, as the gnubby is already enrolled. Map ok to WRONG_DATA, so the | 95 // caller, as the gnubby is already enrolled. Map ok to WRONG_DATA, so the |
99 // caller knows what to do. | 96 // caller knows what to do. |
100 this.notifyError_(DeviceStatusCodes.WRONG_DATA_STATUS); | 97 this.notifyError_(DeviceStatusCodes.WRONG_DATA_STATUS); |
101 } else if (SingleGnubbySigner.signErrorIndicatesInvalidKeyHandle( | 98 } else if (SingleGnubbySigner.signErrorIndicatesInvalidKeyHandle( |
102 signResult.code)) { | 99 signResult.code)) { |
103 var gnubby = signResult['gnubby']; | 100 var gnubby = signResult['gnubby']; |
104 // A valid helper request contains at least one enroll challenge, so use | 101 // A valid helper request contains at least one enroll challenge, so use |
105 // the app id hash from the first challenge. | 102 // the app id hash from the first challenge. |
106 var appIdHash = this.request_.enrollChallenges[0].appIdHash; | 103 var appIdHash = this.request_.enrollChallenges[0].appIdHash; |
107 DEVICE_FACTORY_REGISTRY.getGnubbyFactory().notEnrolledPrerequisiteCheck( | 104 DEVICE_FACTORY_REGISTRY.getGnubbyFactory().notEnrolledPrerequisiteCheck( |
108 gnubby, appIdHash, this.gnubbyPrerequisitesChecked_.bind(this)); | 105 gnubby, appIdHash, this.gnubbyPrerequisitesChecked_.bind(this)); |
109 } else { | 106 } else { |
110 // Unexpected error in signing? Send this immediately to the caller. | 107 // Unexpected error in signing? Send this immediately to the caller. |
111 this.notifyError_(signResult.code); | 108 this.notifyError_(signResult.code); |
112 } | 109 } |
113 }; | 110 }; |
114 | 111 |
115 /** | 112 /** |
116 * Called with the result of a gnubby prerequisite check. | 113 * Called with the result of a gnubby prerequisite check. |
117 * @param {number} rc The result of the prerequisite check. | 114 * @param {number} rc The result of the prerequisite check. |
118 * @param {Gnubby=} opt_gnubby The gnubby whose prerequisites were checked. | 115 * @param {Gnubby=} opt_gnubby The gnubby whose prerequisites were checked. |
119 * @private | 116 * @private |
120 */ | 117 */ |
121 UsbEnrollHandler.prototype.gnubbyPrerequisitesChecked_ = | 118 UsbEnrollHandler.prototype.gnubbyPrerequisitesChecked_ = function( |
122 function(rc, opt_gnubby) { | 119 rc, opt_gnubby) { |
123 if (rc || this.timer_.expired()) { | 120 if (rc || this.timer_.expired()) { |
124 // Do nothing: | 121 // Do nothing: |
125 // If the timer is expired, the signerCompleted_ callback will indicate | 122 // If the timer is expired, the signerCompleted_ callback will indicate |
126 // timeout to the caller. | 123 // timeout to the caller. |
127 // If there's an error, this gnubby is ineligible, but there's nothing we | 124 // If there's an error, this gnubby is ineligible, but there's nothing we |
128 // can do about that here. | 125 // can do about that here. |
129 return; | 126 return; |
130 } | 127 } |
131 // If the callback succeeded, the gnubby is not null. | 128 // If the callback succeeded, the gnubby is not null. |
132 var gnubby = /** @type {Gnubby} */ (opt_gnubby); | 129 var gnubby = /** @type {Gnubby} */ (opt_gnubby); |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
205 * @private | 202 * @private |
206 */ | 203 */ |
207 UsbEnrollHandler.prototype.tryEnroll_ = function(gnubby, version) { | 204 UsbEnrollHandler.prototype.tryEnroll_ = function(gnubby, version) { |
208 var challenge = this.getChallengeOfVersion_(version); | 205 var challenge = this.getChallengeOfVersion_(version); |
209 if (!challenge) { | 206 if (!challenge) { |
210 this.removeWrongVersionGnubby_(gnubby); | 207 this.removeWrongVersionGnubby_(gnubby); |
211 return; | 208 return; |
212 } | 209 } |
213 var challengeValue = B64_decode(challenge['challengeHash']); | 210 var challengeValue = B64_decode(challenge['challengeHash']); |
214 var appIdHash = challenge['appIdHash']; | 211 var appIdHash = challenge['appIdHash']; |
215 var individualAttest = | 212 var individualAttest = DEVICE_FACTORY_REGISTRY.getIndividualAttestation() |
216 DEVICE_FACTORY_REGISTRY.getIndividualAttestation(). | 213 .requestIndividualAttestation(appIdHash); |
217 requestIndividualAttestation(appIdHash); | 214 gnubby.enroll( |
218 gnubby.enroll(challengeValue, B64_decode(appIdHash), | 215 challengeValue, B64_decode(appIdHash), |
219 this.enrollCallback_.bind(this, gnubby, version), individualAttest); | 216 this.enrollCallback_.bind(this, gnubby, version), individualAttest); |
220 }; | 217 }; |
221 | 218 |
222 /** | 219 /** |
223 * Finds the (first) challenge of the given version in this helper's challenges. | 220 * Finds the (first) challenge of the given version in this helper's challenges. |
224 * @param {string} version Protocol version | 221 * @param {string} version Protocol version |
225 * @return {Object} challenge, if found, or null if not. | 222 * @return {Object} challenge, if found, or null if not. |
226 * @private | 223 * @private |
227 */ | 224 */ |
228 UsbEnrollHandler.prototype.getChallengeOfVersion_ = function(version) { | 225 UsbEnrollHandler.prototype.getChallengeOfVersion_ = function(version) { |
229 for (var i = 0; i < this.enrollChallenges.length; i++) { | 226 for (var i = 0; i < this.enrollChallenges.length; i++) { |
230 if (this.enrollChallenges[i]['version'] == version) { | 227 if (this.enrollChallenges[i]['version'] == version) { |
231 return this.enrollChallenges[i]; | 228 return this.enrollChallenges[i]; |
232 } | 229 } |
233 } | 230 } |
234 return null; | 231 return null; |
235 }; | 232 }; |
236 | 233 |
237 /** | 234 /** |
238 * Called with the result of an enroll request to a gnubby. | 235 * Called with the result of an enroll request to a gnubby. |
239 * @param {Gnubby} gnubby Gnubby instance | 236 * @param {Gnubby} gnubby Gnubby instance |
240 * @param {string} version Protocol version | 237 * @param {string} version Protocol version |
241 * @param {number} code Status code | 238 * @param {number} code Status code |
242 * @param {ArrayBuffer=} infoArray Returned data | 239 * @param {ArrayBuffer=} infoArray Returned data |
243 * @private | 240 * @private |
244 */ | 241 */ |
245 UsbEnrollHandler.prototype.enrollCallback_ = | 242 UsbEnrollHandler.prototype.enrollCallback_ = function( |
246 function(gnubby, version, code, infoArray) { | 243 gnubby, version, code, infoArray) { |
247 if (this.notified_) { | 244 if (this.notified_) { |
248 // Enroll completed after previous success or failure. Disregard. | 245 // Enroll completed after previous success or failure. Disregard. |
249 return; | 246 return; |
250 } | 247 } |
251 switch (code) { | 248 switch (code) { |
252 case -GnubbyDevice.GONE: | 249 case -GnubbyDevice.GONE: |
253 // Close this gnubby. | 250 // Close this gnubby. |
254 this.removeWaitingGnubby_(gnubby); | 251 this.removeWaitingGnubby_(gnubby); |
255 if (!this.waitingForTouchGnubbies_.length) { | 252 if (!this.waitingForTouchGnubbies_.length) { |
256 // Last enroll attempt is complete and last gnubby is gone. | 253 // Last enroll attempt is complete and last gnubby is gone. |
257 this.anyGnubbiesFound_ = false; | 254 this.anyGnubbiesFound_ = false; |
258 if (this.timer_.expired()) { | 255 if (this.timer_.expired()) { |
259 this.notifyError_(DeviceStatusCodes.TIMEOUT_STATUS); | 256 this.notifyError_(DeviceStatusCodes.TIMEOUT_STATUS); |
260 } else if (this.signer_) { | 257 } else if (this.signer_) { |
261 this.signer_.reScanDevices(); | 258 this.signer_.reScanDevices(); |
262 } | |
263 } | 259 } |
| 260 } |
264 break; | 261 break; |
265 | 262 |
266 case DeviceStatusCodes.WAIT_TOUCH_STATUS: | 263 case DeviceStatusCodes.WAIT_TOUCH_STATUS: |
267 case DeviceStatusCodes.BUSY_STATUS: | 264 case DeviceStatusCodes.BUSY_STATUS: |
268 case DeviceStatusCodes.TIMEOUT_STATUS: | 265 case DeviceStatusCodes.TIMEOUT_STATUS: |
269 if (this.timer_.expired()) { | 266 if (this.timer_.expired()) { |
270 // Record that at least one gnubby timed out, to return a timeout status | 267 // Record that at least one gnubby timed out, to return a timeout status |
271 // from the complete callback if no other eligible gnubbies are found. | 268 // from the complete callback if no other eligible gnubbies are found. |
272 /** @private {boolean} */ | 269 /** @private {boolean} */ |
273 this.anyTimeout_ = true; | 270 this.anyTimeout_ = true; |
274 // Close this gnubby. | 271 // Close this gnubby. |
275 this.removeWaitingGnubby_(gnubby); | 272 this.removeWaitingGnubby_(gnubby); |
276 if (!this.waitingForTouchGnubbies_.length) { | 273 if (!this.waitingForTouchGnubbies_.length) { |
277 // Last enroll attempt is complete: return this error. | 274 // Last enroll attempt is complete: return this error. |
278 console.log(UTIL_fmt('timeout (' + code.toString(16) + | 275 console.log( |
279 ') enrolling')); | 276 UTIL_fmt('timeout (' + code.toString(16) + ') enrolling')); |
280 this.notifyError_(DeviceStatusCodes.TIMEOUT_STATUS); | 277 this.notifyError_(DeviceStatusCodes.TIMEOUT_STATUS); |
281 } | 278 } |
282 } else { | 279 } else { |
283 DEVICE_FACTORY_REGISTRY.getCountdownFactory().createTimer( | 280 DEVICE_FACTORY_REGISTRY.getCountdownFactory().createTimer( |
284 UsbEnrollHandler.ENUMERATE_DELAY_INTERVAL_MILLIS, | 281 UsbEnrollHandler.ENUMERATE_DELAY_INTERVAL_MILLIS, |
285 this.tryEnroll_.bind(this, gnubby, version)); | 282 this.tryEnroll_.bind(this, gnubby, version)); |
286 } | 283 } |
287 break; | 284 break; |
288 | 285 |
289 case DeviceStatusCodes.OK_STATUS: | 286 case DeviceStatusCodes.OK_STATUS: |
(...skipping 17 matching lines...) Expand all Loading... |
307 /** | 304 /** |
308 * Notifies the callback with an error code. | 305 * Notifies the callback with an error code. |
309 * @param {number} code The error code to report. | 306 * @param {number} code The error code to report. |
310 * @private | 307 * @private |
311 */ | 308 */ |
312 UsbEnrollHandler.prototype.notifyError_ = function(code) { | 309 UsbEnrollHandler.prototype.notifyError_ = function(code) { |
313 if (this.notified_ || this.closed_) | 310 if (this.notified_ || this.closed_) |
314 return; | 311 return; |
315 this.notified_ = true; | 312 this.notified_ = true; |
316 this.close(); | 313 this.close(); |
317 var reply = { | 314 var reply = {'type': 'enroll_helper_reply', 'code': code}; |
318 'type': 'enroll_helper_reply', | |
319 'code': code | |
320 }; | |
321 this.cb_(reply); | 315 this.cb_(reply); |
322 }; | 316 }; |
323 | 317 |
324 /** | 318 /** |
325 * @param {string} version Protocol version | 319 * @param {string} version Protocol version |
326 * @param {string} info B64 encoded success data | 320 * @param {string} info B64 encoded success data |
327 * @private | 321 * @private |
328 */ | 322 */ |
329 UsbEnrollHandler.prototype.notifySuccess_ = function(version, info) { | 323 UsbEnrollHandler.prototype.notifySuccess_ = function(version, info) { |
330 if (this.notified_ || this.closed_) | 324 if (this.notified_ || this.closed_) |
331 return; | 325 return; |
332 this.notified_ = true; | 326 this.notified_ = true; |
333 this.close(); | 327 this.close(); |
334 var reply = { | 328 var reply = { |
335 'type': 'enroll_helper_reply', | 329 'type': 'enroll_helper_reply', |
336 'code': DeviceStatusCodes.OK_STATUS, | 330 'code': DeviceStatusCodes.OK_STATUS, |
337 'version': version, | 331 'version': version, |
338 'enrollData': info | 332 'enrollData': info |
339 }; | 333 }; |
340 this.cb_(reply); | 334 this.cb_(reply); |
341 }; | 335 }; |
OLD | NEW |