Hi, 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 support. 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 going). 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 $ export SSH_SK_PROVIDER $ 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 detect. 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 distribution. 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. Cheers, Damien Miller
On Fri, 1 Nov 2019 19:36:16 +1100 (AEDT) Damien Miller <djm at mindrot.org> wrote:> Theft or disclosure of the on-disk id_ecdsa_sk private > key alone should yield attackers the ability to authenticate using a > U2F tokenJust to clarify: I assume that should read "should *not* yield"? (Great stuff, by the way.) Christian
On Fri, 1 Nov 2019, Christian Kandeler wrote:> On Fri, 1 Nov 2019 19:36:16 +1100 (AEDT) > Damien Miller <djm at mindrot.org> wrote: > > > Theft or disclosure of the on-disk id_ecdsa_sk private > > key alone should yield attackers the ability to authenticate using a > > U2F token > > Just to clarify: I assume that should read "should *not* yield"?your right - that was a great place for a grammatical error... -d
On 11/1/19 4:36 AM, Damien Miller wrote:> new key type "sk-ecdsa-sha2-nistp256 at openssh.com"Was ECDSA with NIST P-256 strictly necessary, or would Ed25519 be possible as well? Thanks, - Joe -- Joseph S. Testa II Founder & Principal Security Consultant Positron Security
I've had a patch on the bugzilla for a while related to U2F with support for a few additional settings such as providing a path to a specific key to use instead of the first one found and setting if user presence is required when using the key. Is there any objection to folding those parts in if appropriate? Joseph, to offer comment on NIST P-256. There was originally quite a limited subset of support in U2F, originally ES256 or RS256. There's since been more added (Ed25519 appears to be one of them at a cursory glance). If you take a look at param.h in the libfido2 repository you'll see the list of supported algorithm constants (COSE_*). From personal experience though I've had a few different brands of pure-u2f-only tokens and never seen support for anything other than P-256 in the wild. Yubicos U2F only keys for example are currently listed on their site as only having P-256 support. I imagine multi-purpose keys might have more expansive support though. RS256 also appears to be marked as deprecated. On Sat, Nov 2, 2019 at 7:54 PM Joseph S. Testa II <jtesta at positronsecurity.com> wrote:> > On 11/1/19 4:36 AM, Damien Miller wrote: > > new key type "sk-ecdsa-sha2-nistp256 at openssh.com" > > Was ECDSA with NIST P-256 strictly necessary, or would Ed25519 be > possible as well? > > Thanks, > - Joe > > -- > Joseph S. Testa II > Founder & Principal Security Consultant > Positron Security > _______________________________________________ > openssh-unix-dev mailing list > openssh-unix-dev at mindrot.org > https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev
Joseph S. Testa II <jtesta at positronsecurity.com> writes:> On 11/1/19 4:36 AM, Damien Miller wrote: > > new key type "sk-ecdsa-sha2-nistp256 at openssh.com" > > Was ECDSA with NIST P-256 strictly necessary, or would Ed25519 be > possible as well?I would guess that it largely comes down to support of the algoirthms in the hardware keys and the libfido2 library. Some tokens built on top of TPM 2.0 hardware may only support ECDSA or RSA.... YubiKey 5.2.3 enhancements to FIDO 2 Support recently announced (on October 22, 2019) URL: https://support.yubico.com/support/solutions/articles/15000027138-yubikey-5-2-3-enhancements-to-fido-2-support | Additional Encryption Algorithms | | To ensure a high level of security for the FIDO2 authentication | credentials, the supported encryption algorithms have been updated. | Support for the Ed25519 curve has been added, while support for RSA keys | has been removed. This makes sense in that the new NISP FIPS 186-5 draft does provide for EdDSA with both 25519 and 448 curves and YubiKey is something the US Fed folks are in favor of using for some places where CAC cards are not as desirable to use. Right now, the libfido2 library seems to support ECDSA P-256 with SHA-256 and PKCS#1.5 2048-bit RSA with SHA-256. Given that the US Federal Government is mandating RSA-PSS for most everything rather than PKCS#1.5 RSA, the odds are good that RSA support will be going down in a lot of places. I suspect that both ECDSA P256 and ECDSA P384 are supported on many PIV Smart Card devices. I fully expect to see EdDSA 25519 based devices eventually. -- Mark
On Sat, 2 Nov 2019, Joseph S. Testa II wrote:> On 11/1/19 4:36 AM, Damien Miller wrote: > > new key type "sk-ecdsa-sha2-nistp256 at openssh.com" > > Was ECDSA with NIST P-256 strictly necessary,Yes, it is the only key type specified for U2F.> or would Ed25519 be possible as well?I think Ed25519 might be specified for FIDO2, if so we'll look at adding it in the future. -d
On Fri, 1 Nov 2019, Damien Miller wrote:> Hi, > > 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").An update on this: I've just committed internal support for U2F/FIDO2 security keys to OpenSSH. If ./configure can find a compatible libfido2 then it will be used automatically, with no additional configuration required in OpenSSH tools. You should use libfido2 HEAD for now until they make their next release. Practically, this means that you can just run "ssh-keygen -t ecdsa-sk" and it will work without fiddling with middleware binaries, etc. Please give this a try - security key support is a substantial change and it really needs testing ahead of the next release. -d
On 2019-11-14, Damien Miller <djm at mindrot.org> wrote:> Please give this a try - security key support is a substantial change and > it really needs testing ahead of the next release.Hi Damien, Thanks for working on security key support, this is a really nice feature to have in openssh. My non-FIDO2 security key (YubiKey NEO) doesn't work with the latest changes to openssh and libfido2, failing with `try_device: fido_dev_get_assert: FIDO_ERR_USER_PRESENCE_REQUIRED`. I'm not sure if this is a problem in libfido2 or sk-usbhid.c (I also reported this issue at https://github.com/Yubico/libfido2/issues/73). Is try_device incompatible with U2F keys? It seems to me to be trying to detect the presence of a key handle using an assert with up=0, but that causes the U2F codepath in libfido2 to return an error FIDO_ERR_USER_PRESENCE_REQUIRED. I believe that since try_device is only trying to find the device with the key, FIDO_ERR_USER_PRESENCE_REQUIRED should be ignored here, since that seems to indicate that the key lookup succeeded, but authentication was not attempted. I attached a diff that makes this change and it seems to fix my issue. -------------- next part -------------- diff --git a/sk-usbhid.c b/sk-usbhid.c index c0a6bd0d..00c07685 100644 --- a/sk-usbhid.c +++ b/sk-usbhid.c @@ -204,7 +204,7 @@ try_device(fido_dev_t *dev, const uint8_t *message, size_t message_len, out: fido_assert_free(&assert); - return r != FIDO_OK ? -1 : 0; + return r != FIDO_OK && r != FIDO_ERR_USER_PRESENCE_REQUIRED ? -1 : 0; } /* Iterate over configured devices looking for a specific key handle */
On Fri, 15 Nov 2019, Damien Miller wrote:> On Fri, 1 Nov 2019, Damien Miller wrote: > > > Hi, > > > > 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"). > > An update on this: I've just committed internal support for U2F/FIDO2 > security keys to OpenSSH. If ./configure can find a compatible libfido2 > then it will be used automatically, with no additional configuration > required in OpenSSH tools. You should use libfido2 HEAD for now until > they make their next release. > > Practically, this means that you can just run "ssh-keygen -t ecdsa-sk" > and it will work without fiddling with middleware binaries, etc. > > Please give this a try - security key support is a substantial change and > it really needs testing ahead of the next release.One more note: you'll need to pass --with-security-key-builtin to configure to enable the built-in security key support. If it finds the libraries that it depends on then you should see something like: U2F/FIDO support: built-in In configure's final summary. -d
Hi Damien, On Nov 14, 2019, at 3:26 PM, Damien Miller <djm at mindrot.org> wrote:> On Fri, 1 Nov 2019, Damien Miller wrote: >> 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"). > > An update on this: I've just committed internal support for U2F/FIDO2 > security keys to OpenSSH. If ./configure can find a compatible libfido2 > then it will be used automatically, with no additional configuration > required in OpenSSH tools. You should use libfido2 HEAD for now until > they make their next release. > > Practically, this means that you can just run "ssh-keygen -t ecdsa-sk" > and it will work without fiddling with middleware binaries, etc. > > Please give this a try - security key support is a substantial change and > it really needs testing ahead of the next release.I?ve been following this work with great interest, as I?ve been looking to add this support to my Python ?AsyncSSH? package. I got a chance to look more closely at this over the last few days, and I?ve now got an initial implementation working and interoperating with OpenSSH-portable HEAD! As part of my testing, I?ve been experimenting with not only plan SK keys, but also various combinations of creating certificates of SK keys signed by non-SK CA keys and vice-versa. I?ve generally been able to get OpenSSH to work with certificates of SK keys to work when signed with either non-SK or SK CAs. However, I haven?t been able to get OpenSSH to work when signing a non-SK key with an SK CA. I haven?t dug into the OpenSSH code to figure out what might be going wrong here, but I wanted to let you know about it. These keys work fine when I use OpenSSH as a client to talk to my AsyncSSH server with this support, and AsyncSSH can use these keys successfully to talk to itself, so I think the problem is specifically in the OpenSSH server-side support for this case. Creating a certificate for an SK key signed by an SK CA works fine when OpenSSH is the server with either itself or AsyncSSH as the client. So, you?ll need to specifically try a non-SK key signed by an SK CA. One other thing I wanted to ask you about was whether you had any plans to support SK keys as host keys. Your current PROTOCOL.u2f file says that these keys ?are not used for host-based user authentication or server host key authentication?. However, I think there are a few use cases where this could make sense. One use case is for reverse-direction connections, such as NETCONF ?Call Home? described in RFC 8071. I know this isn?t something OpenSSH supports yet, but I recently added such support to AsyncSSH. See https://asyncssh.readthedocs.io/en/latest/#reverse-direction-example <https://asyncssh.readthedocs.io/en/latest/#reverse-direction-example> for more details. Basically, this use case involves a client running a process that makes an outbound TCP connection to a server but then using the resulting TCP connection once it is set up to authentication the remote server as a ?user? and the client as a ?host?, reversing the normal SSH direction and allowing the remote server to run commands on the local system. So, in the case of security keys, you?d create an SK server host key, and when you make the outbound connection and begin the key exchange, you?d hit the user presence button on the security key to perform the signing operation with the server host key and allow the reverse connection to be established. A second use case would be to simply use a security key with user presence disabled as a more secure key store than just keeping a private key on the local disk of an SSH server. As long as the security key was present, the SSH server could accept new connections. However, you could remove the key at any time to disable this function, and there would be no concern about the key being remotely stolen from the server ? someone would need physical access to steal the security key and need the enrollment information for that to do them any good. The same argument can be applied to using a security key for host-based user authentication, and there you could even leave user presence enabled. Finally, I noticed a few minor things in the PROTOCOL.u2f doc that didn?t look right. Specifically, that doc says:> 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_handleThis isn?t really the format that these parameters are provided during a signing operation, though, at least not to the middleware library documented later in the doc. You may just want to leave this part of the description out, or at least sync it up a bit better with that later description. For instance, the key handle length is a size_t there, not a single byte, and the ?control bits? is later called ?flags?. The arguments are also in a different order, and there?s a missing argument which specified the key type (?alg?). Following this you show the signed blob as:> 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)How would the ?extensions? be encoded here, though, and how would the hardware know where they end and the SHA256 of the message begins? The doc mentions an ?extensions present? flag, but I don?t see that defined. Just after this, you show the signature returned from the U2F hardware as:> 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).The signature is more than 32 bytes here, though. The middleware library returns the signature as an (r, s) pair, where each is a 32-byte string value that is later converted to integers and then encoded as a pair of MPInts. I suspect the hardware might be returning (r, s) as DER encoded in some cases and that the middleware library is hiding that, but either way the text above isn?t quite right. Later, in the description of the sk_enroll() call, you show a ?challenge? argument, but it?s not clear how that?s used. Are you doing anything with that today? I tried looking in various online docs about U2F/FIDO to see if it was described there, but I couldn?t really find anything that matched up with that. Most of what I found was much too high-level, or focused on things like the Javascript APIs in the browser and not the underlying code talking to the hardware tokens. Thanks for all your work on this! I look forward to doing more testing on this as I fill out things like key enrollment and talking to an SSH agent process. I?ll report back here if I run into any issues with that. -- Ron Frederick ronf at timeheart.net