10  Key Exchange and Public-key Encryption

So far, we have talked about encryption systems that require the sender and recipient to share a secret key. In this chapter, we discuss how the sender and recipient can agree on a shared secret even if they only ever communicate over an open (insecure) network.

10.1 Key exchange

A key-agreement scheme over keyspace \(\calK\) is defined by efficient functions \((\Gen, \Derive)\):

  • \(\Gen() \to (\sk, \pk)\). The \(\Gen\) algorithm generates a secret key and a public key for one party.
  • \(\Derive(\sk_A, \pk_B) \to k\). The \(\Derive\) algorithm takes as input one party’s secret key \(\sk_A\) and the other party’s public key \(\pk_B\) and outputs a shared key \(k \in \calK\).

Formally, \(\Gen\) also takes as input the security parameter.

Given this syntax, let us see how two parties can use a key-agreement scheme \((\Gen, \Derive)\) to agree on a shared secret:

  1. Alice runs \((\sk_A, \pk_A) \gets \Gen()\) and sends \(\pk_A\) to Bob.
  2. Bob runs \((\sk_B, \pk_B) \gets \Gen()\) and sends \(\pk_B\) to Alice.
  3. Alice and Bob both then compute a key \(k \in \calK\) as:
    • Alice computes \(k \gets \Derive(\sk_A, \pk_B)\).
    • Bob computes \(k \gets \Derive(\sk_B, \pk_A)\).

Correctness. Correctness for a key-agreement scheme just says that the two parties should always agree on the same shared secret:

Definition 10.1 (Key Agreement Correctness) We say that a key-agreement scheme is correct if for all \((\sk_A, \pk_A) \gets \Gen()\) and \((\sk_B, \pk_B) \gets \Gen()\), it holds that: \[ \Derive(\sk_A, \pk_B) = \Derive(\sk_B, \pk_A). \]

Security. The standard notion of security for a key-agreement scheme only considers passive attacks: we consider adversaries that can view the network traffic but cannot modify it. In practice, we can combine key-agreement schemes with authentication schemes (e.g., digital signatures) to prevent active attacks by in-network adversaries.

Our security definition for key agreement says that even if an adversary sees both parties’ public keys, it should not be able to distinguish the shared secret from random. That is, the following probability distributions \(D_\text{real}\) and \(D_\text{random}\) should be computationally indistinguishable:

When we say that two distributions are computationally indistinguishable, we mean that if we give the adversary a sample from one or the other, it can guess which sample it got with probability at most \(1/2 + \text{``negligible''}\).

\[\begin{aligned} D_\text{real} &\deq \left\{ (\pk_A, \pk_B, k) \colon \begin{aligned} (\sk_A, \pk_A) &\getsr \Gen()\\ (\sk_B, \pk_B) &\getsr \Gen()\\ k &\getsr \Derive(\sk_A, \pk_B) \end{aligned} \right\} \\ D_\text{random} &\deq \left\{ (\pk_A, \pk_B, k) \colon \begin{aligned} (\_, \pk_A) &\getsr \Gen()\\ (\_, \pk_B) &\getsr \Gen()\\ k &\getsr \calK \end{aligned} \right\} \end{aligned}\]

10.2 Diffie-Hellman key exchange

We now give a simple and beautiful key-exchange protocol, due to Diffie and Hellman.

So far, we have been able to construct our cryptographic entities from constructs like a PRF, which meant that we could use “unstructured” algorithms like AES to compute them. We so far only know how to construct key-exchange schemes from more structured problems (e.g., based on number theory).

The version we will see uses large primes \(p\) and \(q\) a public parameters, along with a number \(g \in \Z^*_p\), called the “generator.” (Warning: The parameters \(p\), \(q\), and \(g\) must have some particular relation. So do not attempt to pick these parameters yourself.) Typically, we will have \(p \approx q \approx 2^{2048}\).

For a prime \(p\), the notation \(\Z^*_p\) just denotes the non-zero integers modulo \(p\). So when we write \(ab \in \Z^*_p\), we mean \(a \cdot b \bmod p\).

The keyspace of the Diffie-Hellman scheme is \(\calK = \Z^*_p\) and the algorithms are as follows:

  • \(\Gen() \to (\sk, \pk)\).
    • Sample \(\sk \getsr \{1, \dots, q\}\).
    • Set \(\pk \gets g^{\sk} \in \Z^*_p\).
    • Output \((\sk, \pk)\).
  • \(\Derive(\sk_A, \pk_B) \to k\).
    • We have \(\sk_A \in \{1, \dots, q\}\) and \(\pk_B \in \Z^*_p\).
    • Output \(k \gets (\pk_B)^{\sk_A} \in \Z^*_p\).

