Warmup question:
- Suppose you're allowed to buy two kinds of network connections:
  insecure Ethernet, and "secure" direct lines.  Ethernet links can
  be used to build any-to-any networks, but "secure" wires can only
  go between two endpoints (think of a telephone wire).
- You want to support the goals of Kerberos.  How to most effectively
  do so?
  - Simple answer: Buy n^2 secure wires between all pairs of hosts.
    Problem: very expensive.
  - Better answer: Establish a central trusted host, A.
    Buy n secure wires and n insecure wires: one secure wire from every
    host to A, and one insecure Ethernet connection for every host.
    If client C wants to talk to server S, client C should contact A,
    ask A to create a session key k for communication with S, then A tells
    k to C and S (all this is done over the secure wires); finally,
    C and S can set up a secure connection over the insecure Ethernet
    by using the crypto key k.

Followup question:
- Suppose C and S share a session key k, and want to establish
  a secure connection.
- C constructs an "authenticator" using k, sends it to S:
    C->S: {C,address_C}_k
  S constructs another "authenticator", returns it to C:
    S->C: {S,address_S}_k
  Once C and S confirm each other's identity, they continue the TCP connection.
- Q: Is this secure?
  - A: replay attacks, reflection attacks, TCP hijacking, etc.

Crypto notation:
 {x}_k    x encrypted under key k
 [x]_k    x protected by a MAC under key k
 [{x}]_k  x protected by both encryption and a MAC using shared key k
 (x,y)    concatenation of x and y
Caveats:
- Encryption might not protect integrity
- Early protocol papers get this wrong; they often implicitly
  assumed that encryption provides integrity (wrong in practice),
  and so never made a distinction between secrecy and integrity
- You'll rarely see [x]_k or [{x}]_k in the literature; that's
  my own preferred notation, though
- Concatenation should be done so that x and y can be uniquely,
  unambiguously recovered from (x,y)
- For [{x}]_k, we should use separate keys for encryption and MAC;
  e.g., let (k0,k1) = hash(k), then [{x}]_k = {x}_k1, [{x}_k1]_k2



Setting up a secure channel:
- What we'd like: If C wants to send message M to S, a magic fairy
  carries M to S in a way that noone else can see the message, C knows
  that the message will always reach S (and only S), and S knows that
  the message came from C (and only C).  Repeated messages will be
  delivered reliably and in-order.  Sadly, magic fairies don't exist...
- Given a shared key K_{C,S}, we can set up the abstraction of
  a reliable, unordered, tamperproof, confidential communication
  channel between C and S using crypto
  +-+        +-+
  |C|<------>|S|
  +-+        +-+
- when C wants to send a message M over this abstract channel, we actually send
  something like
    C->S: [{M}]_{K_{C,S}}
    where [{...}]_k denotes encryption + MAC under key k
- Q: why doesn't the above quite work?
  A: messages can be replayed
  A: messages can be deleted
  A: direction is not clear (reflection attacks)
