2
Unconditional Cryptography

Rudiments of Provable Security

2.1. Attack scenarios as libraries

In the previous chapter, we showed how to formalize an attack scenario in terms of an adversary calling a special subroutine. Later in this book, we will encounter attack scenarios that are not easy to describe with just a single subroutine.

  • In some attack scenarios, the victim can perform several different kinds of actions. For example, the adversary may compel the victim to encrypt one thing, then decrypt another. It is convenient to model fundamentally different actions as separate subroutines.

  • Many attack scenarios feature stateful victims. For example, a victim holds a long-term key that it uses to encrypt many plaintexts. We need a way to describe persistent values that are reused across different subroutine calls.

Instead of using a single subroutine, we can formalize attack scenarios as libraries:

Definition 2.1.1 (Libraries)

A library is simply a collection of subroutines. We specify libraries as concrete pseudocode, and we have some important conventions about the meaning of that code:

  • All variables are private to their own library's scope and cannot be accessed by the calling program.

  • All variables are global to the library and accessible to all subroutines in the same library.

  • Lines of code outside of any subroutine (always written at the top of the library) are executed at the beginning of time. Usually these lines initialize global variables.

  • Certain types of variables are initialized to a default value so that we don't need to explicitly write their initialization.

    boolean variable initialized to false\myfalse
    integer variable / counter initialized to zero
    set variable / collection initialized to the empty set
    associative array / dictionary initialized to the empty map
  • Code comments begin with //\bit{//}.

Example 2.1.2

The following library has two subroutines and one global variable DD:

Ldice-guess\lib{dice-guess}
D{1,2,3,4,5,6}D \gets \{1,2,3,4,5,6\}
guess(G)\subname{guess}(G):
return D==GD == G
reroll()\subname{reroll}(\,):
D{1,2,3,4,5,6}D \gets \{1,2,3,4,5,6\}
// without an explicit return
// statement, returns null\mynull

This library models an attack scenario in which a victim holds a 6-sided die. The first line of the library (outside of any subroutine) represents what the victim does at the beginning of time: It rolls the die. Both subroutines access a global variable DD. The two subroutines represent actions that the adversary can cause, at any time: The adversary can make a guess about the current value of the die and learn whether the guess was correct. The adversary can also instruct the victim to (privately) re-roll the die, learning nothing about the result of that roll.

The adversary plays the role of a calling program that invokes the subroutines of a library. We need a way to write the act of combining a calling program with a library and executing the resulting combined program.

Example 2.1.4 (Output probabilities)

Below is an example calling program A\A linked to the dice-rolling library Ldice-guess\lib{dice-guess} from example 2.1.2:

A\A
if guess(6)\subname{guess}(6):
return true\mytrue
reroll()\subname{reroll}()
return guess(6)\subname{guess}(6)
\link
Ldice-guess\lib{dice-guess}
D{1,2,3,4,5,6}D \gets \{1,2,3,4,5,6\}
guess(G)\subname{guess}(G):
return D==GD == G
reroll()\subname{reroll}(\,):
D{1,2,3,4,5,6}D \gets \{1,2,3,4,5,6\}
return null\mynull

The combined program outputs true\mytrue if the victim rolls a 6 in either of its first two rolls. One way to calculate the resulting output probability is the following:

Pr[ALtrue]=1Pr[ALfalse]=1Pr[first roll 6]Pr[second roll 6]=15656=12536=1136.\begin{aligned} \PR{ \A \link \L \outputs \mytrue } &= 1 - \PR{ \A \link \L \outputs \myfalse } \\ &= 1 - \PR{ \text{first roll } \ne 6 } \PR{ \text{second roll } \ne 6 } \\ &= 1 - \frac 56 \cdot \frac 56 = 1 - \frac{25}{36} = \frac{11}{36}.\end{aligned}

When we reason about security properties, we write a lot of different libraries on the page. But remember, security is about what happens when an adversary is allowed to call the library's subroutines. We just can't write the adversary on the page, because adversaries are always unspecified and arbitrary. Whenever you see the code of a library, always remember to picture an adversary in the role of a calling program.

It is standard in cryptography to define security in terms of adversaries interacting with libraries that play the role of the victims, but the idea of calling them libraries is unique to this book. Elsewhere, you might see libraries called (security) games or (security) experiments.

2.2. Interchangeable libraries

In the previous chapter, we discussed the security of OTP by showing that the following two subroutines have the same behavior; on every input, they generate the same output distribution:

attack(M)\subname{attack}(\ptxt):
K{0,1}n\key \gets \bits^n
C:=KM\ctxt := \key \oplus \ptxt
return C\ctxt
attack(M)\subname{attack}(\ptxt):
R{0,1}nR \gets \bits^n
return RR

Let's extend this idea from simple subroutines to more complicated libraries. What does it mean for two libraries to “produce the same output behavior,” especially if they have several subroutines, or persistent/static variables? We will say that two libraries L1\lib{1} and L2\lib{2} “have the same behavior” if they induce the same output probability in every calling program.

Definition 2.2.1 (Interchangeable libraries)

Suppose two libraries L1\lib{1} and L2\lib{2} have the same interface: identically named subroutines with identical argument types and return types. We say that L1\lib{1} and L2\lib{2} are interchangeable, and we write L1L2\hl{\lib{1} \equiv \lib{2}}, if no calling program behaves differently when linked to either of the two libraries. More formally, L1L2\lib{1} \equiv \lib{2} if, for every calling program A\A with boolean output,

Pr[AL1true]=Pr[AL2true]. \PR{ \A \link \lib{1} \outputs \mytrue } = \PR{ \A \link \lib{2} \outputs \mytrue }.

It might help to think of a calling program as a distinguisher whose goal is to determine whether it is linked to L1\lib 1 or to L2\lib 2. The two libraries are interchangeable if no distinguisher can succeed, not even with the smallest change in its output probability.

It is important to understand exactly what information can pass between A\A and L\L in the combined program AL\A \link \L. In short, the only thing that the calling program can learn about the library's operation is the return value of its subroutines. In particular:

  • The calling program cannot access the variables inside the library. This is what we mean when we say that the library's variables are privately scoped to the library. The calling program can learn about private variables only through what L\L's subroutines are willing to provide as return values.

  • The calling program cannot measure how long (e.g., number of clock cycles) the library takes to compute the response. The calling program cannot detect cache hits/misses that might indicate whether the library accessed a certain region of memory. Remember, the point of all this is to formally prove things about security. Our calling programs and libraries are clean mathematical abstractions, not literal (messy) physical computers.

    You can use the methods in this book to prove security guarantees that involve an adversary who can observe response times and/or CPU cache behaviors, and so on. You simply need to make these channels of information explicit in your libraries; they don't exist automatically like they do when programs are running together on the same physical computer.

It is also important to understand that definition 2.2.1 involves two completely independent program executions. We consider the output probability of A\A linked to L1\L_1, then, separately, consider the output probability of the same A\A linked to L2\L_2. We never consider any scenario in which A\A is linked to both libraries at once, or where one library is swapped for the other during an ongoing execution of A\A.

Claim 2.2.2 (Re-stating OTP security using libraries)

Claim 1.4.1 can be rephrased as follows: The following two libraries are interchangeable.

Lotp-real\lib{otp-real}
otp.enc(M)\otpenc(\ptxt):
K{0,1}n\key \gets \bits^n
C:=KM\ctxt := \key \oplus \ptxt
return C\ctxt
\equiv
Lotp-rand\lib{otp-rand}
otp.enc(M)\otpenc(\ptxt):
R{0,1}nR \gets \bits^n
return RR

In other words, no adversary can tell the difference between OTP ciphertexts and uniformly sampled strings, given the ability to arbitrarily choose the plaintexts, when OTP keys are sampled uniformly and used for only one encryption.

In the proof of claim 1.4.1 we showed that these two subroutines induce identical output distributions for every choice of input. Since the only information passed from library to calling program is this subroutine output, this implies that the libraries are interchangeable.

Finally, definition 2.2.1 puts absolutely no restriction on the calling program. The two libraries must induce identical output probabilities on all calling programs, even those whose running time is astronomical. Only in later chapters will we care about the running time of calling programs.

Ways to be interchangeable: You're probably familiar with many ways that two different programs can “have the same behavior.” Here are a few examples:

Example 2.2.3 (Interchangeable libraries)

Two libraries are interchangeable if:

  • Their only difference happens in unreachable lines of code.

    foo(n)\subname{foo}(n):
    \cdots
    X{1,,n}X \gets \{1, \ldots, n\}
    if X<0X < 0:
    return 0n\bit 0^n
    \cdots
    \equiv
    foo(n)\subname{foo}(n):
    \cdots
    X{1,,n}X \gets \{1, \ldots, n\}
    if X<0X < 0:
    return 1n\bit 1^n
    \cdots
  • Their only difference is the value they assign to a variable that is never actually used.

    foo(A,B)\subname{foo}(A,B):
    \cdots
    C:=bar(A)C := \subname{bar}(\hl{A})
    \cdots
    bar(M)\subname{bar}(M):
    X{0,1}nX \gets \bits^n
    return XX
    \equiv
    foo(A,B)\subname{foo}(A,B):
    \cdots
    C:=bar(B)C := \subname{bar}(\hl{B})
    \cdots
    bar(M)\subname{bar}(M):
    X{0,1}nX \gets \bits^n
    return XX
  • Their only difference is that one library unrolls a loop that occurs in the other library.

    foo(n)\subname{foo}(n):
    if n<1n<1: return null\mynull
    for i=1i = 1 to nn: bar(i)\subname{bar}(i)
    \cdots
    \equiv
    foo(n)\subname{foo}(n):
    if n<1n<1: return null\mynull
    for i=1i = 1 to n1n-1: bar(i)\subname{bar}(i)
    bar(n)\hl{\subname{bar}(n)}
    \cdots
  • Their only difference is that one library inlines a subroutine call that occurs in the other library.

    foo(A)\subname{foo}(A):
    \cdots
    C:=sample()C := \subname{sample}()
    \cdots
    sample()\subname{sample}(\,):
    X{0,1}nX \gets \bits^n
    return XX
    \equiv
    foo(A)\subname{foo}(A):
    \cdots
    C{0,1}nC \gets \bits^n
    \cdots

