Only grant permissions to new extensions from sync if they have the expected version
[chromium-blink-merge.git] / chrome / browser / resources / cryptotoken / gnubby-u2f.js
bloba5ae8b566660e1994f0490086c65a331b6033e76
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.
5 /**
6  * @fileoverview Gnubby methods related to U2F support.
7  */
8 'use strict';
10 // Commands and flags of the Gnubby applet
11 /** Enroll */
12 Gnubby.U2F_ENROLL = 0x01;
13 /** Request signature */
14 Gnubby.U2F_SIGN = 0x02;
15 /** Request protocol version */
16 Gnubby.U2F_VERSION = 0x03;
18 /** Request applet version */
19 Gnubby.APPLET_VERSION = 0x11;  // First 3 bytes are applet version.
21 // APDU.P1 flags
22 /** Test of User Presence required */
23 Gnubby.P1_TUP_REQUIRED = 0x01;
24 /** Consume a Test of User Presence */
25 Gnubby.P1_TUP_CONSUME = 0x02;
26 /** Test signature only, no TUP. E.g. to check for existing enrollments. */
27 Gnubby.P1_TUP_TESTONLY = 0x04;
28 /** Attest with device key */
29 Gnubby.P1_INDIVIDUAL_KEY = 0x80;
31 // Version values
32 /** V1 of the applet. */
33 Gnubby.U2F_V1 = 'U2F_V1';
34 /** V2 of the applet. */
35 Gnubby.U2F_V2 = 'U2F_V2';
37 /** Perform enrollment
38  * @param {Array<number>|ArrayBuffer|Uint8Array} challenge Enrollment challenge
39  * @param {Array<number>|ArrayBuffer|Uint8Array} appIdHash Hashed application
40  *     id
41  * @param {function(...)} cb Result callback
42  * @param {boolean=} opt_individualAttestation Request the individual
43  *     attestation cert rather than the batch one.
44  */
45 Gnubby.prototype.enroll = function(challenge, appIdHash, cb,
46     opt_individualAttestation) {
47   var p1 = Gnubby.P1_TUP_REQUIRED | Gnubby.P1_TUP_CONSUME;
48   if (opt_individualAttestation) {
49     p1 |= Gnubby.P1_INDIVIDUAL_KEY;
50   }
51   var apdu = new Uint8Array(
52       [0x00,
53        Gnubby.U2F_ENROLL,
54        p1,
55        0x00, 0x00, 0x00,
56        challenge.length + appIdHash.length]);
57   var u8 = new Uint8Array(apdu.length + challenge.length +
58       appIdHash.length + 2);
59   for (var i = 0; i < apdu.length; ++i) u8[i] = apdu[i];
60   for (var i = 0; i < challenge.length; ++i) u8[i + apdu.length] =
61     challenge[i];
62   for (var i = 0; i < appIdHash.length; ++i) {
63     u8[i + apdu.length + challenge.length] = appIdHash[i];
64   }
65   this.apduReply(u8.buffer, cb);
68 /** Request signature
69  * @param {Array<number>|ArrayBuffer|Uint8Array} challengeHash Hashed
70  *     signature challenge
71  * @param {Array<number>|ArrayBuffer|Uint8Array} appIdHash Hashed application
72  *     id
73  * @param {Array<number>|ArrayBuffer|Uint8Array} keyHandle Key handle to use
74  * @param {function(...)} cb Result callback
75  * @param {boolean=} opt_nowink Request signature without winking
76  *     (e.g. during enroll)
77  */
78 Gnubby.prototype.sign = function(challengeHash, appIdHash, keyHandle, cb,
79                                     opt_nowink) {
80   var self = this;
81   // The sign command's format is ever-so-slightly different between V1 and V2,
82   // so get this gnubby's version prior to sending it.
83   this.version(function(rc, opt_data) {
84     if (rc) {
85       cb(rc);
86       return;
87     }
88     var version = UTIL_BytesToString(new Uint8Array(opt_data || []));
89     var apduDataLen =
90       challengeHash.length + appIdHash.length + keyHandle.length;
91     if (version != Gnubby.U2F_V1) {
92       // The V2 sign command includes a length byte for the key handle.
93       apduDataLen++;
94     }
95     var apdu = new Uint8Array(
96         [0x00,
97          Gnubby.U2F_SIGN,
98          Gnubby.P1_TUP_REQUIRED | Gnubby.P1_TUP_CONSUME,
99          0x00, 0x00, 0x00,
100          apduDataLen]);
101     if (opt_nowink) {
102       // A signature request that does not want winking.
103       // These are used during enroll to figure out whether a gnubby was already
104       // enrolled.
105       // Tell applet to not actually produce a signature, even
106       // if already touched.
107       apdu[2] |= Gnubby.P1_TUP_TESTONLY;
108     }
109     var u8 = new Uint8Array(apdu.length + apduDataLen + 2);
110     for (var i = 0; i < apdu.length; ++i) u8[i] = apdu[i];
111     for (var i = 0; i < challengeHash.length; ++i) u8[i + apdu.length] =
112       challengeHash[i];
113     for (var i = 0; i < appIdHash.length; ++i) {
114       u8[i + apdu.length + challengeHash.length] = appIdHash[i];
115     }
116     var keyHandleOffset = apdu.length + challengeHash.length + appIdHash.length;
117     if (version != Gnubby.U2F_V1) {
118       u8[keyHandleOffset++] = keyHandle.length;
119     }
120     for (var i = 0; i < keyHandle.length; ++i) {
121       u8[i + keyHandleOffset] = keyHandle[i];
122     }
123     self.apduReply(u8.buffer, cb, opt_nowink);
124   });
127 /** Request version information
128  * @param {function(...)} cb Callback
129  */
130 Gnubby.prototype.version = function(cb) {
131   if (!cb) cb = Gnubby.defaultCallback;
132   if (this.version_) {
133     cb(-GnubbyDevice.OK, this.version_);
134     return;
135   }
136   var self = this;
137   var apdu = new Uint8Array([0x00, Gnubby.U2F_VERSION, 0x00, 0x00, 0x00,
138       0x00, 0x00, 0x00, 0x00]);
139   this.apduReply(apdu.buffer, function(rc, data) {
140     if (rc == 0x6d00) {
141       // Command not implemented. Pretend this is v1.
142       var v1 = new Uint8Array(UTIL_StringToBytes(Gnubby.U2F_V1));
143       self.version_ = v1.buffer;
144       cb(-GnubbyDevice.OK, v1.buffer);
145     } else {
146       if (!rc) {
147         self.version_ = data;
148       }
149       cb(rc, data);
150     }
151   });