Subject: [tcpm] draft-ietf-tcpm-tcp-auth-opt-00

From: "Adam Langley" <[email-masked]>
Date: Tue, 1 Jul 2008 10:45:48 -0700

> 2.2. TCP-AO Option

[snip]

> The MAC is computed over the following fields in the following order:

Currently AO is suggesting the same serialisation as TCPMD5:
  sig = MAC(key, pseudoheader + header + data)

I suggest, instead, that the default serialisation be:
  sig = MAC(key, H(data) + pseudoheader + header)

When the TCP stack is getting data from userspace, it generally knows
the MSS and splits the data into packets right away. With the
suggested serialisation, the data can be hashed as it's copied and the
hash stored with the data. This saves traversing the data again at
transmit time to calculate the MD5 signature. The latter is no less
secure, I believe, and opens up possibilities for optimisation so
should be a clear gain.

Note that MAC(key, H(data) + pseudoheader + header) is better than
MAC(key, data + pseudoheader + header) because, with the latter, a
partial block needs to be stored as well. With common hash functions
(MD5, SHA-1) this is 64-bytes of extra data.

Additionally, the draft already mentions an Option Kind Exclusion List
and I'd suggest another option: that the pseudoheader be omitted and
the source/destination ports in the TCP header be zeroed. This would
allowed the signature to be robust to most (all?) NATs. The NAT
workaround suggested in section 7 is impractical for many uses and a
significant fraction of hosts are now imprisoned behind NATs.

Currently unspecified is a nonce for the packets. Modern MAC
functions, based on polynomials (such as [1]), can have the very
pleasing property of being provably within a factor as secure as
another function (like a block cipher). However, they often require
nonces as inputs.

A trivial solution would be to keep a 64-bit counter, increment it for
each transmission and transmit it along with the signature. However,
we'll struggle to fit that all in the options space; esp for SYN
packets. Next we could consider taking the seq number as the lower
32-bits of a 64-bit counter. However, that doesn't guarantee that two
different messages wouldn't be transmitted with the same nonce. We
could just transmit the bottom (say) 16-bits of the nonce, but a
prolonged outage could knock the endpoints out of sync. I don't have a
good solution here.

Also, is encryption of the payload considered out-of-scope for this spec?

[1] http://cr.yp.to/mac.html

-- 
Adam Langley [email-masked] http://www.imperialviolet.org
_______________________________________________
tcpm mailing list
[email-masked]
https://www.ietf.org/mailman/listinfo/tcpm

---

From: Joe Touch <[email-masked]>
Date: Tue, 08 Jul 2008 21:49:21 -0700

> secure, I believe, and opens up possibilities for optimisation so

The current algorithm is optimized for the most common case, where 
TCP-AO is implemented inside the stack, and the hash is computed just 
before the packet is emitted. The same is true on the receiving end - 
i.e., in both cases, the data is processed as a single stream, whichis 
why the order is [pseudoheader, header, data]. This works well for both 
software implementations inside the stack and hardware as well.

The above is optimized assuming that the hash is computed during a copy 
operation that may or may not occur (e.g., it would not in a zero-copy 
stack), and further assumes that the copy is performed one MSS at a time 
(vs. copying the entire write() call argument, i.e., copy-on-write). 
It's not clear that optimizing the algorithm for this corner case is 
desirable, especially given the impact on more traditional 
implementations (i.e., the extra header copies/moves).

...
> significant fraction of hosts are now imprisoned behind NATs.

I'd like to hear what others think of this. My view is that NATs do 
other things to TCP packets (e.g., rewrite sequence numbers, modify TCP 
options) that TCP-AO should probably detect as attacks. An earlier 
version of this work did consider replacing these values (addr/port 
pairs), not zeroing them out - i.e., replacing them with values 
determined during key exchange. I would prefer that to zeroing them out, 
though.

> prolonged outage could knock the endpoints out of sync. I don't have a

TCP already includes a 16-bit counter - the sequence number. The use of 
longer counters that retain the high-order bits of that number were 
discussed in Philadelphia. The key problems involve retransmission at 
the edge of counter wrap; a goal of TCP-AO is to avoid modifying the TCP 
protocol itself. As you note, there didn't seem to be a good solution here.

...
> Also, is encryption of the payload considered out-of-scope for this spec?

Yes. Payload encryption is provided by SSL.

oe

---

From: "Adam Langley" <[email-masked]>
Date: Wed, 9 Jul 2008 09:48:16 -0700

