summaryrefslogtreecommitdiff
path: root/PROTOCOL.u2f
diff options
context:
space:
mode:
Diffstat (limited to 'PROTOCOL.u2f')
-rw-r--r--PROTOCOL.u2f337
1 files changed, 337 insertions, 0 deletions
diff --git a/PROTOCOL.u2f b/PROTOCOL.u2f
new file mode 100644
index 00000000..917e669c
--- /dev/null
+++ b/PROTOCOL.u2f
@@ -0,0 +1,337 @@
+This document describes OpenSSH's support for U2F/FIDO security keys.
+
+Background
+----------
+
+U2F is an open standard for two-factor authentication hardware, widely
+used for user authentication to websites. U2F tokens are ubiquitous,
+available from a number of manufacturers and are currently by far the
+cheapest way for users to achieve hardware-backed credential storage.
+
+The U2F protocol however cannot be trivially used as an SSH protocol key
+type as both the inputs to the signature operation and the resultant
+signature differ from those specified for SSH. For similar reasons,
+integration of U2F devices cannot be achieved via the PKCS#11 API.
+
+U2F also offers a number of features that are attractive in the context
+of SSH authentication. They can be configured to require indication
+of "user presence" for each signature operation (typically achieved
+by requiring the user touch the key). They also offer an attestation
+mechanism at key enrollment time that can be used to prove that a
+given key is backed by hardware. Finally the signature format includes
+a monotonic signature counter that can be used (at scale) to detect
+concurrent use of a private key, should it be extracted from hardware.
+
+U2F private keys are generated through an enrollment operation,
+which takes an application ID - a URL-like string, typically "ssh:"
+in this case, but a HTTP origin for the case of web authentication,
+and a challenge string (typically randomly generated). The enrollment
+operation returns a public key, a key handle that must be used to invoke
+the hardware-backed private key, some flags and signed attestation
+information that may be used to verify that a private key is hosted on a
+particular hardware instance.
+
+It is common for U2F hardware to derive private keys from the key handle
+in conjunction with a small per-device secret that is unique to the
+hardware, thus requiring little on-device storage for an effectively
+unlimited number of supported keys. This drives the requirement that
+the key handle be supplied for each signature operation. U2F tokens
+primarily use ECDSA signatures in the NIST-P256 field, though the FIDO2
+standard specifies additional key types, including one based on Ed25519.
+
+SSH U2F Key formats
+-------------------
+
+OpenSSH integrates U2F as new key and corresponding certificate types:
+
+ sk-ecdsa-sha2-nistp256@openssh.com
+ sk-ecdsa-sha2-nistp256-cert-v01@openssh.com
+ sk-ssh-ed25519@openssh.com
+ sk-ssh-ed25519-cert-v01@openssh.com
+
+While each uses ecdsa-sha256-nistp256 as the underlying signature primitive,
+keys require extra information in the public and private keys, and in
+the signature object itself. As such they cannot be made compatible with
+the existing ecdsa-sha2-nistp* key types.
+
+The format of a sk-ecdsa-sha2-nistp256@openssh.com public key is:
+
+ string "sk-ecdsa-sha2-nistp256@openssh.com"
+ string curve name
+ ec_point Q
+ string application (user-specified, but typically "ssh:")
+
+The corresponding private key contains:
+
+ string "sk-ecdsa-sha2-nistp256@openssh.com"
+ string curve name
+ ec_point Q
+ string application (user-specified, but typically "ssh:")
+ uint8 flags
+ string key_handle
+ string reserved
+
+The format of a sk-ssh-ed25519@openssh.com public key is:
+
+ string "sk-ssh-ed25519@openssh.com"
+ string public key
+ string application (user-specified, but typically "ssh:")
+
+With a private half consisting of:
+
+ string "sk-ssh-ed25519@openssh.com"
+ string public key
+ string application (user-specified, but typically "ssh:")
+ uint8 flags
+ string key_handle
+ string reserved
+
+The certificate form for SSH U2F keys appends the usual certificate
+information to the public key:
+
+ string "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com"
+ string nonce
+ string curve name
+ ec_point Q
+ string application
+ uint64 serial
+ uint32 type
+ string key id
+ string valid principals
+ uint64 valid after
+ uint64 valid before
+ string critical options
+ string extensions
+ string reserved
+ string signature key
+ string signature
+
+and for security key ed25519 certificates:
+
+ string "sk-ssh-ed25519-cert-v01@openssh.com"
+ string nonce
+ string public key
+ string application
+ uint64 serial
+ uint32 type
+ string key id
+ string valid principals
+ uint64 valid after
+ uint64 valid before
+ string critical options
+ string extensions
+ string reserved
+ string signature key
+ string signature
+
+Both security key certificates use the following encoding for private keys:
+
+ string type (e.g. "sk-ssh-ed25519-cert-v01@openssh.com")
+ string pubkey (the above key/cert structure)
+ string application
+ uint8 flags
+ string key_handle
+ string reserved
+
+During key generation, the hardware also returns attestation information
+that may be used to cryptographically prove that a given key is
+hardware-backed. Unfortunately, the protocol required for this proof is
+not privacy-preserving and may be used to identify U2F tokens with at
+least manufacturer and batch number granularity. For this reason, we
+choose not to include this information in the public key or save it by
+default.
+
+Attestation information is useful for out-of-band key and certificate
+registration workflows, e.g. proving to a CA that a key is backed
+by trusted hardware before it will issue a certificate. To support this
+case, OpenSSH optionally allows retaining the attestation information
+at the time of key generation. It will take the following format:
+
+ string "ssh-sk-attest-v00"
+ string attestation certificate
+ string enrollment signature
+ uint32 reserved flags
+ string reserved string
+
+OpenSSH treats the attestation certificate and enrollment signatures as
+opaque objects and does no interpretation of them itself.
+
+SSH U2F signatures
+------------------
+
+In addition to the message to be signed, the U2F signature operation
+requires the key handle and a few additional parameters. The signature
+is signed over a blob that consists of:
+
+ byte[32] SHA256(application)
+ byte flags (including "user present", extensions present)
+ uint32 counter
+ byte[] extensions
+ byte[32] SHA256(message)
+
+No extensions are yet defined for SSH use. If any are defined in the future,
+it will be possible to infer their presence from the contents of the "flags"
+value.
+
+The signature returned from U2F hardware takes the following format:
+
+ byte flags (including "user present")
+ uint32 counter
+ byte[] ecdsa_signature (in X9.62 format).
+
+For use in the SSH protocol, we wish to avoid server-side parsing of ASN.1
+format data in the pre-authentication attack surface. Therefore, the
+signature format used on the wire in SSH2_USERAUTH_REQUEST packets will
+be reformatted to better match the existing signature encoding:
+
+ string "sk-ecdsa-sha2-nistp256@openssh.com"
+ string ecdsa_signature
+ byte flags
+ uint32 counter
+
+Where the "ecdsa_signature" field follows the RFC5656 ECDSA signature
+encoding:
+
+ mpint r
+ mpint s
+
+For Ed25519 keys the signature is encoded as:
+
+ string "sk-ssh-ed25519@openssh.com"
+ string signature
+ byte flags
+ uint32 counter
+
+ssh-agent protocol extensions
+-----------------------------
+
+ssh-agent requires a protocol extension to support U2F keys. At
+present the closest analogue to Security Keys in ssh-agent are PKCS#11
+tokens, insofar as they require a middleware library to communicate with
+the device that holds the keys. Unfortunately, the protocol message used
+to add PKCS#11 keys to ssh-agent does not include any way to send the
+key handle to the agent as U2F keys require.
+
+To avoid this, without having to add wholly new messages to the agent
+protocol, we will use the existing SSH2_AGENTC_ADD_ID_CONSTRAINED message
+with a new key constraint extension to encode a path to the middleware
+library for the key. The format of this constraint extension would be:
+
+ byte SSH_AGENT_CONSTRAIN_EXTENSION
+ string sk-provider@openssh.com
+ string middleware path
+
+This constraint-based approach does not present any compatibility
+problems.
+
+OpenSSH integration
+-------------------
+
+U2F tokens may be attached via a number of means, including USB and NFC.
+The USB interface is standardised around a HID protocol, but we want to
+be able to support other transports as well as dummy implementations for
+regress testing. For this reason, OpenSSH shall support a dynamically-
+loaded middleware libraries to communicate with security keys, but offer
+support for the common case of USB HID security keys internally.
+
+The middleware library need only expose a handful of functions:
+
+ #define SSH_SK_VERSION_MAJOR 0x00050000 /* API version */
+ #define SSH_SK_VERSION_MAJOR_MASK 0xffff0000
+
+ /* Flags */
+ #define SSH_SK_USER_PRESENCE_REQD 0x01
+ #define SSH_SK_USER_VERIFICATION_REQD 0x04
+ #define SSH_SK_RESIDENT_KEY 0x20
+
+ /* Algs */
+ #define SSH_SK_ECDSA 0x00
+ #define SSH_SK_ED25519 0x01
+
+ /* Error codes */
+ #define SSH_SK_ERR_GENERAL -1
+ #define SSH_SK_ERR_UNSUPPORTED -2
+ #define SSH_SK_ERR_PIN_REQUIRED -3
+ #define SSH_SK_ERR_DEVICE_NOT_FOUND -4
+
+ struct sk_enroll_response {
+ uint8_t *public_key;
+ size_t public_key_len;
+ uint8_t *key_handle;
+ size_t key_handle_len;
+ uint8_t *signature;
+ size_t signature_len;
+ uint8_t *attestation_cert;
+ size_t attestation_cert_len;
+ };
+
+ struct sk_sign_response {
+ uint8_t flags;
+ uint32_t counter;
+ uint8_t *sig_r;
+ size_t sig_r_len;
+ uint8_t *sig_s;
+ size_t sig_s_len;
+ };
+
+ struct sk_resident_key {
+ uint32_t alg;
+ size_t slot;
+ char *application;
+ struct sk_enroll_response key;
+ };
+
+ struct sk_option {
+ char *name;
+ char *value;
+ uint8_t important;
+ };
+
+ /* Return the version of the middleware API */
+ uint32_t sk_api_version(void);
+
+ /* Enroll a U2F key (private key generation) */
+ int sk_enroll(uint32_t alg,
+ const uint8_t *challenge, size_t challenge_len,
+ const char *application, uint8_t flags, const char *pin,
+ struct sk_option **options,
+ struct sk_enroll_response **enroll_response);
+
+ /* Sign a challenge */
+ int sk_sign(uint32_t alg, const uint8_t *message, size_t message_len,
+ const char *application,
+ const uint8_t *key_handle, size_t key_handle_len,
+ uint8_t flags, const char *pin, struct sk_option **options,
+ struct sk_sign_response **sign_response);
+
+ /* Enumerate all resident keys */
+ int sk_load_resident_keys(const char *pin, struct sk_option **options,
+ struct sk_resident_key ***rks, size_t *nrks);
+
+The SSH_SK_VERSION_MAJOR should be incremented for each incompatible
+API change.
+
+The options may be used to pass miscellaneous options to the middleware
+as a NULL-terminated array of pointers to struct sk_option. The middleware
+may ignore unsupported or unknown options unless the "important" flag is
+set, in which case it should return failure if an unsupported option is
+requested.
+
+At present the following options names are supported:
+
+ "device"
+
+ Specifies a specific FIDO device on which to perform the
+ operation. The value in this field is interpreted by the
+ middleware but it would be typical to specify a path to
+ a /dev node for the device in question.
+
+ "user"
+
+ Specifies the FIDO2 username used when enrolling a key,
+ overriding OpenSSH's default of using an all-zero username.
+
+In OpenSSH, the middleware will be invoked by using a similar mechanism to
+ssh-pkcs11-helper to provide address-space containment of the
+middleware from ssh-agent.
+