diff options
Diffstat (limited to 'PROTOCOL.u2f')
-rw-r--r-- | PROTOCOL.u2f | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/PROTOCOL.u2f b/PROTOCOL.u2f new file mode 100644 index 000000000..748111d56 --- /dev/null +++ b/PROTOCOL.u2f | |||
@@ -0,0 +1,337 @@ | |||
1 | This document describes OpenSSH's support for U2F/FIDO security keys. | ||
2 | |||
3 | Background | ||
4 | ---------- | ||
5 | |||
6 | U2F is an open standard for two-factor authentication hardware, widely | ||
7 | used for user authentication to websites. U2F tokens are ubiquitous, | ||
8 | available from a number of manufacturers and are currently by far the | ||
9 | cheapest way for users to achieve hardware-backed credential storage. | ||
10 | |||
11 | The U2F protocol however cannot be trivially used as an SSH protocol key | ||
12 | type as both the inputs to the signature operation and the resultant | ||
13 | signature differ from those specified for SSH. For similar reasons, | ||
14 | integration of U2F devices cannot be achieved via the PKCS#11 API. | ||
15 | |||
16 | U2F also offers a number of features that are attractive in the context | ||
17 | of SSH authentication. They can be configured to require indication | ||
18 | of "user presence" for each signature operation (typically achieved | ||
19 | by requiring the user touch the key). They also offer an attestation | ||
20 | mechanism at key enrollment time that can be used to prove that a | ||
21 | given key is backed by hardware. Finally the signature format includes | ||
22 | a monotonic signature counter that can be used (at scale) to detect | ||
23 | concurrent use of a private key, should it be extracted from hardware. | ||
24 | |||
25 | U2F private keys are generated through an enrollment operation, | ||
26 | which takes an application ID - a URL-like string, typically "ssh:" | ||
27 | in this case, but a HTTP origin for the case of web authentication, | ||
28 | and a challenge string (typically randomly generated). The enrollment | ||
29 | operation returns a public key, a key handle that must be used to invoke | ||
30 | the hardware-backed private key, some flags and signed attestation | ||
31 | information that may be used to verify that a private key is hosted on a | ||
32 | particular hardware instance. | ||
33 | |||
34 | It is common for U2F hardware to derive private keys from the key handle | ||
35 | in conjunction with a small per-device secret that is unique to the | ||
36 | hardware, thus requiring little on-device storage for an effectively | ||
37 | unlimited number of supported keys. This drives the requirement that | ||
38 | the key handle be supplied for each signature operation. U2F tokens | ||
39 | primarily use ECDSA signatures in the NIST-P256 field, though the FIDO2 | ||
40 | standard specifies additional key types, including one based on Ed25519. | ||
41 | |||
42 | SSH U2F Key formats | ||
43 | ------------------- | ||
44 | |||
45 | OpenSSH integrates U2F as new key and corresponding certificate types: | ||
46 | |||
47 | sk-ecdsa-sha2-nistp256@openssh.com | ||
48 | sk-ecdsa-sha2-nistp256-cert-v01@openssh.com | ||
49 | sk-ssh-ed25519@openssh.com | ||
50 | sk-ssh-ed25519-cert-v01@openssh.com | ||
51 | |||
52 | While each uses ecdsa-sha256-nistp256 as the underlying signature primitive, | ||
53 | keys require extra information in the public and private keys, and in | ||
54 | the signature object itself. As such they cannot be made compatible with | ||
55 | the existing ecdsa-sha2-nistp* key types. | ||
56 | |||
57 | The format of a sk-ecdsa-sha2-nistp256@openssh.com public key is: | ||
58 | |||
59 | string "sk-ecdsa-sha2-nistp256@openssh.com" | ||
60 | string curve name | ||
61 | ec_point Q | ||
62 | string application (user-specified, but typically "ssh:") | ||
63 | |||
64 | The corresponding private key contains: | ||
65 | |||
66 | string "sk-ecdsa-sha2-nistp256@openssh.com" | ||
67 | string curve name | ||
68 | ec_point Q | ||
69 | string application (user-specified, but typically "ssh:") | ||
70 | uint8 flags | ||
71 | string key_handle | ||
72 | string reserved | ||
73 | |||
74 | The format of a sk-ssh-ed25519@openssh.com public key is: | ||
75 | |||
76 | string "sk-ssh-ed25519@openssh.com" | ||
77 | string public key | ||
78 | string application (user-specified, but typically "ssh:") | ||
79 | |||
80 | With a private half consisting of: | ||
81 | |||
82 | string "sk-ssh-ed25519@openssh.com" | ||
83 | string public key | ||
84 | string application (user-specified, but typically "ssh:") | ||
85 | uint8 flags | ||
86 | string key_handle | ||
87 | string reserved | ||
88 | |||
89 | The certificate form for SSH U2F keys appends the usual certificate | ||
90 | information to the public key: | ||
91 | |||
92 | string "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com" | ||
93 | string nonce | ||
94 | string curve name | ||
95 | ec_point Q | ||
96 | string application | ||
97 | uint64 serial | ||
98 | uint32 type | ||
99 | string key id | ||
100 | string valid principals | ||
101 | uint64 valid after | ||
102 | uint64 valid before | ||
103 | string critical options | ||
104 | string extensions | ||
105 | string reserved | ||
106 | string signature key | ||
107 | string signature | ||
108 | |||
109 | and for security key ed25519 certificates: | ||
110 | |||
111 | string "sk-ssh-ed25519-cert-v01@openssh.com" | ||
112 | string nonce | ||
113 | string public key | ||
114 | string application | ||
115 | uint64 serial | ||
116 | uint32 type | ||
117 | string key id | ||
118 | string valid principals | ||
119 | uint64 valid after | ||
120 | uint64 valid before | ||
121 | string critical options | ||
122 | string extensions | ||
123 | string reserved | ||
124 | string signature key | ||
125 | string signature | ||
126 | |||
127 | Both security key certificates use the following encoding for private keys: | ||
128 | |||
129 | string type (e.g. "sk-ssh-ed25519-cert-v01@openssh.com") | ||
130 | string pubkey (the above key/cert structure) | ||
131 | string application | ||
132 | uint8 flags | ||
133 | string key_handle | ||
134 | string reserved | ||
135 | |||
136 | During key generation, the hardware also returns attestation information | ||
137 | that may be used to cryptographically prove that a given key is | ||
138 | hardware-backed. Unfortunately, the protocol required for this proof is | ||
139 | not privacy-preserving and may be used to identify U2F tokens with at | ||
140 | least manufacturer and batch number granularity. For this reason, we | ||
141 | choose not to include this information in the public key or save it by | ||
142 | default. | ||
143 | |||
144 | Attestation information is useful for out-of-band key and certificate | ||
145 | registration worksflows, e.g. proving to a CA that a key is backed | ||
146 | by trusted hardware before it will issue a certificate. To support this | ||
147 | case, OpenSSH optionally allows retaining the attestation information | ||
148 | at the time of key generation. It will take the following format: | ||
149 | |||
150 | string "ssh-sk-attest-v00" | ||
151 | string attestation certificate | ||
152 | string enrollment signature | ||
153 | uint32 reserved flags | ||
154 | string reserved string | ||
155 | |||
156 | OpenSSH treats the attestation certificate and enrollment signatures as | ||
157 | opaque objects and does no interpretation of them itself. | ||
158 | |||
159 | SSH U2F signatures | ||
160 | ------------------ | ||
161 | |||
162 | In addition to the message to be signed, the U2F signature operation | ||
163 | requires the key handle and a few additional parameters. The signature | ||
164 | is signed over a blob that consists of: | ||
165 | |||
166 | byte[32] SHA256(application) | ||
167 | byte flags (including "user present", extensions present) | ||
168 | uint32 counter | ||
169 | byte[] extensions | ||
170 | byte[32] SHA256(message) | ||
171 | |||
172 | No extensons are yet defined for SSH use. If any are defined in the future, | ||
173 | it will be possible to infer their presence from the contents of the "flags" | ||
174 | value. | ||
175 | |||
176 | The signature returned from U2F hardware takes the following format: | ||
177 | |||
178 | byte flags (including "user present") | ||
179 | uint32 counter | ||
180 | byte[] ecdsa_signature (in X9.62 format). | ||
181 | |||
182 | For use in the SSH protocol, we wish to avoid server-side parsing of ASN.1 | ||
183 | format data in the pre-authentication attack surface. Therefore, the | ||
184 | signature format used on the wire in SSH2_USERAUTH_REQUEST packets will | ||
185 | be reformatted to better match the existing signature encoding: | ||
186 | |||
187 | string "sk-ecdsa-sha2-nistp256@openssh.com" | ||
188 | string ecdsa_signature | ||
189 | byte flags | ||
190 | uint32 counter | ||
191 | |||
192 | Where the "ecdsa_signature" field follows the RFC5656 ECDSA signature | ||
193 | encoding: | ||
194 | |||
195 | mpint r | ||
196 | mpint s | ||
197 | |||
198 | For Ed25519 keys the signature is encoded as: | ||
199 | |||
200 | string "sk-ssh-ed25519@openssh.com" | ||
201 | string signature | ||
202 | byte flags | ||
203 | uint32 counter | ||
204 | |||
205 | ssh-agent protocol extensions | ||
206 | ----------------------------- | ||
207 | |||
208 | ssh-agent requires a protocol extension to support U2F keys. At | ||
209 | present the closest analogue to Security Keys in ssh-agent are PKCS#11 | ||
210 | tokens, insofar as they require a middleware library to communicate with | ||
211 | the device that holds the keys. Unfortunately, the protocol message used | ||
212 | to add PKCS#11 keys to ssh-agent does not include any way to send the | ||
213 | key handle to the agent as U2F keys require. | ||
214 | |||
215 | To avoid this, without having to add wholly new messages to the agent | ||
216 | protocol, we will use the existing SSH2_AGENTC_ADD_ID_CONSTRAINED message | ||
217 | with a new key constraint extension to encode a path to the middleware | ||
218 | library for the key. The format of this constraint extension would be: | ||
219 | |||
220 | byte SSH_AGENT_CONSTRAIN_EXTENSION | ||
221 | string sk-provider@openssh.com | ||
222 | string middleware path | ||
223 | |||
224 | This constraint-based approach does not present any compatibility | ||
225 | problems. | ||
226 | |||
227 | OpenSSH integration | ||
228 | ------------------- | ||
229 | |||
230 | U2F tokens may be attached via a number of means, including USB and NFC. | ||
231 | The USB interface is standardised around a HID protocol, but we want to | ||
232 | be able to support other transports as well as dummy implementations for | ||
233 | regress testing. For this reason, OpenSSH shall support a dynamically- | ||
234 | loaded middleware libraries to communicate with security keys, but offer | ||
235 | support for the common case of USB HID security keys internally. | ||
236 | |||
237 | The middleware library need only expose a handful of functions: | ||
238 | |||
239 | #define SSH_SK_VERSION_MAJOR 0x00040000 /* API version */ | ||
240 | #define SSH_SK_VERSION_MAJOR_MASK 0xffff0000 | ||
241 | |||
242 | /* Flags */ | ||
243 | #define SSH_SK_USER_PRESENCE_REQD 0x01 | ||
244 | #define SSH_SK_USER_VERIFICATION_REQD 0x04 | ||
245 | #define SSH_SK_RESIDENT_KEY 0x20 | ||
246 | |||
247 | /* Algs */ | ||
248 | #define SSH_SK_ECDSA 0x00 | ||
249 | #define SSH_SK_ED25519 0x01 | ||
250 | |||
251 | /* Error codes */ | ||
252 | #define SSH_SK_ERR_GENERAL -1 | ||
253 | #define SSH_SK_ERR_UNSUPPORTED -2 | ||
254 | #define SSH_SK_ERR_PIN_REQUIRED -3 | ||
255 | #define SSH_SK_ERR_DEVICE_NOT_FOUND -4 | ||
256 | |||
257 | struct sk_enroll_response { | ||
258 | uint8_t *public_key; | ||
259 | size_t public_key_len; | ||
260 | uint8_t *key_handle; | ||
261 | size_t key_handle_len; | ||
262 | uint8_t *signature; | ||
263 | size_t signature_len; | ||
264 | uint8_t *attestation_cert; | ||
265 | size_t attestation_cert_len; | ||
266 | }; | ||
267 | |||
268 | struct sk_sign_response { | ||
269 | uint8_t flags; | ||
270 | uint32_t counter; | ||
271 | uint8_t *sig_r; | ||
272 | size_t sig_r_len; | ||
273 | uint8_t *sig_s; | ||
274 | size_t sig_s_len; | ||
275 | }; | ||
276 | |||
277 | struct sk_resident_key { | ||
278 | uint32_t alg; | ||
279 | size_t slot; | ||
280 | char *application; | ||
281 | struct sk_enroll_response key; | ||
282 | }; | ||
283 | |||
284 | struct sk_option { | ||
285 | char *name; | ||
286 | char *value; | ||
287 | uint8_t important; | ||
288 | }; | ||
289 | |||
290 | /* Return the version of the middleware API */ | ||
291 | uint32_t sk_api_version(void); | ||
292 | |||
293 | /* Enroll a U2F key (private key generation) */ | ||
294 | int sk_enroll(uint32_t alg, | ||
295 | const uint8_t *challenge, size_t challenge_len, | ||
296 | const char *application, uint8_t flags, const char *pin, | ||
297 | struct sk_option **options, | ||
298 | struct sk_enroll_response **enroll_response); | ||
299 | |||
300 | /* Sign a challenge */ | ||
301 | int sk_sign(uint32_t alg, const uint8_t *message, size_t message_len, | ||
302 | const char *application, | ||
303 | const uint8_t *key_handle, size_t key_handle_len, | ||
304 | uint8_t flags, const char *pin, struct sk_option **options, | ||
305 | struct sk_sign_response **sign_response); | ||
306 | |||
307 | /* Enumerate all resident keys */ | ||
308 | int sk_load_resident_keys(const char *pin, struct sk_option **options, | ||
309 | struct sk_resident_key ***rks, size_t *nrks); | ||
310 | |||
311 | The SSH_SK_VERSION_MAJOR should be incremented for each incompatible | ||
312 | API change. | ||
313 | |||
314 | The options may be used to pass miscellaneous options to the middleware | ||
315 | as a NULL-terminated array of pointers to struct sk_option. The middleware | ||
316 | may ignore unsupported or unknown options unless the "important" flag is | ||
317 | set, in which case it should return failure if an unsupported option is | ||
318 | requested. | ||
319 | |||
320 | At present the following options names are supported: | ||
321 | |||
322 | "device" | ||
323 | |||
324 | Specifies a specific FIDO device on which to perform the | ||
325 | operation. The value in this field is interpreted by the | ||
326 | middleware but it would be typical to specify a path to | ||
327 | a /dev node for the device in question. | ||
328 | |||
329 | "user" | ||
330 | |||
331 | Specifies the FIDO2 username used when enrolling a key, | ||
332 | overriding OpenSSH's default of using an all-zero username. | ||
333 | |||
334 | In OpenSSH, the middleware will be invoked by using a similar mechanism to | ||
335 | ssh-pkcs11-helper to provide address-space containment of the | ||
336 | middleware from ssh-agent. | ||
337 | |||