blob: fd0cd0de0abfa37288c95dc104716fd3ef7135e0 [file] [log] [blame]
djm@openbsd.org57ecc102019-10-31 21:14:17 +00001This document describes OpenSSH's support for U2F/FIDO security keys.
2
3Background
4----------
5
6U2F is an open standard for two-factor authentication hardware, widely
7used for user authentication to websites. U2F tokens are ubiquitous,
8available from a number of manufacturers and are currently by far the
9cheapest way for users to achieve hardware-backed credential storage.
10
11The U2F protocol however cannot be trivially used as an SSH protocol key
12type as both the inputs to the signature operation and the resultant
13signature differ from those specified for SSH. For similar reasons,
14integration of U2F devices cannot be achieved via the PKCS#11 API.
15
16U2F also offers a number of features that are attractive in the context
17of SSH authentication. They can be configured to require indication
18of "user presence" for each signature operation (typically achieved
19by requiring the user touch the key). They also offer an attestation
20mechanism at key enrollment time that can be used to prove that a
21given key is backed by hardware. Finally the signature format includes
22a monotonic signature counter that can be used (at scale) to detect
23concurrent use of a private key, should it be extracted from hardware.
24
naddy@openbsd.orgad384062019-11-01 12:10:43 +000025U2F private keys are generated through an enrollment operation,
djm@openbsd.org57ecc102019-10-31 21:14:17 +000026which takes an application ID - a URL-like string, typically "ssh:"
27in this case, but a HTTP origin for the case of web authentication,
28and a challenge string (typically randomly generated). The enrollment
29operation returns a public key, a key handle that must be used to invoke
30the hardware-backed private key, some flags and signed attestation
naddy@openbsd.orgad384062019-11-01 12:10:43 +000031information that may be used to verify that a private key is hosted on a
djm@openbsd.org57ecc102019-10-31 21:14:17 +000032particular hardware instance.
33
34It is common for U2F hardware to derive private keys from the key handle
35in conjunction with a small per-device secret that is unique to the
36hardware, thus requiring little on-device storage for an effectively
37unlimited number of supported keys. This drives the requirement that
38the key handle be supplied for each signature operation. U2F tokens
djm@openbsd.org13066432019-11-18 04:34:47 +000039primarily use ECDSA signatures in the NIST-P256 field, though the FIDO2
naddy@openbsd.org416f1532019-12-20 20:28:55 +000040standard specifies additional key types, including one based on Ed25519.
djm@openbsd.org57ecc102019-10-31 21:14:17 +000041
42SSH U2F Key formats
43-------------------
44
djm@openbsd.org13066432019-11-18 04:34:47 +000045OpenSSH integrates U2F as new key and corresponding certificate types:
djm@openbsd.org57ecc102019-10-31 21:14:17 +000046
47 sk-ecdsa-sha2-nistp256@openssh.com
48 sk-ecdsa-sha2-nistp256-cert-v01@openssh.com
djm@openbsd.org13066432019-11-18 04:34:47 +000049 sk-ssh-ed25519@openssh.com
50 sk-ssh-ed25519-cert-v01@openssh.com
djm@openbsd.org57ecc102019-10-31 21:14:17 +000051
djm@openbsd.org57ecc102019-10-31 21:14:17 +000052While each uses ecdsa-sha256-nistp256 as the underlying signature primitive,
53keys require extra information in the public and private keys, and in
54the signature object itself. As such they cannot be made compatible with
55the existing ecdsa-sha2-nistp* key types.
56
57The format of a sk-ecdsa-sha2-nistp256@openssh.com public key is:
58
59 string "sk-ecdsa-sha2-nistp256@openssh.com"
djm@openbsd.org93fa2a62019-11-18 04:16:53 +000060 string curve name
djm@openbsd.org57ecc102019-10-31 21:14:17 +000061 ec_point Q
62 string application (user-specified, but typically "ssh:")
63
64The corresponding private key contains:
65
66 string "sk-ecdsa-sha2-nistp256@openssh.com"
djm@openbsd.org93fa2a62019-11-18 04:16:53 +000067 string curve name
djm@openbsd.org57ecc102019-10-31 21:14:17 +000068 ec_point Q
69 string application (user-specified, but typically "ssh:")
djm@openbsd.org71856e12019-11-18 04:29:50 +000070 uint8 flags
djm@openbsd.org57ecc102019-10-31 21:14:17 +000071 string key_handle
djm@openbsd.org57ecc102019-10-31 21:14:17 +000072 string reserved
73
djm@openbsd.org13066432019-11-18 04:34:47 +000074The 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
80With 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:")
djm@openbsd.orga62f4e12019-12-10 23:37:31 +000085 uint8 flags
djm@openbsd.org13066432019-11-18 04:34:47 +000086 string key_handle
87 string reserved
88
89The certificate form for SSH U2F keys appends the usual certificate
djm@openbsd.org57ecc102019-10-31 21:14:17 +000090information to the public key:
91
naddy@openbsd.orgad384062019-11-01 12:10:43 +000092 string "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com"
djm@openbsd.org57ecc102019-10-31 21:14:17 +000093 string nonce
djm@openbsd.org93fa2a62019-11-18 04:16:53 +000094 string curve name
djm@openbsd.org57ecc102019-10-31 21:14:17 +000095 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
djm@openbsd.orga62f4e12019-12-10 23:37:31 +0000109and for security key ed25519 certificates:
110
djm@openbsd.org13066432019-11-18 04:34:47 +0000111 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
djm@openbsd.orga62f4e12019-12-10 23:37:31 +0000127Both 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
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000136During key generation, the hardware also returns attestation information
137that may be used to cryptographically prove that a given key is
138hardware-backed. Unfortunately, the protocol required for this proof is
139not privacy-preserving and may be used to identify U2F tokens with at
140least manufacturer and batch number granularity. For this reason, we
141choose not to include this information in the public key or save it by
142default.
143
144Attestation information is very useful however in an organisational
naddy@openbsd.orgad384062019-11-01 12:10:43 +0000145context, where it may be used by a CA as part of certificate
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000146issuance. In this case, exposure to the CA of hardware identity is
147desirable. To support this case, OpenSSH optionally allows retaining the
148attestation information at the time of key generation. It will take the
149following format:
150
151 string "sk-attest-v00"
152 uint32 version (1 for U2F, 2 for FIDO2 in future)
153 string attestation certificate
154 string enrollment signature
155
156SSH U2F signatures
157------------------
158
djm@openbsd.org18e84bf2019-11-28 05:20:54 +0000159In addition to the message to be signed, the U2F signature operation
djm@openbsd.orgc4036fe2019-12-10 22:36:08 +0000160requires the key handle and a few additional parameters. The signature
161is signed over a blob that consists of:
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000162
163 byte[32] SHA256(application)
164 byte flags (including "user present", extensions present)
165 uint32 counter
166 byte[] extensions
167 byte[32] SHA256(message)
168
djm@openbsd.orgc33d4682019-12-11 22:19:47 +0000169No extensons are yet defined for SSH use. If any are defined in the future,
170it will be possible to infer their presence from the contents of the "flags"
171value.
172
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000173The signature returned from U2F hardware takes the following format:
174
175 byte flags (including "user present")
176 uint32 counter
djm@openbsd.orgc4036fe2019-12-10 22:36:08 +0000177 byte[] ecdsa_signature (in X9.62 format).
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000178
179For use in the SSH protocol, we wish to avoid server-side parsing of ASN.1
180format data in the pre-authentication attack surface. Therefore, the
181signature format used on the wire in SSH2_USERAUTH_REQUEST packets will
djm@openbsd.orga70d92f2019-11-19 22:23:19 +0000182be reformatted to better match the existing signature encoding:
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000183
djm@openbsd.orga70d92f2019-11-19 22:23:19 +0000184 string "sk-ecdsa-sha2-nistp256@openssh.com"
185 string ecdsa_signature
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000186 byte flags
187 uint32 counter
188
djm@openbsd.orga70d92f2019-11-19 22:23:19 +0000189Where the "ecdsa_signature" field follows the RFC5656 ECDSA signature
190encoding:
191
192 mpint r
193 mpint s
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000194
markus@openbsd.orgb556cc32019-11-12 19:34:40 +0000195For Ed25519 keys the signature is encoded as:
196
197 string "sk-ssh-ed25519@openssh.com"
198 string signature
199 byte flags
200 uint32 counter
201
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000202ssh-agent protocol extensions
203-----------------------------
204
naddy@openbsd.orgad384062019-11-01 12:10:43 +0000205ssh-agent requires a protocol extension to support U2F keys. At
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000206present the closest analogue to Security Keys in ssh-agent are PKCS#11
207tokens, insofar as they require a middleware library to communicate with
208the device that holds the keys. Unfortunately, the protocol message used
209to add PKCS#11 keys to ssh-agent does not include any way to send the
210key handle to the agent as U2F keys require.
211
naddy@openbsd.orgad384062019-11-01 12:10:43 +0000212To avoid this, without having to add wholly new messages to the agent
213protocol, we will use the existing SSH2_AGENTC_ADD_ID_CONSTRAINED message
214with a new key constraint extension to encode a path to the middleware
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000215library for the key. The format of this constraint extension would be:
216
217 byte SSH_AGENT_CONSTRAIN_EXTENSION
djm@openbsd.org22d4beb2019-12-10 23:21:56 +0000218 string sk-provider@openssh.com
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000219 string middleware path
220
221This constraint-based approach does not present any compatibility
222problems.
223
224OpenSSH integration
225-------------------
226
227U2F tokens may be attached via a number of means, including USB and NFC.
228The USB interface is standardised around a HID protocol, but we want to
229be able to support other transports as well as dummy implementations for
djm@openbsd.org13066432019-11-18 04:34:47 +0000230regress testing. For this reason, OpenSSH shall support a dynamically-
231loaded middleware libraries to communicate with security keys, but offer
232support for the common case of USB HID security keys internally.
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000233
234The middleware library need only expose a handful of functions:
235
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000236 #define SSH_SK_VERSION_MAJOR 0x00040000 /* API version */
djm@openbsd.orgef65e7d2019-12-30 09:25:29 +0000237 #define SSH_SK_VERSION_MAJOR_MASK 0xffff0000
238
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000239 /* Flags */
240 #define SSH_SK_USER_PRESENCE_REQD 0x01
djm@openbsd.org4532bd02019-12-30 09:19:52 +0000241 #define SSH_SK_USER_VERIFICATION_REQD 0x04
242 #define SSH_SK_RESIDENT_KEY 0x20
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000243
markus@openbsd.orgfd1a3b52019-11-12 19:32:30 +0000244 /* Algs */
245 #define SSH_SK_ECDSA 0x00
246 #define SSH_SK_ED25519 0x01
247
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000248 /* Error codes */
249 #define SSH_SK_ERR_GENERAL -1
250 #define SSH_SK_ERR_UNSUPPORTED -2
251 #define SSH_SK_ERR_PIN_REQUIRED -3
252
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000253 struct sk_enroll_response {
254 uint8_t *public_key;
255 size_t public_key_len;
256 uint8_t *key_handle;
257 size_t key_handle_len;
258 uint8_t *signature;
259 size_t signature_len;
260 uint8_t *attestation_cert;
261 size_t attestation_cert_len;
262 };
263
264 struct sk_sign_response {
265 uint8_t flags;
266 uint32_t counter;
267 uint8_t *sig_r;
268 size_t sig_r_len;
269 uint8_t *sig_s;
270 size_t sig_s_len;
271 };
272
djm@openbsd.orgef65e7d2019-12-30 09:25:29 +0000273 struct sk_resident_key {
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000274 uint32_t alg;
djm@openbsd.orgef65e7d2019-12-30 09:25:29 +0000275 size_t slot;
276 char *application;
277 struct sk_enroll_response key;
278 };
279
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000280 struct sk_option {
281 char *name;
282 char *value;
283 uint8_t important;
284 };
285
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000286 /* Return the version of the middleware API */
287 uint32_t sk_api_version(void);
288
289 /* Enroll a U2F key (private key generation) */
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000290 int sk_enroll(uint32_t alg,
291 const uint8_t *challenge, size_t challenge_len,
djm@openbsd.orgef65e7d2019-12-30 09:25:29 +0000292 const char *application, uint8_t flags, const char *pin,
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000293 struct sk_option **options,
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000294 struct sk_enroll_response **enroll_response);
295
296 /* Sign a challenge */
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000297 int sk_sign(uint32_t alg, const uint8_t *message, size_t message_len,
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000298 const char *application,
299 const uint8_t *key_handle, size_t key_handle_len,
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000300 uint8_t flags, const char *pin, struct sk_option **options,
djm@openbsd.orgef65e7d2019-12-30 09:25:29 +0000301 struct sk_sign_response **sign_response);
302
303 /* Enumerate all resident keys */
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000304 int sk_load_resident_keys(const char *pin, struct sk_option **options,
djm@openbsd.orgef65e7d2019-12-30 09:25:29 +0000305 struct sk_resident_key ***rks, size_t *nrks);
306
307The SSH_SK_VERSION_MAJOR should be incremented for each incompatible
308API change.
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000309
djm@openbsd.orgc312ca02020-01-06 02:00:46 +0000310The options may be used to pass miscellaneous options to the middleware
311as a NULL-terminated array of pointers to struct sk_option. The middleware
312may ignore unsupported or unknown options unless the "important" flag is
313set, in which case it should return failure if an unsupported option is
314requested.
315
316At present the following options names are supported:
317
318 "device"
319
320 Specifies a specific FIDO device on which to perform the
321 operation. The value in this field is interpreted by the
322 middleware but it would be typical to specify a path to
323 a /dev node for the device in question.
324
325 "user"
326
327 Specifies the FIDO2 username used when enrolling a key,
328 overriding OpenSSH's default of using an all-zero username.
329
330In OpenSSH, the middleware will be invoked by using a similar mechanism to
djm@openbsd.org18e84bf2019-11-28 05:20:54 +0000331ssh-pkcs11-helper to provide address-space containment of the
332middleware from ssh-agent.
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000333