Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / cryptotoken / usbsignhandler.js
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
10 var CORRUPT_sign = false;
11
12 /**
13  * @param {!SignHelperRequest} request The sign request.
14  * @constructor
15  * @implements {RequestHandler}
16  */
17 function 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  */
31 UsbSignHandler.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  */
39 UsbSignHandler.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  */
72 UsbSignHandler.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  */
91 UsbSignHandler.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  */
118 UsbSignHandler.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  */
149 UsbSignHandler.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  */
164 UsbSignHandler.prototype.close = function() {
165   if (this.signer_) {
166     this.signer_.close();
167     this.signer_ = null;
168   }
169 };