Annotation of src/usr.bin/ssh/PROTOCOL.u2f, Revision 1.26
1.1 djm 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:
1.2 naddy 25: U2F private keys are generated through an enrollment operation,
1.1 djm 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
1.2 naddy 31: information that may be used to verify that a private key is hosted on a
1.1 djm 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
1.7 djm 39: primarily use ECDSA signatures in the NIST-P256 field, though the FIDO2
1.14 naddy 40: standard specifies additional key types, including one based on Ed25519.
1.1 djm 41:
1.22 djm 42: Use of U2F security keys does not automatically imply multi-factor
1.23 djm 43: authentication. From sshd's perspective, a security key constitutes a
1.22 djm 44: single factor of authentication, even if protected by a PIN or biometric
45: authentication. To enable multi-factor authentication in ssh, please
46: refer to the AuthenticationMethods option in sshd_config(5).
47:
48:
1.1 djm 49: SSH U2F Key formats
50: -------------------
51:
1.7 djm 52: OpenSSH integrates U2F as new key and corresponding certificate types:
1.1 djm 53:
54: sk-ecdsa-sha2-nistp256@openssh.com
55: sk-ecdsa-sha2-nistp256-cert-v01@openssh.com
1.7 djm 56: sk-ssh-ed25519@openssh.com
57: sk-ssh-ed25519-cert-v01@openssh.com
1.1 djm 58:
59: While each uses ecdsa-sha256-nistp256 as the underlying signature primitive,
60: keys require extra information in the public and private keys, and in
61: the signature object itself. As such they cannot be made compatible with
62: the existing ecdsa-sha2-nistp* key types.
63:
64: The format of a sk-ecdsa-sha2-nistp256@openssh.com public key is:
65:
66: string "sk-ecdsa-sha2-nistp256@openssh.com"
1.5 djm 67: string curve name
1.1 djm 68: ec_point Q
69: string application (user-specified, but typically "ssh:")
70:
71: The corresponding private key contains:
72:
73: string "sk-ecdsa-sha2-nistp256@openssh.com"
1.5 djm 74: string curve name
1.1 djm 75: ec_point Q
76: string application (user-specified, but typically "ssh:")
1.6 djm 77: uint8 flags
1.1 djm 78: string key_handle
79: string reserved
80:
1.7 djm 81: The format of a sk-ssh-ed25519@openssh.com public key is:
82:
83: string "sk-ssh-ed25519@openssh.com"
84: string public key
85: string application (user-specified, but typically "ssh:")
86:
87: With a private half consisting of:
88:
89: string "sk-ssh-ed25519@openssh.com"
90: string public key
91: string application (user-specified, but typically "ssh:")
1.12 djm 92: uint8 flags
1.7 djm 93: string key_handle
94: string reserved
95:
96: The certificate form for SSH U2F keys appends the usual certificate
1.1 djm 97: information to the public key:
98:
1.2 naddy 99: string "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com"
1.1 djm 100: string nonce
1.5 djm 101: string curve name
1.1 djm 102: ec_point Q
103: string application
104: uint64 serial
105: uint32 type
106: string key id
107: string valid principals
108: uint64 valid after
109: uint64 valid before
110: string critical options
111: string extensions
112: string reserved
113: string signature key
114: string signature
115:
1.12 djm 116: and for security key ed25519 certificates:
117:
1.7 djm 118: string "sk-ssh-ed25519-cert-v01@openssh.com"
119: string nonce
120: string public key
121: string application
122: uint64 serial
123: uint32 type
124: string key id
125: string valid principals
126: uint64 valid after
127: uint64 valid before
128: string critical options
129: string extensions
130: string reserved
131: string signature key
132: string signature
133:
1.12 djm 134: Both security key certificates use the following encoding for private keys:
135:
136: string type (e.g. "sk-ssh-ed25519-cert-v01@openssh.com")
137: string pubkey (the above key/cert structure)
138: string application
139: uint8 flags
140: string key_handle
141: string reserved
142:
1.1 djm 143: During key generation, the hardware also returns attestation information
144: that may be used to cryptographically prove that a given key is
145: hardware-backed. Unfortunately, the protocol required for this proof is
146: not privacy-preserving and may be used to identify U2F tokens with at
147: least manufacturer and batch number granularity. For this reason, we
148: choose not to include this information in the public key or save it by
149: default.
150:
1.19 djm 151: Attestation information is useful for out-of-band key and certificate
1.20 dtucker 152: registration workflows, e.g. proving to a CA that a key is backed
1.19 djm 153: by trusted hardware before it will issue a certificate. To support this
154: case, OpenSSH optionally allows retaining the attestation information
155: at the time of key generation. It will take the following format:
1.1 djm 156:
1.26 ! djm 157: string "ssh-sk-attest-v01"
! 158: string attestation certificate
! 159: string enrollment signature
! 160: string authenticator data (CBOR encoded)
! 161: uint32 reserved flags
! 162: string reserved string
! 163:
! 164: A previous version of this format, emitted prior to OpenSSH 8.4 omitted
! 165: the authenticator data.
! 166:
1.19 djm 167: string "ssh-sk-attest-v00"
1.1 djm 168: string attestation certificate
169: string enrollment signature
1.19 djm 170: uint32 reserved flags
171: string reserved string
172:
173: OpenSSH treats the attestation certificate and enrollment signatures as
174: opaque objects and does no interpretation of them itself.
1.1 djm 175:
176: SSH U2F signatures
177: ------------------
178:
1.9 djm 179: In addition to the message to be signed, the U2F signature operation
1.10 djm 180: requires the key handle and a few additional parameters. The signature
181: is signed over a blob that consists of:
1.1 djm 182:
183: byte[32] SHA256(application)
184: byte flags (including "user present", extensions present)
185: uint32 counter
186: byte[] extensions
187: byte[32] SHA256(message)
188:
1.20 dtucker 189: No extensions are yet defined for SSH use. If any are defined in the future,
1.13 djm 190: it will be possible to infer their presence from the contents of the "flags"
191: value.
192:
1.1 djm 193: The signature returned from U2F hardware takes the following format:
194:
195: byte flags (including "user present")
196: uint32 counter
1.10 djm 197: byte[] ecdsa_signature (in X9.62 format).
1.1 djm 198:
199: For use in the SSH protocol, we wish to avoid server-side parsing of ASN.1
200: format data in the pre-authentication attack surface. Therefore, the
201: signature format used on the wire in SSH2_USERAUTH_REQUEST packets will
1.8 djm 202: be reformatted to better match the existing signature encoding:
1.1 djm 203:
1.8 djm 204: string "sk-ecdsa-sha2-nistp256@openssh.com"
205: string ecdsa_signature
1.1 djm 206: byte flags
207: uint32 counter
208:
1.8 djm 209: Where the "ecdsa_signature" field follows the RFC5656 ECDSA signature
210: encoding:
211:
212: mpint r
213: mpint s
1.1 djm 214:
1.4 markus 215: For Ed25519 keys the signature is encoded as:
216:
217: string "sk-ssh-ed25519@openssh.com"
218: string signature
219: byte flags
220: uint32 counter
221:
1.24 djm 222: webauthn signatures
223: -------------------
224:
225: The W3C/FIDO webauthn[1] standard defines a mechanism for a web browser to
226: interact with FIDO authentication tokens. This standard builds upon the
227: FIDO standards, but requires different signature contents to raw FIDO
228: messages. OpenSSH supports ECDSA/p256 webauthn signatures through the
229: "webauthn-sk-ecdsa-sha2-nistp256@openssh.com" signature algorithm.
230:
231: The wire encoding for a webauthn-sk-ecdsa-sha2-nistp256@openssh.com
232: signature is similar to the sk-ecdsa-sha2-nistp256@openssh.com format:
233:
234: string "webauthn-sk-ecdsa-sha2-nistp256@openssh.com"
235: string ecdsa_signature
236: byte flags
237: uint32 counter
238: string origin
239: string clientData
240: string extensions
241:
242: Where "origin" is the HTTP origin making the signature, "clientData" is
243: the JSON-like structure signed by the browser and "extensions" are any
244: extensions used in making the signature.
245:
246: [1] https://www.w3.org/TR/webauthn-2/
247:
1.1 djm 248: ssh-agent protocol extensions
249: -----------------------------
250:
1.2 naddy 251: ssh-agent requires a protocol extension to support U2F keys. At
1.1 djm 252: present the closest analogue to Security Keys in ssh-agent are PKCS#11
253: tokens, insofar as they require a middleware library to communicate with
254: the device that holds the keys. Unfortunately, the protocol message used
255: to add PKCS#11 keys to ssh-agent does not include any way to send the
256: key handle to the agent as U2F keys require.
257:
1.2 naddy 258: To avoid this, without having to add wholly new messages to the agent
259: protocol, we will use the existing SSH2_AGENTC_ADD_ID_CONSTRAINED message
260: with a new key constraint extension to encode a path to the middleware
1.1 djm 261: library for the key. The format of this constraint extension would be:
262:
263: byte SSH_AGENT_CONSTRAIN_EXTENSION
1.11 djm 264: string sk-provider@openssh.com
1.1 djm 265: string middleware path
266:
267: This constraint-based approach does not present any compatibility
268: problems.
269:
270: OpenSSH integration
271: -------------------
272:
273: U2F tokens may be attached via a number of means, including USB and NFC.
274: The USB interface is standardised around a HID protocol, but we want to
275: be able to support other transports as well as dummy implementations for
1.7 djm 276: regress testing. For this reason, OpenSSH shall support a dynamically-
277: loaded middleware libraries to communicate with security keys, but offer
278: support for the common case of USB HID security keys internally.
1.1 djm 279:
1.26 ! djm 280: The middleware library need only expose a handful of functions and
! 281: numbers listed in sk-api.h. Included in the defined numbers is a
! 282: SSH_SK_VERSION_MAJOR that should be incremented for each incompatible
1.16 djm 283: API change.
1.1 djm 284:
1.26 ! djm 285: miscellaneous options may be passed to the middleware as a NULL-
! 286: terminated array of pointers to struct sk_option. The middleware may
! 287: ignore unsupported or unknown options unless the "required" flag is set,
! 288: in which case it should return failure if an unsupported option is
1.17 djm 289: requested.
290:
291: At present the following options names are supported:
292:
293: "device"
294:
295: Specifies a specific FIDO device on which to perform the
296: operation. The value in this field is interpreted by the
297: middleware but it would be typical to specify a path to
298: a /dev node for the device in question.
299:
300: "user"
301:
302: Specifies the FIDO2 username used when enrolling a key,
303: overriding OpenSSH's default of using an all-zero username.
304:
305: In OpenSSH, the middleware will be invoked by using a similar mechanism to
1.9 djm 306: ssh-pkcs11-helper to provide address-space containment of the
307: middleware from ssh-agent.
1.1 djm 308:
1.26 ! djm 309: $OpenBSD: PROTOCOL.u2f,v 1.25 2020/08/31 00:17:41 djm Exp $