Hopefully it's quite clear why each of these pairs of libraries “have the same behavior.” Each example is extremely simple, yet each one appears somewhere in a proof later in this book.

Most attack scenarios involve randomized libraries, and these can be interchangeable in more interesting ways:

Example 2.2.4 (Interchangeable libraries involving randomness)
  • Concatenating two strings, which were sampled independently and uniformly, has the same effect as sampling from the uniform distribution over longer strings.

    sample()\subname{sample}(\,):
    X{0,1}nX \gets \bits^n
    Y{0,1}mY \gets \bits^m
    // “\|” denotes string concatenation:
    return XYX \| Y
    \equiv
    sample()\subname{sample}(\,):
    R{0,1}n+mR \gets \bits^{n+m}
    return RR
  • It does not matter when a value is sampled, as long as it is sampled before its first use. In the left library below, SS is sampled eagerly, at the beginning of time. In the right library, SS is sampled lazily, at the last possible instant before it is used.

    S{0,1}nS \gets \bits^n
    get()\subname{get}(\,):
    return SS
    \equiv
    // S:=undefS := \myundef by default
    get()\subname{get}(\,):
    if SS undefined:
    S{0,1}nS \gets \bits^n
    return SS
  • If we sample uniformly, but re-sample whenever we get an “undesirable” value, this has the same effect as sampling uniformly from the set of “desirable” values. In the example below, values above 90 are “undesirable”:

    foo()\subname{foo}(\,):
    do:
    A{1,,100}A \gets \{1,\ldots,100\}
    while A>90A > 90
    return AA
    \equiv
    foo()\subname{foo}(\,):
    A{1,,90}A \gets \{1,\ldots,90\}
    return AA

Kerckhoffs's principle: How is Kerckhoffs's principle reflected in the idea of interchangeability? Two libraries must induce the same output probability in all calling programs, even calling programs whose source code depends arbitrarily on the source code of the libraries. In plain language, the calling program can “know” the source code of the two libraries that it tries to distinguish.

Remember: Even if its source code is public, a library can still have secrets. Knowing that the library executes the statement “K{0,1}n\key \gets \bits^n” doesn't help you know the specific of value K\key that was chosen at runtime.

2.3. How to distinguish two libraries

Every security definition in this book is phrased in terms of two interchangeable libraries. So when we need to demonstrate that something is insecure, we show that those two libraries are not interchangeable. To do this, it's enough to describe just one calling program that produces different output probabilities in the presence of the two libraries. There are usually many different ways to write such a calling program, making this a good outlet for expressing your creativity.

Demonstrating Insecurity / “Attacking” Insecure Schemes

When you are asked to show that something is insecure, you are being asked to show that two libraries are not interchangeable.

  1. First, figure out which two libraries are involved.

  2. Write down the code of a calling program. This program's goal is to behave differently in the presence of the two libraries.

  3. Calculate the two relevant output probabilities. If they are different, then your attack has succeeded.

Let's see some examples of how to distinguish two libraries. Consider a victim who encrypts many OTP plaintexts (of the adversary's choice) using the same key. We want to show that the resulting ciphertexts will not appear uniformly distributed. We can do so by showing that the following two libraries are not interchangeable:

L1\lib{1}
K{0,1}n\key \gets \bits^n
otp.enc(M)\otpenc(\ptxt):
C:=KM\ctxt := \key \oplus \ptxt
return C\ctxt
≢\not\equiv
L2\lib{2}
otp.enc(M)\otpenc(\ptxt):
C{0,1}n\ctxt \gets \bits^n
return C\ctxt

Library L1\lib{1} describes an attack scenario in which the key K\key is chosen once at the beginning of time, and reused in each call to otp.enc\otpenc. Library L2\lib{2} describes a scenario in which the adversary sees only “random junk.” Below are a few ways to think about a successful attack distinguishing these libraries:

Example 2.3.1 (Attacking OTP with a reused key, #1)

In OTP, it is possible to obtain the key by requesting an encryption of the all-zeros plaintext. In L1\lib{1}, making multiple calls of the form otp.enc(0n)\otpenc(\bit 0^n) will result in the same response K\key every time. We wouldn't expect the same response every time from L2\lib{2}. We formalize this idea by writing the following adversary program:

A\A
C1:=otp.enc(0n)\ctxt_1 := \otpenc(\bit 0^n)
C2:=otp.enc(0n)\ctxt_2 := \otpenc(\bit 0^n)
return C1==C2\ctxt_1 == \ctxt_2

When A\A is linked to L1\lib{1}, as we argued, C1=C2=K\ctxt_1 = \ctxt_2 = \key. Therefore, AL1\A \link \lib{1} outputs true\mytrue with probability 1.

However, when A\A is linked to L2\lib{2}, the values C1\ctxt_1 and C2\ctxt_2 are distributed uniformly and independently. The probability that C1=C2\ctxt_1 = \ctxt_2 is therefore only 1/2n1/2^n. Hence:

Pr[AL1true]=1,Pr[AL2true]=1/2n.\begin{aligned} \PR{ \A \link \lib{1} \outputs \mytrue } &= 1, \\ \PR{ \A \link \lib{2} \outputs \mytrue } &= 1/2^n.\end{aligned}

The probabilities are different, so the two libraries are not interchangeable.

Example 2.3.2 (Attacking OTP with a reused key, #2)

Another observation we might make is that a plaintext M\ptxt is encrypted to the same ciphertext C\ctxt every time. If an adversary calls otp.enc(M)\otpenc(\ptxt) multiple times with the same M\ptxt, then in L1\lib{1} the responses will be the same, but in L2\lib{2} the responses will be sampled independently.

We can formalize this idea by writing the following adversary program:

A\A
M:=\ptxt := {}arbitrary element of {0,1}n\bits^n
C1:=otp.enc(M)\ctxt_1 := \otpenc(\ptxt)
C2:=otp.enc(M)\ctxt_2 := \otpenc(\ptxt)
return C1==C2\ctxt_1 == \ctxt_2

Using almost identical reasoning as in the previous example, we can calculate:

Pr[AL1true]=1,Pr[AL2true]=1/2n.\begin{aligned} \PR{ \A \link \lib{1} \outputs \mytrue } &= 1, \\ \PR{ \A \link \lib{2} \outputs \mytrue } &= 1/2^n.\end{aligned}

We reach the same conclusion: The two libraries are not interchangeable.

This attack generalizes the first example, which can be obtained by fixing M=0n\ptxt = \bit0^n. In a sense, we have shown that there is nothing particularly special about the all-zeros plaintext.

Example 2.3.3 (Attacking OTP with a reused key, #3)

One useful strategy is to start with a “skeleton” adversary, with many details missing:

A\A
C1:=otp.enc(M1=??)\ctxt_1 := \otpenc(\ptxt_1 ={} ??)
C2:=otp.enc(M2=??)\ctxt_2 := \otpenc(\ptxt_2 ={} ??)
\cdots
return ????

We haven't decided yet which Mi\ptxt_i values to use as inputs to the otp.enc\otpenc subroutine, or how many calls to make. But we know that in L1\lib{1} the ii-th response will be Ci=KMi\ctxt_i = \key \oplus \ptxt_i. In particular, each expression Ci=KMi\ctxt_i = \key \oplus \ptxt_i includes the same term K\key. Using the algebraic properties of xor, we can therefore observe:

CiCj=(KMi)(KMj)=KKMiMj=0nMiMj=MiMj.\begin{aligned} \ctxt_i \oplus \ctxt_j &= (\key \oplus \ptxt_i) \oplus (\key \oplus \ptxt_j) \\ &= \key \oplus \key \oplus \ptxt_i \oplus \ptxt_j \\ &= \bit0^n \oplus \ptxt_i \oplus \ptxt_j \\ &= \ptxt_i \oplus \ptxt_j .\end{aligned}

The adversary knows every term appearing in the condition CiCj=MiMj\ctxt_i \oplus \ctxt_j = \ptxt_i \oplus \ptxt_j, and can therefore check whether it holds. It indeed always holds in L1\lib{1}, and we might also guess that it doesn't always hold in L2\lib{2}. The specific choice of Mi,Mj\ptxt_i, \ptxt_j doesn't seem to be important, so we can write the details of our adversary as follows:

A\A
M1,M2:=\ptxt_1, \ptxt_2 := {}arbitrary elements of {0,1}n\bits^n
C1:=otp.enc(M1)\ctxt_1 := \otpenc(\ptxt_1)
C2:=otp.enc(M2)\ctxt_2 := \otpenc(\ptxt_2)
return C1C2==M1M2\ctxt_1 \oplus \ctxt_2 == \ptxt_1 \oplus \ptxt_2

As in the previous examples, it is possible to show that:

Pr[AL1true]=1,Pr[AL2true]=1/2n.\begin{aligned} \PR{ \A \link \lib{1} \outputs \mytrue } &= 1, \\ \PR{ \A \link \lib{2} \outputs \mytrue } &= 1/2^n.\end{aligned}

We reach the same conclusion: The two libraries are not interchangeable.

Once again this attack generalizes the previous one, which can be obtained by fixing M1=M2\ptxt_1 = \ptxt_2. When that is the case, M1M2=0n\ptxt_1 \oplus \ptxt_2 = \bit0^n and the two conditions C1==C2\ctxt_1 == \ctxt_2 (used in the previous attack) and C1C2==M1M2=0n\ctxt_1 \oplus \ctxt_2 == \ptxt_1 \oplus \ptxt_2 = \bit0^n (used in this attack) are logically equivalent. Thus, there is nothing particularly special about calling otp.enc\otpenc with a repeated argument.

This final attack example also demonstrates a useful strategy that appears throughout the book:

The xor Cancellation Strategy

Express the values that the adversary would see, algebraically as xor expressions. If two expressions have a common term, then xor'ing the values will cause the common term to cancel out, and the result will often be useful in an attack.

2.4. How to prove that two libraries are interchangeable

In cryptography, as in life, you should always break down a complicated problem into small, manageable pieces. In a security proof, we must show that a certain pair of libraries are interchangeable. How can we break such a proof into smaller pieces? Since the \equiv operator is transitive, we can use the following idea:

Hybrid Proof Technique

The hybrid proof technique is a way to prove LstartLend\lib{start} \equiv \lib{end}, by demonstrating:

LstartL1L2LnLend. \lib{start} \equiv \L_{1} \equiv \L_{2} \equiv \cdots \equiv \L_{n} \equiv \lib{end}.

The intermediate libraries L1,L2,\L_1, \L_2, \ldots (called hybrid libraries) are chosen so that each individual step LiLi+1\L_{i} \equiv \L_{i+1} is easy to justify.

The vast majority of security proofs in the book use this simple but powerful technique. Each individual step LiLi+1\L_{i} \equiv \L_{i+1} in a hybrid proof can be extremely simple. Every situation listed in example 2.2.3 and example 2.2.4 is used in some hybrid proof in this book.

Another common step in a hybrid proof is based on the following observation:

Claim 2.4.1 (Chain rule)

If L1L2\lib1 \equiv \lib2, then for all L\L^*, we have LL1LL2\L^* \link \lib1 \equiv \L^* \link \lib2. Here we are interpreting LLi\L^* \link \lib{i} as a compound library.

Proof:

For every calling program A\A, we have:

Pr[A(LL1)true]=(1)Pr[(AL)L1true]=(2)Pr[(AL)L2true]=(3)Pr[A(LL2)true].\begin{aligned} \PR{ \A \link (\L^* \link \lib1) \outputs \mytrue } &\overset{(1)}= \PR{ (\A \link \L^*) \link \lib1 \outputs \mytrue }\\ &\overset{(2)}= \PR{ (\A \link \L^*) \link \lib2 \outputs \mytrue }\\ &\overset{(3)}= \PR{ \A \link (\L^* \link \lib2) \outputs \mytrue }.\end{aligned}

Steps (1) and (3) follow from the fact that the \link operator is associative. Step (2) follows from the fact that L1L2\lib1 \equiv \lib2: These two libraries have the same effect on all calling programs, and (AL)(\A \link \L^*) is one such calling program.

An example hybrid proof: Let's look at the following example that nicely showcases several common kinds of steps that can be used in a hybrid proof, including claim 2.4.1. Incidentally, the following claim is one of the most useful tools in future security proofs throughout the book.

Claim 2.4.2 (A convenient property of xor)

The following two libraries are interchangeable:

Lxor-samp-1\lib{xor-samp-1}
sample(M)\subname{sample}(M):
X{0,1}nX \gets \bits^n
Y:=XMY := X \oplus M
return (X,Y)(X,Y)
\equiv
Lxor-samp-2\lib{xor-samp-2}
sample(M)\subname{sample}(M):
Y{0,1}nY \gets \bits^n
X:=YMX := Y \oplus M
return (X,Y)(X,Y)

A plain-English translation of this claim would be:

If you want two values whose xor is MM, you can either sample the first uniformly and solve for the second, or you can sample the second uniformly and solve for the first. Both methods induce the same distribution.

Proof:

The sequence of hybrid libraries is presented below; it contains five intermediate hybrids besides the starting and ending libraries. Later in the book, we will discuss more intuition and strategies for generating your own hybrid proofs. For now your goal should be to simply understand why each pair of consecutive hybrid libraries are interchangeable—that is, why each change has no effect on any calling program.

Hybrid Sequence:
The starting point of the hybrid proof is library Lxor-samp-1\lib{xor-samp-1}.
We first introduce a new variable XX', which is not used anywhere. This change has no effect on the calling program.
You can check for yourself (using the algebraic properties of xor) that XX and XX' always hold the same value. It therefore has no effect on the calling program to change a reference to XX into a reference to XX'.
We want to argue that YY is uniformly distributed, and we can do so by observing that YY looks like an OTP ciphertext with XX as the key and MM as the plaintext. To use this fact, we first “factor out” the computation of YY in terms of the Lotp-real\lib{otp-real} library from claim 2.2.2. Factoring out these statements leaves us with a compound library, but has no effect on the calling program.
Now we are in a situation where we can use claim 2.2.2 and the chain rule (claim 2.4.1). Replacing Lotp-real\lib{otp-real} with Lotp-rand\lib{otp-rand} in this compound library (while keeping the other part Lhyb-3-4\lib{hyb-3-4} the same) has no effect on any calling program.
It has no effect on the calling program to inline a subroutine call.
Finally, we globally rename variable XX' to XX. This change clearly has no effect on the calling program. The result is the library Lxor-samp-2\lib{xor-samp-2}.
Lxor-samp-1\lib{xor-samp-1}
sample(MM):
X{0,1}nX \gets \bits^n
YY
:={}:= {}
XMX \oplus M
otp.enc(M)\otpenc(M)
{0,1}n{}\gets \bits^n
return
(X,Y)(X,Y)
(X,Y)(X',Y)
(X,Y)(X,Y)
\link
Lotp-real\lib{otp-real}
otp.enc\otpenc(M\ptxt):
K{0,1}n\key \gets \bits^n
C:=KM\ctxt := \key \oplus \ptxt
return C\ctxt
Lotp-rand\lib{otp-rand}
otp.enc\otpenc(M\ptxt):
C{0,1}n\ctxt \gets \bits^n
return C\ctxt

Through this sequence of small changes, we proved that the two libraries Lxor-samp-1\lib{xor-samp-1} and Lxor-samp-2\lib{xor-samp-2} are interchangeable.

There is one step the proof that may seem mysterious: We introduced a new variable XX' that always holds the same value as XX. To see why the new variable was necessary, let's focus on the following step in the proof:

Lhyb-2\lib{hyb-2}
sample(M)\subname{sample}(M):
X{0,1}nX \gets \bits^n
Y:=XMY := X \oplus M
X:=YMX' := Y \oplus M
return (X,Y)(X',Y)
\equiv
Lhyb-3-4\lib{hyb-3-4}
sample(M)\subname{sample}(M):
Y:=otp.enc(M)Y := \otpenc(M)
X:=YMX' := Y \oplus M
return (X,Y)(X',Y)
\link
Lotp-real\lib{otp-real}
otp.enc(M)\otpenc(\ptxt):
K{0,1}n\key \gets \bits^n
C:=KM\ctxt := \key \oplus \ptxt
return C\ctxt

This step works because we have essentially renamed the variable XX in Lhyb-2\lib{hyb-2} to the variable K\key in Lotp-real\lib{otp-real}. If we had not introduced the new variable XX', this step would have gone like this:

sample(M)\subname{sample}(M):
X{0,1}nX \gets \bits^n
Y:=XMY := X \oplus M
// X:=YMX' := Y \oplus M
return (X,Y)(\hl{X},Y)
≢!\overset{!}{\not\equiv}
sample(M)\subname{sample}(M):
Y:=otp.enc(M)Y := \otpenc(M)
return (X??,Y)(\hl{X^{??}},Y)
\link
Lotp-real\lib{otp-real}
otp.enc(M)\otpenc(\ptxt):
K{0,1}n\key \gets \bits^n
C:=KM\ctxt := \key \oplus \ptxt
return C\ctxt

If the goal of this step is to rename XX to K\key, then how do we handle the appearance of XX in the expression “return (X,Y)(\hl{X},Y)”? It's not possible to change this expression into “return (K,Y)(\hl{\key},Y)” because K\key is a private variable, scoped only to Lotp-real\lib{otp-real}. We can, however, recompute K\key from outside Lotp-real\lib{otp-real}, and that's exactly what the line “X:=YMX' := Y \oplus \ptxt” does.

2.4.1. The three-hop maneuver

Let's focus more closely on how we moved between the following two hybrids in the proof of claim 2.4.2:

sample(M)\subname{sample}(M):
X{0,1}n\hl{X \gets \bits^n}
Y:=XMY \hl{:= X \oplus M}
X:=YMX' := Y \oplus M
return (X,Y)(X',Y)
\equiv \cdots \equiv
sample(M)\subname{sample}(M):
Y{0,1}nY \hl{\gets\bits^n}
X:=YMX' := Y \oplus M
return (X,Y)(X',Y)

There were three steps:

  1. First, we factored out the highlighted lines from the library on the left so that a separate instance of the Lotp-real\lib{otp-real} library appeared.

  2. Then, we replaced Lotp-real\lib{otp-real} with Lotp-rand\lib{otp-rand}, while keeping the “main” library intact, taking advantage of claim 2.4.1.

  3. Finally, we inlined Lotp-rand\lib{otp-rand} into the main library.

The net effect of these three steps was to replace the two lines “X{0,1}nX \gets \bits^n; Y:=XMY := X \oplus \ptxt” with “Y{0,1}nY \gets \bits^n.

This sequence of steps—factor out, swap sub-libraries, inline—are standard and appear in every security proof in this book, often several times. This idiom deserves a special name:

The Three-Hop Maneuver

The three-hop maneuver consists of the following steps:

L1    L2LA    L2LB    L3. \L_{1} ~~\equiv~~ \L_2 \link \lib{A} ~~\equiv~~ \L_2 \link \lib{B} ~~\equiv~~ \L_3.
  1. L1L2LA\L_1 \equiv \L_2 \link \lib{A}: Factor out some lines of L1\L_1 in terms of explicit subroutine calls to LA\lib{A}. Other lines of code stay behind, as L2\L_2.

  2. L2LAL2LB\L_2 \link \lib{A} \equiv \L_2 \link \lib{B}: Replace LA\lib{A} with LB\lib{B}, while keeping L2\L_2 unchanged.

  3. L2LBL3\L_2 \link \lib{B} \equiv \L_3: Inline LB\lib{B}, resulting in a monolithic library L3\L_3.

2.5. Abstract cryptographic primitives

Specific algorithms like OTP are important in cryptography, but OTP is just one instance of an encryption scheme, and it's also important that we can discuss encryption schemes and their security properties in the abstract. In cryptography, useful abstractions like “encryption scheme” are called primitives. Three things are important when defining a cryptographic primitive:

  • The syntax of a primitive specifies its basic raw interface. What are its algorithms? How many inputs and outputs are there, and what are their types?

  • Correctness properties are the basic functionality we expect from the primitive, that do not involve any adversary—things like “decryption should be the inverse of encryption.”

  • Security properties are guarantees that hold in a specific attack scenario, in the presence of an adversary.

Symmetric-key encryption is a primitive whose syntax is defined as follows:

Definition 2.5.1 (Syntax of symmetric-key encryption)

A symmetric-key encryption (SKE) scheme consists of the following algorithms:

  • Enc\Enc: a (possibly randomized) algorithm that takes a key KK\key \in \K and plaintext MM\ptxt \in \M as input, and outputs a ciphertext CC\ctxt \in \C.

  • Dec\Dec: a deterministic algorithm that takes a key KK\key \in \K and ciphertext CC\ctxt \in \C as input, and outputs a plaintext MM\ptxt \in \M.

The set K\K of possible keys is called the scheme's key space, and similarly M\M and C\C are called the plaintext space and ciphertext space, respectively. We sometimes use Σ\Sigma to refer to the encryption scheme as a whole, with Σ.Enc\Sigma.\Enc, Σ.Dec\Sigma.\Dec, Σ.K\Sigma.\K, etc., denoting its constituent algorithms and sets.

An encryption scheme should satisfy a correctness property:

Definition 2.5.2 (Correctness for SKE)

An SKE Σ\Sigma is correct if encryption and decryption are inverses, in the following sense:

Pr[Σ.Dec(K,Σ.Enc(K,M))=M]=1, \PR{ \Sigma.\Dec\bigl(\key,\Sigma.\Enc(\key,\ptxt)\bigr) = \ptxt } = 1,

for all MΣ.M\ptxt \in \Sigma.\M and KΣ.K\key \in \Sigma.\K. The definition involves a probability because Σ.Enc\Sigma.\Enc may be a randomized algorithm.

We will see several security goals for SKE, but the simplest is the one we have been considering for OTP:

Definition 2.5.3 (One-time secrecy)

An SKE scheme Σ\Sigma has one-time secrecy if the following two libraries are interchangeable:

Lots-realΣ\lib{ots-real}^\Sigma
ots.enc(M)\otsenc(\ptxt):
KΣ.K\key \gets \Sigma.\K
C:=Σ.Enc(K,M)\ctxt := \Sigma.\Enc(\key,\ptxt)
return C\ctxt
\equiv
Lots-randΣ\lib{ots-rand}^\Sigma
ots.enc(M)\otsenc(\ptxt):
CΣ.C\ctxt \gets \Sigma.\C
return C\ctxt

We might translate this formal definition into plain language as:

An encryption scheme has one-time secrecy if its ciphertexts are uniformly distributed, when keys are sampled uniformly, kept secret, and used for only one encryption, and no matter how the plaintexts are chosen.

A security definition is a template meant to be filled in. If we have a particular encryption scheme Σ\Sigma in mind and want to prove (or disprove) its security, we populate the template with the scheme's key space K\K, encryption algorithm Enc\Enc, and ciphertext space C\C, then compare the resulting two libraries.

Example 2.5.4 (One-time secrecy of OTP)

One-time pad (OTP) is defined as follows:

K={0,1}nM={0,1}nC={0,1}n\begin{aligned}\K &= \bits^n \\ \M &= \bits^n \\ \C &= \bits^n\end{aligned}
\qquad
Enc(K,M)\Enc(\key,\ptxt):
C:=KM\ctxt := \key \oplus \ptxt
return C\ctxt
\qquad
Dec(K,C)\Dec(\key,\ctxt):
M:=KC\ptxt := \key \oplus \ctxt
return M\ptxt

If we populate the template from definition 2.5.3 with these specifics of OTP, we obtain the following two libraries:

Lots-real\lib{ots-real}
ots.enc(M)\otsenc(\ptxt):
K{0,1}n\key \gets \hl{\bits^n} // K\K
C:=KM\ctxt := \hl{\key \oplus \ptxt} // Enc(K,M)\Enc(\key,\ptxt)
return C\ctxt
Lots-rand\lib{ots-rand}
ots.enc(M)\otsenc(\ptxt):
C{0,1}n\ctxt \gets \hl{\bits^n} // C\C
return C\ctxt

As we have already discussed, these two libraries are interchangeable, so we conclude that OTP has one-time secrecy.

Example 2.5.5 (Insecure OTP variant)

Let's revisit the OTP variant from example 1.7.1 that uses and instead of xor.

K={0,1}nM={0,1}nC={0,1}n\begin{aligned}\K &= \bits^n \\ \M &= \bits^n \\ \C &= \bits^n\end{aligned}
\qquad
Enc(K,M)\Enc(\key,\ptxt):
C:=K&M\ctxt := \key \mathbin{\&} \ptxt
return C\ctxt

You may have already noticed that the scheme does not satisfy the correctness property! To see why, consider that the extreme case of the key K=0n\key = \bit0^n encrypts every plaintext to the ciphertext 0n\bit0^n.

Regardless of whether the scheme satisfies correctness, we can ask whether it has one-time secrecy. We populate the template from definition 2.5.3 with this scheme's parameters and ask whether the resulting libraries are interchangeable:

Lots-real\lib{ots-real}
ots.enc(M)\otsenc(\ptxt):
K{0,1}n\key \gets \hl{\bits^n} // K\K
C:=K&M\ctxt := \hl{\key \mathbin{\&} \ptxt} // Enc(K,M)\Enc(\key,\ptxt)
return C\ctxt
\quad
Lots-rand\lib{ots-rand}
ots.enc(M)\otsenc(\ptxt):
C{0,1}n\ctxt \gets \hl{\bits^n} // C\C
return C\ctxt

In this case, the libraries are not interchangeable, because the following adversary can distinguish them:

A\A
M:=0n\ptxt := \bit{0}^n
C:=ots.enc(M)\ctxt := \otsenc(\ptxt)
return C==0n\ctxt == \bit 0^n
  • When A\A is linked to Lots-real\lib{ots-real}, the ciphertext C\ctxt is always computed as K&M=K&0n=0n\key \mathbin{\&} \ptxt = \key \mathbin{\&} \bit 0^n = \bit 0^n. Therefore, A\A always outputs true: Pr[ALots-realtrue]=1\PR{ \A \link \lib{ots-real} \outputs \mytrue } = 1.

  • When A\A is linked to Lots-rand\lib{ots-rand}, the value C\ctxt is chosen uniformly. The probability that C=0n\ctxt = \bit 0^n is 1/2n1/2^n. Hence, Pr[ALots-randtrue]=1/2n\PR{ \A \link \lib{ots-rand} \outputs \mytrue } = 1/2^n.

Since these two probabilities are different (for any nn), the libraries are not interchangeable, and the encryption scheme does not have one-time secrecy.

There are many other strategies that can successfully distinguish these two libraries. Perhaps you can think of a few and write them formally as calling programs.

2.6. Modular cryptographic constructions

Cryptographic primitives are like building blocks, which we can use to build more complicated things in a modular way. We can use the security properties of the underlying primitives to prove security of the larger construction.

Let's see an example of such a modular construction. It involves a rather indirect approach to encryption. Suppose Alice wants to privately send some message M\ptxt to Bob, and they already share a secret key K\key. Alice can do the following:

  1. Sample a new key K\key', uniformly.

  2. Use K\key to encrypt K\key', resulting in ciphertext C1\ctxt_1.

  3. Use K\key' (not K\key!) to encrypt M\ptxt, resulting in C2\ctxt_2.

  4. Send both C1\ctxt_1 and C2\ctxt_2 to Bob.

Bob can decrypt in two steps:

  1. Use K\key to decrypt C1\ctxt_1, and learn K\key'.

  2. Use K\key' to decrypt C2\ctxt_2, and learn M\ptxt.

What I have just described is a recipe to construct a new (complicated) encryption scheme out of an existing (simpler) one. The recipe is modular; it doesn't specify exactly how Alice “uses K\key' to encrypt M\ptxt” or how Bob “uses K\key to decrypt C1\ctxt_1.” We can imagine plugging in any encryption scheme into this recipe, and maybe even different schemes for the two different encryption/decryption steps in the recipe! The recipe can be formalized as follows:

Construction 2.6.1 (A modular way to combine two encryption schemes)

Let Σ1\Sigma_1 and Σ2\Sigma_2 be encryption schemes for which Σ2.KΣ1.M\Sigma_2.\K \subseteq \Sigma_1.\M (that is, Σ1\Sigma_1 can encrypt keys for Σ2\Sigma_2). Define a new scheme Σ\Sigma^* as follows:

Σ.K=Σ1.KΣ.M=Σ2.MΣ.C=Σ1.C×Σ2.C\begin{aligned} \Sigma^*.\K &= \Sigma_1.\K \\ \Sigma^*.\M &= \Sigma_2.\M \\ \Sigma^*.\C &= \Sigma_1.\C \times \Sigma_2.\C \end{aligned}
\quad
Σ.Enc(K,M)\Sigma^*.\Enc( \key, \ptxt):
KΣ2.K\key' \gets \Sigma_2.\K
C1:=Σ1.Enc(K,K)\ctxt_1 := \Sigma_1.\Enc(\key, \key')
C2:=Σ2.Enc(K,M)\ctxt_2 := \Sigma_2.\Enc(\key', \ptxt)
return (C1,C2)(\ctxt_1, \ctxt_2)
\quad
Σ.Dec(K,(C1,C2))\Sigma^*.\Dec\bigl( \key, (\ctxt_1,\ctxt_2) \bigr):
K:=Σ1.Dec(K,C1)\key' := \Sigma_1.\Dec(\key, \ctxt_1)
M:=Σ2.Dec(K,C2)\ptxt := \Sigma_2.\Dec(\key', \ctxt_2)
return M\ptxt

Construction 2.6.1 is heavy on notation, exactly because it is so modular and refers to arbitrary/unspecified encryption schemes Σ1\Sigma_1 and Σ2\Sigma_2. Be sure you understand the connection between the notation and the simple informal recipe described above. The new construction Σ\Sigma^* supports keys that are the same as Σ1\Sigma_1's keys, and plaintexts that are the same as Σ2\Sigma_2's plaintexts. The ciphertexts in Σ\Sigma^* consist of a pair of ciphertexts, one from each of Σ1\Sigma_1 and Σ2\Sigma_2.

Construction 2.6.1 is also our first example of an encryption scheme whose Enc\Enc algorithm is randomized. The Enc\Enc algorithm samples a fresh K\key' each time it is called. Thus, encrypting the same plaintext, even under the same key (which is not advised for the encryption schemes we've seen so far) can produce different ciphertexts. Randomized encryption schemes will become important later in the book.

Staying in the realm of abstract, unspecified encryption schemes, we can ask whether the recipe always results in a secure encryption scheme.

Claim 2.6.2 (One-time secrecy of the construction)

If Σ1\Sigma_1 and Σ2\Sigma_2 both have one-time secrecy, then construction 2.6.1 also has one-time secrecy. In other words,

if: \quad
Lots-realΣ1\lib{ots-real}^{\Sigma_1}
ots.enc1(M)\otsenc_1(\ptxt):
KΣ1.K\key \gets \Sigma_1.\K
C:=Σ1.Enc(K,M)\ctxt := \Sigma_1.\Enc(\key,\ptxt)
return C\ctxt
\equiv
Lots-randΣ1\lib{ots-rand}^{\Sigma_1}
ots.enc1(M)\otsenc_1(\ptxt):
CΣ1.C\ctxt \gets \Sigma_1.\C
return C\ctxt
\quad (Σ1\Sigma_1 secure)
and: \quad
Lots-realΣ2\lib{ots-real}^{\Sigma_2}
ots.enc2(M)\otsenc_2(\ptxt):
KΣ2.K\key \gets \Sigma_2.\K
C:=Σ2.Enc(K,M)\ctxt := \Sigma_2.\Enc(\key,\ptxt)
return C\ctxt
\equiv
Lots-randΣ2\lib{ots-rand}^{\Sigma_2}
ots.enc2(M)\otsenc_2(\ptxt):
CΣ2.C\ctxt \gets \Sigma_2.\C
return C\ctxt
\quad (Σ2\Sigma_2 secure)
then: \quad
Lots-realΣ\lib{ots-real}^{\Sigma^*}
ots.enc(M)\otsenc^*(\ptxt):
KΣ1.K\key \gets \Sigma_1.\K // =Σ.K{} = \Sigma^*.\K
// (C1,C2):=Σ.Enc(K,M)(\ctxt_1,\ctxt_2) := \Sigma^*.\Enc(\key,\ptxt):
KΣ2.K\key' \gets \Sigma_2.\K
C1:=Σ1.Enc(K,K)\ctxt_1 := \Sigma_1.\Enc(\key, \key')
C2:=Σ2.Enc(K,M)\ctxt_2 := \Sigma_2.\Enc(\key', \ptxt)
return (C1,C2)(\ctxt_1, \ctxt_2)
\equiv
Lots-randΣ\lib{ots-rand}^{\Sigma^*}
ots.enc(M)\otsenc^*(\ptxt):
// CΣ.C\ctxt \gets \Sigma^*.\C:
C1Σ1.C\ctxt_1 \gets \Sigma_1.\C
C2Σ2.C\ctxt_2 \gets \Sigma_2.\C
return (C1,C2)(\ctxt_1,\ctxt_2)
\quad (Σ\Sigma^* secure)

The claim is fundamentally an if-then statement involving three separate instances of one-time secrecy. It therefore involves three different pairs of Lots-*\lib{ots-*} libraries. Since this is a source of potential confusion, I have changed subroutine names to ots.enc1,ots.enc2,ots.enc\otsenc_1, \otsenc_2, \otsenc^* to help keep track of which scheme's security we are talking about at any given point. Most future constructions and proofs won't be quite so confusing, since they involve security properties of different primitives, involving different-looking libraries.

Because the claim is so abstract/modular, it is less about any particular encryption scheme, and more about the implications of the one-time secrecy definition itself. “One-time secrecy is preserved under this way of combining two encryption schemes.”

Proof:

We use the hybrid technique to prove that Lots-realΣLots-randΣ\lib{ots-real}^{\Sigma^*} \equiv \lib{ots-rand}^{\Sigma^*}, while assuming the hypotheses Lots-realΣ1Lots-randΣ1\lib{ots-real}^{\Sigma_1} \equiv \lib{ots-rand}^{\Sigma_1} and Lots-realΣ2Lots-randΣ2\lib{ots-real}^{\Sigma_2} \equiv \lib{ots-rand}^{\Sigma_2}.

Hybrid Sequence:
The starting point is Lots-realΣ\lib{ots-real}^{\Sigma^*}, and our goal is to make a sequence of interchangeable modifications until we eventually arrive at Lots-randΣ\lib{ots-rand}^{\Sigma^*}.
We can first apply the security of Σ1\Sigma_1 with a three-hop maneuver. The three hops are: (1) factor out the operations involving Σ1\Sigma_1, so that an instance of its library Lots-realΣ1\lib{ots-real}^{\Sigma_1} appears; (2) replace Lots-realΣ1\lib{ots-real}^{\Sigma_1} with Lots-randΣ1\lib{ots-rand}^{\Sigma_1}; (3) inline Lots-randΣ1\lib{ots-rand}^{\Sigma_1}. These changes have no effect on the calling program.
We can now apply the security property of Σ2\Sigma_2 in a similar three-step maneuver. The final result is Lots-randΣ\lib{ots-rand}^{\Sigma^*}, which completes the proof.
Lots-realΣ\lib{ots-real}^{\Sigma^*}
ots.enc\otsenc^*(M\ptxt):
KΣ1.K\key \gets \Sigma_1.\K // =Σ.K{} = \Sigma^*.\K
// (C1,C2):=Σ.Enc(K,M)(\ctxt_1,\ctxt_2) := \Sigma^*.\Enc(\key,\ptxt):
KΣ2.K\key' \gets \Sigma_2.\K
C1\ctxt_1
:={}:= {}
Σ1.Enc(K,K)\Sigma_1.\Enc(\key, \key')
ots.enc1(K)\otsenc_1(\key')
Σ1.C{}\gets \Sigma_1.\C
C2\ctxt_2
:={}:= {}
Σ2.Enc(K,M)\Sigma_2.\Enc(\key', \ptxt)
ots.enc2(M)\otsenc_2(\ptxt)
Σ2.C{}\gets \Sigma_2.\C
return (C1,C2)(\ctxt_1, \ctxt_2)
\link
Lots-realΣ1\lib{ots-real}^{\Sigma_1}
ots.enc1\otsenc_1(M\ptxt):
KΣ1.K\key \gets \Sigma_1.\K
C:=Σ1.Enc(K,M)\ctxt := \Sigma_1.\Enc(\key, \ptxt)
return C\ctxt
Lots-randΣ1\lib{ots-rand}^{\Sigma_1}
ots.enc1\otsenc_1(M\ptxt):
CΣ1.C\ctxt \gets \Sigma_1.\C
return C\ctxt
\link
Lots-realΣ2\lib{ots-real}^{\Sigma_2}
ots.enc2\otsenc_2(M\ptxt):
KΣ2.K\key \gets \Sigma_2.\K
C:=Σ2.Enc(K,M)\ctxt := \Sigma_2.\Enc(\key, \ptxt)
return C\ctxt
Lots-randΣ2\lib{ots-rand}^{\Sigma_2}
ots.enc2\otsenc_2(M\ptxt):
CΣ2.C\ctxt \gets \Sigma_2.\C
return C\ctxt

2.7. ☆ “Real-or-random” vs. “left-or-right”

Security definitions don't represent some timeless, universal truth, handed down to us from the Cryptography Gods. Rather, we (mere Cryptography Humans) do our best to model a reasonable attack scenario and say something precise about what can happen in that scenario. There could be more than one way to do this.

In this book, the default way to define security is the real-or-random paradigm: Something is “secure” if its outputs look like the uniform distribution. But that's not the only sensible way to define security, at least for encryption. In the left-or-right paradigm, we say that an encryption scheme is “secure” if encryptions of one plaintext look like encryptions of any other plaintext—but not necessarily like the uniform distribution. In the left-or-right paradigm, we formalize an attack scenario in which the adversary chooses two plaintexts but only one of them gets encrypted. The adversary should not be able to determine which of the two plaintexts is encrypted.

Definition 2.7.1 (Left-or-right formulation of one-time secrecy)

An SKE scheme Σ\Sigma satisfies left-or-right one-time secrecy (OTS) if the following two libraries are interchangeable:

Lots-leftΣ\lib{ots-left}^\Sigma
ots.enc(ML,MR)\otsenc(\ptxt_L, \ptxt_R):
KΣ.K\key \gets \Sigma.\K
C:=Σ.Enc(K,ML)\ctxt := \Sigma.\Enc(\key,\hl{\ptxt_L})
return C\ctxt
\equiv
Lots-rightΣ\lib{ots-right}^\Sigma
ots.enc(ML,MR)\otsenc(\ptxt_L, \ptxt_R):
KΣ.K\key \gets \Sigma.\K
C:=Σ.Enc(K,MR)\ctxt := \Sigma.\Enc(\key,\hl{\ptxt_R})
return C\ctxt

Lots-left\lib{ots-left} always encrypts the left plaintext, while Lots-right\lib{ots-right} always encrypts the right one.

We now have two definitions of security for encryption, and, perhaps unfortunately, they are not equivalent. One is a strictly stronger security requirement than the other. Any scheme that satisfies the real-or-random flavor of security also satisfies the left-or-right flavor (see exercise 2.24), but the converse is not true.

Σ satisfies real-or-random securityΣ satisfies left-or-right security.⇍notnecessarily \begin{array}{rcl} & \Rightarrow & \\ \text{$\Sigma$ satisfies real-or-random security} & & \text{$\Sigma$ satisfies left-or-right security.} \\ & \underset{\substack{\text{not}\\\text{necessarily}}}{\not\Leftarrow} \end{array}

We must acknowledge the existence of an uncanny gap between the two definitions, containing schemes that are secure in the left-or-right sense but not the real-or-random sense. However, this gap is relatively insignificant in practice. The schemes that inhabit it are highly unnatural; they look like they were designed just to prove a philosophical point about security definitions—because almost all of them were! I know of only one exception to this rule, a truly natural encryption scheme that we will see later in section 14.5, which is secure in the left-or-right sense but not the real-or-random sense. Every other encryption scheme we actually care about satisfies both definitions.

So which is the “correct” way to define security of encryption? Neither paradigm is objectively “correct”; they simply demand different things from an encryption scheme. What's most important is to recognize that there is a choice to be made at all! Here are a few more philosophical and cultural differences between the real-or-random and left-or-right paradigms:

  • Left-or-right is the more “traditional” way to define security for encryption. You'll see it appear as the standard approach in most other references and in the research literature.

  • On the other hand, the real-or-random paradigm is the standard (and often, only sensible) way to define security for many other, non-encryption primitives.

  • I find that security definitions in the real-or-random paradigm are more intuitive and easier to use. The adversary provides just one plaintext at a time, not two.

  • Security proofs in the real-or-random paradigm are often half the length of comparable proofs in the left-or-right paradigm. To prove security in the left-or-right paradigm, you need a sequence of hybrids in which the only net change is to replace ML\ptxt_L with MR\ptxt_R. Usually there is a hybrid precisely in the middle of the proof in which ciphertexts are generated uniformly, and the hybrids on either side of this midpoint are like mirror images, with their only difference being ML\ptxt_L vs. MR\ptxt_R. A security proof of real-or-random security is already complete once it reaches that middle point.

  • Left-or-right security is a more minimal definition and fits better with many natural intuitions you probably have about security. Does a secure encryption scheme become suddenly insecure if you append zeros to every ciphertext? What if you repeat every bit in every ciphertext? What if you simply change the definition of the ciphertext space C\C? It seems silly that such benign changes should have an impact on security.

    And indeed, if we define security in the left-or-right paradigm, then all of these changes preserve security (see exercises 2.25 and 2.26). But they break security in the real-or-random sense.

  • When a scheme is insecure according to the left-or-right paradigm, the resulting attack is often easier to appreciate as a security problem. “I can tell whether your ciphertext contains this or that” just feels like more of an attack than “Your ciphertexts don't look uniformly chosen.”

Exercises

  1. Show that the following two libraries are not interchangeable:

    L1\lib1
    samp()\subname{samp}(\,):
    xZ10x \gets \Z_{10}
    return xx
    L2\lib2
    samp()\subname{samp}(\,):
    xZ10x \gets \Z_{10}
    return 2x%102x \pct 10
  2. Show that the following two libraries are not interchangeable:

    L1\lib1
    R{0,1}nR \gets \bits^n
    foo(X)\subname{foo}(X):
    R:=RXR := R \oplus X
    return RR
    L2\lib2
    foo(X)\subname{foo}(X):
    Y{0,1}nY \gets \bits^n
    return YY

    Note that the variable RR in L1\lib{1} is static/persistent, and is changed in each call to foo.

  3. Give a convincing justification that Pr[AL2true]=1/2n\PR{ \A \link \lib{2} \outputs \mytrue } = 1/2^n in example 2.3.3.

  4. Let Σ\Sigma be the variant of OTP that uses boolean-and, and let Lots-realΣ\lib{ots-real}^\Sigma and Lots-randΣ\lib{ots-rand}^\Sigma be the libraries involved in one-time secrecy (shown in example 2.5.5). Let A\A be the following calling program:

    A\A
    M{0,1}n\ptxt \gets \bits^n
    C:=ots.enc(M)\ctxt := \otsenc(\ptxt)
    return C==0n\ctxt == \bit 0^n

    Calculate Pr[ALots-realtrue]\PR{ \A \link \lib{ots-real} \outputs \mytrue } and Pr[ALots-randtrue]\PR{ \A \link \lib{ots-rand} \outputs \mytrue }.

  5. Prove formally that the \equiv operator is transitive: If L1L2\lib{1} \equiv \lib{2} and L2L3\lib{2} \equiv \lib{3}, then L1L3\lib{1} \equiv \lib{3}.

  6. Prove that the following two libraries are interchangeable, for all n\nmod.

    Lmod-samp-1\lib{mod-samp-1}
    sample(M)\subname{sample}(M):
    XZnX \gets \Z_\nmod
    Y:=(MX)%nY := (M - X) \pct \nmod
    return (X,Y)(X,Y)
    \equiv
    Lmod-samp-2\lib{mod-samp-2}
    sample(M)\subname{sample}(M):
    YZnY \gets \Z_\nmod
    X:=(MY)%nX := (M - Y) \pct \nmod
    return (X,Y)(X,Y)

    This exercise generalizes claim 2.4.2: Each library generates random XX and YY whose sum mod n\nmod is MM.

  7. h Prove that the following two libraries are interchangeable:

    Lleft\lib{left}
    sample()\subname{sample}(\,):
    R{0,1}nR \gets \bits^n
    return R\overline{R}
    \equiv
    Lright\lib{right}
    sample()\subname{sample}(\,):
    R{0,1}nR \gets \bits^n
    return RR

    R\overline{R} denotes the bitwise complement of RR—that is, the result of flipping every bit in the string RR.

    Write R\overline{R} in terms of xor.

  8. Prove that the following two libraries are interchangeable:

    Lleft\lib{left}
    sample()\subname{sample}(\,):
    A{0,1}nA \gets \bits^n
    B{0,1}nB \gets \bits^n
    return A(AB)A \big\| (A\oplus B)
    \equiv
    Lright\lib{right}
    sample()\subname{sample}(\,):
    R{0,1}2nR \gets \bits^{2n}
    return RR
  9. Below are two pairs of libraries. One pair is interchangeable, one is not. Give a proof and a distinguishing attack:

    1. Lleft\lib{left}
      sample()\subname{sample}(\,):
      A{0,1}nA \gets \bits^n
      B{0,1}nB \gets \bits^n
      C{0,1}nC \gets \bits^n
      return (AB)(BC)(CA)(A \oplus B) \big\| (B\oplus C) \big\| \hl{(C \oplus A)}
      \quad
      Lright\lib{right}
      sample()\subname{sample}(\,):
      R{0,1}3nR \gets \bits^{3n}
      return RR
    2. Lleft\lib{left}
      sample()\subname{sample}(\,):
      A{0,1}nA \gets \bits^n
      B{0,1}nB \gets \bits^n
      C{0,1}nC \gets \bits^n
      return (AB)(BC)C(A \oplus B) \big\| (B\oplus C) \big\| \hl{C}
      \quad
      Lright\lib{right}
      sample()\subname{sample}(\,):
      R{0,1}3nR \gets \bits^{3n}
      return RR
  10. \star In abstract algebra, a (finite) group is a finite set G\G of items together with an operator \otimes that satisfies the following axioms:

    • Closure: For all a,bGa,b \in \G, we have abGa \otimes b \in \G.

    • Identity: There is a special identity element eGe \in \G that satisfies ea=ae=ae \otimes a = a \otimes e = a for all aGa \in \G.

    • Associativity: For all a,b,cGa,b,c \in \G, we have (ab)c=a(bc)(a \otimes b) \otimes c = a \otimes (b \otimes c).

    • Inverses: For all aGa \in \G, there exists an inverse element called a1G\hl{a^{-1}} \in \G such that aa1=a1a=ea \otimes a^{-1} = a^{-1} \otimes a = e.

    Define the following encryption scheme in terms of an arbitrary group (G,)(\G, \otimes):

    K=GM=GC=G\begin{aligned} \K &= \G \\ \M &= \G \\ \C &= \G \end{aligned}
    \quad
    Enc(K,M)\Enc(\key,\ptxt):
    return KM\key \otimes \ptxt
    \quad
    Dec(K,C)\Dec(\key,\ctxt):
    return ??{}
    1. Prove that {0,1}λ\bits^\secpar is a group with respect to the xor operator. What is the identity element, and what is the inverse of a value X{0,1}λX \in \bits^\secpar?

    2. Fill in the details of the Dec\Dec algorithm and prove (using the group axioms) that the scheme satisfies correctness.

    3. Prove that the scheme satisfies one-time secrecy.

  11. Write a different attack that distinguishes the libraries in example 2.5.5, and calculate the two output probabilities. Try to make your attack as different as possible from the one given in the example.

  12. Why do you think I did not show the decryption algorithm for the variant of OTP that uses boolean-and, in example 2.5.5?

  13. Prove that the following encryption scheme does not have one-time secrecy:

    K={1,,9}M={1,,9}C={0,,9}\begin{aligned} \K &= \{1,\ldots,9\} \\ \M &= \{1,\ldots,9\} \\ \C &= \{0,\ldots,9\} \end{aligned}
    \quad
    Enc(K,M)\Enc(\key,\ptxt):
    return (K×M)%10(\key \times \ptxt) \pct 10
  14. h In this exercise you will show that an encryption scheme cannot achieve one-time secrecy if it has fewer keys than plaintexts. Let Σ\Sigma be an encryption scheme with Σ.K<Σ.M|\Sigma.\K| < |\Sigma.\M|.

    1. Suppose we fix an arbitrary ciphertext CΣ.C\ctxt \in \Sigma.\C, and run the following code:

      S:=\mathcal{S} := \emptyset
      for each KΣ.K\key \in \Sigma.\K:
      add Σ.Dec(K,C)\Sigma.\Dec(\key,\ctxt) to the set S\mathcal{S}
      MΣ.M\ptxt \gets \Sigma.\M
      if MS\ptxt \in \mathcal{S}: return true\mytrue
      else: return false\myfalse

      Argue that this program outputs false\myfalse with some nonzero probability.

    2. Show that the following program A\A can successfully distinguish Lots-realΣ\lib{ots-real}^\Sigma and Lots-randΣ\lib{ots-rand}^\Sigma (definition 2.5.3):

      A\A
      MΣ.M\ptxt \gets \Sigma.\M
      C:=ots.enc(M)\ctxt := \otsenc(\ptxt)
      S:=\mathcal{S} := \emptyset
      for each KΣ.K\key \in \Sigma.\K:
      add Σ.Dec(K,C)\Sigma.\Dec(\key,\ctxt) to the set S\mathcal{S}
      if MS\ptxt \in \mathcal{S}: return true\mytrue
      else: return false\myfalse

      Calculate both relevant output probabilities.

    In part (a), how does the size of the set S\mathcal{S} compare to M|\M|? In part (b), show that ALots-randΣ\A \link \lib{ots-rand}^\Sigma and the code from part (a) are interchangeable.

  15. Let Σ\Sigma be an SKE scheme and define the following library:

    LguessΣ\lib{guess}^\Sigma
    b{0,1}b \gets \{0,1\}
    ots.enc(M)\otsenc(\ptxt):
    if b==0b == 0:
    KΣ.K\key \gets \Sigma.\K
    return Σ.Enc(K,M)\Sigma.\Enc(\key,\ptxt)
    else: // b==1b == 1
    CΣ.C\ctxt \gets \Sigma.\C
    return C\ctxt
    1. Suppose that Σ\Sigma has one-time secrecy. Prove that for all calling programs A\A, we have

      Pr[ALguessΣb]=1/2, \PR{ \A \link \lib{guess}^\Sigma \outputs b } = 1/2,

      where bb is the global variable chosen at the beginning of time in Lguess\lib{guess}.

    2. Prove the converse of part (a). Namely, if Σ\Sigma does not have one-time secrecy then there is a calling program A\A for which Pr[ALguessΣb]1/2.\PR{ \A \link \lib{guess}^\Sigma \outputs b } \ne 1/2.

  16. Let Πn\Pi_n denote the set of all permutations over {1,,n}\{1, \ldots, n\}, which we write as functions K:{1,,n}{1,,n}K : \{1,\ldots,n\} \to \{1,\ldots, n\}. Show that the following encryption scheme does not have one-time secrecy:

    K=ΠnM={0,1}nC={0,1}n\begin{aligned} \K &= \Pi_n \\ \M &= \bits^n \\ \C &= \bits^n \end{aligned}
    Enc(K,M)\Enc(\key,\ptxt):
    for i=1i=1 to nn:
    // iith bit of C\ctxt = K(i)\key(i)'th bit of M\ptxt
    C[i]:=M[K(i)]\ctxt[i] := \ptxt[ \key(i) ]
    return C\ctxt
    Dec(K,C)\Dec(\key,\ctxt):
    for i=1i=1 to nn:
    // K(i)\key(i)'th bit of M\ptxt = iith bit of C\ctxt
    M[K(i)]:=C[i]\ptxt[ \key(i) ] := \ctxt[i]
    return M\ptxt
  17. Show that construction 2.6.1 satisfies the correctness property for an SKE, if Σ1\Sigma_1 and Σ2\Sigma_2 both do.

  18. The proof of claim 2.6.2 first applies the security of Σ1\Sigma_1, then of Σ2\Sigma_2, each in a three-hop maneuver. Explain why the proof doesn't work if these three-hop maneuvers are done in the opposite order.

  19. Suppose we instantiate the scheme Σ\Sigma^* from claim 2.6.2 with Σ1=Σ2=\Sigma_1 = \Sigma_2 ={} OTP. What happens when a victim repeats a key (to Σ\Sigma^*)? Show an attack analogous to the ones in section 2.3.

  20. Construction 2.6.1 is a randomized encryption scheme. Suppose Σ2.K={0,1}n\Sigma_2.\K = \bits^n and we make construction 2.6.1 deterministic with the following change:

    Σ.Enc(K,M)\Sigma^*.\Enc( \key, \ptxt):
    K:=0n\key' \hl{:= \bit0^n}
    C1:=Σ1.Enc(K,K)\ctxt_1 := \Sigma_1.\Enc(\key, \key')
    C2:=Σ2.Enc(K,M)\ctxt_2 := \Sigma_2.\Enc(\key', \ptxt)
    return (C1,C2)(\ctxt_1, \ctxt_2)

    Does the resulting Σ\Sigma^* still have one-time secrecy after this change? Give either a security proof or an attack.

  21. Here is a modular construction that illustrates the intuitive idea that it is safe to encrypt the same plaintext twice, under two independent keys. In other words, perhaps for the sake of redundancy, if we always encrypt all plaintexts twice under independent keys, do we get a secure encryption scheme? (You may wonder why such an “obvious” statement requires proof, but later in the book we study security definitions for which it is not safe.)

    Let Σ\Sigma be an SKE scheme. Define the following new scheme Σ2\Sigma_2:

    Σ2.K=Σ.K×Σ.KΣ2.M=Σ.MΣ2.C=Σ.C×Σ.C\begin{aligned} \Sigma_2.\K & = \Sigma.\K \times \Sigma.\K \\ \Sigma_2.\M &= \Sigma.\M \\ \Sigma_2.\C &= \Sigma.\C \times \Sigma.\C \end{aligned}
    \quad
    Σ2.Enc((K1,K2),M)\Sigma_2.\Enc\bigl( (\key_1,\key_2), \ptxt \bigr):
    C1:=Σ.Enc(K1,M)\ctxt_1 := \Sigma.\Enc(\key_1, \ptxt)
    C2:=Σ.Enc(K2,M)\ctxt_2 := \Sigma.\Enc(\key_2, \ptxt)
    return (C1,C2)(\ctxt_1, \ctxt_2)
    Σ2.Dec((K1,K2),(C1,C2))\Sigma_2.\Dec\bigl( (\key_1,\key_2), (\ctxt_1, \ctxt_2) \bigr):
    M1:=Σ.Dec(K1,C1)\ptxt_1 := \Sigma.\Dec(\key_1, \ctxt_1)
    M2:=Σ.Dec(K2,C2)\ptxt_2 := \Sigma.\Dec(\key_2, \ctxt_2)
    if M1M2\ptxt_1 \ne \ptxt_2: return err\myerr
    return M1\ptxt_1

    Prove that Σ2\Sigma_2 has one-time secrecy if Σ\Sigma does.

  22. Here is a modular construction that illustrates the intuitive idea that it is safe to encrypt a long plaintext by separately encrypting its two halves (under independent keys). (You may wonder why such an “obvious” statement requires proof, but later in the book we study security definitions for which it is not safe.)

    Let Σ\Sigma be an SKE scheme. Define the following new scheme Σ2\Sigma_2:

    Σ2.K=Σ.K×Σ.KΣ2.M=Σ.M×Σ.MΣ2.C=Σ.C×Σ.C\begin{aligned} \Sigma_2.\K & = \Sigma.\K \times \Sigma.\K \\ \Sigma_2.\M &= \Sigma.\M \times \Sigma.\M \\ \Sigma_2.\C &= \Sigma.\C \times \Sigma.\C \end{aligned}
    Σ2.Enc((K1,K2),(M1,M2))\Sigma_2.\Enc\bigl( (\key_1,\key_2), (\ptxt_1, \ptxt_2) \bigr):
    C1:=Σ.Enc(K1,M1)\ctxt_1 := \Sigma.\Enc(\key_1, \ptxt_1)
    C2:=Σ.Enc(K2,M2)\ctxt_2 := \Sigma.\Enc(\key_2, \ptxt_2)
    return (C1,C2)(\ctxt_1, \ctxt_2)
    Σ2.Dec((K1,K2),(C1,C2))\Sigma_2.\Dec\bigl( (\key_1,\key_2), (\ctxt_1, \ctxt_2) \bigr):
    M1:=Σ.Dec(K1,C1)\ptxt_1 := \Sigma.\Dec(\key_1, \ctxt_1)
    M2:=Σ.Dec(K2,C2)\ptxt_2 := \Sigma.\Dec(\key_2, \ctxt_2)
    return (M1,M2)(\ptxt_1, \ptxt_2)

    Prove that Σ2\Sigma_2 has one-time secrecy if Σ\Sigma does.

  23. Let Σ\Sigma be an SKE scheme for which Σ.CΣ.M\Sigma.\C \subseteq \Sigma.\M, so that it makes sense to treat a ciphertext also as a plaintext that can be encrypted. Prove that if Σ\Sigma has one-time secrecy, then so does the following scheme Σ\Sigma':

    Σ.K=Σ.K×Σ.KΣ.M=Σ.MΣ.C=Σ.C\begin{aligned} \Sigma'.\K & = \Sigma.\K \times \Sigma.\K \\ \Sigma'.\M &= \Sigma.\M \\ \Sigma'.\C &= \Sigma.\C \end{aligned}
    \quad
    Σ.Enc((K1,K2),M)\Sigma'.\Enc\bigl( (\key_1,\key_2), \ptxt \bigr):
    C1:=Σ.Enc(K1,M)\ctxt_1 := \Sigma.\Enc(\key_1, \ptxt)
    C2:=Σ.Enc(K2,C1)\ctxt_2 := \Sigma.\Enc(\key_2, \ctxt_1)
    return C2\ctxt_2
    \quad
    Σ.Dec((K1,K2),C2)\Sigma'.\Dec\bigl( (\key_1,\key_2), \ctxt_2 \bigr):
    C1:=Σ.Dec(K2,C2)\ctxt_1 := \Sigma.\Dec(\key_2, \ctxt_2)
    M:=Σ.Dec(K1,C1)\ptxt := \Sigma.\Dec(\key_1, \ctxt_1)
    return M\ptxt
  24. Let Σ\Sigma be an SKE scheme. Prove that if Σ\Sigma satisfies real-or-random OTS (definition 2.5.3) then it also satisfies left-or-right OTS (definition 2.7.1).

  25. Let Σ\Sigma be an SKE scheme, and let Σ+0\Sigma^{+\bit0} be the encryption scheme defined by

    Σ+0.Enc(K,M)=Σ.Enc(K,M)0. \Sigma^{+\bit0}.\Enc(\key,\ptxt) = \Sigma.\Enc(\key,\ptxt) \| \bit0.
    1. Show that Σ+0\Sigma^{+\bit0} does not have (real-or-random) OTS, even if Σ\Sigma does.

    2. Show that Σ+0\Sigma^{+\bit0} does have left-or-right OTS (definition 2.7.1), if Σ\Sigma does.

  26. Let Σ\Sigma be an SKE scheme, and let Σ×2\Sigma^{\times 2} be the encryption scheme defined by

    Σ×2.Enc(K,M)\Sigma^{\times 2}.\Enc(\key, \ptxt):
    C:=Σ.Enc(K,M)\ctxt := \Sigma.\Enc(\key,\ptxt)
    return CC\ctxt \| \ctxt
    1. Show that Σ×2\Sigma^{\times 2} does not have (real-or-random) OTS, even if Σ\Sigma does.

    2. Show that Σ×2\Sigma^{\times 2} does have left-or-right OTS (definition 2.7.1), if Σ\Sigma does.

  27. Prove that the following two libraries are interchangeable if and only if Σ\Sigma satisfies left-or-right one-time secrecy:

    LrealΣ\lib{real}^\Sigma
    ots.enc(M)\otsenc(\ptxt):
    KΣ.K\key \gets \Sigma.\K
    C:=Σ.Enc(K,M)\ctxt := \Sigma.\Enc(\key,\ptxt)
    return C\ctxt
    \equiv
    LdummyΣ\lib{dummy}^\Sigma
    ots.enc(M)\otsenc(\ptxt):
    KΣ.K\key \gets \Sigma.\K
    MΣ.M\hl{\ptxt' \gets \Sigma.\M}
    C:=Σ.Enc(K,M)\ctxt := \Sigma.\Enc(\key,\hl{\ptxt'})
    return C\ctxt

    In other words, these two libraries are an equivalent definition for left-or-right one-time secrecy. You must prove both directions—that is, LrealLdummy    Lots-leftLots-right\lib{real} \equiv \lib{dummy} \implies \lib{ots-left} \equiv \lib{ots-right}, and Lots-leftLots-right    LrealLdummy\lib{ots-left} \equiv \lib{ots-right} \implies \lib{real} \equiv \lib{dummy}.

  28. Prove that the following two libraries are interchangeable if and only if Σ\Sigma satisfies left-or-right one-time secrecy:

    LrealΣ\lib{real}^\Sigma
    ots.enc(M1,M2)\otsenc(\ptxt_1, \ptxt_2):
    K1Σ.K\key_1 \gets \Sigma.\K
    K2Σ.K\key_2 \gets \Sigma.\K
    C1:=Σ.Enc(K1,M1)\ctxt_1 := \Sigma.\Enc(\key_1,\ptxt_1)
    C2:=Σ.Enc(K2,M2)\ctxt_2 := \Sigma.\Enc(\key_2,\ptxt_2)
    return (C1,C2)(\ctxt_1, \ctxt_2)
    \equiv
    LswappedΣ\lib{swapped}^\Sigma
    ots.enc(M1,M2)\otsenc(\ptxt_1, \ptxt_2):
    K1Σ.K\key_1 \gets \Sigma.\K
    K2Σ.K\key_2 \gets \Sigma.\K
    C1:=Σ.Enc(K1,M1)\ctxt_1 := \Sigma.\Enc(\key_1,\ptxt_1)
    C2:=Σ.Enc(K2,M2)\ctxt_2 := \Sigma.\Enc(\key_2,\ptxt_2)
    return (C2,C1)(\ctxt_2, \ctxt_1)

    In other words, these two libraries are an equivalent definition for left-or-right one-time secrecy. You must prove both directions—that is, LrealLswapped    Lots-leftLots-right\lib{real} \equiv \lib{swapped} \implies \lib{ots-left} \equiv \lib{ots-right}, and Lots-leftLots-right    LrealLswapped\lib{ots-left} \equiv \lib{ots-right} \implies \lib{real} \equiv \lib{swapped}.

Chapter Notes

Library-based security is a simplified dialect of the game-hopping methodology for security proofs, introduced by Shoup [206] and by Bellare and Rogaway [29,30]. In the game-based paradigm, security is defined in terms of an abstract interactive game played against an adversary; the programmatic code of the game plays a prominent role in security reasoning. Kilian and Rogaway [133] were the first to prove security in what we would now recognize as the modern game-hopping style. Library-based security shares many similarities with the state-separating proofs methodology, developed independently by Brzuska, Delignat-Lavaud, Fournet, Kohbrok, and Kohlweiss [56]. In both the library-based and state-separation methodologies, secrecy of values is reflected in their scope, and hybrid proofs involve frequently factoring out certain target libraries/modules.

Bellare, Desai, Jokipii, and Rogaway [15] were the first to formalize a security definition for encryption in the left-or-right paradigm. They also described another style of definition, which they called “real-or-random,” but which is different than how we use this term in the book. Theirs is analogous to the definition in exercise 2.27 and is actually equivalent to the left-or-right definition (one definition implies the other). Rogaway, Bellare, Black, and Krovetz [193] were the first to propose a security definition for encryption using our sense of the term “real-or-random.”

  1. Previous Chapter1. One-Time Pad and the Provable Security Mindset
  2. Next Chapter3. ☆ Secret Sharing