U2F support in OpenSSH HEAD

Damien Miller djm at mindrot.org
Wed Dec 11 09:10:59 AEDT 2019


On Mon, 2 Dec 2019, Ron Frederick wrote:

> Hi Damien,
> 
> 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!

Excellent - that's really good news.

> 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.

It looks like I neglected to add the SK algorithms to the server's
CASignatureAlgorithms list. I'll fix that now.

> 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.

It wasn't something we'd really considered, but it wouldn't be very
hard to add. I'll look at it.

> 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.

I'm not sure that I'm ready to add support to sshd for prompting the
user to touch their security key, I think it's too confusing as we've
never intended sshd to be run interactively.

> 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.

This is good motivation - we'd probably want to require that security
keys do not require user presence for this case.

> 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_handle
>
> This 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”).

AFAIK this is how things appear on the wire to a U2F token, but yeah
it's probably superfluous here.

> 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.

extensions are only possible for FIDO2 keys and nothing we've defined
requests them (if they did, they'd break the signature). I mentioned it
there because that's where they'll go in the future if we ever support
them.

> 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.

Yes, it's DER encoded. I'll adjust the docs.

> 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.

The challenge can be used as part of a workflow that uses the attestation
information coming back from the security key. The attestation object can
be used to prove that a particular key is hosted in hardware, typically
down to manufacturer/batch granularity. The challenge is used to ensure
that attestations are not subject to replay.

We don't disclose the attestation in the public key as it gives away a
bit too much identifying data, but I have plans to optionally write it
out as a sidecar to the private key.

> 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.

Thanks for porting it to another SSH implementation. I should mention
that there's a small chance that some of the formats might change before
we make a release with this code in it, but I'll try to let you know if
this happens.

-d


More information about the openssh-unix-dev mailing list