IETF-SSH archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: Binary packet protocol rethink



I learned about this discussion thread from Peter’s mention of it on the TLS list, where as he mentioned a parallel debate is going on…  (Thanks Peter; hope you don’t mind me stalk…er, following your hint over here. ;) )

On 30 Nov 2015, at 12:34, Niels Möller <nisse%lysator.liu.se@localhost> wrote:
> Simon Tatham <anakin%pobox.com@localhost> writes:
> 
>> an attacker either guesses the true length by correlating to the
>> TCP headers, or probes it by means of the byte-at-a-time dribbling
>> attack, or actively corrupts the cipher block containing the length and
>> waits to see when the resulting MAC failure is reported, 
> 
> Would you be happier if the length field were independently
> authenticated? I'm not sure how strong an authenticator we need, it
> seems a bit silly to use an authentication tag which is much larger than
> the message, but maybe it's really needed.

It’s hard for me to see how separately authenticating the length field would be a benefit; in fact I would worry about whether it could introduce a weakness, e.g., where an attacker could somehow contrive “mix-and-match” attacks that could get a receiver to use one (correctly-MAC’d) length field with a different (correctly but independently MAC’d) payload to cause Bad Things of whatever kind to happen.  I also don’t see any essential reason or even important advantage of doing so.

However, I do (think) I see the tension that might seem to suggest treating the length independently from the payload: in a protocol like SSH that encrypts both header and payload, you would like to read and decrypt the length first, then separately read and decrypt the payload (perhaps at a different location in memory).  Especially if the protocol moves to using AEAD ciphers, which has been suggested and I agree is probably generally a worthwhile idea, the standard AEAD API wants to assume the length is already known at decryption/integrity-check time, e.g., by being carried in the normally-unencrypted Additional Data (AD) field.

On the TLS list I posted a proposal for one way (of many) to resolve this seeming conflict between a desire to move to AEAD and a desire to continue encrypting headers (which I strongly support, whether in TLS or SSH or any other encrypted protocol).  A copy of my proposal is attached below, FWIW.  In summary, you still only need one MAC, and you integrity-check the header (including length) together with the payload in the “main” AEAD decryption, but you separately encrypt (without authentication) the header using either a stream cipher or an AEAD used as a stream cipher.  The fundamental argument for why the unauthenticated encryption of the header is safe is because it just changes the encoding of the header without affecting how the header is authenticated (separately, while authenticating the body).  Thus, changing the header encoding while leaving the authentication and body-encryption unchanged from standard AEAD practice cannot make security any worse than just transmitting the header (AD part) in cleartext, and can (sometimes) be a security benefit.

>> by making sure that the encrypted block boundaries do not also
>> reveal the length or position of any actually important data, such as a
>> particular SSH_MSG_anything.
> 
> Can we do that with the current protocol? If so, guidance is
> appreciated. What I object to is removing a feature (encrypted message
> lengths) which enables known counter measures to traffic analysis, and
> replace it by nothing.

Strongly agreed on this.  The fact that a particular security measure (like encrypting headers) isn’t by itself an end-all one-stop solution to all the world’s traffic analysis problems doesn’t make it not worthwhile.

Cheers
Bryan

————————————————(snip)————————————————

The idea of encrypting TLS record headers has come up before, the most
important purpose being to hide record lengths and boundaries and make
fingerprinting and traffic analysis harder.  I had convinced myself that
goal this would be "too hard" to accomplish in TLS 1.3, but after
further thought I'm not so sure.  So I would like to request comment on
one approach that strikes me as a practical and requires only a rather
minor change to the current spec.

The quick summary:

* To encrypt a record, we first AEAD-encrypt the record's payload,
protecting the header fields via the additional_data, exactly as
currently specified.  But then we XOR-encrypt the 5-byte TLS header just
before transmission, using a (separate) stream cipher indexed by a nonce
that depends on record sequence number and *_write_iv, in exactly the
same way the AEAD is already nonce-indexed.

