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.
6 * @fileoverview Gnubby methods related to U2F support.
10 // Commands and flags of the Gnubby applet
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.
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;
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
41 * @param {function(...)} cb Result callback
42 * @param {boolean=} opt_individualAttestation Request the individual
43 * attestation cert rather than the batch one.
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;
51 var apdu = new Uint8Array(
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] =
62 for (var i = 0; i < appIdHash.length; ++i) {
63 u8[i + apdu.length + challenge.length] = appIdHash[i];
65 this.apduReply(u8.buffer, cb);
69 * @param {Array<number>|ArrayBuffer|Uint8Array} challengeHash Hashed
71 * @param {Array<number>|ArrayBuffer|Uint8Array} appIdHash Hashed application
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)
78 Gnubby.prototype.sign = function(challengeHash, appIdHash, keyHandle, cb,
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) {
88 var version = UTIL_BytesToString(new Uint8Array(opt_data || []));
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.
95 var apdu = new Uint8Array(
98 Gnubby.P1_TUP_REQUIRED | Gnubby.P1_TUP_CONSUME,
102 // A signature request that does not want winking.
103 // These are used during enroll to figure out whether a gnubby was already
105 // Tell applet to not actually produce a signature, even
106 // if already touched.
107 apdu[2] |= Gnubby.P1_TUP_TESTONLY;
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] =
113 for (var i = 0; i < appIdHash.length; ++i) {
114 u8[i + apdu.length + challengeHash.length] = appIdHash[i];
116 var keyHandleOffset = apdu.length + challengeHash.length + appIdHash.length;
117 if (version != Gnubby.U2F_V1) {
118 u8[keyHandleOffset++] = keyHandle.length;
120 for (var i = 0; i < keyHandle.length; ++i) {
121 u8[i + keyHandleOffset] = keyHandle[i];
123 self.apduReply(u8.buffer, cb, opt_nowink);
127 /** Request version information
128 * @param {function(...)} cb Callback
130 Gnubby.prototype.version = function(cb) {
131 if (!cb) cb = Gnubby.defaultCallback;
133 cb(-GnubbyDevice.OK, this.version_);
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) {
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);
147 self.version_ = data;