# Secure Channel
We have learned how to protect individual messages with cryptography.
Now we protect entire conversations.
**Threat:** A Dolev–Yao attacker.
**Harm:** The contents of a conversation can be learned
(violating confidentiality) or changed (violating integrity) by the attacker.
**Vulnerability:** The conversation is held over a communication channel that
is controlled by principals other than the sender and receiver.
**Countermeasure:**
Cryptography, including encryption, MACs, and hashes, and some new techniques
that we will introduce.
What is a "secure channel"? As we're using the term, a *channel* is a means
for bidirectional communication between two principals, who we'll call
Alice and Bob. We think of Alice as the initiator of the conversation,
though of course Alice and Bob might well have multiple conversations
going in parallel in which either one of them might be the initiator.
So "Alice" and "Bob" here are really roles rather than identities.
The communication might occur spatially over a network, or temporally
over a storage device. In the latter case, a principal might even
have a conversation with itself. As for *secure*, we want to achieve
these goals:
1. The channel does not reveal anything about messages except for their
timing and size. (Confidentiality)
2. If Alice sends a sequence of messages m1, m2, ... then Bob receives a
subsequence of that, and furthermore Bob knows which subsequence.
And the same for Bob sending to Alice. (Integrity)
We do not attempt to achieve any kind of anonymity or availability.
Nor do we attempt to defend against *traffic analysis*: leakage of
information based on who is communicating with whom and how much.
### Message numbers
To help ensure the integrity property, we use *message numbers*
aka *sequence numbers*. Every message that Alice sends is numbered
1, 2, 3, and so forth. Bob keeps state to remember the last message
number he received; he accepts only increasing message numbers.
The same goes for Bob sending messages to Alice. So each principal
keeps two independent counters: one for messages sent, another
for messages received.
What happens if Bob detects a gap? e.g. he receives 1, 2, 5, but 3 and
4 are missing. Maybe Mallory deleted messages 3 and 4 from network, or
maybe she detectably changed 3 and 4, causing Bob to discard them. In
either case, the channel is under active attack. This would be a good
time to panic. Bob aborts the protocol, produces appropriate information
for later auditing, and shuts down the channel.
Note that we assume the underlying network transport protocol (e.g., TCP)
is itself guaranteeing in-order message delivery and attempting to retry
messages that are dropped. If we wanted to build atop an unreliable
transport protocol (e.g., UDP), we'd have to make the secure channel
itself more complicated. That would be in tension with Economy of Mechanism.
Message numbers are usually implemented as a fixed-size unsigned integer,
e.g., 32 or 48 or 64 bits. What if that `int` overflows and wraps back
around to 0? The conversation must stop at that point, though of course
the principals could start a new conversation. Otherwise, Mallory
could begin replaying messages from earlier in the conversation.
### Authenticated encryption
To help ensure both the confidentiality and integrity properties, we
use authenticated encryption. Let's assume that Alice and Bob already
share a session key k for this conversation; we'll address how they get
that key later.
For now, note that a single key isn't good enough: authenticated encryption
will require both an encryption key and a MAC key, and under the principle
that every key should have a unique purpose we should even have separate
keys for sending from Alice to Bob and for sending from Bob to Alice. So
we really need four keys:
1. kea: Encrypt Alice to Bob
2. keb: Encrypt Bob to Alice
3. kma: MAC Alice to Bob
4. kmb: MAC Bob to Alice
**Key derivation.**
How can we *derive* new keys from the one we already have? We use
a cryptographic hash function H:
1. kea = H(k, "Enc Alice to Bob")
2. keb = H(k, "Enc Bob to Alice")
3. kma = H(k, "MAC Alice to Bob")
4. kmb = H(k, "MAC Bob to Alice")
Hashing destroys any structure in its input, producing random looking output, which
can be truncated to the appropriate size for whatever Enc or MAC scheme we're using.
Because of the collision-resistance property of H, it's unlikely any of the four
keys will turn out to be the same. And because of the one-way property of H,
even if one of the four keys ever leaks to the adversary, it's hard to invert
the hash to recover k and get the other keys.
There is an issue, though, with whether the output of H is compatible with the
keys that are produced by the Gen algorithms. For most block ciphers and MACs,
this turns out not to be a problem: they happily take any uniformly random
sequence of bits of the right length as keys. But for (deprecated) DES,
it is a problem, because there are *weak keys* that DES's Gen would reject.
It's easy to check for those, though. For many asymmetric algorithms,
the output of a hash would not work as a key, because the keys must
satisfy certain algebraic properties that depend on the number theory
involved.
**Algorithms.** We've seen three ways to construct authenticated
encryption: Enc and MAC, Enc then MAC, MAC then Enc.
Let's unify all with a pair of algorithms:
* AuthEnc(m; ke; km): produce an authenticated ciphertext x of
message m under encryption key ke and MAC key km.
* AuthDec(x; ke; km): recover the plaintext message m from
authenticated ciphertext x, and verify that the MAC is valid,
using ke and km. Abort if MAC is invalid.
We could even provide just a single key argument to both, and use
what we know about key derivation to derive separate encryption and MAC
keys.
**Protocols.** For A to send a message to B:
```
1. A:
increment sent_ctr;
if sent_ctr overflows then abort;
x = AuthEnc(sent_ctr, m; kea; kma)
2. A -> B: x
3. B:
i,m = AuthDec(x; kea; kma);
increment rcvd_ctr;
if i != rcvd_ctr then abort;
output m
```
And for B to send a message to A:
```
1. B:
increment sent_ctr;
if sent_ctr overflows then abort;
x = AuthEnc(sent_ctr, m; keb; kmb)
2. B -> A: x
3. A:
i,m = AuthDec(x; keb; kmb);
increment rcvd_ctr;
if i != rcvd_ctr then abort;
output m
```
### Key establishment
How can Alice and Bob arrange to share a key k? They run
a *key establishment protocol*. In these protocols,
a *user* is a principal who will use the generated session key
for further communication; other principals might be involved
but won't learn or use the key. A *key transport protocol*
is used to generate a session key by **one** principal then transfer it
to all users. A *key agreement protocol*, on the other hand,
generates a session key as a function of inputs from **all** users
and transfers it to all users.
Boyd (1993) proved a theorem showing that it is impossible to
establish secure channels between principals who do not already
(i) share a key with each other, or
(ii) separately share a key with a trusted third party, or
(iii) have the means to ascertain a public key for each other.
In other words, you can't get something for nothing.
Here, let's build a key transport protocol using assumption (ii):
Alice and Bob already share a key with a trusted server who gets
to pick the session key. This seemingly small task turns out
to be remarkably difficult.
**Assume:** A trusted server S with whom A shares long-term key kAS
and B shares long-term key kBS.
**Output:** A new session key kAB generated by S (who can then
immediately forget it)
**Security goals:**
* Goal 1. Only A and B (and S) know kAB. (Confidentiality)
* (more to come...)
**Protocol 1.**
```
1. A -> S: A, B
2. S -> A: kAB
3. A -> B: A, kAB
```
Protocol 1 obviously doesn't satisfy the confidentiality goal.
The adversary trivially learns kAB.
**Protocol 2.**
```
1. A -> S: A, B
2. S -> A: Enc(kAB;kAS), Enc(kAB;kBS)
3. A -> B: A, Enc(kAB;kBS)
```
Protocol 2 achieves the confidentiality goal. But consider the following
abuse of the protocol by Mallory:
```
1. A -> S: A, B
2. S -> A: Enc(kAB;kAS), Enc(kAB;kBS)
3. A -> M: A, Enc(kAB;kBS)
3'. M -> B: C, Enc(kAB;kBS)
```
In step 3', C is any principal identifier other than A. Now B
believes he shares kAB with C rather than A. That's clearly not
what we intended to have happen. So let's add a new security goal:
* Goal 2: Users associate the shared key with the correct principal
identities. (Integrity)
Here's another way that Mallory can abuse the protocol:
```
1. A -> M: A, B
1'. M -> S: A, M
2. S -> M: Enc(kAM;kAS), Enc(kAM;kMS)
2'. M -> S: Enc(kAM;kAS), Enc(kAM;kMS)
3. A -> M: A, Enc(kAM;kMS)
```
Now M knows the shared key, violating Goal 1, and
A believes the key is shared with B rather than M, violating Goal 2.
**Protocol 3.**
Let's use authenticated encryption to prevent the two attacks we just saw.
```
1. A -> S: A, B
2. S -> A: AuthEnc(B,kAB;kAS), AuthEnc(A,kAB;kBS)
3. A -> B: AuthEnc(A,kAB;kBS)
```
Now M cannot change the messages as she did before. But there are still
ways M can abuse the protocol. For example, M could *replay* messages
from old executions of the protocol:
```
1. A -> M: A, B
2. M -> A: AuthEnc(B,old_kAB;kAS), AuthEnc(A,old_kAB;kBS)
3. A -> B: AuthEnc(A,old_kAB;kBS)
```
This attack could result in A and B replaying an old conversation (perhaps
causing A to repeat an order for goods, or repeat a payment, etc.). Maybe
M has even cracked old_kAB in the meantime, thus allowing her to manipulate
the new conversation. Neither of those is desirable. So let's add another
security goal:
* Goal 3: The session key is *fresh*. (Integrity)
We'll achieve that goal by employing nonces as part of a
*challenge–response* protocol.
**Protocol 4.** [Needham and Schroeder 1978]
```
1. A -> S: A, B, nA
2. S -> A: AuthEnc(B, nA, kAB, AuthEnc(A, kAB; kBS); kAS)
3. A -> B: AuthEnc(A, kAB; kBS)
4. B -> A: AuthEnc(nB; kAB)
5. A -> B: AuthEnc(nB-1; kAB)
```
Here, nA and nB are unique nonces chosen by A and B.
This is a well-known protocol that is the grandfather of many others.
One weakness with it is that, if kAB is ever disclosed to M, then
M could use it to start a new conversation with B simply by resuming
the protocol at step 3.
**Protocol 5.** [Denning and Sacco 1981]
One way to defend against that attack on Needham–Schroeder is by
assuming synchronized clocks and using timestamps:
```
1. A -> S: A, B
2. S -> A: AuthEnc(B, tS, kAB, AuthEnc(A, tS, kAB; kBS); kAS)
3. A -> B: AuthEnc(A, tS, kAB; kBS)
```
Here, tS is the time at S's local clock when it constructs message 2.
A and B must reject any messages that are not within tS±δ
for some bound δ, which might be on the order of seconds, minutes,
or hours.
**Protocol 6.** [Bauer et al. 1983]
Another way to defend against the attack on Needham–Schroeder is by
using nonces contributed by both users to the server:
```
1. B -> A: B, nB
2. A -> S: A, B, nA, nB
3. S -> A: AuthEnc(B, nA, kAB; kAS), AuthEnc(A, nB, kAB; kBS)
4. A -> B: AuthEnc(A, nB, kAB; kBS)
```
**Lessons learned:**
Designing even a simple cryptographic protocol is hard. The attacks aren't
obvious, nor are the security goals. We ended up with three;
there are many more contemplated in literature.