U2F support in OpenSSH HEAD

Damien Miller djm at mindrot.org
Fri Nov 1 19:36:16 AEDT 2019


As of this morning, OpenSSH now has experimental U2F/FIDO support, with
U2F being added as a new key type "sk-ecdsa-sha2-nistp256 at openssh.com"
or "ecdsa-sk" for short (the "sk" stands for "security key").

If you're not familiar with U2F, this is an open standard for making
inexpensive hardware security tokens. These are easily the cheapest way
for users to get a hardware-backed keypair and there is a good range of
vendors who sell them including Yubico, Feitian, Thetis and Kensington.
Hardware-backed keys offer the benefit of being considerably more
difficult to steal - an attacker typically has to steal the physical
token (or at least persistent access to it) in order to steal the key.

Since there are a number of ways to talk to U2F devices, including USB,
Bluetooth and NFC, we didn't want to burden OpenSSH with a bunch of
dependencies. Instead we've delegated the task of communicating with the
tokens to a small middleware library that is loaded in a manner similar
to the existing PKCS#11 support.

We've written a basic middleware for Yubico's libfido2 that is capable
of talking to any standard USB HID U2F or FIDO2 token. The middleware
source is hosted in the libfido2 tree, so building that and OpenSSH HEAD
is sufficient to get started.

Some quickstart instructions:

1. Build and install OpenSSH

If you're using OpenBSD, then you can use a snapshot release dated
after 2019-11-01 and it will have an OpenSSH with the necessary

If you're on another operating system then you'll need to clone portable
OpenSSH from https://github.com/openssh/openssh-portable and follow
the build instructions in README,md. U2F support adds no additional
dependencies to this step.

2. Build libfido2:

If you're on OpenBSD, the see my recent posts to the ports@ mailing
list for libfido2 and its dependencies. Hopefully these will be added
to the ports tree soon.

Otherwise, you'll need to install libfido2's dependencies and
libfido2 HEAD yourself by cloning https://github.com/Yubico/libfido2
and following its build instructions. libfido2 will install a
${libdir}/libsk-libfido2.so shared object - that's the middleware you
need for the subsequent steps.

The existing middleware is pretty basic: it will attempt to locate and
use the first U2F token it finds attached via USB. It's therefore likely
to get confused if you happen to have more than one token attached to
your machine.

libfido2 includes support for OpenBSD, Linux, OS X and Windows (though
I expect more work will be needed on the OpenSSH side for to get Windows

3. Generate a key.

The OpenSSH tools use the $SSH_SK_PROVIDER environment variable to
point to the middleware, though all tools that support security keys
accept dedicated command-line or configuration options (e.g. ssh_config
SecurityKeyProvider). This provider needs to be available for key
generation and signing (e.g. pubkey authentication) operations.

$ SSH_SK_PROVIDER=/path/to/libsk-libfido2.so
$ ssh-keygen -t ecdsa-sk

You will typically need to tap your token to confirm the keygen
operation, but once complete this will yield a keypair at
~/.ssh/id_ecdsa_sk. It can be used much like any other key -
id_ecdsa_sk.pub can be copied to a server's authorized_keys file and
can be used for authentication, Note that the server only verifies
signatures, so it doesn't need to communicate with tokens.

The id_ecdsa_sk private key generated in this step is basically a U2F
"key handle" that is combined with a per-token secret to yield the real
public key. Theft or disclosure of the on-disk id_ecdsa_sk private
key alone should yield attackers the ability to authenticate using a
U2F token - they must steal the hardware itself (or access to it at
least). Moreover, the default mode for key creation requires the user
demonstrate physical presence for each signature operation, typically by
tapping the token. This makes theft-of-use more difficult and easy to

In any case, ssh-keygen will prompt for a passphrase for the on-disk
private key file for U2F keys as usual and this passphrase may be used
as an additional layer of protection.

4. Authenticate using a U2F token

This step is very straightforward; append the public key to
authorized_keys as you would normally. Note that U2F keys are a new
OpenSSH key type, so the server must support it too.

The only other requirement is that ssh(1) have access to the
middleware library. If you set the $SSH_SK_PROVIDER environment above
then you're already done, but otherwise you might want to use the
SecurityKeyProvider option in your configuration or on the command-line
to point ssh at the middleware.

At authentication time, you will need to tap your token to confirm the
private key signing operation.

5. Add a U2F key to a ssh-agent.

U2F keys may be added to ssh-agent just like any other key. The only
additional requirements are 1) that the agent supports the new key type,
2) the middleware library and physical token must be present on the host
running the agent (much like PKCS#11), 3) that ssh-add be told the path
to the middleware library (via the environment or using the -S option)
and 4) that the middleware library be situated at a whitelisted path
(see the documentation for ssh-agent's -P option).

If you've set the environment variable and have installed
libsk-libfido.so to a common system library directory like /usr/lib or
/usr/local/lib then it will be covered by the default whitelist and you
can just proceed to add the key like any other:

ssh-add ~/.ssh/id_ecdsa_sk

You'll still need to tap the key for each authentication operation. I'll
look at adding support for reminding the user via ssh-askpass soon.


There's some more detail on the new key format and other technical
aspects of the feature in the PROTOCOL.u2f file in the OpenSSH source

We chose to add U2F devices to the SSH protocol as keys rather than
as another more web-like authentication methods because SSH users are
familiar with keys and there are many tools that support them. It was
not possible to enable U2F keys using the existing SSH ECDSA key format
because, despite U2F devices using ECDSA for signatures, the signature
format differs from the plain signatures used in SSH by including some
additional fields.

I'm pretty excited about this feature so please give it a try and let
me know your feedback. I'm happy to answer any questions you might have.

Damien Miller

More information about the openssh-unix-dev mailing list