• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
3// found in the LICENSE file.
4
5/**
6 * @fileoverview Implements a sign handler using USB gnubbies.
7 */
8'use strict';
9
10var CORRUPT_sign = false;
11
12/**
13 * @param {!SignHelperRequest} request The sign request.
14 * @constructor
15 * @implements {RequestHandler}
16 */
17function UsbSignHandler(request) {
18  /** @private {!SignHelperRequest} */
19  this.request_ = request;
20
21  /** @private {boolean} */
22  this.notified_ = false;
23  /** @private {boolean} */
24  this.anyGnubbiesFound_ = false;
25}
26
27/**
28 * Default timeout value in case the caller never provides a valid timeout.
29 * @const
30 */
31UsbSignHandler.DEFAULT_TIMEOUT_MILLIS = 30 * 1000;
32
33/**
34 * Attempts to run this handler's request.
35 * @param {RequestHandlerCallback} cb Called with the result of the request and
36 *     an optional source for the sign result.
37 * @return {boolean} whether this set of challenges was accepted.
38 */
39UsbSignHandler.prototype.run = function(cb) {
40  if (this.cb_) {
41    // Can only handle one request.
42    return false;
43  }
44  /** @private {RequestHandlerCallback} */
45  this.cb_ = cb;
46  if (!this.request_.signData || !this.request_.signData.length) {
47    // Fail a sign request with an empty set of challenges, and pretend to have
48    // alerted the caller in case the enumerate is still pending.
49    this.notified_ = true;
50    return false;
51  }
52  var timeoutMillis =
53      this.request_.timeoutSeconds ?
54      this.request_.timeoutSeconds * 1000 :
55      UsbSignHandler.DEFAULT_TIMEOUT_MILLIS;
56  /** @private {MultipleGnubbySigner} */
57  this.signer_ = new MultipleGnubbySigner(
58      false /* forEnroll */,
59      this.signerCompleted_.bind(this),
60      this.signerFoundGnubby_.bind(this),
61      timeoutMillis,
62      this.request_.logMsgUrl);
63  return this.signer_.doSign(this.request_.signData);
64};
65
66
67/**
68 * Called when a MultipleGnubbySigner completes.
69 * @param {boolean} anyPending Whether any gnubbies are pending.
70 * @private
71 */
72UsbSignHandler.prototype.signerCompleted_ = function(anyPending) {
73  if (!this.anyGnubbiesFound_ || anyPending) {
74    this.notifyError_(DeviceStatusCodes.TIMEOUT_STATUS);
75  } else if (this.signerError_ !== undefined) {
76    this.notifyError_(this.signerError_);
77  } else {
78    // Do nothing: signerFoundGnubby_ will have returned results from other
79    // gnubbies.
80  }
81};
82
83/**
84 * Called when a MultipleGnubbySigner finds a gnubby that has completed signing
85 * its challenges.
86 * @param {MultipleSignerResult} signResult Signer result object
87 * @param {boolean} moreExpected Whether the signer expects to produce more
88 *     results.
89 * @private
90 */
91UsbSignHandler.prototype.signerFoundGnubby_ =
92    function(signResult, moreExpected) {
93  this.anyGnubbiesFound_ = true;
94  if (!signResult.code) {
95    var gnubby = signResult['gnubby'];
96    var challenge = signResult['challenge'];
97    var info = new Uint8Array(signResult['info']);
98    this.notifySuccess_(gnubby, challenge, info);
99  } else if (!moreExpected) {
100    // If the signer doesn't expect more results, return the error directly to
101    // the caller.
102    this.notifyError_(signResult.code);
103  } else {
104    // Record the last error, to report from the complete callback if no other
105    // eligible gnubbies are found.
106    /** @private {number} */
107    this.signerError_ = signResult.code;
108  }
109};
110
111/**
112 * Reports the result of a successful sign operation.
113 * @param {Gnubby} gnubby Gnubby instance
114 * @param {SignHelperChallenge} challenge Challenge signed
115 * @param {Uint8Array} info Result data
116 * @private
117 */
118UsbSignHandler.prototype.notifySuccess_ = function(gnubby, challenge, info) {
119  if (this.notified_)
120    return;
121  this.notified_ = true;
122
123  gnubby.closeWhenIdle();
124  this.close();
125
126  if (CORRUPT_sign) {
127    CORRUPT_sign = false;
128    info[info.length - 1] = info[info.length - 1] ^ 0xff;
129  }
130  var responseData = {
131    'appIdHash': B64_encode(challenge['appIdHash']),
132    'challengeHash': B64_encode(challenge['challengeHash']),
133    'keyHandle': B64_encode(challenge['keyHandle']),
134    'signatureData': B64_encode(info)
135  };
136  var reply = {
137    'type': 'sign_helper_reply',
138    'code': DeviceStatusCodes.OK_STATUS,
139    'responseData': responseData
140  };
141  this.cb_(reply, 'USB');
142};
143
144/**
145 * Reports error to the caller.
146 * @param {number} code error to report
147 * @private
148 */
149UsbSignHandler.prototype.notifyError_ = function(code) {
150  if (this.notified_)
151    return;
152  this.notified_ = true;
153  this.close();
154  var reply = {
155    'type': 'sign_helper_reply',
156    'code': code
157  };
158  this.cb_(reply);
159};
160
161/**
162 * Closes the MultipleGnubbySigner, if any.
163 */
164UsbSignHandler.prototype.close = function() {
165  if (this.signer_) {
166    this.signer_.close();
167    this.signer_ = null;
168  }
169};
170