Notes on Security Protocols 15-440, Fall 2012 Carnegie Mellon University Randal E. Bryant References: Tannenbaum: 9.1, 9.2 (skip 9.2.3), 9.4.1 BASICS Desired attributes of security: Condidentiality: Do not reveal information to untrusted parties Integrity: Ensure that information has not been corrupted Authentication: Ensure identity of source of information Availability: Ensure that desired resource can be used. Attacking strategies (Depict with box diagrams) Passive Code breaking: Determine sensitive data or keys Traffice analysis: Learn from overall communication patterns Active Masquerade: Pretend to be someone else Replay: Reuse old information Alteration: Modify message Create spurious messages: (For denial of service) CRYPTO BUILDING BLOCKS: 1. Private key crypto: Have key K that must be kept secret between parties Operations K(M): Encrypt message M using key K to create cyphertext C K-1(C): Decrypt cyphertext using key K. Should give M. (Desired) Attributes: Can be made fast and safe Requires key distribution & preservation Given C, hard to determine M or K Given C & P, hard to determine K Attack strategies (from most difficult to easiest): Ciphertext. Given multiple samples C1, C2, ... Deduce message(s) or K Known plaintext. Given multiple pairs (M1, C1), (M2, C2), ... Deduce K Chosen plaintext. Given ability to compute K() Generate pairs (M1, C1), ... for chosen messages Deduce K Example encryption schemes: DES: 56-bit keys, encrypt 64-bit blocks Security: No longer good enough. Can buy hardware DES crackers that do brute-force attack in 1 day Triple DES: K = (K1, K2, K3) (168 bits total) K(M) = K1(K2-1(K3(M))) K-1(M) = K3-1(K2(K1-1(C))) Why the middle inversion? If let K1=K2=K3, then get regular DES. If let K1=K3, then get double-DES. AES (a.k.a. Rijndael): 128-bit blocks, key = 128, 192, or 256 2. Public Key Crypto Systems Two keys: K+: Public key. Can be widely disseminated K-: Private key. Known only to key owner. Work both ways as encrpytion/decryption pair: K+(K-(M)) = M K-(K+(M)) = M Examples: RSA Diffie-Hellman (will cover later. Does not have same functionality as RSA) Besides being useful for encryption, public key systems can be used to "sign" documents. Given document D: * Agent A "signs" the document using private key: S = Ka-(D). * A send message "I have created document D and I affix my signature S on it" * Given document D, and signature S, others can check authenticity by making sure that Ka+(S) = D. - Shows that A indeed generated signature - Signature is specific to single document, so others cannot reuse on some other document D' Stream encryption Standard encryption schemes are block-based. Work on fixed size blocks. What if have message that is longer than single block? Obvious (Electronic Codebook [ECB]): Break M into blocks M1, M2, ..., Mn (Pad final if necessary) Encrypt each separately K(M) = [K(M1), ..., K(Mn)] Better (Cypher block chaining [CBC]): (Assume block size and cyphertext size are same) Generate random block R K(M) = C0, C1, C2, ..., Cn where C0 = R, Ci = K(Ci-1 XOR Mi) Decoding Recover M1, M2, ... Mi = K-1(Ci) XOR Ci-1 SECURITY PROTOCOLS Shared Key authentication Suppose parties Alice (A) and Bob (B) have shared secret key Kab. A wants to initiate session with B where each party is sure of who is at the other end. (or at least, that party at each end knows Kab). Requires 4 rounds: 1. A->B: A 2. B->A: Rb Rb is a "nonce" short for "number used once". Should be randomly generated so that no one will be able to guess next nonce to be used by B. See go function crypto/rand.Int() 3. A->B: Kab(Rb), Ra First part is A's way of proving to B that she knows Kab. 4. B->A: Kab(Ra) This is B's way of proving that he knows Kab. Interesting idea: Can we reduce this to 3 rounds: 1. A->B: A, Ra 2. B->A: Rb, Kab(Ra) A sure that B knows Kab 3. Kab(Rb) B sure that A knows Kab Vulnerability: B is providing a free encryption service that can be used by attacker C: 1. C->B: A, Ra 2. B->A (intercepted by C): Rb, Kab(Ra) 1'. C->B: A, Rb 2'. B->A (intercepted by C): Rb', Kab(Rb) 3. C->B: Kab(Rb) Bob now thinks that C knows Kab, and so C can masquerade as A. General principle. Can characterize each agent by what it knows. Say [A:X] means "A knows X" Inference rules 1. Decryption. If [A:K], and B sends A message K(M), then A knows M. Write as rule: [A:K], B -> A: K(M) ---------------- [A:M] 2. Challenge/response If A knows K, A sends nonce Ra to B, and B sends A K(Ra), then A knows that B knows K. Write [A:[B:K]] But also, that everyone knows the plain/cypher-text pair Ra, K(Ra) [A:K], A->B:Ra; B->A:K(Ra) -------------------------- [A:[B:K]], [All: Ra, K(Ra)] Slight variant 1. A->B: Ra 2. B->A: K(Ra+1) Then [A:[B:K]], but no known plain/cypher-text pair. [A:K], A->B:Ra; B->A:K(Ra) -------------------------- [A:[B:K]], [All: Ra, K(Ra+1)] Sometimes use K(Ra-1) as well. Revisiting protocol: Initially [A:Kab] [B:Kab] [All:{}] (What a potential attacker knows) 1. A->B: A 2. B->A: Rb 3. A->B: Kab(Rb+1), Ra [B:[A:Kab]] [All:[Rb,Kab(Rb+1)]] 4. B->A: Kab(Ra+1) [A:[B:Kab]] [All:Ra,Kab(Ra+1)] Key Distribution Suppose have many clients: A, B, C, ... Impractical to require that each pair X, Y has secret key Kxy. Instead, have centralized "key distribution center" or KDC. Call it S. Assume KDC is trustworthy. Keeps its secrets. Does not attempt malicious behavior. KDC maintains keys for each individual client. Kas, Kbs, ... When A & B want to communicate, get S to create "session key" Kab with which they can conduct a conversation. As name implies, generally used for bounded duration. Version 1: 1. A->S: A,B KDC generate Kab 2. S->A: Kas(Kab) [A:Kab]) 3. S->B: Kbs(Kab) [B:Kab] Observe: A & B aren't really sure of each others identity, but could go through previous authentication protocol & work things out. (This protocol is vulnerable to a replay attack. Covered later.) Version 2: Same idea, but want to eliminate communication from S to B. 1. A->S: A,B KDC generate Kab 2. S->A: Kas(Kab), Kbs(Kab) [A:Kab] Second part is a "ticket". A cannot use this information directly. It's like a sealed envelope that it can present to B. 3. A->B: A, Kbs(Kab) [B:Kab] Again, could follow earlier protocol to have A & B authenticate to each other. VULNERABILLITY: Both of these protocols are vulnerable to replay attacks. E.g., suppose that C had earlier communicated with A via session key Kac. C could spoof the server: 1'. A->S: A,B. Intercepted by C. 2'. C->A: Kas(Kac), Kcs(Kac) [C saw earlier message going to A] Now when A & C engage in authentication protocol, A will think it's talking to B. Needham-Schroeder Protocol Original protocol proposed in 1978. We show slightly enhanced version here, and describe a known weakness. Purpose: * Set up secure channel between A & B with session key Kab, using key server S. * Authenticate A & B to each other. * Make sure all information is fresh. Use three nonces: Ra1, Ra2, Rb 1. A->S: Ra1, A, B 2. S->A, Kas(Ra1, B, Kab, Kbs(A,Kab)) [A:Kab]. [A:[S:Kas]] A knows this message was in direct response to #1. So, this is a fresh session key from S. Fine Points: * Include B to prevent man-in-middle attack: 1'. A->S (intercepted by C): Ra1, A, B ... C->S: Ra1, A, C If didn't include identity of second party in #2, would have: 2'. S->A: Kas(Ra1, Kac, Kcs(A,Kac)) & A would proceed to set up session with C. Including second party in #2, would have: 2'. S->A: Kas(Ra1, C, Kac, Kcs(A,Kac)) and so A could detect incursion. * Ticket Kbs(A,Kab) includes identity of A. Prevents ticket from some other channel from being reused. * Encrypt everything under Kas, so that attacker cannot learn anything about session, or any plain/cyphertext pairs. 3. A->B: Kab(Ra2), Kbs(A,Kab) [B:Kab, Ra2] (Note that having sent ticket in step #2 encrypted was useless, since we've revealed it here, but there's no choice) 4. B->A: Kab(Ra2+1, Rb) [A:[B:Kbs, Kab]] A can be certain that it's communicating with B (knows Kbs) 5. A->B: Kab(Rb+1) [B:[A:Kab]] Note that the standard N-S protocol does not include Ra2, and therefore there is no way for A to be sure that it's really talking to B (more specifically, that it is talking to a party that knew Kbs.) This protocol is still considered to be a bit weak, in that there is nothing that can assure B that the ticket it receives on step #3 is fresh, not an old ticket that is being reused. This weakness was first described publicly in 1981 by Denning & Sacco, who suggested fixing it by adding a time stamp to the ticket. Later (1987) Needham & Schroeder published a fix that adds two more steps to the beginning, as well as an additional nonce Rb0. Here are the two steps (numbered -1 and 0, to indicate their relationship to the other 5 steps): -1. A->B: A A tells B that it wants to set up a session 0. B->A: Kbs(Rb0) B provides a nonce that A cannot decode. We incorporate this nonce into the next 3 steps of the protocol: 1'. A->S: Ra1, A, B, Kbs(Rb0) 2'. S->A: Kas(Ra1, B, Kab, Kbs(A, Kab, Rb0)) 3'. A->B: Kab(Ra2), Kbs(A, Kab, Rb0) We see that nonce Rb0 gets incorporated into the ticket that A provides B. B can now be sure that this is a newly generated ticket. Kerberos Uses a version of secret key N-S to set up secure channels between users & services. Uses time stamp rather than nonce's to avoid reuse of stale keys. Based on following: If A knows K, B sends K(t) to A, and current time ~= t, then A knows that B knows K. [A:K], B->A:K(t), T[A] ~= t -------------------------- [A:[B:K]] Requires A & B to maintain synchronized clocks. More precise ==> less vulnerable to replay attacks. Public key Needham-Schroeder. A sets up session with B. B generates session key. Does not require separate server. 1. A->B: Kb+(A, Ra) B generates secret session key Kab. [B:Kab, Ra] 2. B->A: Ka+(Ra, Rb, Kab) A certain that message came (recently) from B. [A:Kab, [B:Kab]] 3. A->B: Kab(Rb) [B:[A:Kab]] B certain that message came from A. Note: Authentication based on premise that Kb+ is really a public key for B & Ka+ is really a public key for A. How can we be sure of this? (Will discuss later) Diffie-Hellman(-Merkle) Key Exchange Purpose: Given 2 agents A & B * Each have secret values a & b * Exchange values - A shares something about a without giving away its value - B shares something about b without giving away its value * A & B can then independently generate key K based on their secret value + shared value from other party * K can be used for private key encryption between them. Note that this does not provide authentication: A & B cannot be sure of each others identities. What is required: Public values (Can be used by all parties): p: A large prime number (typically ~300 bits long) See Go function crypto/rand.Prime() g: A value that serves as a "generator" for multiplicative group of integers, mod p (typical values 2, 3, or 5) Secret values a, b: large numbers (typically ~100 bits) See Go function crypto/rand.Int() Mathematical basis: Set of values {1, 2, ..., p-1} forms a group with generator g I.e., Values (g^{i} mod p) for 1 <= i < p generates all {1, ..., p-1} E.g., p = 5, g = 3 i g^{i} mod p 1 3 2 9 mod 5 = 4 3 27 mod 5 = 2 4 81 mod 5 = 1 Other important properties: 1. [(x mod p) * (y mod p)] mod p = xy mod p Implies (x^u mod p)^v mod p = x^{uv} mod p 2. Euler's Theorem: g^{p-1} mod p = 1 3. Every value x = (g^{i} mod p) has inverse x^{-1} * (x * x^{-1} mod p) = 1 * Computed as (g^{p-i-1} mod p) D.H. Algorithm: 1. Compute & exchange values: - A computes x = g^a mod p - B computes y = g^b mod p - A & B exchange their values (OK for anyone to see these) - The function f(a) = g^a mod p is an example of a one-way function * Given x, it is hard to find a such that x = f(a). * So A & B can publish values of x & y without disclosing a or b. 2. Compute secret key - A computes key K = y^a mod p - B computes key K = x^b mod p - Note that both values are equal K = g^{ab} mod p - Even though A doesn't know b, and B doesn't know a. 3. Using as encryption key - Either side encrypts message M as C = (M*K mod p) 4. Decryption - Each can compute decryption key k such that (k * K mod p) = 1 - For A: k = y^{p-1-a} mod p = g^{b(p-1-a)} mod p = g^{b(p-1)-ab} mod p = g^{-ab} mod p [Note above that we compute -ab mod p] [Derivation uses property that g^{b(p-1)} = (g^{p-1})^b = 1^b = 1] - For B: k = y^{p-1-b} mod p - In either case, decrypt C as M = (C*k mod p) Important properties of Diffie-Hellman: * Allows any two agents to set up a private session * Generates both key & encryption method * It's a symmetric form of public key cryptography. - Agent A's private key is a and public key is g^a mod p. * Unlike RSA, does not lend itself to digital signature scheme * Much easier to implement than RSA. In particular, it's very easy to generate new secret keys, since they're just big random numbers. Authentication Protocol based on Diffie-Hellman The key exchange part of Diffie-Hellman does nothing to authenticate the other party. It just creates a way for two parties to generate a shared key without the resulting key ever being communicated. The following "Station-to-Station" (STS) protocol uses D-H + public key signatures to both generate a shared secret key & to authenticate the two parties to each other. (Published by Diffie, van Oorschot, & Wiener, 1992). Components: * A knows B's public key Kb+ * B knows A's public key Ka+. * Values of g & p are publicly known. * A has secret value a & public value x = g^a mod p. * B has secret value b & public value y = g^b mod p. * Both a & b were freshly generated to be used only for key exchange. * A can compute secret key K from values of a & y * B can compute secret key K from values of b & x 1. A-->B: A, x * x is used as nonce 2. B-->A: y, K(Kb-(x,y)) * y is also used as nonce * B signs x to indicate that it is responding to A. * B signs y to indicate that this is the value B wants A to use in generating K. * B encrypts this signed information with K - So that C cannot interfere by supplying signature Kc-(x,y) - Demonstrates to A that B has a working copy of K - Prevents B from being spoofed into providing signatures of arbitrary documents 3. A-->B: K(Ka-(x,y)) * Shows that: - Responding to step #2 - Has working copy of K This protocol has property "Perfect Forward Secrecy." Namely, if someone cracks secret key K generated during one session, that will not give any information about secret key K' generated for a different session. This relies on generating fresh values of a & b for every run of the protocol. Key Management We can see the need in several of these schemes for a trusted third party: * Needham-Schroeder. Need to have trusted server. It manages a key for each authenticated user, and it can be trusted. * Public key crypto. Need key distribution center that, when requested, can provide public key Ka+ for agent A. All known schemes require a "root of trust." There must be SOMEONE that you trust. E.g., public key distribution center. Suppose you trust a key server S, having public key Ks+. Then A can ask: "What is the public key for B?." Server can respond with signed message [Ka+, Ks-(Ka+)]. In general, maintain tree of certification authorities. Root server S can delegate its authority to lower-level authority T by sending "certificate" [Kt+, Ks-(Kt+)]. Now we can use T as key distribution server just the same as we'd use S. Usually associate limited time duration to certificate, so that privileges can be will expire if not renewed. That avoids complexities of having to revoke certification explicitly.