blob: 4e3896419f1e21012e3cda096e9ed3999815577c [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
40standard specified additional key types include 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
52These key types are supported only for user authentication with the
53"publickey" method. They are not used for host-based user authentication
54or server host key authentication.
55
56While each uses ecdsa-sha256-nistp256 as the underlying signature primitive,
57keys require extra information in the public and private keys, and in
58the signature object itself. As such they cannot be made compatible with
59the existing ecdsa-sha2-nistp* key types.
60
61The format of a sk-ecdsa-sha2-nistp256@openssh.com public key is:
62
63 string "sk-ecdsa-sha2-nistp256@openssh.com"
djm@openbsd.org93fa2a62019-11-18 04:16:53 +000064 string curve name
djm@openbsd.org57ecc102019-10-31 21:14:17 +000065 ec_point Q
66 string application (user-specified, but typically "ssh:")
67
68The corresponding private key contains:
69
70 string "sk-ecdsa-sha2-nistp256@openssh.com"
djm@openbsd.org93fa2a62019-11-18 04:16:53 +000071 string curve name
djm@openbsd.org57ecc102019-10-31 21:14:17 +000072 ec_point Q
73 string application (user-specified, but typically "ssh:")
djm@openbsd.org71856e12019-11-18 04:29:50 +000074 uint8 flags
djm@openbsd.org57ecc102019-10-31 21:14:17 +000075 string key_handle
djm@openbsd.org57ecc102019-10-31 21:14:17 +000076 string reserved
77
djm@openbsd.org13066432019-11-18 04:34:47 +000078The format of a sk-ssh-ed25519@openssh.com public key is:
79
80 string "sk-ssh-ed25519@openssh.com"
81 string public key
82 string application (user-specified, but typically "ssh:")
83
84With a private half consisting of:
85
86 string "sk-ssh-ed25519@openssh.com"
87 string public key
88 string application (user-specified, but typically "ssh:")
89 uint32 flags
90 string key_handle
91 string reserved
92
93The certificate form for SSH U2F keys appends the usual certificate
djm@openbsd.org57ecc102019-10-31 21:14:17 +000094information to the public key:
95
naddy@openbsd.orgad384062019-11-01 12:10:43 +000096 string "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com"
djm@openbsd.org57ecc102019-10-31 21:14:17 +000097 string nonce
djm@openbsd.org93fa2a62019-11-18 04:16:53 +000098 string curve name
djm@openbsd.org57ecc102019-10-31 21:14:17 +000099 ec_point Q
100 string application
101 uint64 serial
102 uint32 type
103 string key id
104 string valid principals
105 uint64 valid after
106 uint64 valid before
107 string critical options
108 string extensions
109 string reserved
110 string signature key
111 string signature
112
djm@openbsd.org13066432019-11-18 04:34:47 +0000113 string "sk-ssh-ed25519-cert-v01@openssh.com"
114 string nonce
115 string public key
116 string application
117 uint64 serial
118 uint32 type
119 string key id
120 string valid principals
121 uint64 valid after
122 uint64 valid before
123 string critical options
124 string extensions
125 string reserved
126 string signature key
127 string signature
128
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000129During key generation, the hardware also returns attestation information
130that may be used to cryptographically prove that a given key is
131hardware-backed. Unfortunately, the protocol required for this proof is
132not privacy-preserving and may be used to identify U2F tokens with at
133least manufacturer and batch number granularity. For this reason, we
134choose not to include this information in the public key or save it by
135default.
136
137Attestation information is very useful however in an organisational
naddy@openbsd.orgad384062019-11-01 12:10:43 +0000138context, where it may be used by a CA as part of certificate
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000139issuance. In this case, exposure to the CA of hardware identity is
140desirable. To support this case, OpenSSH optionally allows retaining the
141attestation information at the time of key generation. It will take the
142following format:
143
144 string "sk-attest-v00"
145 uint32 version (1 for U2F, 2 for FIDO2 in future)
146 string attestation certificate
147 string enrollment signature
148
149SSH U2F signatures
150------------------
151
152In addition to the message to be signed, the U2F signature operation
153requires a few additional parameters:
154
155 byte control bits (e.g. "user presence required" flag)
156 byte[32] SHA256(message)
157 byte[32] SHA256(application)
158 byte key_handle length
159 byte[] key_handle
160
161This signature is signed over a blob that consists of:
162
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
169The signature returned from U2F hardware takes the following format:
170
171 byte flags (including "user present")
172 uint32 counter
173 byte[32] ecdsa_signature (in X9.62 format).
174
175For use in the SSH protocol, we wish to avoid server-side parsing of ASN.1
176format data in the pre-authentication attack surface. Therefore, the
177signature format used on the wire in SSH2_USERAUTH_REQUEST packets will
djm@openbsd.orga70d92f2019-11-19 22:23:19 +0000178be reformatted to better match the existing signature encoding:
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000179
djm@openbsd.orga70d92f2019-11-19 22:23:19 +0000180 string "sk-ecdsa-sha2-nistp256@openssh.com"
181 string ecdsa_signature
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000182 byte flags
183 uint32 counter
184
djm@openbsd.orga70d92f2019-11-19 22:23:19 +0000185Where the "ecdsa_signature" field follows the RFC5656 ECDSA signature
186encoding:
187
188 mpint r
189 mpint s
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000190
markus@openbsd.orgb556cc32019-11-12 19:34:40 +0000191For Ed25519 keys the signature is encoded as:
192
193 string "sk-ssh-ed25519@openssh.com"
194 string signature
195 byte flags
196 uint32 counter
197
198
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000199ssh-agent protocol extensions
200-----------------------------
201
naddy@openbsd.orgad384062019-11-01 12:10:43 +0000202ssh-agent requires a protocol extension to support U2F keys. At
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000203present the closest analogue to Security Keys in ssh-agent are PKCS#11
204tokens, insofar as they require a middleware library to communicate with
205the device that holds the keys. Unfortunately, the protocol message used
206to add PKCS#11 keys to ssh-agent does not include any way to send the
207key handle to the agent as U2F keys require.
208
naddy@openbsd.orgad384062019-11-01 12:10:43 +0000209To avoid this, without having to add wholly new messages to the agent
210protocol, we will use the existing SSH2_AGENTC_ADD_ID_CONSTRAINED message
211with a new key constraint extension to encode a path to the middleware
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000212library for the key. The format of this constraint extension would be:
213
214 byte SSH_AGENT_CONSTRAIN_EXTENSION
215 string sk@openssh.com
216 string middleware path
217
218This constraint-based approach does not present any compatibility
219problems.
220
221OpenSSH integration
222-------------------
223
224U2F tokens may be attached via a number of means, including USB and NFC.
225The USB interface is standardised around a HID protocol, but we want to
226be able to support other transports as well as dummy implementations for
djm@openbsd.org13066432019-11-18 04:34:47 +0000227regress testing. For this reason, OpenSSH shall support a dynamically-
228loaded middleware libraries to communicate with security keys, but offer
229support for the common case of USB HID security keys internally.
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000230
231The middleware library need only expose a handful of functions:
232
233 /* Flags */
234 #define SSH_SK_USER_PRESENCE_REQD 0x01
235
markus@openbsd.orgfd1a3b52019-11-12 19:32:30 +0000236 /* Algs */
237 #define SSH_SK_ECDSA 0x00
238 #define SSH_SK_ED25519 0x01
239
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000240 struct sk_enroll_response {
241 uint8_t *public_key;
242 size_t public_key_len;
243 uint8_t *key_handle;
244 size_t key_handle_len;
245 uint8_t *signature;
246 size_t signature_len;
247 uint8_t *attestation_cert;
248 size_t attestation_cert_len;
249 };
250
251 struct sk_sign_response {
252 uint8_t flags;
253 uint32_t counter;
254 uint8_t *sig_r;
255 size_t sig_r_len;
256 uint8_t *sig_s;
257 size_t sig_s_len;
258 };
259
260 /* Return the version of the middleware API */
261 uint32_t sk_api_version(void);
262
263 /* Enroll a U2F key (private key generation) */
markus@openbsd.orgfd1a3b52019-11-12 19:32:30 +0000264 int sk_enroll(int alg, const uint8_t *challenge, size_t challenge_len,
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000265 const char *application, uint8_t flags,
266 struct sk_enroll_response **enroll_response);
267
268 /* Sign a challenge */
markus@openbsd.orgfd1a3b52019-11-12 19:32:30 +0000269 int sk_sign(int alg, const uint8_t *message, size_t message_len,
djm@openbsd.org57ecc102019-10-31 21:14:17 +0000270 const char *application,
271 const uint8_t *key_handle, size_t key_handle_len,
272 uint8_t flags, struct sk_sign_response **sign_response);
273
274In OpenSSH, these will be invoked by generalising the existing
275ssh-pkcs11-helper mechanism to provide containment of the middleware from
276ssh-agent.
277