Before we argue correctness and security, let us consider the computational efficiency of the scheme:

Efficiency. In order for the algorithm to be useful, Alice and Bob must be able to compute \(g^x \in \Z^*_p\) efficiently, for \(x \in \{1, \dots, q \approx 2^{2048}\}\). However, trying to compute \(g^x\) where \(x\) is 2048 bits long naively would certainly not be efficient: it would require \(x \approx 2^{2048}\) multiplications! However, we can compute this exponentiation much more efficiently using the following strategy:

  • Compute powers of \(g\). Write \(\ell \gets \lceil \log_2 p \rceil\). Then compute \((g, g^2, g^4, g^{8}, g^{16}, \dots, g^{2^\ell})\), where all of these are in \(\Z^*_p\). It is possible to compute \(g^{2^{i}}\) with a single multiplication modulo \(p\) as \(g^{2^i} = (g^{2^{(i-1)}})^2 \in \Z^*_p\). So this step takes only \(\ell = 2048\) multiplications.
  • Compute exponentiation. Write the bits of the exponent as \(x = x_0 \cdots x_{\ell-1}\). Then compute: \[ g^x = g^{\sum_{i = 0}^{\ell-1} x_i 2^i} = \prod_{i=0}^{\ell-1} x_i(g^{2^i}) \in \Z^*_p\] This step again takes only \(\ell\) multiplications.

In many applications, the generator \(g\) is fixed in advance. In this case, the implementation can precompute and store these powers of \(g\).

Correctness. Correctness holds since \(g^{xy}=g^{yx} \in \Z^*_p\) for all \(x,y \in \Z\): \[\begin{aligned} \Derive(\sk_A, \pk_B) = (\pk_B)^{\sk_A} = (\pk_A)^{\sk_B} = \Derive(\sk_B, \pk_A). \end{aligned}\]

Security. To argue security, we must rely on a new computational assumption: essentially we just assume that the key-agreement scheme is secure.

Definition 10.2 (Decision Diffie-Hellman (DDH) assumption) The decision Diffie-Hellman assumption states that, for a suitable choice of \(p\), \(q\), and \(g\), the following distributions are computationally indistinguishable:

To make the statement fully formal, we need to let \(p\), \(q\), and \(g\) grow with the security parameter.

\[\begin{aligned} D_\text{real} &\deq \{ (g, g^x, g^y, g^{xy}) \in (\Z^*_p)^4\colon x, y \,\,\,\,\,\getsr \{1, \dots, q\}\}\\ D_\text{ideal} &\deq \{ (g, g^x, g^y, g^z) \,\,\,\in (\Z^*_p)^4 \colon x, y, z \getsr \{1, \dots, q\}\} \end{aligned}\]

In practice, we typically first run Diffie-Hellman key agreement, have the two parties run the shared secret that they get through a cryptographic hash function, and then use the hashed value as an encryption key. If we model the hash function as a random oracle, security can rely on a slightly weaker assumption—the “computational” Diffie-Hellman (CDH) assumption. The CDH assumption asserts, informally, that given \((g, g^x, g^y)\), it is infeasible to compute \(g^{xy}\). More formally, we have:

Definition 10.3 (Computational Diffie-Hellman (CDH) assumption) The computational Diffie-Hellman assumption states that, for a suitable choice of \(p\), \(q\), and \(g\), and for all adversaries \(\calA\): \[ \Pr[\calA(g, g^x, g^y) = g^{xy} \colon x, y, \getsr\{1, \dots, q\} ] \leq \text{``negligible.''}\]

10.3 The discrete-log problem

The DDH assumption (Definition 10.2) is no harder than the following problem, which asserts that computing \(x\) given \((g, g^x) \in (\Z_p^*)^2\) is computationally infeasible:

Definition 10.4 (Discrete-log assumption) The discrete-log assumption states that, for \(p\), \(q\), and \(g\), and for all efficient adversaries \(\calA\): \[ \Pr[\calA(g, g^x) = x \colon x \getsr \{1, \dots, q\} ] \leq \text{``negligible''}.\]

Given an algorithm for the discrete-log problem, we can use it to solve the DDH problem. Given a DDH challenge \((g, g^x, g^y, g^{z})\), compute the discrete log of \(g^x\) and test whether \(g^z = {(g^y)}^x\). For certain choices of \(p\), \(q\), and \(g\), the best known algorithm for the DDH problem is to first solve the discrete-log problem in \(\Z^*_p\).

