diff options
author | Damien Miller <djm@cvs.openbsd.org> | 2019-10-31 21:14:18 +0000 |
---|---|---|
committer | Damien Miller <djm@cvs.openbsd.org> | 2019-10-31 21:14:18 +0000 |
commit | dc7fcc00029e32f2090196aa99eda07c8666b573 (patch) | |
tree | fc52f4dae8aebfc1124636ec7554967bcd50d719 /usr.bin | |
parent | 1876573eb2feee3ae92181dc8e807d66b6322bef (diff) |
Protocol documentation for U2F/FIDO keys in OpenSSH
Diffstat (limited to 'usr.bin')
-rw-r--r-- | usr.bin/ssh/PROTOCOL.u2f | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/usr.bin/ssh/PROTOCOL.u2f b/usr.bin/ssh/PROTOCOL.u2f new file mode 100644 index 00000000000..ab9e3e333e1 --- /dev/null +++ b/usr.bin/ssh/PROTOCOL.u2f @@ -0,0 +1,224 @@ +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 generatted 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 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. + +SSH U2F Key formats +------------------- + +OpenSSH integrates U2F as a new key and corresponding certificate type: + + sk-ecdsa-sha2-nistp256@openssh.com + sk-ecdsa-sha2-nistp256-cert-v01@openssh.com + +These key types are supported only for user authentication with the +"publickey" method. They are not used for host-based user authentication +or server host key authentication. + +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" + ec_point Q + string application (user-specified, but typically "ssh:") + +The corresponding private key contains: + + string "sk-ecdsa-sha2-nistp256@openssh.com" + ec_point Q + string application (user-specified, but typically "ssh:") + string key_handle + uint32 flags + string reserved + +The certificate form of a SSH U2F key appends the usual certificate +information to the public key: + + string "sk-ecdsa-sha2-nistp256@openssh.com" + string nonce + 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 + +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 very useful however in an organisational +context, where it may be used by an CA as part of certificate +issuance. In this case, exposure to the CA of hardware identity is +desirable. To support this case, OpenSSH optionally allows retaining the +attestation information at the time of key generation. It will take the +following format: + + string "sk-attest-v00" + uint32 version (1 for U2F, 2 for FIDO2 in future) + string attestation certificate + string enrollment signature + +SSH U2F signatures +------------------ + +In addition to the message to be signed, the U2F signature operation +requires a few additional parameters: + + byte control bits (e.g. "user presence required" flag) + byte[32] SHA256(message) + byte[32] SHA256(application) + byte key_handle length + byte[] key_handle + +This 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) + +The signature returned from U2F hardware takes the following format: + + byte flags (including "user present") + uint32 counter + byte[32] 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 slightly: + + mpint r + mpint s + byte flags + uint32 counter + +Where 'r' and 's' are extracted by the client or token middleware from the +ecdsa_signature field returned from the hardware. + +ssh-agent protocol extensions +----------------------------- + +ssh-agent requires some 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 wholy new messages to the agent +protocol we will use the existing SSH2_AGENTC_ADD_ID_CONSTRAINED message +with a new a 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@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 perform all U2F operations +via a dynamically-loaded middleware library. + +The middleware library need only expose a handful of functions: + + /* Flags */ + #define SSH_SK_USER_PRESENCE_REQD 0x01 + + 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; + }; + + /* Return the version of the middleware API */ + uint32_t sk_api_version(void); + + /* Enroll a U2F key (private key generation) */ + int sk_enroll(const uint8_t *challenge, size_t challenge_len, + const char *application, uint8_t flags, + struct sk_enroll_response **enroll_response); + + /* Sign a challenge */ + int sk_sign(const uint8_t *message, size_t message_len, + const char *application, + const uint8_t *key_handle, size_t key_handle_len, + uint8_t flags, struct sk_sign_response **sign_response); + +In OpenSSH, these will be invoked by generalising the existing +ssh-pkcs11-helper mechanism to provide containment of the middleware from +ssh-agent. + |