* To decrypt a record, we simply do the reverse: first use the stream
cipher with the appropriate nonce to XOR-decrypt the 5-byte TLS header,
then sanity-check it as usual to determine its length, read the rest of
the record, and submit it to AEAD for decryption and full integrity
checking as before.

That's it, in a nutshell.  Two likely concerns immediately arise,
discussed below, but feel free to TL;DR the rest if you don't share
these concerns.

---

Concern #1: What if an active attacker messes with the TLS header,
especially the length field, since stream ciphers don't protect
integrity?  The simple answer is that *exactly* the same thing happens
as now: the AEAD decryption attempt fails, because the
(stream-decrypted) header is AEAD-protected as additional_data.  Nothing
is gained or lost.

SSH, which did something like this, ran into trouble with attackers
being able to twiddle the record length field to make the record length
look big, causing the receiver to try to receive a very large record,
and hence appear to the user to hang, instead of immediately detecting
the modification and terminating the connection.  But there are three
mitigating factors here: (1) TLS is not usually used for interactive
terminal traffic like SSH is; (2) TLS's 2-byte record length field
imposes a pretty reasonable upper-bound on the maximum size an attacker
could maliciously make a record appear to be; and (3) if this risk of
length-twiddling is at all a problem in this proposed encrypted-header
protocol, then it's already a problem for the current TLS 1.3 spec
without encrypted headers, because active attackers can twiddle the bits
of a cleartext length field just as easily (and even be *certain* they
are making the length appear large!).  So I can't see any way this
length-twiddling vulnerability becomes any worse, and maybe it gets a
bit better (because the attacker can no longer be entirely certain
whether he's setting a 1 bit to 0 or a 0 bit to 1).

---

Concern #2: Do we want to have to go to the trouble of adding a stream
cipher to every TLS 1.3-compatible ciphersuite?  Answer: maybe not, but
we don't necessarily need to.  We could instead just specify a generic
method of using the ciphersuite's main AEAD as a stream cipher for
header encryption/decryption purposes.

The conceptually simplest approach I can think of: In the specification
of how AEAD nonces are generated (section 5.2.2 of
draft-ietf-tls-tls13-07), reserve the least-significant bit of the
record sequence number, so that sequence numbers increment by 2 rather
than 1 each record.  Thus, we get two unique nonces per record from the
same set of symmetric keys.  We first use the nonce with a '0'
least-significant bit to perform the regular AEAD-encryption of the
record with the header info as additional_data.

Then for the same record we use the nonce with a '1' least-significant
to AEAD-encrypt a sequence of five zero bytes ("\0\0\0\0\0"), and use
the first five bytes of result as the cipherstream to XOR the 5-byte TLS
header with before transmitting.  The AEAD will of course uselessly
append some kind of authenticator to this ciphertext that we won't end
up using, but that's OK.  The receiver will just use AEAD-encrypt
(again) on the same five-zero-byte message to reproduce the 5
cipherstream bytes with which to decrypt the TLS header.  Thus, senders
perform two AEAD-encrypts per record, and receivers do one AEAD-encrypt
and one AEAD-decrypt per record.

This approach seems pretty conceptually clean and simple, but has the
performance downside that we always need to invoke the AEAD twice per
record rather than once, which might be (a bit) costly especially when
records are small.  So a simple refinement is to amortize this cost
across records: e.g., once every 256 records (every sequence number
ending in 0x00) we AEAD-encrypt a sequence of 5*256=1280 zero bytes, and
the result in 5-byte chunks as the cipherstream with which to encrypt
and decrypt 256 consecutive TLS record headers.  Thus, we're only adding
one additional AEAD-encryption of a "normal-packet-sized" 1280-byte blob
once every 256 records, which seems likely to be a pretty
inconsequential performance cost.

---

Comments?

Thanks
Bryan

Attachment: smime.p7s
Description: S/MIME cryptographic signature



Home | Main Index | Thread Index | Old Index