Welcome back to Cryptography Dispatches, my lightly edited newsletter on cryptography engineering. As always there’s no tracking, so please reply and say hi! It looks like the last issue about new crypto in Go 1.14 went to spam for most people, hopefully better luck this time!

— Filippo

OpenSSH is on a roll. In February, OpenSSH 8.2 introduced first-class support for FIDO2 (née U2F) security keys, making hardware backed keys accessible for less than $20.

This is not some complicated PAM setup, or some janky cryptographic trick, but a proper public key type, where the private key is protected by the hardware token. And it just works out of the box for USB security keys! No more tedious and unreliable gpg-agent setups, PKCS#11, or third-party agents.

I’m a big fan of hardware tokens because they allow a few things you can’t do with just software cryptography: compromise recovery, because an attacker can’t exfiltrate the key from the hardware to use it after losing access to it; explicit consent, where the user has to physically allow each operation by e.g. tapping the key; and short PINs that can’t be bruteforced, because the retry counters or delays are enforced in hardware.

Let’s cut to the chase, here’s how you generate an SSH key backed by your security key:

As long as you have OpenSSH 8.2 and a U2F/FIDO2 key plugged in, that will generate a private and public key that you can use as normal: adding the public key to authorized_keys, loading the private key into ssh-agent, etc. When you try to use the private key, it will require the security key plugged in and a touch.

Technically, OpenSSH needs a middleware to talk to the security key, but it also ships with a built-in one for USB tokens based on libfido2. Homebrew enables it, and if your distribution doesn’t, you should suggest it!

The Go golang.org/x/crypto/ssh package supports these public keys server side since v0.0.0-20191202143827-86a70503ff7e.

How these security keys manage to be so inexpensive is that they are basically stateless. When they are asked to generate a new key, they return an opaque 64 bytes blob that the application (usually the website) has to hold on to, and pass back to the token when requesting a signature. Most tokens presumably encrypt the generated private key with a hardware master key, and return that, offloading storage to the application. If you want to learn more about U2F/FIDO2 Adam wrote about it in detail.

Indeed, if we look inside an ecdsa-sk OpenSSH private key, we find a key_handle for the blob. There’s also an application which defaults to ssh:, and in a web context would be the website origin.

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

Something I noticed is that OpenSSH offers to encrypt these private keys with a passphrase, which would make you think the hardware token is guaranteed to be useless without the decrypted private key file. As far as I can tell, that’s true when the key handle is indeed an encrypted private key, but there’s nothing in the spec requiring that, and it could as well be an easy to bruteforce index into some hypothetical on-device storage. In practice, Adam tested a bunch of tokens, and although some of them squeak weird, none seem broken enough to circumvent the private key file encryption.

It is definitely advisable to acquire a security key from a reputable vendor anyway, regardless of private key encryption. The blue YubiKeys are great, as well as the open source SoloKeys. Wirecutter has a long review by the great Yael Grauer.

As long as the token does the sensible thing, FIDO2 backed keys have an added security benefit compared to PGP or PIV backed keys in that they require both the private key file and the hardware token to operate. (On the other hand, most tokens don’t support hardware PINs, only touch presence checks.)

If that’s actually the opposite of what you want, there’s a feature called resident keys where the application can store the key handle and metadata back on the hardware key, so it can be moved between machines without having to move the key handle. OpenSSH has full support for this, but my YubiKey 4 doesn’t.

If you want to learn more, the release notes as well as the PROTOCOL.u2f spec file are pretty readable. Now, we just wait for GitHub to start supporting these public keys, which I’m told is blocked on libssh2.

In the meantime, here’s a picture of an empty platform at Milano Centrale back in January. I do miss it. Stay safe and healthy.

Source link