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 Gnubby methods related to U2F support. | 6 * @fileoverview Gnubby methods related to U2F support. |
7 */ | 7 */ |
8 'use strict'; | 8 'use strict'; |
9 | 9 |
10 // Commands and flags of the Gnubby applet | 10 // Commands and flags of the Gnubby applet |
(...skipping 24 matching lines...) Expand all Loading... |
35 Gnubby.U2F_V2 = 'U2F_V2'; | 35 Gnubby.U2F_V2 = 'U2F_V2'; |
36 | 36 |
37 /** Perform enrollment | 37 /** Perform enrollment |
38 * @param {Array<number>|ArrayBuffer|Uint8Array} challenge Enrollment challenge | 38 * @param {Array<number>|ArrayBuffer|Uint8Array} challenge Enrollment challenge |
39 * @param {Array<number>|ArrayBuffer|Uint8Array} appIdHash Hashed application | 39 * @param {Array<number>|ArrayBuffer|Uint8Array} appIdHash Hashed application |
40 * id | 40 * id |
41 * @param {function(...)} cb Result callback | 41 * @param {function(...)} cb Result callback |
42 * @param {boolean=} opt_individualAttestation Request the individual | 42 * @param {boolean=} opt_individualAttestation Request the individual |
43 * attestation cert rather than the batch one. | 43 * attestation cert rather than the batch one. |
44 */ | 44 */ |
45 Gnubby.prototype.enroll = function(challenge, appIdHash, cb, | 45 Gnubby.prototype.enroll = function( |
46 opt_individualAttestation) { | 46 challenge, appIdHash, cb, opt_individualAttestation) { |
47 var p1 = Gnubby.P1_TUP_REQUIRED | Gnubby.P1_TUP_CONSUME; | 47 var p1 = Gnubby.P1_TUP_REQUIRED | Gnubby.P1_TUP_CONSUME; |
48 if (opt_individualAttestation) { | 48 if (opt_individualAttestation) { |
49 p1 |= Gnubby.P1_INDIVIDUAL_KEY; | 49 p1 |= Gnubby.P1_INDIVIDUAL_KEY; |
50 } | 50 } |
51 var apdu = new Uint8Array( | 51 var apdu = new Uint8Array([ |
52 [0x00, | 52 0x00, Gnubby.U2F_ENROLL, p1, 0x00, 0x00, 0x00, |
53 Gnubby.U2F_ENROLL, | 53 challenge.length + appIdHash.length |
54 p1, | 54 ]); |
55 0x00, 0x00, 0x00, | 55 var u8 = |
56 challenge.length + appIdHash.length]); | 56 new Uint8Array(apdu.length + challenge.length + appIdHash.length + 2); |
57 var u8 = new Uint8Array(apdu.length + challenge.length + | 57 for (var i = 0; i < apdu.length; ++i) |
58 appIdHash.length + 2); | 58 u8[i] = apdu[i]; |
59 for (var i = 0; i < apdu.length; ++i) u8[i] = apdu[i]; | 59 for (var i = 0; i < challenge.length; ++i) |
60 for (var i = 0; i < challenge.length; ++i) u8[i + apdu.length] = | 60 u8[i + apdu.length] = challenge[i]; |
61 challenge[i]; | |
62 for (var i = 0; i < appIdHash.length; ++i) { | 61 for (var i = 0; i < appIdHash.length; ++i) { |
63 u8[i + apdu.length + challenge.length] = appIdHash[i]; | 62 u8[i + apdu.length + challenge.length] = appIdHash[i]; |
64 } | 63 } |
65 this.apduReply(u8.buffer, cb); | 64 this.apduReply(u8.buffer, cb); |
66 }; | 65 }; |
67 | 66 |
68 /** Request signature | 67 /** Request signature |
69 * @param {Array<number>|ArrayBuffer|Uint8Array} challengeHash Hashed | 68 * @param {Array<number>|ArrayBuffer|Uint8Array} challengeHash Hashed |
70 * signature challenge | 69 * signature challenge |
71 * @param {Array<number>|ArrayBuffer|Uint8Array} appIdHash Hashed application | 70 * @param {Array<number>|ArrayBuffer|Uint8Array} appIdHash Hashed application |
72 * id | 71 * id |
73 * @param {Array<number>|ArrayBuffer|Uint8Array} keyHandle Key handle to use | 72 * @param {Array<number>|ArrayBuffer|Uint8Array} keyHandle Key handle to use |
74 * @param {function(...)} cb Result callback | 73 * @param {function(...)} cb Result callback |
75 * @param {boolean=} opt_nowink Request signature without winking | 74 * @param {boolean=} opt_nowink Request signature without winking |
76 * (e.g. during enroll) | 75 * (e.g. during enroll) |
77 */ | 76 */ |
78 Gnubby.prototype.sign = function(challengeHash, appIdHash, keyHandle, cb, | 77 Gnubby.prototype.sign = function( |
79 opt_nowink) { | 78 challengeHash, appIdHash, keyHandle, cb, opt_nowink) { |
80 var self = this; | 79 var self = this; |
81 // The sign command's format is ever-so-slightly different between V1 and V2, | 80 // 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. | 81 // so get this gnubby's version prior to sending it. |
83 this.version(function(rc, opt_data) { | 82 this.version(function(rc, opt_data) { |
84 if (rc) { | 83 if (rc) { |
85 cb(rc); | 84 cb(rc); |
86 return; | 85 return; |
87 } | 86 } |
88 var version = UTIL_BytesToString(new Uint8Array(opt_data || [])); | 87 var version = UTIL_BytesToString(new Uint8Array(opt_data || [])); |
89 var apduDataLen = | 88 var apduDataLen = |
90 challengeHash.length + appIdHash.length + keyHandle.length; | 89 challengeHash.length + appIdHash.length + keyHandle.length; |
91 if (version != Gnubby.U2F_V1) { | 90 if (version != Gnubby.U2F_V1) { |
92 // The V2 sign command includes a length byte for the key handle. | 91 // The V2 sign command includes a length byte for the key handle. |
93 apduDataLen++; | 92 apduDataLen++; |
94 } | 93 } |
95 var apdu = new Uint8Array( | 94 var apdu = new Uint8Array([ |
96 [0x00, | 95 0x00, Gnubby.U2F_SIGN, Gnubby.P1_TUP_REQUIRED | Gnubby.P1_TUP_CONSUME, |
97 Gnubby.U2F_SIGN, | 96 0x00, 0x00, 0x00, apduDataLen |
98 Gnubby.P1_TUP_REQUIRED | Gnubby.P1_TUP_CONSUME, | 97 ]); |
99 0x00, 0x00, 0x00, | |
100 apduDataLen]); | |
101 if (opt_nowink) { | 98 if (opt_nowink) { |
102 // A signature request that does not want winking. | 99 // A signature request that does not want winking. |
103 // These are used during enroll to figure out whether a gnubby was already | 100 // These are used during enroll to figure out whether a gnubby was already |
104 // enrolled. | 101 // enrolled. |
105 // Tell applet to not actually produce a signature, even | 102 // Tell applet to not actually produce a signature, even |
106 // if already touched. | 103 // if already touched. |
107 apdu[2] |= Gnubby.P1_TUP_TESTONLY; | 104 apdu[2] |= Gnubby.P1_TUP_TESTONLY; |
108 } | 105 } |
109 var u8 = new Uint8Array(apdu.length + apduDataLen + 2); | 106 var u8 = new Uint8Array(apdu.length + apduDataLen + 2); |
110 for (var i = 0; i < apdu.length; ++i) u8[i] = apdu[i]; | 107 for (var i = 0; i < apdu.length; ++i) |
111 for (var i = 0; i < challengeHash.length; ++i) u8[i + apdu.length] = | 108 u8[i] = apdu[i]; |
112 challengeHash[i]; | 109 for (var i = 0; i < challengeHash.length; ++i) |
| 110 u8[i + apdu.length] = challengeHash[i]; |
113 for (var i = 0; i < appIdHash.length; ++i) { | 111 for (var i = 0; i < appIdHash.length; ++i) { |
114 u8[i + apdu.length + challengeHash.length] = appIdHash[i]; | 112 u8[i + apdu.length + challengeHash.length] = appIdHash[i]; |
115 } | 113 } |
116 var keyHandleOffset = apdu.length + challengeHash.length + appIdHash.length; | 114 var keyHandleOffset = apdu.length + challengeHash.length + appIdHash.length; |
117 if (version != Gnubby.U2F_V1) { | 115 if (version != Gnubby.U2F_V1) { |
118 u8[keyHandleOffset++] = keyHandle.length; | 116 u8[keyHandleOffset++] = keyHandle.length; |
119 } | 117 } |
120 for (var i = 0; i < keyHandle.length; ++i) { | 118 for (var i = 0; i < keyHandle.length; ++i) { |
121 u8[i + keyHandleOffset] = keyHandle[i]; | 119 u8[i + keyHandleOffset] = keyHandle[i]; |
122 } | 120 } |
123 self.apduReply(u8.buffer, cb, opt_nowink); | 121 self.apduReply(u8.buffer, cb, opt_nowink); |
124 }); | 122 }); |
125 }; | 123 }; |
126 | 124 |
127 /** Request version information | 125 /** Request version information |
128 * @param {function(...)} cb Callback | 126 * @param {function(...)} cb Callback |
129 */ | 127 */ |
130 Gnubby.prototype.version = function(cb) { | 128 Gnubby.prototype.version = function(cb) { |
131 if (!cb) cb = Gnubby.defaultCallback; | 129 if (!cb) |
| 130 cb = Gnubby.defaultCallback; |
132 if (this.version_) { | 131 if (this.version_) { |
133 cb(-GnubbyDevice.OK, this.version_); | 132 cb(-GnubbyDevice.OK, this.version_); |
134 return; | 133 return; |
135 } | 134 } |
136 var self = this; | 135 var self = this; |
137 | 136 |
138 function gotResponse(rc, data) { | 137 function gotResponse(rc, data) { |
139 if (!rc) { | 138 if (!rc) { |
140 self.version_ = data; | 139 self.version_ = data; |
141 } | 140 } |
142 cb(rc, data); | 141 cb(rc, data); |
143 } | 142 } |
144 | 143 |
145 var apdu = new Uint8Array([0x00, Gnubby.U2F_VERSION, 0x00, 0x00, 0x00, | 144 var apdu = |
146 0x00, 0x00]); | 145 new Uint8Array([0x00, Gnubby.U2F_VERSION, 0x00, 0x00, 0x00, 0x00, 0x00]); |
147 this.apduReply(apdu.buffer, function(rc, data) { | 146 this.apduReply(apdu.buffer, function(rc, data) { |
148 if (rc == 0x6d00) { | 147 if (rc == 0x6d00) { |
149 // Command not implemented. Pretend this is v1. | 148 // Command not implemented. Pretend this is v1. |
150 var v1 = new Uint8Array(UTIL_StringToBytes(Gnubby.U2F_V1)); | 149 var v1 = new Uint8Array(UTIL_StringToBytes(Gnubby.U2F_V1)); |
151 self.version_ = v1.buffer; | 150 self.version_ = v1.buffer; |
152 cb(-GnubbyDevice.OK, v1.buffer); | 151 cb(-GnubbyDevice.OK, v1.buffer); |
153 return; | 152 return; |
154 } | 153 } |
155 if (rc == 0x6700) { | 154 if (rc == 0x6700) { |
156 // Wrong length. Try with non-ISO 7816-4-conforming layout defined in | 155 // Wrong length. Try with non-ISO 7816-4-conforming layout defined in |
157 // earlier U2F drafts. | 156 // earlier U2F drafts. |
158 apdu = new Uint8Array([0x00, Gnubby.U2F_VERSION, 0x00, 0x00, 0x00, | 157 apdu = new Uint8Array( |
159 0x00, 0x00, 0x00, 0x00]); | 158 [0x00, Gnubby.U2F_VERSION, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); |
160 self.apduReply(apdu.buffer, gotResponse); | 159 self.apduReply(apdu.buffer, gotResponse); |
161 return; | 160 return; |
162 } | 161 } |
163 // Any other response: handle as final result. | 162 // Any other response: handle as final result. |
164 gotResponse(rc, data); | 163 gotResponse(rc, data); |
165 }); | 164 }); |
166 }; | 165 }; |
OLD | NEW |