How hard it to solve the discrete-log problem then?

  1. The most basic attack is to enumerate all \(p\) possible values of \(x\) and check whether the corresponding \(g^x\) matches. This will take time \(p \approx 2^{2048}\).
  2. There is a slightly more clever algorithm, called “Baby Step Giant Step,” that is able to compute \(x\) in time \(\sqrt{p}\).
  3. In \(\Z^*_p\), a much better attack is the Number Field Sieve. This algorithm is able to compute \(x\) in (roughly) time \(\exp((\log p)^{1/3} (\log \log p)^{2/3})\)—sub-exponential time! The existence of this attack is the reason we require \(p\) to be \(2048\) bits long to get \(128\)-bit security.

An attack that runs in time \(\sqrt p\) runs in time \(2^{\frac 12 \log p}\). In contrast, the Number Field Sieve runs in time roughly \(2^{\sqrt[3]{\log p}}\). This is much much much faster than the \(\sqrt p\)-time algorithms, since the exponent grows much more slowly.

10.4 Generalizations of Diffie-Hellman

We have described the Diffie-Hellman protocol in terms of \(\Z^*_p\)—multiplication of integers modulo \(p\). In particular, the protocol uses a set of elements (here, \(\Z^*_p\)) and a binary operation on elements (here, multiplication modulo \(p\)). There is a natural generalization of the Diffie-Hellman protocol that works with other sets of elements and binary operations that operate on them.

More precisely, we can define Diffie-Hellman key exchange with respect to any finite cyclic group—a concept from abstract algebra. A cyclic group just consists of a set of elements and a binary operation on elements. The set and operation need to satisfy certain mathematical properties—associativity, etc.

Given a group \(\G\), we can then define a discrete-log assumption on \(\G\) and whenever discrete-log is hard on \(\G\), we can use \(\G\) to construct cryptosystems.

The most widely used version of the Diffie-Hellman protocol today uses elliptic-curve groups. The set of elements in an elliptic-curve group is a set of points \((x,y) \in \Z^2\) in two-dimensional space, where \(0 \leq x, y \leq p\) for some prime \(p \approx 2^{256}\). The binary operation on elements is some geometric operation on two points that yields a third point. Even though the underlying objects are now points instead of integers modulo \(p\), the Diffie-Hellman protocol looks exactly the same in this setting.

The appeal of elliptic-curve cryptosystems is that the best known discrete-log algorithm on certain elliptic-curve groups is Baby Step Giant Step, which runs in \(\sqrt p\) time. So, we can use elliptic-curve groups of size \(2^{256}\) and the Diffie-Hellman public keys take only \(\approx 256\) bits to represent. In contrast, when working in \(\Z^*_p\), we need to work modulo a prime \(p \approx 2^{2048}\) to defeat the Number Field Sieve attack, so Diffie-Hellman public keys in this setting take \(\approx 2048\) bits to represent.

10.5 Defining Public-Key Encryption

The definition for a public-key encryption scheme will be similar to the definitions we saw for symmetric-key encryption:

Definition 10.5 (Public-Key Encryption Scheme) A public-key encryption scheme over message space \(\calM\) consists of three efficient algorithms \((\Gen, \Enc, \Dec)\):

  • \(\Gen() \to (\sk, \pk)\): Generates a keypair with secret key \(\sk\) and public key \(\pk\).
  • \(\Enc(\pk, m) \to c\): Uses public key \(\pk\) to encrypts a message \(m \in \calM\) to a ciphertext \(c\).
  • \(\Dec(\sk, c) \to m\): Uses secret key \(\sk\) to decrypt ciphertext \(c\) into message \(m \in \calM\).

Definition 10.6 (Public-Key Encryption - Correctness) For all keypairs \((\sk, \pk) \gets \Gen()\) and for all messages \(m \in \calM\), it holds that \[\Dec(\sk, \Enc(\pk, m)) = m.\]

Definition 10.7 (Public-Key Encryption - Security against chosen-plaintext attacks (Weak))  

The literature sometimes calls security against chosen-plaintext attacks (“CPA security”) semantic security.

A public-key encryption scheme \((\Gen, \Enc, \Dec)\) is secure against chosen-plaintext attacks if all efficient adversaries \(\calA\) win the following game with probability \(\leq \tfrac{1}{2} + \negl\):

  • The challenger generates \((\sk, \pk) \gets \Gen()\) and \(b \getsr \bin\) and sends the public key \(\pk\) to \(\A\).
  • The adversary \(\A\) sends \(m_0, m_1 \in \calM\) to the challenger, where \(\abs{m_0} = \abs{m_1}\).
  • The challenger responds with \(\Enc(\pk, m_b)\)
  • The adversary outputs \(b'\) and wins if \(b' = b\).