On Tue, Jul 8, 2008 at 9:49 PM, Joe Touch <[email-masked]> wrote:
> (vs. copying the entire write() call argument, i.e., copy-on-write). It's
> not clear that optimizing the algorithm for this corner case is desirable,
> especially given the impact on more traditional implementations (i.e., the
> extra header copies/moves).

I don't believe that it would involve moving any extra data, you just
update the hash context with chunks in a different order. Although I
feel it's very low cost when not used and useful where it can be used,
it is fundamentally just a trick and not that important.

> I'd like to hear what others think of this. My view is that NATs do other
> things to TCP packets (e.g., rewrite sequence numbers, modify TCP options)
> that TCP-AO should probably detect as attacks. An earlier version of this
> work did consider replacing these values (addr/port pairs), not zeroing them
> out - i.e., replacing them with values determined during key exchange. I
> would prefer that to zeroing them out, though.

Although some NATs do change seq numbers, I've only heard it used for
FTP PORT commands. By not including the sequence numbers in a hash,
you are really reducing the power of the signatures, but it would
still have some use (against blind sweeps of the sequence space, for
example), so it shouldn't be dismissed out of hand.

Since the option exclusion list is so general, it would seem
inconsistent if the spec was to be inflexible here. NATs which change
ports and IPs are now, sadly, almost ubiquitous and cannot be ignored.

The most general solution would be for applications to specify a 20
byte mask and a single bit to determine if the pseudoheader is to be
included. Then the TCP header for the hash is: (header & mask) |
(filler & !mask) where "header" is the header on the wire and "filler"
is key-dependent material. (Basically, a zero bit in the mask means
"replace this bit with a bit from the filler"). The bits corresponding
to the TCP checksum should probably be forced to zero by the kernel.

Since only applications can decide what should be considered an
attack, that covers all the cases, it's fast and easy to do in
hardware.

The filler needs to be 20 bytes, so I'd suggest iterating H(key),
H("\x00" + key), H("\x00\x00" + key) etc until 20 bytes have been
generated. For SHA1, only one iteration is needed. For MD5, only two.
("+" is a concatenation operator here)

> discussed in Philadelphia.

The security properties of these functions usually depend strongly on
the condition that no two messages be transmitted with the same nonce.
If the seq number were to be taken as the bottom 32-bits of a nonce
counter, we could still hit the situation where two different packets
were transmitted with the same nonce. For example, a host acking data,
but not transmitting any itself. If we included both seq and ack in
the nonce, then I believe the retransmitted ACKs with and without SACK
options could still break this.

> The key problems involve retransmission at the
> edge of counter wrap; a goal of TCP-AO is to avoid modifying the TCP

I don't see that's a big problem: The host maintains a belief of the
high 32-bits (H) of the counter and the last low-32 bits that it saw
(L). For each packet with a new sequence number (S):
  If the distance from L..S is < S..L (mod 2**32) then assume
out-of-order reception; if L...S crosses a rollover, decrement H. L <-
S.
  Otherwise, if S..L crosses a rollover, increment H. L <- S

This can be confused by massive reordering. However, that just leads
to the packet being rejected and it's probably been retransmitted
anyway. Also, if a huge number of packets were lost the hosts could be
knocked out of sync. Some additional logic could cope with this
(assume it might have happened and check the MAC for another sequence
number), but I believe that the window would have to be about 2GB big
for this to happen.

So, I believe the non-uniqueness problem is much greater.

Cheers

AGL

---

From: Joe Touch <[email-masked]>
Date: Wed, 09 Jul 2008 11:48:45 -0700

Adam Langley wrote:
...
...
>> discussed in Philadelphia.

(I had the IP ID on my brain; it's a 32-bit counter, as noted below).

> the condition that no two messages be transmitted with the same nonce.

Note that the inclusion of the IP ID may help in this regard; not only 
does the 32-bit seqno need to be repeated, but so does the ID; overall, 
the pair of those two should could be sufficient for statistical 
uniqueness (although, if we relax the ID requirement as per my intarea 
draft, that might not be the case)...

We talked in Phila about the use of either counters for the high-order 
bits of the 32-bit space, or about the use of rekeying to handle this.

> number), but I believe that the window would have to be about 2GB big

IMO, this is much more effectively handled by rekeying. Doing so every 
4GB (or even twice as often) should be more than sufficient.

Joe