Subject: [Curdle] SSH/QUIC draft

From: denis bider <[email-masked]>
Date: Sat, 11 Jul 2020 12:50:09 -0500

Hey everyone,

I noticed that QUIC - a secure, multi-stream replacement for TLS over TCP -
is close to standardization at the IETF.

For at least 7 years now, I've been pondering that TCP is a poor transport
for SSH and should be replaced by UDP. However, it has always looked like a
lot of work.

Now, most of the work seems to have been done by QUIC. There's also a nifty
implementation by Cloudflare which has a liberal BSD license and seems very
suitable to adaptation for SSH. It nicely separates the TLS handshake so it
can be pulled out while the TLS cipher suites are kept: this makes it easy
to replace the TLS handshake with an SSH-friendly key exchange.

With this inspiration, I wrote an SSH/QUIC spec which fixes all of the
architectural problems I can think of in SSH from experience over the years:

https://datatracker.ietf.org/doc/draft-bider-ssh-quic/

The HTML version of the draft has the most reader-friendly presentation - I
suggest using that one.

I wonder if anyone is interested in implementing this. I'm seriously
considering it, probably based on Quiche. I've forked it already, but I
haven't yet done real work:

https://github.com/denisbider/QuiSSH

denis

---

From: Ilari Liusvaara <[email-masked]>
Date: Sat, 11 Jul 2020 22:35:24 +0300

> https://datatracker.ietf.org/doc/draft-bider-ssh-quic/

Some comments:

- Section 2.1 just seems odd. Either one should run SSH over QUIC on
  separate port, or use ALPN to demultiplex it from other QUIC
  traffic.
- I do not see what would prevent attacker from passively listening
  for legimate connection and then mounting dictionary attack
  against obfustication code to discover it.
- I think HTTP/3 uses 1200 bytes instead of 1400 bytes as padding
  target (which is within IPv6 MinMTU, but considerably above IPv4
  MinMTU).
- Why is there key exchange stuff? One should have TLS do the key
  exchange and then extract any needed keys.
- Then the overall session ID should be also extracted from TLS.
- I presume server authentication should be done using the TLS
  server certificate. All of RSA, ECDSA and EdDSA (Ed25519/Ed448)
  keys (including raw) are supported.
- For client authentication, one probably should use SSH-layer
  mechanisms, due to some limitations of TLS-layer ones (extract nonces
  or shared secrets from TLS if you need them).
- For server multiplexing to work (and no server multiplexing is the
  single biggest complaint I have with all of SSH!), active migration
  MUST be disabled. As far as I can tell, handing NAT rebindings is
  still possible even with active migration off.
  * The hacks people do to deal with this are ugly to say the least.
  * Seriously, I would much rather lose NAT rebind handling than
   server multiplexing.

Looking at QUIC specifications and trying to figure out what
QUIC looks from above:

The basic data transport primitive is streams:

- Each stream can be unidirectional or bidirectional.
- Stream is of arbitrary length.
- Each stream is reliable ordered at byte granularity.
- All streams are independent.
- Stream can be opened by either peer.
- Aborts are directional (not undirectional like in TCP)!
  * Makes it possible for one side to close TX and abort RX. This is
    NOT possible in TCP.

Flow control:

- There are three flow controllers.
  * One operates on connection level, and limits the amount of aggerate
    data that can be sent on all streams together.
  * One operates on stream level, and limits data that can be sent
    on that stream.
  * One limits the number of streams that can be open at once.
  * All are independent in both directions.

Encryption:

- For selecting endpoint, there server name (nice for server
  multiplexing) and application layer protocol name fields.
- QUIC always does its own key exchange.
- QUIC always encrypts all application data streams.
- Server authentication with certificate or raw key is mandatory.
  RSA, ECDSA and EdDSA (Ed25519/Ed448) are all supported.
- Client authentication is optional.
  * The negotiation is really limited here!
- QUIC uses TLS, which can provode nonces and shares secrets for
  application use.

-Ilari

---

From: denis bider <[email-masked]>
Date: Sat, 11 Jul 2020 21:02:50 -0500

Ilari:

> Section 2.1 just seems odd. Either one should run SSH over QUIC on separate
port, or use ALPN to demultiplex it from other QUIC traffic.

I do not understand this comment. SSH/QUIC is a protocol which is neither
SSH nor QUIC, it is SSH/QUIC. Section 2.1 explains how to tell SSH over UDP
key exchange packets apart from QUIC packets. This is doable,
straightforward, it makes sense, and is much preferable to asking users to
configure two separate ports for one protocol.

> I do not see what would prevent attacker from passively listening for
legimate connection and then mounting dictionary attack against
obfustication code to discover it.

The fact that the attacker against whom obfuscation is effective is in
China or Russia and no legitimate connection is from there.

