OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * @fileoverview usbGnubby methods related to U2F support. | 6 * @fileoverview usbGnubby methods related to U2F support. |
7 */ | 7 */ |
8 'use strict'; | 8 'use strict'; |
9 | 9 |
10 // Commands and flags of the Gnubby applet at | |
11 /** Enroll */ | 10 /** Enroll */ |
12 usbGnubby.U2F_ENROLL = 0x01; | 11 usbGnubby.U2F_ENROLL = 0x01; |
13 /** Request signature */ | 12 /** Request signature */ |
14 usbGnubby.U2F_SIGN = 0x02; | 13 usbGnubby.U2F_SIGN = 0x02; |
15 /** Request protocol version */ | 14 /** Request protocol version */ |
16 usbGnubby.U2F_VERSION = 0x03; | 15 usbGnubby.U2F_VERSION = 0x03; |
17 | 16 |
18 /** Request applet version */ | 17 /** Request applet version */ |
19 usbGnubby.APPLET_VERSION = 0x11; // First 3 bytes are applet version. | 18 usbGnubby.APPLET_VERSION = 0x11; // First 3 bytes are applet version. |
20 | 19 |
21 // APDU.P1 flags | 20 // APDU.P1 flags |
22 /** Test of User Presence required */ | 21 /** Test of User Presence required */ |
23 usbGnubby.P1_TUP_REQUIRED = 0x01; | 22 usbGnubby.P1_TUP_REQUIRED = 0x01; |
24 /** Consume a Test of User Presence */ | 23 /** Consume a Test of User Presence */ |
25 usbGnubby.P1_TUP_CONSUME = 0x02; | 24 usbGnubby.P1_TUP_CONSUME = 0x02; |
26 /** Test signature only, no TUP. E.g. to check for existing enrollments. */ | 25 /** Test signature only, no TUP. E.g. to check for existing enrollments. */ |
27 usbGnubby.P1_TUP_TESTONLY = 0x04; | 26 usbGnubby.P1_TUP_TESTONLY = 0x04; |
28 /** Attest with device key */ | 27 /** Attest with device key */ |
29 usbGnubby.P1_INDIVIDUAL_KEY = 0x80; | 28 usbGnubby.P1_INDIVIDUAL_KEY = 0x80; |
30 | 29 |
| 30 // Version values |
| 31 /** V1 of the applet. */ |
| 32 usbGnubby.U2F_V1 = 'U2F_V1'; |
| 33 /** V2 of the applet. */ |
| 34 usbGnubby.U2F_V2 = 'U2F_V2'; |
| 35 |
31 /** Perform enrollment | 36 /** Perform enrollment |
32 * @param {ArrayBuffer|Uint8Array} challenge Enrollment challenge | 37 * @param {ArrayBuffer|Uint8Array} challenge Enrollment challenge |
33 * @param {ArrayBuffer|Uint8Array} appIdHash Hashed application id | 38 * @param {ArrayBuffer|Uint8Array} appIdHash Hashed application id |
34 * @param {function(...)} cb Result callback | 39 * @param {function(...)} cb Result callback |
35 */ | 40 */ |
36 usbGnubby.prototype.enroll = function(challenge, appIdHash, cb) { | 41 usbGnubby.prototype.enroll = function(challenge, appIdHash, cb) { |
37 var apdu = new Uint8Array( | 42 var apdu = new Uint8Array( |
38 [0x00, | 43 [0x00, |
39 usbGnubby.U2F_ENROLL, | 44 usbGnubby.U2F_ENROLL, |
40 usbGnubby.P1_TUP_REQUIRED | usbGnubby.P1_TUP_CONSUME | | 45 usbGnubby.P1_TUP_REQUIRED | usbGnubby.P1_TUP_CONSUME | |
(...skipping 15 matching lines...) Expand all Loading... |
56 /** Request signature | 61 /** Request signature |
57 * @param {ArrayBuffer|Uint8Array} challengeHash Hashed signature challenge | 62 * @param {ArrayBuffer|Uint8Array} challengeHash Hashed signature challenge |
58 * @param {ArrayBuffer|Uint8Array} appIdHash Hashed application id | 63 * @param {ArrayBuffer|Uint8Array} appIdHash Hashed application id |
59 * @param {ArrayBuffer|Uint8Array} keyHandle Key handle to use | 64 * @param {ArrayBuffer|Uint8Array} keyHandle Key handle to use |
60 * @param {function(...)} cb Result callback | 65 * @param {function(...)} cb Result callback |
61 * @param {boolean=} opt_nowink Request signature without winking | 66 * @param {boolean=} opt_nowink Request signature without winking |
62 * (e.g. during enroll) | 67 * (e.g. during enroll) |
63 */ | 68 */ |
64 usbGnubby.prototype.sign = function(challengeHash, appIdHash, keyHandle, cb, | 69 usbGnubby.prototype.sign = function(challengeHash, appIdHash, keyHandle, cb, |
65 opt_nowink) { | 70 opt_nowink) { |
66 var apdu = new Uint8Array( | 71 var self = this; |
67 [0x00, | 72 // The sign command's format is ever-so-slightly different between V1 and V2, |
68 usbGnubby.U2F_SIGN, | 73 // so get this gnubby's version prior to sending it. |
69 usbGnubby.P1_TUP_REQUIRED | usbGnubby.P1_TUP_CONSUME, | 74 this.version(function(rc, opt_data) { |
70 0x00, 0x00, 0x00, | 75 if (rc) { |
71 challengeHash.length + appIdHash.length + keyHandle.length]); | 76 cb(rc); |
72 if (opt_nowink) { | 77 return; |
73 // A signature request that does not want winking. | 78 } |
74 // These are used during enroll to figure out whether a gnubby was already | 79 var version = UTIL_BytesToString(new Uint8Array(opt_data || [])); |
75 // enrolled. | 80 var apduDataLen = |
76 // Tell applet to not actually produce a signature, even | 81 challengeHash.length + appIdHash.length + keyHandle.length; |
77 // if already touched. | 82 if (version != usbGnubby.U2F_V1) { |
78 apdu[2] |= usbGnubby.P1_TUP_TESTONLY; | 83 // The V2 sign command includes a length byte for the key handle. |
79 } | 84 apduDataLen++; |
80 var u8 = new Uint8Array(apdu.length + challengeHash.length + | 85 } |
81 appIdHash.length + keyHandle.length + 2); | 86 var apdu = new Uint8Array( |
82 for (var i = 0; i < apdu.length; ++i) u8[i] = apdu[i]; | 87 [0x00, |
83 for (var i = 0; i < challengeHash.length; ++i) u8[i + apdu.length] = | 88 usbGnubby.U2F_SIGN, |
84 challengeHash[i]; | 89 usbGnubby.P1_TUP_REQUIRED | usbGnubby.P1_TUP_CONSUME, |
85 for (var i = 0; i < appIdHash.length; ++i) { | 90 0x00, 0x00, 0x00, |
86 u8[i + apdu.length + challengeHash.length] = appIdHash[i]; | 91 apduDataLen]); |
87 } | 92 if (opt_nowink) { |
88 for (var i = 0; i < keyHandle.length; ++i) { | 93 // A signature request that does not want winking. |
89 u8[i + apdu.length + challengeHash.length + appIdHash.length] = | 94 // These are used during enroll to figure out whether a gnubby was already |
90 keyHandle[i]; | 95 // enrolled. |
91 } | 96 // Tell applet to not actually produce a signature, even |
92 this.apduReply_(u8.buffer, cb, opt_nowink); | 97 // if already touched. |
| 98 apdu[2] |= usbGnubby.P1_TUP_TESTONLY; |
| 99 } |
| 100 var u8 = new Uint8Array(apdu.length + apduDataLen + 2); |
| 101 for (var i = 0; i < apdu.length; ++i) u8[i] = apdu[i]; |
| 102 for (var i = 0; i < challengeHash.length; ++i) u8[i + apdu.length] = |
| 103 challengeHash[i]; |
| 104 for (var i = 0; i < appIdHash.length; ++i) { |
| 105 u8[i + apdu.length + challengeHash.length] = appIdHash[i]; |
| 106 } |
| 107 var keyHandleOffset = apdu.length + challengeHash.length + appIdHash.length; |
| 108 if (version != usbGnubby.U2F_V1) { |
| 109 u8[keyHandleOffset++] = keyHandle.length; |
| 110 } |
| 111 for (var i = 0; i < keyHandle.length; ++i) { |
| 112 u8[i + keyHandleOffset] = keyHandle[i]; |
| 113 } |
| 114 self.apduReply_(u8.buffer, cb, opt_nowink); |
| 115 }); |
93 }; | 116 }; |
94 | 117 |
95 /** Request version information | 118 /** Request version information |
96 * @param {function(...)} cb Callback | 119 * @param {function(...)} cb Callback |
97 */ | 120 */ |
98 usbGnubby.prototype.version = function(cb) { | 121 usbGnubby.prototype.version = function(cb) { |
99 if (!cb) cb = usbGnubby.defaultCallback; | 122 if (!cb) cb = usbGnubby.defaultCallback; |
| 123 if (this.version_) { |
| 124 cb(-llGnubby.OK, this.version_); |
| 125 return; |
| 126 } |
| 127 var self = this; |
100 var apdu = new Uint8Array([0x00, usbGnubby.U2F_VERSION, 0x00, 0x00, 0x00, | 128 var apdu = new Uint8Array([0x00, usbGnubby.U2F_VERSION, 0x00, 0x00, 0x00, |
101 0x00, 0x00, 0x00, 0x00]); | 129 0x00, 0x00, 0x00, 0x00]); |
102 this.apduReply_(apdu.buffer, function(rc, data) { | 130 this.apduReply_(apdu.buffer, function(rc, data) { |
103 if (rc == 0x6d00) { | 131 if (rc == 0x6d00) { |
104 // Command not implemented. Pretend this is v1. | 132 // Command not implemented. Pretend this is v1. |
105 var v1 = new Uint8Array(UTIL_StringToBytes('U2F_V1')); | 133 var v1 = new Uint8Array(UTIL_StringToBytes(usbGnubby.U2F_V1)); |
| 134 self.version_ = v1.buffer; |
106 cb(-llGnubby.OK, v1.buffer); | 135 cb(-llGnubby.OK, v1.buffer); |
107 } else { | 136 } else { |
| 137 if (!rc) { |
| 138 self.version_ = data; |
| 139 } |
108 cb(rc, data); | 140 cb(rc, data); |
109 } | 141 } |
110 }); | 142 }); |
111 }; | 143 }; |
OLD | NEW |