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
;