- fix:
    C->S: [{C sends "M" to S with seq # s}]_...
         where s is a non-repeating seq #
         (recipient must check that they are in-order and none are missed!)
    S->C: [{C sends "M" to S at time t}]_...
         where t is a timestamp
         (recipient must check that timestamp is within some window around
          the current time; and, must cache old messages within that window
          and avoid repeats of a ciphertext)
  - note: seq #'s are stateful; timestamping is stateless
- Q: btw, the above is still not quite perfect; why?
  A: still possible to truncate a communication stream ("an undetected DoS")
  fixes are possible (have recipient ACK receipt; then DoS can be detected,
  if not prevented), but not always necessary
- Lesson: If you have a shared key, you can set up a secure channel


Motivation for session keys:
- Though a single shared key suffices for communication,
  might not want to use just one
  - Forward secrecy: Compromise of just one key shouldn't reveal too much
  - Multiplexing: Logically, we often have multiple different connections,
    and we will want to avoid getting confused about which message goes
    with which connection
- A common solution: Use a session key
  - Used just for a single session
  - Time-limited, then deleted; prevents after-the-fact compromise
    (rough quote from the Walker trial:
      Judge: "Why would anyone care about a used key?"
      Witness: "But, Judge, a used key is the most valuable kind.")




How to establish a session key:
- Assume we've got a secure channel; how can we establish a fresh session key?
- C picks k, and sends
   C->S: "Let's use k for a session between C<->S with lifetime T_0 .. T_exp"
      (over the secure channel)
- Then use k to establish a new secure channel
- Property: At most C,S now have access to the new secure channel
- If confirmation is required (so that we know that both C and S do indeed
  have access to the new secure channel), both parties can send an ACK
  over the newly established channel
  - challenge-response:
        C->S: ""Show me you're listening by repeating N""  (N is a nonce)
        S->C: ""Ok; I heard you send N""
  - time-stamping
        S->C: ""I can send messages at time T""
                  (all messages above over the new secure channel)
- Note: Once you've got a secure channel, it doesn't matter how the
  underlying encrypted content makes its way to your communicant
- Lesson: If you've got a secure channel, you can establish a fresh session key


Generalizing to n>2 entities:
- A long-term key K_{C,S} for each pair (C,S) of entities (O(n^2) keys)
- C knows K_{C,S} for each S (he knows O(n) keys)
- Problem: Doesn't scale too well; lots of keys


Better scaling, with a central trusted third party:
- A long-term key K_{A,e} for each entity e
- e knows 1 key, K_{A,e}
- A knows n keys, i.e., all the K_{A,e}'s
        +-+
       /|A|\
      / +-+ \
  +-+/       \+-+
  |C|<------->|S|
  +-+         +-+
- Note that C (resp. S) can use K_{A,C} (K_{A,S}) to establish
  a secure channel with A
- If C and S trust A to pick a session key for them, they can use their
  secure channels to tell them what key to use to talk to each other
- e.g.,
  C->A: "I, C, want a session with S; dated, T_0"
  A->C: "As requested by C at T_0, k is good for C<->S for T_0 .. T_exp"
  A->S: "As requested by C at T_0, k is good for C<->S for T_0 .. T_exp"
    (all messages over secure channel C<->A or S<->A)
- C and S can now use k to establish a private secure channel C<->S
  - They should probably confirm 
- Note: much better scaling




Optimizing the above:
- Triangle routing:  the A->S message can be routed through C
    C->A: "I want a session..."
    A->C: "Here's k...; also, please send the following to S: ``Here's k...''"
    C->S: S says ``Here's k''; also, `I can send messages...'
    S->C: `I can send messages too...'
  where ""   = secure channel for A<->C
        ``'' = secure channel for A<->S
        `'   = new, fresh secure channel for C<->S
- Bi-level authentication:
  - Use A to get a secure channel to a ticket-granting-service TGS
  - Use TGS to get a secure channel to other services
      -- Why?  (So only TGS needs to know about the list of all servers;
         a level of indirection; and for key management -- you get a master
             ticket which lets you get access to more tickets for specific servers)
- By the way, this is usually written in some abstract mathematical notation
  that leaves out all the words and hopes you'll be able to infer the intent
  - Q: Why?  (A: performance optimization? shorter descriptions? I dunno)
  - You can probably tell what I think of this idea
- Combine all the above, and you get Kerberos
- Notice the power of the abstraction of a "secure channel";
  Kerberos designers apparently worked without this abstraction,
  and their task was made significantly harder as a consequence
  (witness the reading for today)


Testing your understanding:
-- Q: what's in the TCB?
   A: AS, TGS, timeserver, end hosts


Experience with Kerberos:
- The real Kerberos actually gives you just an authenticated channel
  by default; encryption is optional.
  - Q: why?
  - A: performance optimization
  - Kerberos shows its age here;
    today encryption is almost free and doesn't hurt,
    so you'd better have a really good reason to omit the encryption

- Single point of failure: if A goes down, whole network is unavailable
  - Q: how do you fix it?
    A: replication
  - Q: how do you ensure consistency?
    A: everything is stateless

- Timeouts were an extremely good idea
      -- Ticket only good for (say) 12 hours
      -- Limits impact of a potential host compromise



-- Revocation
      -- Easy: just go disable the account at the AS (and potentially TGS)
      -- If the user has an outstanding ticket, you may have to wait 12 hours;
         otherwise, that's it
      -- But note that cost of setup is high
-- Comparison to public-key crypto
     -- Pubkey allows for same O(n) scaling without global trust in
        the server -- except that it is trusted not to impersonate folks,
            but otherwise it can't read encrypted traffic.  Why is the
            distinction important?  (Maybe there's not much difference if
            the server is malicious, but there's a big difference if the
            server is temporarily hacked.)
     -- However, revocation is much harder with pubkey stuff
     -- This suggests a rule of thumb:
        (cost of setup) + (cost of revocation) = constant

-- Threat model
      -- What can the AS+TGS do if they are compromised for a few days?
         (Defeat authentication for those few days; decrypt all past traffic
              (and possibly future traffic, if keys aren't changed))
      -- But that sucks!
      -- Q: So why did they use a single trusted central
         server, anyway?  (A: The n^2 scaling problem; public-key stuff not
             really mature yet; revocation; because authentication, not
             confidentiality, was primary original focus; and because it
             was designed for scenarios where all hosts are in the same
             administrative domain and thus have to trust some central set
             of machines anyway)
      -- Kerberos is missing forward secrecy and backward secrecy...


Implementation:
   -- Servers check IP address, so it's not enough to break Kerberos,
      you also have to do IP spoofing.  Seems like a useful property
      (and ensures that Kerberos can't make things any *worse*, no matter
      what happens).
      -- But bad interactions with multi-homing.
   -- Gets MACs wrong.  Uses encrypted checksum, a big no-no.
      And guess what -- there are attacks.  Fix: Use a MAC.
   -- Dictionary attacks.  If you can guess K_{C<->AS} (e.g. via a
      wordlist), you can test your guesses offline and eliminate wrong ones.
      Don't even need to intercept an encrypted {T_{C<->TGS}}_{K_{C<->AS}},
      why?  (Can just ask the AS nicely for it.)
      -- But, you need to know a username and realm name.
         However, there was a bug ("buffer underflow") that discloses
         usernames: you send it a malformed packet, and it sends back
         a packet containing an error message.  However, the packet
         contains left-over data from some unsanitized data structures;
         and that contains the name of the last user to request a TGT
         as well as the realm.



   -- Time stamp woes.
      -- If you can hack NTP, what breaks?
         (Can replay old authenticators, if can move S's clock back.
         If can move AS's and TGS's and C's clock forward, also can do
         attacks.)
      -- Replays.  Endpoints usually accept a clock skew of up to 5 minutes;
         but this has security implications.  Original paper suggested that
         there was no chance of replay within 5 minutes -- clearly bogus.
         (Why?) What's the right thing to do?  (Store a copy of all the
         "authenticators" you got in the past 5 minutes, check that list for
         replays, time out entries after 5 minutes.  This was actually
         suggested but apparently not implemented.)
         But this "windowing" idea is a pretty general technique;
         you'll see it again in e.g. IP security.
      -- Not easy to do secure network time service...
      -- Simple fix?  (Challenge-response.)  But challenge-response involves
         one more roundtrip; timestamps were used for performance fine-tuning.
         This is a pretty general trade-off.  In general, I think the
         tendency today is to go for the challenge-response protocols,
         since then you don't need to trust any timeservers.
   -- Key generation: used bad randomness (getpid(), time of day).
      Was totally breakable in K4.  A failure of both software engineering and
      social processes...
   -- And other attacks found by a student as part of a class project
      the last time we gave this class.  (By the way, she got her paper
      published!  I have confidence that you can do the same.)

-- Summary
   -- K5 is in NT and Win2000.  Is used in Wall Street.  Etc.
   -- Sort of the ancient dinosaur we all view with respect, awe, and
      a little bit of fear.




-- For reference: the Protocol
   -- N = nonce (random challenge)
   -- T_{C<->S} = (K_{C<->S}, C, S, ip address, timestamp, lifetime, N)
   -- The basic protocol:
      1. C->AS: C, S, expiration date, N
      2. AS->C: {T_{C<->S}}_{K_{C<->AS}}, {T_{C<->S}}_{K_{S<->AS}}
      3. C->S:  {C,S,timestamp}_{K_{C<->S}}, {T_{C<->S}}_{K_{S<->AS}}
      4. S->C:  {C,S,timestamp+1}_{K_{C<->S}} (optional)
      At the end, S and C share a good key k.  S knows it is speaking
      to C.  If they did msg 4, C knows it is talking to S.
   -- The complete protocol:
      A. Do 1--4 above to get a key for C<->TGS from AS.
      B. Do 1--4 above to get a key for C<->S from TGS.
         (Note: can piggyback A3 on B1 and A4 on B2.)
   -- T_{C<->TGS} is known as a TGT (ticket granting ticket).
   -- Note: K_{C<->AS} is your password.
   -- {C,S,timestamp}_{K_{C<->S}} also called an "authenticator".
   -- If you leave out "authenticator", what breaks?  (Replays.)
   -- If you leave out the name of the client in the ticket, what breaks?
      (Client can request a ticket from TGS under one name C, then can
      give it to server under another name C'.)
   -- If you leave out the name of the server in the ticket, what breaks?
      (Answer: man in the middle attack, where client asks TGS for ticket
      for server S, but M modifies changes the S to a S', and client gets
      back a ticket for S'; then when client sends the ticket to S, M
      redirects it to S'; and the client thinks he is speaking to S even
      though he is really talking to S'.)
   -- If a key K_{C<->S} ever gets compromised, what happens?
      (You can replay it, if you managed to the handshake messages;
      and you can create new authenticators, because you know the key
      they are encrypted under.)



Extra stuff from last time:
   -- Chosen-plaintext attacks.
       -- Same key used for both authentication and message encryption,
          and for multiple sessions; if you can get a message of your
          choosing encrypted, you can spoof future sessions.
   -- How would you do this on a PDA?  (Split proxy.)
      How do you implement the split proxy?  (Charon.)
      -- K_{C<->AS} (your password) and K_{C<->TGS} live on the PDA;
         session keys and service tickets T_{C<->S} live on the proxy.
      -- How does this help?  (Less trust in proxy.)
         How about giving K_{C<->TGS} to the proxy?  (More trust in proxy.)
         The way it currently is, proxy just has to be at least as secure
         as the server S you're using.
      -- Protocol:
         1. Client gets a TGT from AS; the proxy just copies bits.
         2. Client gets a secure connection with proxy, by making proxy
            a kerberized service.  Remaining messages go over this link.
         3. To access a server S, client talks to the TGS, gets a ticket
            and a session key, then gives the session key and ticket
            T_{C<->S} to the proxy (over the secure link established in 2),
            and the proxy can access the service on the client's behalf.

-- Cross-realm issues
      -- Realm = set of hosts under one AS/TGS
      -- How do you decide which hosts to put in which realms?
         (By administrative domain, usually, because all clients + servers
                 have to trust the AS+TGS)
      -- But then what if you want to connect to a host in another realm?
         -- Can ensure that all realm-pairs share a set of keys (K4), and
                set up "roaming agreements"; but this has n^2 scaling
         -- K5: multi-hop authentication, + hierarchical structure on realms.
            Disadvantages?  (Global trust in the root, inflexibility of
            hierarchical structure, transitive trust.  This one sucks:
            it has all the disadvantages of the worst of hierarchical and
            peer-to-peer models, and none of the advantages.  Yuck.)