
OpenSSH's Support for U2F/Fido Security - prennert
https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.u2f
======
darkwater
What are the biggest advantages/improvements over generating a "classic"
private/public keypair with for example a Yubikey [1]?

[1] [https://github.com/drduh/YubiKey-Guide](https://github.com/drduh/YubiKey-
Guide)

~~~
HHad3
\- The administrator can enforce use of hardware-backed keys [1]

\- U2F Tokens cost $8-$25 and are significantly cheaper than full-blown
Yubikeys

\- U2F Tokens do not have a key limit, which enables using a unique public key
per server for privacy/anonymity

[1] [https://github.com/openssh/openssh-
portable/blob/master/PROT...](https://github.com/openssh/openssh-
portable/blob/master/PROTOCOL.u2f#L92)

~~~
harshreality
> U2F Tokens do not have a key limit

They must have some limit. EC Keys are roughly 32 (256 bit) or 48 (384 bit)
bytes (x2 to store the public half, right?), and most of these devices don't
have MB of storage afaik... I think they have security chips with like
64-256kb of secure storage. So once you get into the range of thousands of
sites, wouldn't you run out of room?

ETA: thanks akerl, I didn't realize that's what they (or most of them) were
doing; I didn't even realize deterministically generating an EC key from a
seed was efficient enough to do it quickly on a tiny embedded chip.

~~~
akerl_
In theory, yes. In practice, the way that most U2F tokens (for example,
Yubikeys) work is to storage a single device-specific secret key on the token,
which never changes. Then, when “adding” a key for a site, they take the
provided details from the server as inputs, along with the secret key, to
derive a new, site-specific private key, which is used for U2F. That allows
them to “store” an unlimited number of site keys. Note that this method isn’t
mandated by the U2F spec, so it’s not guaranteed that any key which implements
U2F will do so in this way.

The OpenSSH protocol doc alludes to this requirement when they talk about why
U2F needed a custom method for interfacing with SSH: the site’s handle needs
to be provided as input to the key, so that it can re-derive the site-specific
private key, and that communication channel didn’t already exist in ssh-agent.

~~~
tialaramex
Critically this isn't quite correct.

The handle (IIRC the FIDO spec calls it a cookie but that doesn't matter) is
NOT chosen by the relying party (in this case a SSH server you're logging
into). It's chosen by the token itself during the enrollment process and given
to the relying party with the other results of enrollment to be stored.

The token will use local random data in addition to random data supplied by
the relying party for choosing a private key (and thus in the design you
describe, the handle). This is a common design choice in cryptographic systems
because it means if either Alice OR Bob really used random numbers the results
are truly random, things only go badly if both parties defect.

In WebAuthn this means if you use your token to enroll with Google, and
Facebook, and say Twitter, so long as the people who built the token did a
good job it won't matter if both Google and Facebook are trying to betray you
and deliberately didn't use random data, they can't attack your Twitter
account this way.

Edit: refer to the device as a "token" not a key for clarity

~~~
akerl_
I feel like a much simpler correction might have been “you meant application
ID instead of key handle”, given that the application ID is based on the
individual server (it’s generally partly the URL and a server-provided
challenge), and is combined with the token secret key to create the full
handle.

Nothing in my comment implied that if google betrayed you, it would leak your
facebook token.

~~~
tialaramex
Er no? I think you're still not getting it. The essential trick is that the
token gets to randomly pick the value for this cookie, it isn't depending on
the application ID.

One example way you can do this goes like this:

* Token has a Secret Key S baked inside it

* During enrollment the token makes a random private key P1 and a corresponding public key K1

* It makes a Cookie C1 by encrypting P1 using S

* It signs a message Ma with P1 to produce Za

* It sends C1, K1 and Za

The relying party gets to influence Ma but they do NOT get to pick any of
these other values like P1, C1, K1.

The relying party verifies that Za is indeed a signature of Ma with K1 and if
so enrollment worked, store C1 and K1

Now, after enrollment the relying party can produce new challenge messages Mb
Mc Md and so on for ever, and each time it supplies the token with C1.

Given C1 and Mb, the token can decrypt C1 with S to get back P1, and then it
can sign Mb with P1 to produce Zb. It sends that back.

The relying party can confirm that Zb is a signature of Mb with K1 and so this
must be the genuine token that was enrolled previously.

~~~
rdslw
Does such approach as desribed by you mean that token needs to store P1/K1 for
every site?

AFAIK It does not.

And if it does not, your explanation contains bigger errors about
enrollment/process.

@akrel described it much better.

~~~
tialaramex
In my description I specifically explain that the token gets P1 back by
decrypting C1 with S so that it needn't be stored.

You've become fixated on the part akerl_ (not @akrel) got right, that tokens
don't store the private key but missed the _essential_ element they got wrong
which is how this key is generated.

One really obvious consequence of things working as I explained rather than as
akerl_ (presumably mistakenly not maliciously) explained is what happens if we
do enrollment again with the same token for the same Relying Party, for
example maybe Alice and Bob are a couple sharing a token.

In reality, as in my explanation, these enrollments produce completely
different results, the token will pick a random P1 (thus K1, C1, etc.) when
Alice enrolls and a different random P2 (thus K2, C2, etc.) when Bob enrolls,
and for the Relying Party everything is different between these two
enrollments, just as if the token was different.

In akerl_'s description Alice and Bob would get the same application ID, same
results, and now a Relying Party can tell that Alice and Bob are using the
same token.

------
Freak_NL
Does this mean U2F support is coming to OpenSSH (server and client) without
the need for custom patches? That would be really awesome.

~~~
_wldu
Yes. [https://marc.info/?l=openssh-unix-
dev&m=157259802529972&w=2](https://marc.info/?l=openssh-unix-
dev&m=157259802529972&w=2)

------
tialaramex
This doc doesn't explain what the enrollment process will look like, there are
opportunities here to make this better or worse. Enrollment is crucial to the
user experience for WebAuthn/ U2F.

There's also a weird intermediary introduced that I think users may struggle
to get their heads around. The FIDO cookie (this doc calls it a "handle" and
the WebAuthn spec calls it the "Credential ID") is needed before a client can
authenticate, and naturally you'd want to store this on a server, but in SSH
the client chooses the authentication method to try not the server, so this
cookie has to be stored on the client machine.

Say you enroll on your laptop. The laptop knows a cookie, the FIDO can take
that cookie and prove it knows the private key, and that's enough to sign into
the server. But without the FIDO key the laptop can't do anything, and without
the laptop the FIDO key doesn't know which cookie is needed.

In effect it's two of the same factor. Something you have: The laptop and
Something Else that you also have: The FIDO token.

~~~
sigmar
>so this cookie has to be stored on the client machine.

Not following how you arrived at this. In the U2F, the server holds on to a
copy of the key handle. Then during authentication, sends the key handle and
challenge (referred to as "message" in this openssh doc) to the client.

~~~
tialaramex
That is exactly how it works for WebAuthn, yes. The server sends over this
cookie and then the client uses it.

But SSH is defined with the _Client_ picking how to authenticate first, so
they can't do this.

There isn't a step in the SSH Authentication protocol where the server can say
"Oh, here's the FIDO cookie you need". The SSH design says that (for public
keys) the client starts by proposing "Hi, I can prove I know the private key
that goes with this public key K1" and then the server either says "OK prove
it" or "No, what else?". You can see this corresponds to the authorized_keys
file on the server.

So instead you'll see the document calls this value "key_handle" and you'll
see it is in the "private key" stored on the client, not with the "public key"
stored on servers.

------
dboreham
Praise the Maker. I've been waiting 9 years for this.

------
prennert
I posted this, because I find this very cool. But before using this in
production anywhere, I wanted to find out how secure people think this is and
what should be done regarding audits etc before it can be used in a serious
system.

Does anyone have insight into the auditing process at openssh?

~~~
Leace
> I wanted to find out how secure people think this is

It depends on what do you compare this with. PKCS/OpenPGP smartcard is
protected by a PIN (6 digits min, 3 tries and it locks itself out) and then
touch on each use (with Yubikey).

U2F on the other hand it just protected by touch so anyone having the token
can authorize themselves. (Sites usually use it in conjunction with
username/password that is "something you know").

On the yet another hand I didn't review the implementation and would welcome
corrections here.

~~~
ecesena
U2F/FIDO2 supports user verification (UV) in addition to user presence (UP).
So you should be able to do the same, but I haven't tested if the current
implementation supports it.

~~~
tialaramex
Their own documentation suggests these are the same thing and both refer to
the need to press a button or close a contact or whatever:

Generically, the term “User Verification” may also refer to this “Test for
User Presence”

from e.g.

[https://fidoalliance.org/specs/fido-security-
requirements-v1...](https://fidoalliance.org/specs/fido-security-
requirements-v1.0-fd-20170524/fido-authenticator-security-
requirements_20170524.html)

~~~
ecesena
Weird, this spec has a little bit of a different terminology. I think it's
better to refer to CTAP2 or WebAuthn directly. UV refers to PIN or biometric,
it's in addition to UP.

[https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-
cl...](https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-
authenticator-protocol-v2.0-ps-20190130.html)

~~~
Boulth
Note that CTAP and WebAuthn are newer and although they contain U2F for
backwards compatibility reasons U2F doesn't contain CTAP2 and WebAuthn. U2F is
also surprisingly simple protocol (two specs on FIDO site).

~~~
ecesena
Yes, and exactly because FIDO2 is backward compatible with U2F the definition
of UV can't be ambiguous, otherwise you could have older U2F keys that pretend
to do FIDO2-UV when in fact they aren't.

------
tomxor
Wow neat. I've been using yubico-pam module for a while now, to enforce
hardware auth for SSH _without clientside config_ (yubikeys can send a OTP by
acting as a keyboard).

U2F support would achieve this without the extra server PAM config unique to
yubico and remove reliance on a separate auth server - not to mention remove
reliance on these particular type of keys that support their OTP which are
pricey.

------
BasicObject
Can this be used with a Nitrokey?

~~~
jans23
I understand OpenSSH is going to support the FIDO U2F standard in which case
all standard-conform implementations would work, including Nitrokey FIDO U2F
devices.