Note that since this is a public-key encryption scheme, the adversary can use the public key to generate encryptions of any message of their choice. As in the secret-key setting, here it is crucial that the encryption algorithm be randomized—otherwise two encryptions of the same message are identical and the attacker can break CPA security.

For symmetric-key encryption, we also defined a stronger notion of security that we called security against chosen-ciphertext attacks (CCA security). We can extend this definition of CCA security to the public-key setting.

This security definition asserts that the attacker should not be able to distinguish the encryption \(c^*\) of two messages of its choice, even if the attacker can obtain decryptions of any ciphertext except \(c^*\). This is a very strong notion of security (since the security definition gives the attacker a lot of power) and it is the notion of security that we typically demand in practice.

Definition 10.8 (Public-Key Encryption - Security against chosen-ciphertext attacks (Strong)) A public-key encryption scheme \((\Gen, \Enc, \Dec)\) is secure against chosen-ciphertext attacks if all efficient adversaries \(\calA\) win the following game with probability \(\leq \tfrac{1}{2} + \negl\):

  • The challenger generates \((\sk, \pk) \gets \Gen()\) and \(b \getsr \bin\) and sends the public key \(\pk\) to the adversary \(\A\).
  • The adversary may make polynomially many decryption queries:
    • The adversary \(\A\) sends ciphertext \(c\) to the challenger.
    • The challenger responds with \(m \gets \Dec(\sk, c)\)
  • At some point, the adversary sends a pair of messages \(m_0, m_1 \in \calM\) to the challenger, where \(\abs{m_0}=\abs{m_1}\).
  • The challenger returns \(c^* \gets \Enc(\pk, m_b)\).
  • The adversary can continue to make decryption queries, provided that it never asks the challenger to decrypt the ciphertext \(c^*\).
  • The adversary outputs \(b'\) and wins if \(b' = b\).

10.6 ElGamal Encryption Scheme

In public-key encryption, we want a sender to be able to encrypt a message to a recipient. The goal of public-key encryption is to perform encryption without a shared secret key.

ElGamal’s encryption scheme essentially uses Diffie-Hellman key exchange to allow the sender and recipient to agree on a shared secret key \(k\), and then has the sender encrypt her message with a symmetric-key cryptosystem under key \(k\).

ElGamal’s scheme was not the first public-key encryption scheme. The first scheme, RSA, was much more complicated despite Diffie-Hellman already having been published.

Definition 10.9 (Hashed ElGamal Encryption) Let \(p\), \(q\), and \(g\) be integers of the sort we use for Diffie-Hellman key exchange (Section 10.2). In particular, \(p \approx q \approx 2^{2048}\) and \(g \in \Z^*_p\).

In practice, we typically use elliptic-curve groups to instantiate ElGamal encryption, instead of \(\Z^*_p\). But the general principle is exactly the same.

Let \(H: \Z^*_p \rightarrow \bin^*\) be a hash function (modelled as a random oracle). Let \((\Enc', \Dec')\) be a symmetric-key authenticated-encryption scheme. Then define:

  • \(\Gen() \to (\sk, \pk)\):
    • Choose \(a \gets \{1, \dots, q\}\).
    • Compute \(A \gets g^a \in \Z^*_p\).
    • Output \((\sk, \pk) \gets (a, A)\).
  • \(\Enc(\pk, m) \to c\):
    • Choose \(r \gets \{1, \dots, q\}\).
    • Compute \(R \gets g^r \in \Z^*_p\).
    • Compute \(k \gets H(\pk^r \in \Z^*_p)\).
    • Output \(c \gets (R, \Enc'(k, m)\).
  • \(\Dec(\sk, c) \to m\):
    • Parse \((R, c') \gets c\).
    • Compute \(k \gets H(R^a \in \Z^*_p)\).
    • Output \(m \gets \Dec'(k, c')\).

Performance

The performance of this scheme is limited by the exponentiations—the symmetric encryption scheme is quite fast (gigabytes per second), but a single exponentiation can take a millisecond on modern processors. For encryption, this scheme requires two exponentiations (\(g^r\) and \(\pk^r\)). Decryption requires one exponentiation \((R^\sk)\). To speed up the encryption routine, we can precompute powers of \(g\): \(g^2, g^4, g^8, g^{16}, \dots\), which saves a factor of two in exponentiations.

Hashed ElGamal encryption is one of the most common public-key encryption schemes used today.

Security

If we model the hash function \(H\) as a random oracle, we can prove security of ElGamal encryption from (a) the computational Diffie-Hellman assumption (Definition 10.3) and (b) the CCA security of the underlying authenticated-encryption scheme \((\Enc', \Dec')\).