Edit

IABSD.fr/src/usr.bin/ssh/PROTOCOL.u2f

Branch :

  • Show log

    Commit

  • Author : djm
    Date : 2020-06-22 05:58:35
    Hash : c25c45ac
    Message : Add support for FIDO webauthn (verification only). webauthn is a standard for using FIDO keys in web browsers. webauthn signatures are a slightly different format to plain FIDO signatures - this support allows verification of these. Feedback and ok markus@

  • usr.bin/ssh/PROTOCOL.u2f
  • 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.
    
    Use of U2F security keys does not automatically imply multi-factor
    authentication. From sshd's perspective, a security key constitutes a
    single factor of authentication, even if protected by a PIN or biometric
    authentication.  To enable multi-factor authentication in ssh, please
    refer to the AuthenticationMethods option in sshd_config(5).
    
    
    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
    
    webauthn signatures
    -------------------
    
    The W3C/FIDO webauthn[1] standard defines a mechanism for a web browser to
    interact with FIDO authentication tokens. This standard builds upon the
    FIDO standards, but requires different signature contents to raw FIDO
    messages. OpenSSH supports ECDSA/p256 webauthn signatures through the
    "webauthn-sk-ecdsa-sha2-nistp256@openssh.com" signature algorithm.
    
    The wire encoding for a webauthn-sk-ecdsa-sha2-nistp256@openssh.com
    signature is similar to the sk-ecdsa-sha2-nistp256@openssh.com format:
    
    	string		"webauthn-sk-ecdsa-sha2-nistp256@openssh.com"
    	string		ecdsa_signature
    	byte		flags
    	uint32		counter
    	string		origin
    	string		clientData
    	string		extensions
    
    Where "origin" is the HTTP origin making the signature, "clientData" is
    the JSON-like structure signed by the browser and "extensions" are any
    extensions used in making the signature.
    
    [1] https://www.w3.org/TR/webauthn-2/
    
    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.