People will use trivial obfuscation keywords, or just leave it empty. The
key feature here is that you can easily make it so that BEFORE the attacker
can even start guessing passwords, they have to guess another password.

I thought of using Argon2 to derive the obfuscation encryption key from the
keyword, but it seems to introduce WAY too much complexity for an optional,
nice-to-have feature. Folks whose SSH implementations run on RFID chips
embedded in clothing tags will not want to implement that. This feature
specifically defends against complete strangers who are not on the network
route between legitimate clients and servers, and incidentally that's by
far the most common type of attacks on SSH servers.

> I think HTTP/3 uses 1200 bytes instead of 1400 bytes as padding target
(which is within IPv6 MinMTU, but considerably above IPv4 MinMTU).

Useful to know, thank you!

SSH_QUIC_INIT is inspired by SSH_MSG_KEXINIT, which tends to be long in
practical use of SSH. However, the current SSH_QUIC_INIT has many fewer
elaborately long algorithm negotiation fields, so 1200 could also work as a
target.

> Why is there key exchange stuff? One should have TLS do the key exchange
and then extract any needed keys.

It needs to work with existing SSH configurations which use SSH host keys.
SSH key exchange methods are already specified, known to SSH implementers,
and do not have glaring problems.

> I presume server authentication should be done using the TLS server
certificate.

Server authentication needs to happen using existing SSH host keys with no
change for users. Users should be able to update to new SSH software
versions that support SSH/QUIC and everything should work out of the box
without the user noticing a difference. Server admins may need to open a
UDP port in the firewall, and that's it.

> All of RSA, ECDSA and EdDSA (Ed25519/Ed448) keys (including raw) are
supported.

That would work, but then I can't put obfuscation and trusted fingerprints
in it. In practical use of SSH, these have turned out to be important
missing features.

Looking at it from the other perspective, I don't see what's gained from
using the TLS key exchange. Instead SSH would relinquish flexibility which
it currently has. For example, the current draft does not specify
multi-step key exchange which does not involve host keys, such as the
GSSAPI key exchange methods which allow server authentication using
Kerberos. But future drafts could allow that, or the extension fields could
be used to allow that.

SSH key exchanges are equally as robust as TLS. What does TLS bring? A
strait-jacket? A requirement to implement ASN.1? Those are things we
dislike.

> For client authentication, one probably should use SSH-layer mechanisms

For client authentication, we use the exact same mechanisms currently used
in SSH.  Anything less than "no change for the user" does not fly.

> For server multiplexing to work (and no server multiplexing is the single
biggest complaint I have with all of SSH!)

Can you explain this in more detail?

As someone who has been working on an SSH server for literally 20 years, I
have not seen the term "server multiplexing" used by a customer, ever. But
perhaps this is because none of the people who would want that are our
users.

Do you mean the ability to route the connection differently depending on,
for example, the host name that was entered by the user to connect?

If yes, that sounds very useful to add. I'll add a "server name indication"
field to SSH_QUIC_INIT in the next draft version.

With an SNI-like field, you should be able to dispatch SSH_QUIC_INIT to the
correct back-end server, and then dispatch subsequent packets appropriately
using the QUIC Destination Connection ID.

> Makes it possible for one side to close TX and abort RX. This is NOT
possible in TCP.

Fair point, maybe the draft should address whether aborting RX should be
done and what should happen in that case.

Thank you for your comments!

denis

---

From: Peter Gutmann <[email-masked]>
Date: Sun, 12 Jul 2020 03:23:33 +0000

denis bider <[email-masked]> writes:

>for SSH and should be replaced by UDP.

The problem isn't TCP, it's SSH, specifically SSH's terrible flow control
a.k.a. the SSH performance handbrake, which (for SFTP) layers TCP over TCP
over TCP.  The solution isn't to run it over a protocol that hides one layer
of the handbrake but to fix SSH's flow control problems.

And yes, I know that in theory a fix is possible if every implementation
everywhere is very carefully implemented and tuned to perform exactly the same
precise dance to avoid the handbrake, but better would be to fix the
underlying problem.

In my case for example I just ignore the flow control/handbrake, a.k.a. "no-
flow-control" if you can find anything that supports that, and get pretty much
line speed on data transfers.  OK, this is a bit antisocial, but it avoids
having to answer "why do I get an order-of-magnitude performance drop with SSH
vs.TLS when the same algorithms are used" questions.

Peter.

---

From: denis bider <[email-masked]>
Date: Sat, 11 Jul 2020 22:43:49 -0500

That's one of the problems and it's solved in SSH/TCP for single-channel
connections by implementing "no-flow-control", as you note. It's not solved
for multi-channel connections, and it leaves several other problems of
SSH/TCP unsolved.

The SSH/QUIC draft solves the flow control problem in the general case, for
multi-channel connections, and solves the other several problems of
SSH/TCP. :)

I have just uploaded a new version (-04) with improvements inspired by
feedback from Ilari.

denis