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 A single gnubby signer wraps the process of opening a gnubby, | 6 * @fileoverview A single gnubby signer wraps the process of opening a gnubby, |
7 * signing each challenge in an array of challenges until a success condition | 7 * signing each challenge in an array of challenges until a success condition |
8 * is satisfied, and finally yielding the gnubby upon success. | 8 * is satisfied, and finally yielding the gnubby upon success. |
9 * | 9 * |
10 */ | 10 */ |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
65 /** @private {string|undefined} */ | 65 /** @private {string|undefined} */ |
66 this.logMsgUrl_ = opt_logMsgUrl; | 66 this.logMsgUrl_ = opt_logMsgUrl; |
67 | 67 |
68 /** @private {!Array.<!SignHelperChallenge>} */ | 68 /** @private {!Array.<!SignHelperChallenge>} */ |
69 this.challenges_ = []; | 69 this.challenges_ = []; |
70 /** @private {number} */ | 70 /** @private {number} */ |
71 this.challengeIndex_ = 0; | 71 this.challengeIndex_ = 0; |
72 /** @private {boolean} */ | 72 /** @private {boolean} */ |
73 this.challengesSet_ = false; | 73 this.challengesSet_ = false; |
74 | 74 |
75 /** @private {!Array.<string>} */ | 75 /** @private {!Object.<string, number>} */ |
76 this.notForMe_ = []; | 76 this.cachedError_ = []; |
77 } | 77 } |
78 | 78 |
79 /** @enum {number} */ | 79 /** @enum {number} */ |
80 SingleGnubbySigner.State = { | 80 SingleGnubbySigner.State = { |
81 /** Initial state. */ | 81 /** Initial state. */ |
82 INIT: 0, | 82 INIT: 0, |
83 /** The signer is attempting to open a gnubby. */ | 83 /** The signer is attempting to open a gnubby. */ |
84 OPENING: 1, | 84 OPENING: 1, |
85 /** The signer's gnubby opened, but is busy. */ | 85 /** The signer's gnubby opened, but is busy. */ |
86 BUSY: 2, | 86 BUSY: 2, |
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
235 } | 235 } |
236 }, SingleGnubbySigner.OPEN_DELAY_MILLIS); | 236 }, SingleGnubbySigner.OPEN_DELAY_MILLIS); |
237 } else { | 237 } else { |
238 this.goToError_(DeviceStatusCodes.BUSY_STATUS); | 238 this.goToError_(DeviceStatusCodes.BUSY_STATUS); |
239 } | 239 } |
240 break; | 240 break; |
241 default: | 241 default: |
242 // TODO: This won't be confused with success, but should it be | 242 // TODO: This won't be confused with success, but should it be |
243 // part of the same namespace as the other error codes, which are | 243 // part of the same namespace as the other error codes, which are |
244 // always in DeviceStatusCodes.*? | 244 // always in DeviceStatusCodes.*? |
245 this.goToError_(rc); | 245 this.goToError_(rc, true); |
246 } | 246 } |
247 }; | 247 }; |
248 | 248 |
249 /** | 249 /** |
250 * Called with the result of a version command. | 250 * Called with the result of a version command. |
251 * @param {number} rc Result of version command. | 251 * @param {number} rc Result of version command. |
252 * @param {ArrayBuffer=} opt_data Version. | 252 * @param {ArrayBuffer=} opt_data Version. |
253 * @private | 253 * @private |
254 */ | 254 */ |
255 SingleGnubbySigner.prototype.versionCallback_ = function(rc, opt_data) { | 255 SingleGnubbySigner.prototype.versionCallback_ = function(rc, opt_data) { |
256 if (rc) { | 256 if (rc) { |
257 this.goToError_(rc); | 257 this.goToError_(rc, true); |
258 return; | 258 return; |
259 } | 259 } |
260 this.state_ = SingleGnubbySigner.State.IDLE; | 260 this.state_ = SingleGnubbySigner.State.IDLE; |
261 this.version_ = UTIL_BytesToString(new Uint8Array(opt_data || [])); | 261 this.version_ = UTIL_BytesToString(new Uint8Array(opt_data || [])); |
262 this.doSign_(this.challengeIndex_); | 262 this.doSign_(this.challengeIndex_); |
263 }; | 263 }; |
264 | 264 |
265 /** | 265 /** |
266 * @param {number} challengeIndex Index of challenge to sign | 266 * @param {number} challengeIndex Index of challenge to sign |
267 * @private | 267 * @private |
(...skipping 20 matching lines...) Expand all Loading... |
288 | 288 |
289 if (challengeIndex >= this.challenges_.length) { | 289 if (challengeIndex >= this.challenges_.length) { |
290 this.signCallback_(challengeIndex, DeviceStatusCodes.WRONG_DATA_STATUS); | 290 this.signCallback_(challengeIndex, DeviceStatusCodes.WRONG_DATA_STATUS); |
291 return; | 291 return; |
292 } | 292 } |
293 | 293 |
294 var challenge = this.challenges_[challengeIndex]; | 294 var challenge = this.challenges_[challengeIndex]; |
295 var challengeHash = challenge.challengeHash; | 295 var challengeHash = challenge.challengeHash; |
296 var appIdHash = challenge.appIdHash; | 296 var appIdHash = challenge.appIdHash; |
297 var keyHandle = challenge.keyHandle; | 297 var keyHandle = challenge.keyHandle; |
298 if (this.notForMe_.indexOf(keyHandle) != -1) { | 298 if (this.cachedError_.hasOwnProperty(keyHandle)) { |
299 // Cache hit: return wrong data again. | 299 // Cache hit: return wrong data again. |
300 this.signCallback_(challengeIndex, DeviceStatusCodes.WRONG_DATA_STATUS); | 300 this.signCallback_(challengeIndex, this.cachedError_[keyHandle]); |
301 } else if (challenge.version && challenge.version != this.version_) { | 301 } else if (challenge.version && challenge.version != this.version_) { |
302 // Sign challenge for a different version of gnubby: return wrong data. | 302 // Sign challenge for a different version of gnubby: return wrong data. |
303 this.signCallback_(challengeIndex, DeviceStatusCodes.WRONG_DATA_STATUS); | 303 this.signCallback_(challengeIndex, DeviceStatusCodes.WRONG_DATA_STATUS); |
304 } else { | 304 } else { |
305 var nowink = false; | 305 var nowink = false; |
306 this.gnubby_.sign(challengeHash, appIdHash, keyHandle, | 306 this.gnubby_.sign(challengeHash, appIdHash, keyHandle, |
307 this.signCallback_.bind(this, challengeIndex), | 307 this.signCallback_.bind(this, challengeIndex), |
308 nowink); | 308 nowink); |
309 } | 309 } |
310 }; | 310 }; |
311 | 311 |
312 /** | 312 /** |
313 * Called with the result of a single sign operation. | 313 * Called with the result of a single sign operation. |
314 * @param {number} challengeIndex the index of the challenge just attempted | 314 * @param {number} challengeIndex the index of the challenge just attempted |
315 * @param {number} code the result of the sign operation | 315 * @param {number} code the result of the sign operation |
316 * @param {ArrayBuffer=} opt_info Optional result data | 316 * @param {ArrayBuffer=} opt_info Optional result data |
317 * @private | 317 * @private |
318 */ | 318 */ |
319 SingleGnubbySigner.prototype.signCallback_ = | 319 SingleGnubbySigner.prototype.signCallback_ = |
320 function(challengeIndex, code, opt_info) { | 320 function(challengeIndex, code, opt_info) { |
321 console.log(UTIL_fmt('gnubby ' + JSON.stringify(this.gnubbyId_) + | 321 console.log(UTIL_fmt('gnubby ' + JSON.stringify(this.gnubbyId_) + |
322 ', challenge ' + challengeIndex + ' yielded ' + code.toString(16))); | 322 ', challenge ' + challengeIndex + ' yielded ' + code.toString(16))); |
323 if (this.state_ != SingleGnubbySigner.State.SIGNING) { | 323 if (this.state_ != SingleGnubbySigner.State.SIGNING) { |
324 console.log(UTIL_fmt('already done!')); | 324 console.log(UTIL_fmt('already done!')); |
325 // We're done, the caller's no longer interested. | 325 // We're done, the caller's no longer interested. |
326 return; | 326 return; |
327 } | 327 } |
328 | 328 |
329 // Cache wrong data result, re-asking the gnubby to sign it won't produce | 329 // Cache wrong data or wrong length results, re-asking the gnubby to sign it |
330 // different results. | 330 // won't produce different results. |
331 if (code == DeviceStatusCodes.WRONG_DATA_STATUS) { | 331 if (code == DeviceStatusCodes.WRONG_DATA_STATUS || |
| 332 code == DeviceStatusCodes.WRONG_LENGTH_STATUS) { |
332 if (challengeIndex < this.challenges_.length) { | 333 if (challengeIndex < this.challenges_.length) { |
333 var challenge = this.challenges_[challengeIndex]; | 334 var challenge = this.challenges_[challengeIndex]; |
334 if (this.notForMe_.indexOf(challenge.keyHandle) == -1) { | 335 if (!this.cachedError_.hasOwnProperty(challenge.keyHandle)) { |
335 this.notForMe_.push(challenge.keyHandle); | 336 this.cachedError_[challenge.keyHandle] = code; |
336 } | 337 } |
337 } | 338 } |
338 } | 339 } |
339 | 340 |
340 var self = this; | 341 var self = this; |
341 switch (code) { | 342 switch (code) { |
342 case DeviceStatusCodes.GONE_STATUS: | 343 case DeviceStatusCodes.GONE_STATUS: |
343 this.goToError_(code); | 344 this.goToError_(code); |
344 break; | 345 break; |
345 | 346 |
(...skipping 11 matching lines...) Expand all Loading... |
357 } | 358 } |
358 break; | 359 break; |
359 | 360 |
360 case DeviceStatusCodes.WAIT_TOUCH_STATUS: | 361 case DeviceStatusCodes.WAIT_TOUCH_STATUS: |
361 window.setTimeout(function() { | 362 window.setTimeout(function() { |
362 self.doSign_(self.challengeIndex_); | 363 self.doSign_(self.challengeIndex_); |
363 }, SingleGnubbySigner.SIGN_DELAY_MILLIS); | 364 }, SingleGnubbySigner.SIGN_DELAY_MILLIS); |
364 break; | 365 break; |
365 | 366 |
366 case DeviceStatusCodes.WRONG_DATA_STATUS: | 367 case DeviceStatusCodes.WRONG_DATA_STATUS: |
| 368 case DeviceStatusCodes.WRONG_LENGTH_STATUS: |
367 if (this.challengeIndex_ < this.challenges_.length - 1) { | 369 if (this.challengeIndex_ < this.challenges_.length - 1) { |
368 this.doSign_(++this.challengeIndex_); | 370 this.doSign_(++this.challengeIndex_); |
369 } else if (this.forEnroll_) { | 371 } else if (this.forEnroll_) { |
370 this.goToSuccess_(code); | 372 this.goToSuccess_(code); |
371 } else { | 373 } else { |
372 this.goToError_(code); | 374 this.goToError_(code); |
373 } | 375 } |
374 break; | 376 break; |
375 | 377 |
376 default: | 378 default: |
377 if (this.forEnroll_) { | 379 if (this.forEnroll_) { |
378 this.goToError_(code); | 380 this.goToError_(code, true); |
379 } else if (this.challengeIndex_ < this.challenges_.length - 1) { | 381 } else if (this.challengeIndex_ < this.challenges_.length - 1) { |
380 this.doSign_(++this.challengeIndex_); | 382 this.doSign_(++this.challengeIndex_); |
381 } else { | 383 } else { |
382 this.goToError_(code); | 384 this.goToError_(code, true); |
383 } | 385 } |
384 } | 386 } |
385 }; | 387 }; |
386 | 388 |
387 /** | 389 /** |
388 * Switches to the error state, and notifies caller. | 390 * Switches to the error state, and notifies caller. |
389 * @param {number} code Error code | 391 * @param {number} code Error code |
| 392 * @param {boolean=} opt_warn Whether to warn in the console about the error. |
390 * @private | 393 * @private |
391 */ | 394 */ |
392 SingleGnubbySigner.prototype.goToError_ = function(code) { | 395 SingleGnubbySigner.prototype.goToError_ = function(code, opt_warn) { |
393 this.state_ = SingleGnubbySigner.State.COMPLETE; | 396 this.state_ = SingleGnubbySigner.State.COMPLETE; |
394 console.log(UTIL_fmt('failed (' + code.toString(16) + ')')); | 397 var logFn = opt_warn ? console.warn.bind(console) : console.log.bind(console); |
| 398 logFn(UTIL_fmt('failed (' + code.toString(16) + ')')); |
395 // Since this gnubby can no longer produce a useful result, go ahead and | 399 // Since this gnubby can no longer produce a useful result, go ahead and |
396 // close it. | 400 // close it. |
397 this.close(); | 401 this.close(); |
398 var result = { code: code }; | 402 var result = { code: code }; |
399 this.completeCb_(result); | 403 this.completeCb_(result); |
400 }; | 404 }; |
401 | 405 |
402 /** | 406 /** |
403 * Switches to the success state, and notifies caller. | 407 * Switches to the success state, and notifies caller. |
404 * @param {number} code Status code | 408 * @param {number} code Status code |
(...skipping 11 matching lines...) Expand all Loading... |
416 result['challenge'] = opt_challenge; | 420 result['challenge'] = opt_challenge; |
417 } | 421 } |
418 if (opt_info) { | 422 if (opt_info) { |
419 result['info'] = opt_info; | 423 result['info'] = opt_info; |
420 } | 424 } |
421 } | 425 } |
422 this.completeCb_(result); | 426 this.completeCb_(result); |
423 // this.gnubby_ is now owned by completeCb_. | 427 // this.gnubby_ is now owned by completeCb_. |
424 this.gnubby_ = null; | 428 this.gnubby_ = null; |
425 }; | 429 }; |
OLD | NEW |