State-Separable Proofs for the Curious Cryptographer

This blog post is an introduction to state-separable proofs, a technique for proving the security of Cryptographic schemes.

The target audience for this post is me, a month ago. That is to say, I expect the reader to have some background knowledge around game-based security in Cryptography. The goal of this post is to show people familiar with proving security using games what state-separable proofs bring to the table, and how they offer a neat way of making proof writing a lot easier.

If you’re not familiar with game-based security, I’m not sure if this post will be easy to understand, but I did write a list of Cryptography book recommendations a couple weeks ago, so that may be a good starting point if you’d like to learn more. I’m also planning on writing a more beginner-friendly introduction to provable security, so keep your eyes peeled for if I follow through on that promise.

Introduction

The security of Cryptographic schemes is usually reasoned about with games. In these games, an adversary tries to break some scheme, by interacting with a challenger making use of that scheme. State-separable proofs are a technique to make writing this style of proof simpler.

The gist of the technique is that it allows you to turn a complicated proof into a series of much simpler steps. This is done by decomposing large monolithic games into smaller, more composable snippets of code, which we call packages.

Why We Care

Personally I care about state-separable proofs because I’ve found them to be much easier to work with. Hopefully I can impart some of this feeling onto you, but ultimately this is somewhat subjective.

A more objective argument in favor of this technique is that it makes it much easier to create reusable proof techniques. With traditional game-based proofs, certain arguments, like hybrid arguments, are often repeated each time they’re used. You remember the basic technique of a hybrid argument, but you have to spell it out each time you want to use it.

With state-separable proofs, a lot more of the mechanics of writing proofs is encoded in the formalism you use. More steps of the proof correspond to concrete actions with the mathematical objects representing various components of games, if that makes sense. This means that it’s a lot easier to formulate things like a hybrid argument in a way which is generic, and thus immediately reusable in different contexts.

Continuing with this example, in this post we’ll see a “generic hybrid argument” which just requires a few bits of structure to instantiate, and then the result can immediately be reused, without having to invoke or reuse the details of the argument.

I’ve been somewhat vague in this section, but hopefully the rest of this post will be able to better illustrate how this reusability plays out in practice.

Overview

In the rest of this post, we’ll first briefly review how traditional game-based proofs work, and then learn the formalism we use for state-separable proofs. We’ll then see how reductions work with this technique, using the example of reducing an encryption scheme to the security of a pseudo-random-function (PRF). We’ll then see how to make a generic hybrid argument, applying that to multiple vs single-query encryption security. Finally, we’ll conclude with an example on hybrid encryption, which lets us see how to model “computational problems” like the computational Diffie Hellman problem (CDH), and how random oracles work with this technique.

State-Separable What?

I’ve praised the benefits of state-separable proofs quite a bit already, so you might be impatient to learn exactly what it is I’m talking about.

What they boil down to is a formalism for games, which lends itself better to composability. They’re sort of an alternative to game-based security, in the sense that they have their own formalism for various aspects of that technique. On the other hand, the strategy for proving things is still somewhat similar, so in another sense state-separable proofs are more of an extension of traditional game-based proofs.

Security Games

Before we get to state-separable proofs, it might be useful to recall how traditional game-based proofs work.

The basic idea is that the security of some scheme, say, encryption, takes the form of a game. This game is played between a challenger, C\mathcal{C}, and an adversary A\mathcal{A}. The challenger sort of resembles the user of a scheme, in that they have private information, like keys, associated with the scheme. The adversary plays the role of a malicious actor trying to break the scheme.

These two players interact, and at the end of the game, it’s clear whether or not the adversary has won.

As an example, you might have a game where an adversary can ask the challenger to encrypt messages, and the goal of the adversary is to guess the secret key.

There are different ways to formalize and write this game down. One way I liked was to write down what messages get exchanged, like this:

kRKmiciEnc(k,mi)cik^wink^=k \boxed{ \begin{aligned} &k \xleftarrow{R} \mathcal{K}\cr &&\xleftarrow{m_i}\cr &c_i \gets \text{Enc}(k, m_i)\cr &&\xrightarrow{c_i}\cr &&\xleftarrow{\hat{k}}\cr &\text{win} \gets \hat{k} = k\cr \end{aligned} }

The challenger is sort of like a box, and the adversary interacts with them by sending and receiving messages.

Since the adversary can win, and the game involves randomness, we usually talk about the advantage of an adversary based on the probability that they win the game. Sometimes you can win relatively often by just playing randomly, so we often subtract this “trivial win rate” in order to get the advantage.

In this example, we might define the advantage as:

Adv[A]:=P[win=1K1 \text{Adv}[\mathcal{A}] := |P[\text{win} = 1| - |\mathcal{K}|^{-1}|

Sometimes security involves several games, and then the advantage is defined as something like:

Adv[A]:=P[out=1  Game0]P[out=1  Game1] \text{Adv}[\mathcal{A}] := |P[\text{out} = 1\ |\ \text{Game}_0] - P[\text{out} = 1\ |\ \text{Game}_1] |

The idea in this case is that you want to capture how well the adversary does at distinguishing between the two games. For example, can the adversary distinguish a real encryption scheme from a perfect scheme which just returns random ciphertexts?

Reductions work by taking an adversary for one game, and creating an adversary for a different game. This often takes the form of what I call a “three-column proof” where you have the challenger on the left, the adversary on the right, and then some wrapping code in the middle:

kRKmimimi1miciEnc(k,mi)cicik^k^wink^=k \boxed{ \begin{aligned} &k \xleftarrow{R} \mathcal{K}\cr &&&&\xleftarrow{m_i}\cr &&&m_i \gets m_i \oplus 1&\cr &&\xleftarrow{m_i}\cr &c_i \gets \text{Enc}(k, m_i)\cr &&\xrightarrow{c_i}\cr &&&&\xrightarrow{c_i}\cr &&&&\xleftarrow{\hat{k}}\cr &&\xleftarrow{\hat{k}}\cr &\text{win} \gets \hat{k} = k\cr \end{aligned} }

The idea is that the wrapping code takes the adversary on the right, and then tweaks its messages, in order to play the game on the left. But this is just a convenient formalism to describe an adversary for the game on the left. You could describe it in other ways, and there’s not really a standard way of describing this process of creating new adversaries when doing reductions.

For many schemes, you’ll need multiple reductions, since the security of the scheme might depend on several different assumptions. For example, a public key encryption scheme might depend on a symmetric encryption scheme, and also the RSA assumption.

Packages

With state-separable proofs, we decompose large security games into smaller units, called packages. A package consists of pseudo-code. This is similar to the kind of code we use to describe games. How we interpret this code isn’t very important. We can give it a precise semantics with Turing machines, or some other formalism, but all that matters is that we agree on what the code means.

Part of this code is dedicated to the state of the package, with code dedicated to defining and initializing this state. The rest of this package is dedicated to functions. As an example, consider this package:

LF kR{0,1}λF(m): return mk \boxed{ \begin{aligned} &\colorbox{#FBCFE8}{\large $\mathcal{L}_F$ }\cr \cr &k \xleftarrow{R} \{0, 1\}^\lambda\cr \cr &\underline{\mathtt{F}(m):}\cr &\ \texttt{return } m \oplus k\cr \end{aligned} }

The state of this package is the variable kk. The set of functions exposed by this package is {F}\{\texttt{F}\}. We say that the exports of LF\mathcal{L}_F are out(LF)={F}\text{out}(\mathcal{L}_F) = \{\texttt{F}\}. Before any of the functions in this package can be called, the initialization code is executed. This will set kk to a (uniform) random value in {0,1}λ\{0, 1\}^\lambda.

Packages are not limited to just exporting functions. They can also import functions. For example:

LG kR{0,1}λG(m): return F(m)k \boxed{ \begin{aligned} &\colorbox{#FBCFE8}{\large $\mathcal{L}_G$ }\cr \cr &k \xleftarrow{R} \{0, 1\}^\lambda\cr \cr &\underline{\mathtt{G}(m):}\cr &\ \texttt{return } \texttt{F}(m) \oplus k\cr \end{aligned} }

This package is similar to the previous one, except that it relies on a function F\texttt{F}, which isn’t defined inside of the package. Instead, this package depends on some external function. Think of this like a computer program which depends on another library. We say that the imports of LG\mathcal{L}_G are in(LG)={F}{\text{in}(\mathcal{L}_G) = \{\texttt{F}\}}.

Composition

Since LG\mathcal{L}_G imports the function F\texttt{F}, and LF\mathcal{L}_F exports that same function, a natural construction would be to create a larger package by linking the two packages together. Whenever LG\mathcal{L}_G would make a call to F\texttt{F}, the code inside of LF\mathcal{L}_F would be executed. This linking is defined as sequential composition.

Whenever we have two packages AA and BB, such that in(A)out(B)\text{in}(A) \subseteq \text{out}(B), we can define their sequential composition:

AB A \circ B

This package has the same exports as AA, with out(AB)=out(A)\text{out}(A \circ B) = \text{out}(A), and the same imports as BB, with in(AB)=in(B)\text{in}(A \circ B) = \text{in}(B).

This composition defines a new package whose state is the combination of the states in AA and BB. We replace a call to a function in BB by inlining the code of that function directly inside of AA.

Let’s take the example of LG\mathcal{L}_G and LF\mathcal{L}_F. One slight issue is that their internal state kk shares a name, so simply inlining the code in LF\mathcal{L}_F wouldn’t work. To get around this, one convention I like to use is that everything inside of a package is implicitly namespaced by that package. So the kk inside of LF\mathcal{L}_F is really LF.k\mathcal{L}_F.k, and the kk inside of LG\mathcal{L}_G is also shorthand for LG.k\mathcal{L}_G.k. The same goes for function names. With this convention, we can explicitly describe their composition:

LGLF LF.kR{0,1}λLG.kR{0,1}λG(m): return mLF.kLG.k \boxed{ \begin{aligned} &\colorbox{#FBCFE8}{\large $\mathcal{L}_G \circ \mathcal{L}_F$ }\cr \cr &\mathcal{L}_F.k \xleftarrow{R} \{0, 1\}^\lambda\cr &\mathcal{L}_G.k \xleftarrow{R} \{0, 1\}^\lambda\cr \cr &\underline{\mathtt{G}(m):}\cr &\ \texttt{return } m \oplus \mathcal{L}_F.k \oplus \mathcal{L}_G.k\cr \end{aligned} }

Instead of the call to F\texttt{F} we had before, we’ve now inlined that code directly in the package. To resolve ambiguities in the variable names, we’ve also explicitly written them down with their namespace.

Associativity

One interesting aspect of composition is that it’s associative. In other words, (AB)C(A \circ B) \circ C is the exact same package as A(BC)A \circ (B \circ C). The order in which you inline function definitions doesn’t matter. When you first inline the definitions in BB, and then those in CC, this yields the result as first inlining those in CC, and then inlining all of that inside of AA.

Now, I’ve said that these packages are “the same”, but I mean this in a specific way. In this case I mean that (AB)C(A \circ B) \circ C and A(BC)A \circ (B \circ C) are the exact same package, the state and code are all exactly the same, up to a potential renaming of variable and function names. We’ll refer to this kind of relation between packages as definitional equality, and denote it by \equiv. Continuing with this example, we have:

(AB)CA(BC) (A \circ B) \circ C \equiv A \circ (B \circ C)

Another silly example is that if we append the word foo\text{foo} to every name inside of the package AA, in order to get the package AfooA_{\text{foo}}, then we’d have:

AAfoo A \equiv A_{\text{foo}}

because definitional equality doesn’t care about variable or function names.

Parallel Composition

So far we’ve seen how to link packages together, using the exports of one package to satisfy the imports of another. We called this sequential composition. There’s another form of composition which isn’t as useful, but is still interesting to look at. This is parallel composition. The idea is that if the functions exported by two packages are distinct, then we can form a new package by putting the state of these packages side-by-side, and exporting all the functions these packages provide. We denote this composition by AB\begin{matrix}A\cr \hline B \end{matrix} or ABA \otimes B.

As an example, we have:

A kRZ/(q)F(m): return m+kB kRZ/(q)G(m): return mkAB A.kRZ/(q)B.kRZ/(q)F(m): return m+A.kG(m): return mB.k \boxed{ \begin{aligned} &\colorbox{#FBCFE8}{\large $A$ }\cr \cr &k \xleftarrow{R} \mathbb{Z}/(q)\cr \cr &\underline{\mathtt{F}(m):}\cr &\ \texttt{return } m + k\cr \end{aligned} } \otimes \boxed{ \begin{aligned} &\colorbox{#FBCFE8}{\large $B$ }\cr \cr &k \xleftarrow{R} \mathbb{Z}/(q)\cr \cr &\underline{\mathtt{G}(m):}\cr &\ \texttt{return } m \cdot k\cr \end{aligned} } \equiv \boxed{ \begin{aligned} &\colorbox{#FBCFE8}{\large $A \otimes B$ }\cr \cr &A.k \xleftarrow{R} \mathbb{Z}/(q)\cr &B.k \xleftarrow{R} \mathbb{Z}/(q)\cr \cr &\underline{\mathtt{F}(m):}\cr &\ \texttt{return } m + A.k\cr &\underline{\mathtt{G}(m):}\cr &\ \texttt{return } m \cdot B.k\cr \end{aligned} }

The parallel composition combines both states, and exposes both of the functions.

More formally, this composition is well defined when out(A)out(B)=\text{out}(A) \cap \text{out}(B) = \emptyset, and gives us:

out(AB)=out(A)out(B)in(AB)=in(A)in(B) \begin{aligned} \text{out}(A \otimes B) &= \text{out}(A) \cup \text{out}(B)\cr \text{in}(A \otimes B) &= \text{in}(A) \cup \text{in}(B)\cr \end{aligned}

One property of parallel composition which is apparent from the definition is that it’s associative and commutative. You have:

(AB)CA(BC) (A \otimes B) \otimes C \equiv A \otimes (B \otimes C)

and

ABBA A \otimes B \equiv B \otimes A

Combining Sequential and Parallel Composition

These two notions of composition actually work quite well together. If you have 4 packages A,A,B,BA, A', B, B', then you have:

AABBABAB \frac{A}{A'} \circ \frac{B}{B'} \equiv \frac{A \circ B}{A' \circ B'}

provided that the individual compositions are well defined, based on the imported and exported functions.

This equality holds because inlining one function doesn’t affect any of the other functions. This equality is very useful when linking packages defined via parallel composition.

Adversaries

So far, we’ve defined packages which import and export functions, and can be composed together in various ways. While this is very neat, it’s not yet “praktisch” (as they’d say in German); we’re still pretty far away from capturing the notion of security, as we did with the more traditional game-based definition. To do this, we need to introduce a few more notions based on packages.

The first notion is that of a game. A game is simply a package GG with no imports, i.e. in(G)=\text{in}(G) = \emptyset. This captures the idea that a game is something you can interact with directly. If the game had any imports, there wouldn’t be a well defined notion of what it does, because the imported functions could significantly change the behavior.

The second notion is that of an adversary. An adversary is a package A\mathcal{A} with many potential imports, but only a single exported function. This function, commonly named run():{0,1}\texttt{run}(): \{0, 1\} is used to execute the adversary.

The idea here is that each adversary plays against some interface of functions it can interact with. When we link an adversary A\mathcal{A} with a game GG implementing the right interface, we end up with a package AG\mathcal{A} \circ G, exposing a single function run\texttt{run}. Executing this function makes the adversary and the game interact, and finishes with a single bit 00 or 11. In fact, because of the random choices taken by these packages, we have a probability distribution over these outputs.

Advantages

This leads us to the natural notion of advantage. The idea is that we have a pair of games G0G_0 and G1G_1, with out(G0)=out(G1)\text{out}(G_0) = \text{out}(G_1). The advantage of an adversary A\mathcal{A} interacting with GG tells us how well that adversary can distinguish G0G_0 from G1G_1.

We define the advantage of such an adversary as:

ϵ(AGb):=P[1AG0]P[1AG1] \epsilon(\mathcal{A} \circ G_b) := |P[1 \gets \mathcal{A} \circ G_0] - P[1 \gets \mathcal{A} \circ G_1]|

We look at the probability that run\texttt{run} returns 11 in either situation, and the advantage measures how well the adversary is able to separate the two cases.

One useful game I like to define is ?b(A,B)?_b(A, B), which behaves like AA when b=0b = 0, and BB otherwise. In other words:

?0(A,B)A?1(A,B)B \begin{aligned} ?_0(A, B) &\equiv A\cr ?_1(A, B) &\equiv B \end{aligned}

Equality

Sometimes games might have different code, but behave in exactly the same way.

For example, consider these two games:

G0 kR{0,1}λF(m): return mkG1 k0,k1R{0,1}λF(m): return mk0k1 \boxed{ \begin{aligned} &\colorbox{#FBCFE8}{\large $G_0$ }\cr \cr &k \xleftarrow{R} \{0, 1\}^\lambda\cr \cr &\underline{\mathtt{F}(m):}\cr &\ \texttt{return } m \oplus k\cr \end{aligned} } \quad \quad \boxed{ \begin{aligned} &\colorbox{#FBCFE8}{\large $G_1$ }\cr \cr &k_0, k_1 \xleftarrow{R} \{0, 1\}^\lambda\cr \cr &\underline{\mathtt{F}(m):}\cr &\ \texttt{return } m \oplus k_0 \oplus k_1 \cr \end{aligned} }

It’s not true that G0G1G_0 \equiv G_1, because the code is cleary different, even after potential renaming. On the other hand, the result is the same, because k0k1k_0 \oplus k_1 has the same distribution as kk.

Capturing what it means to have “the same behavior” is tricky. One way to do this is to lean on the notion of advantage we’ve just defined.

We say that two games A,BA, B are equal, denoted A=BA = B, when for any unbounded adversary A\mathcal{A}, we have:

ϵ(A?b(A,B))=0 \epsilon(\mathcal{A} \circ ?_b(A, B)) = 0

In other words, no adversary, even with an unbounded amount of computation, can distinguish between the two games. This captures the intuitive idea that their behavior is the same.

In our previous example, we had G0=G1G_0 = G_1, because the distributions were indeed the same.

In most situations, == is much more useful than \equiv, since it’s more robust to unimportant changes, like moving variables around, or replacing a complicated expression with something having the same distribution. In fact, outside of the basic definitions, \equiv is rarely needed, and == is used instead.

Indistinguishability

Now, this notion of equality is much too strong. It requires games to look exactly the same, even under the scrutiny of an adversary with no limits on their computation time. There’s not a lot of Cryptography which can be done under these constraints.

We relax this notion by only considering “efficient” adversaries. These adversaries have a runtime polynomial in some ambient security parameter λ\lambda. In fact, when we refer to “adversaries” we usually mean “efficient adversaries”.

This leads us to the notion of ϵ\epsilon-indistinguishability. We say that two games AA and BB are ϵ\epsilon-indistinguishable with respect to A\mathcal{A}, or AϵBA \stackrel{\epsilon}{\approx} B when we have

ϵ(A?b(A,B))ϵ \epsilon(\mathcal{A} \circ ?_b(A, B)) \leq \epsilon

(the adversary is usually left implicit)

In other words, this adversary can only distinguish the two games with advantage at most ϵ\epsilon.

One useful property of this definition comes from the triangle inequality:

Aϵ1B, Bϵ2C    Aϵ1+ϵ2C A \stackrel{\epsilon_1}{\approx} B, \ B \stackrel{\epsilon_2}{\approx} C \implies A \stackrel{\epsilon_1 + \epsilon_2}{\approx} C

This property allows us to do “game-hopping” where we chain small differences together to examine the indistinguishability of a larger game.

Finally, we say that two games are indistinguishable when for every efficient adversary A\mathcal{A}, there exists an ϵ\epsilon, which is a negligible function of λ\lambda, such that these games are ϵ\epsilon-indistinguishable.

Defining Security

This notion of indistinguishability is what we’ll use to define the security of different constructions. Whenever we have a construction, like a signature scheme, or an encryption scheme, and we want to reason about it’s security, we’ll do so with a pair of games GbG_b. The scheme will be considered secure if these games are indistinguishable.

One slight difference with game-based security is that we’ll have a strong preference for games in a “real vs ideal” style. We’ll want one game to be based on interacting with our actual scheme, and another to be based on interacting with an ideal version of our scheme.

For example, with encryption, you might consider a real game where you use your cipher, as compared to an ideal game, where encryption returns a random ciphertext. If you can’t distinguish between the two, then the encryption scheme is secure.

With traditional game-based security, sometimes you have other styles of game definition which also work. For example, the IND\text{IND} notion security for encryption involves asking for the encryption of one of two messages. In one game, you get the left message, in the other, the right message.

The reason we prefer real vs ideal is that it allows us to consistently define games in this paradigm, which is much more amenable to composition. Using small ideal components, you build up a large ideal construction.

Reductions

Taking our notion of security at face value, to prove that a scheme is secure, we’d prove that two games G0,G1G_0, G_1 are indistinguishable. To do that, we’d need to show that for every adversary A\mathcal{A}, we have ϵ(AGb)ϵ\epsilon(\mathcal{A} \circ G_b) \leq \epsilon, for some negligible ϵ\epsilon.

Proving that every adversary has a negligible advantage is essentially impossible for anything but the simplest of schemes. In fact, in many cases, such a proof would immediately lead you to conclude that PNPP \neq \text{NP}, which a lot of people have spent a considerable amount of time trying to prove.

What you do instead is rely on the hardness of some other problem. For example, you may assume that factoring integers is hard, and use that to build an encryption scheme which is also hard.

The way this linking is done, is via reductions. A reduction uses an adversary A\mathcal{A} for some game AbA_b to build other adversaries Bi\mathcal{B}_i for games BbiB^i_b. If AbA_b is broken, then all of the BbiB^i_b games are also broken. Conversely, if all of these games are secure, then AbA_b must be as well.

More concretely, a reduction is a statement (and corresponding proof) of the form:

For all adversaries A\mathcal{A} against AbA_b, there exists adversaries Bi\mathcal{B}_i against BbiB^i_b such that: ϵ(AAb)f(ϵ(B0Bb0),,ϵ(BNBbN)) \epsilon(\mathcal{A} \circ A_b) \leq f(\epsilon(\mathcal{B}_0 \circ B^0_b), \ldots, \epsilon(\mathcal{B}_N \circ B^N_b))

Now, ff is usually some very simple function like f(x,y)=2x+yf(x, y) = 2 \cdot x + y, or something like that. It should be the case that if the arguments to ff are negligible, then so will its output be.

This equation has the nice property that if the right side is negligible, then so is the left side. In other words, if all the games on the right are secure, then so is the game on the left. Conversely, if the game on the left is broken, then one of the games on the right is too.

A shorthand notation I like to use for this statement is:

Abf(Bb0,,BbN) A_b \leq f(B^0_b, \ldots, B^N_b)

This is just shorthand for the above statement about adversaries and advantages, and allows a concise overview of the relation between different games.

Building Reductions

Our notion of reduction is basically the same as with traditional game-based security, so it isn’t exactly clear what advantage is gained by using state-separable proofs.

This advantage comes from associativity.

Let’s say we have an adversary A\mathcal{A} for GbG_b, and we want to use this adversary to attack HbH_b. To do that, we need to build an adversary B\mathcal{B} against HbH_b. What we can do is build a shim package SS, such that in(S)=out(Hb)\text{in}(S) = \text{out}(H_b) and out(S)=in(Gb)\text{out}(S) = \text{in}(G_b). We then have a game:

SHb S \circ H_b

The advantage of A\mathcal{A} against this game is:

ϵ(A(SHb)) \epsilon(\mathcal{A} \circ (S \circ H_b))

But, because of associativity, this is the same quantity as:

ϵ((AS)Hb) \epsilon((\mathcal{A} \circ S) \circ H_b)

We can see this situation as either A\mathcal{A} playing against (SHb)(S \circ H_b), or the adversary (AS)(\mathcal{A} \circ S) playing against HbH_b.

In this example, our reduction would proceed straightforwardly:

G0=SH0ϵ0SH1=G1G_0 = S \circ H_0 \stackrel{\epsilon_0}{\approx} S \circ H_1 = G_1

with ϵ0:=ϵ(BHb)\epsilon_0 := \epsilon(\mathcal{B} \circ H_b), and B:=(AS)\mathcal{B} := (\mathcal{A} \circ S).

From this equation, we see that G0ϵ0G1G_0 \stackrel{\epsilon_0}{\approx} G_1, and thus conclude that:

ϵ(AGb)ϵ(BHb) \epsilon(\mathcal{A} \circ G_b) \leq \epsilon(\mathcal{B} \circ H_b)

or, in shorthand:

GbHb G_b \leq H_b

This example really captures the essence of what makes state-separable proofs nice. Our reductions will be made through the use of clever shim packages like SS, which implicitly define new adversaries for us to use. Our full reduction then becomes a matter of constructing a series of small hops, to navigate our way from G0G_0 to G1G_1.

In traditional game-based security, you often need to explicitly construct the adversaries you use in a reduction, which is more cumbersome, and less amenable to the local reasoning you get with state-separable proofs.

In the rest of this post, we’ll go over several more examples of how reductions work, so hopefully that will clarify things quite a bit more, and give you an even better feel for how reductions work in practice.

Example: Encryption with a PRF

As an example, let’s consider the case of using a Pseudo-Random Function (PRF) in order to build an encryption scheme secure under chosen plaintext attack.

PRFs

A PRF F:K×XYF : \mathcal{K} \times \mathcal{X} \to \mathcal{Y} is a function from XY\mathcal{X} \to \mathcal{Y} which also takes in a key. The idea is that if you don’t know the key, you shouldn’t be able to tell this function apart from a random function. We capture this notion of security through a pair of games:

PRF0 kRKQueryF(m:X):Y return F(k,m)PRF1 out[]QueryF(m:X):Y if mout: out[m]RY return out[m] \boxed{ \begin{aligned} &\colorbox{#FBCFE8}{\large $\text{PRF}_0$ }\cr \cr &k \xleftarrow{R} \mathcal{K}\cr \cr &\underline{\mathtt{QueryF}(m : \mathcal{X}): \mathcal{Y}}\cr &\ \texttt{return } F(k, m)\cr \end{aligned} } \quad \quad \boxed{ \begin{aligned} &\colorbox{#FBCFE8}{\large $\text{PRF}_1$ }\cr \cr &\text{out}[\cdot] \gets \bot\cr \cr &\underline{\mathtt{QueryF}(m : \mathcal{X}): \mathcal{Y}}\cr &\ \texttt{if } m \notin \text{out}:\cr &\ \quad \text{out}[m] \xleftarrow{R} \mathcal{Y}\cr &\ \texttt{return } \text{out}[m] \cr \end{aligned} }

In one game, we actually query the PRF, and in the other, we’re querying a random function. We say that the PRF FF is secure if PRF0\text{PRF}_0 and PRF1\text{PRF}_1 are indistinguishable.

Encryption

A symmetric encryption scheme consists of two functions:

E:K×MRCD:K×CM \begin{aligned} E &: \mathcal{K} \times \mathcal{M} \xrightarrow{R} \mathcal{C}\cr D &: \mathcal{K} \times \mathcal{C} \to \mathcal{M} \end{aligned}

Both of them take a key. One encrypts a message with a key, producing a ciphertext, and the other decrypts a ciphertext with a key, producing a message. Encryption can be randomized, while decryption should be deterministic.

For the scheme to be considered correct, encrypting a message and then decrypting the ciphertext should return the same message.

In terms of security, the intuitive idea is that an adversary shouldn’t be able to distinguish between an encryption of an actual message and an encryption of a random message.

Using a real vs a random message is often referred to as IND$\text{IND}\text{\textdollar}, with IND\text{IND} being reserved for a variant of the game where you submit two messages, and receive the encryption of one of the messages.

With state-separable proofs, you really want to use “real vs ideal” as the defining principle for all of your games, since that makes composition a lot easier.

Because of this, I’ll refer to this notion as IND\text{IND}. We’ll also also let the adversary encrypt messages of their choice. This addition is usually referred to as “chosen plaintext attack” (CPA).

Thus, IND-CPA\text{IND-CPA} security is defined by a pair of games:

IND-CPAb kRKChallenge(m0:M):C m1RM return Encrypt(mb)Encrypt(m:M):C return E(k,m) \boxed{ \begin{aligned} &\colorbox{#FBCFE8}{\large $\text{IND-CPA}_b$ }\cr \cr &k \xleftarrow{R} \mathcal{K}\cr \cr &\underline{\mathtt{Challenge}(m_0 : \mathcal{M}): \mathcal{C}}\cr &\ m_1 \xleftarrow{R} \mathcal{M}\cr &\ \texttt{return } \texttt{Encrypt}(m_b)\cr \cr &\underline{\mathtt{Encrypt}(m : \mathcal{M}): \mathcal{C}}\cr &\ \texttt{return } E(k, m)\cr \end{aligned} }

An encryption scheme is IND-CPA\text{IND-CPA} secure if these two games are indistinguishable.

Encryption with a PRF

The simplest encryption scheme is the one-time pad. One problem with this scheme is that we can only encrypt one message. A PRF bypasses this limitation, by letting us generate new pads on demand.

Given a PRF over X\mathcal{X} and Y\mathcal{Y}, with Y\mathcal{Y} some type where \oplus makes sense, we can define an encryption scheme with M=Y\mathcal{M} = \mathcal{Y} and C=X×Y\mathcal{C} = \mathcal{X} \times \mathcal{Y} as follows:

E(k,m): xRX return (x,mF(k,x))D(k,(x,c)): return cF(k,x) \begin{aligned} &\underline{E(k, m):}\cr &\ x \xleftarrow{R} \mathcal{X}\cr &\ \texttt{return } (x, m \oplus F(k, x))\cr \cr &\underline{D(k, (x, c)):}\cr &\ \texttt{return } c \oplus F(k, x) \cr \end{aligned}

What we want to show is that the security of this scheme reduces to the security of the underlying PRF.

In particular, we want to show that IND-CPAb\text{IND-CPA}_b reduces to PRFb\text{PRF}_b. The precise statement is that:

IND-CPAbQ2X+2PRFb \text{IND-CPA}_b \leq \frac{Q^2}{|\mathcal{X}|} + 2\text{PRF}_b

using our short-hand notation for reductions from earlier, and with QQ denoting the number of queries made to the Encrypt\texttt{Encrypt} function in the IND-CPAb\text{IND-CPA}_b game.

Our goal is to start with IND-CPA0\text{IND-CPA}_0, and then hop our way over to IND-CPA1\text{IND-CPA}_1, quantifying the distinguishing advantage along the way, based on the security of the underlying PRF.

First, note that:

IND-CPA1=kRKChallenge(m0:Y):C xRX yRY return (x,y)Encrypt(m:Y):C xRX return (x,mF(k,x)) \text{IND-CPA}_1 = \boxed{ \begin{aligned} &k \xleftarrow{R} \mathcal{K}\cr \cr &\underline{\mathtt{Challenge}(m_0 : \mathcal{Y}): \mathcal{C}}\cr &\ x \xleftarrow{R} \mathcal{X}\cr &\ y \xleftarrow{R} \mathcal{Y}\cr &\ \texttt{return } (x, y)\cr \cr &\underline{\mathtt{Encrypt}(m : \mathcal{Y}): \mathcal{C}}\cr &\ x \xleftarrow{R} \mathcal{X}\cr &\ \texttt{return } (x, m \oplus F(k, x))\cr \end{aligned} }

This is because xoring with a random message is equivalent to just sampling a random output.

Second, note that we can abstract out the use of the PRF inside of these games:

IND-CPAb=Γb0 Challenge(m0:M):C m1RM return Encrypt(mb)Encrypt(m:M):C xRX return (x,mQueryF(x))PRF0 \text{IND-CPA}_b = \boxed{ \begin{aligned} &\colorbox{#FBCFE8}{\large $\Gamma^0_b$ }\cr \cr &\underline{\mathtt{Challenge}(m_0 : \mathcal{M}): \mathcal{C}}\cr &\ m_1 \xleftarrow{R} \mathcal{M}\cr &\ \texttt{return } \texttt{Encrypt}(m_b)\cr \cr &\underline{\mathtt{Encrypt}(m : \mathcal{M}): \mathcal{C}}\cr &\ x \xleftarrow{R} \mathcal{X}\cr &\ \texttt{return } (x, m \oplus \texttt{QueryF}(x))\cr \end{aligned} } \circ \text{PRF}_0

Then we have Γb0PRF0ϵ0Γb0PRF1\Gamma^0_b \circ \text{PRF}_0 \stackrel{\epsilon_0}{\approx} \Gamma^0_b \circ \text{PRF}_1, with:

ϵ0=ϵ(BPRFb) \epsilon_0 = \epsilon(\mathcal{B} \circ \text{PRF}_b)

for some adversary B\mathcal{B}.

Now, if we inline the random function in PRF1\text{PRF}_1, we have:

Γ00PRF1=Γ1 out[]Challenge(m:M):C return Encrypt(m)Encrypt(m:M):C xRX if xout: out[x]RY return (x,mout[x]) \Gamma^0_0 \circ \text{PRF}_1 = \boxed{ \begin{aligned} &\colorbox{#FBCFE8}{\large $\Gamma^1$ }\cr \cr &\text{out}[\cdot] \gets \bot\cr \cr &\underline{\mathtt{Challenge}(m : \mathcal{M}): \mathcal{C}}\cr &\ \texttt{return } \texttt{Encrypt}(m)\cr \cr &\underline{\mathtt{Encrypt}(m : \mathcal{M}): \mathcal{C}}\cr &\ x \xleftarrow{R} \mathcal{X}\cr &\ \texttt{if } x \notin \text{out}:\cr &\ \quad \text{out}[x] \xleftarrow{R} \mathcal{Y}\cr &\ \texttt{return } (x, m \oplus \text{out}[x])\cr \end{aligned} }

Now, if we were to remove the xoutx \notin \text{out} check, this would only change the behavior if the same xx were generated twice. This happens with probability at most Q22X\frac{Q^2}{2 |\mathcal{X}|}, by a standard birthday paradox argument. Thus, we have Γ1Q2/2XΓ2\Gamma^1 \stackrel{Q^2 / 2 |\mathcal{X}|}{\approx} \Gamma^2, where Γ2\Gamma^2 is defined as:

Γ2 Challenge(m:M):C return Encrypt(m)Encrypt(m:M):C xRX yRY return (x,my) \boxed{ \begin{aligned} &\colorbox{#FBCFE8}{\large $\Gamma^2$ }\cr \cr &\underline{\mathtt{Challenge}(m : \mathcal{M}): \mathcal{C}}\cr &\ \texttt{return } \texttt{Encrypt}(m)\cr \cr &\underline{\mathtt{Encrypt}(m : \mathcal{M}): \mathcal{C}}\cr &\ x \xleftarrow{R} \mathcal{X}\cr &\ y \xleftarrow{R} \mathcal{Y}\cr &\ \texttt{return } (x, m \oplus y)\cr \end{aligned} }

An equivalent way to write this would be:

Γ2=Challenge(m:M):C xRX yRY return (x,y)Encrypt(m:M):C xRX yRY return (x,my) \Gamma^2 = \boxed{ \begin{aligned} &\underline{\mathtt{Challenge}(m : \mathcal{M}): \mathcal{C}}\cr &\ x \xleftarrow{R} \mathcal{X}\cr &\ y \xleftarrow{R} \mathcal{Y}\cr &\ \texttt{return } (x, y)\cr \cr &\underline{\mathtt{Encrypt}(m : \mathcal{M}): \mathcal{C}}\cr &\ x \xleftarrow{R} \mathcal{X}\cr &\ y \xleftarrow{R} \mathcal{Y}\cr &\ \texttt{return } (x, m \oplus y)\cr \end{aligned} }

We can then add in a random table again, to get:

Γ3 out[]Challenge(m:M):C xRX yRY return (x,y)Encrypt(m:M):C xRX if xout: out[x]RY return (x,mout[x]) \boxed{ \begin{aligned} &\colorbox{#FBCFE8}{\large $\Gamma^3$ }\cr \cr &\text{out}[\cdot] \gets \bot\cr \cr &\underline{\mathtt{Challenge}(m : \mathcal{M}): \mathcal{C}}\cr &\ x \xleftarrow{R} \mathcal{X}\cr &\ y \xleftarrow{R} \mathcal{Y}\cr &\ \texttt{return } (x, y)\cr \cr &\underline{\mathtt{Encrypt}(m : \mathcal{M}): \mathcal{C}}\cr &\ x \xleftarrow{R} \mathcal{X}\cr &\ \texttt{if } x \notin \text{out}:\cr &\ \quad \text{out}[x] \xleftarrow{R} \mathcal{Y}\cr &\ \texttt{return } (x, m \oplus \text{out}[x])\cr \end{aligned} }

By the same difference lemma argument as before, we have Γ3Q2/2XΓ2\Gamma^3 \stackrel{Q^2 / 2 |\mathcal{X}|}{\approx} \Gamma^2.

Notice that we have:

Γ3=Γ10PRF1 \Gamma^3 = \Gamma^0_1 \circ \text{PRF}_1

since in the case where we use a random message, the call to Encrypt\texttt{Encrypt} doesn’t matter, as we saw earlier. From this point, we can reach IND-CPA1\text{IND-CPA}_1, so we’ve done all the hops we need. Putting everything together, we have:

IND-CPA0=Γ00PRF0ϵ0Γ00PRF1=Γ1Q2/2XΓ2Q2/2XΓ3=Γ10PRF1ϵ0Γ10PRF0=IND-CPA1 \begin{aligned} \text{IND-CPA}_0 &= \Gamma^0_0 \circ \text{PRF}_0\cr &\stackrel{\epsilon_0}{\approx} \Gamma^0_0 \circ \text{PRF}_1\cr &= \Gamma^1\cr &\stackrel{Q^2 / 2 |\mathcal{X}|}{\approx} \Gamma^2\cr &\stackrel{Q^2 / 2 |\mathcal{X}|}{\approx} \Gamma^3\cr &= \Gamma^0_1 \circ \text{PRF}_1\cr &\stackrel{\epsilon_0}{\approx} \Gamma^0_1 \circ \text{PRF}_0\cr &= \text{IND-CPA}_1 \end{aligned}

So all we have to do is apply the triangle lemma, to get:

IND-CPA0ϵIND-CPA1 \text{IND-CPA}_0 \stackrel{\epsilon}{\approx} \text{IND-CPA}_1

with:

ϵQ2X+2ϵ(BPRFb) \epsilon \leq \frac{Q^2}{|\mathcal{X}|} + 2 \epsilon(\mathcal{B} \circ \text{PRF}_b)

In more words, we’ve shown that given an adversary A\mathcal{A} for IND-CPAb\text{IND-CPA}_b, there exists an adversary B\mathcal{B} for PRFb\text{PRF}_b such that:

ϵ(AIND-CPAb)Q2X+2ϵ(BPRFb) \epsilon(\mathcal{A} \circ \text{IND-CPA}_b) \leq \frac{Q^2}{\mathcal{|X|}} + 2\epsilon(\mathcal{B} \circ \text{PRF}_b)

with QQ the number of queries made to the encryption oracle.

\square

Hybrid Arguments

In our definition of the IND-CPA\text{IND-CPA} game, we allowed adversaries to make multiple queries to Challenge\texttt{Challenge}. A different variant of the game only allows one query to be made:

IND-CPA-1b count0kRKChallenge(m0:M):C assert count=0 countcount+1 m1RM return E(k,mb)Encrypt(m:M):C return E(k,m) \boxed{ \begin{aligned} &\colorbox{#FBCFE8}{\large $\text{IND-CPA-1}_b$ }\cr \cr &\text{count} \gets 0\cr &k \xleftarrow{R} \mathcal{K}\cr \cr &\underline{\mathtt{Challenge}(m_0 : \mathcal{M}): \mathcal{C}}\cr &\ \texttt{assert } \text{count} = 0\cr &\ \text{count} \gets \text{count} + 1\cr &\ m_1 \xleftarrow{R} \mathcal{M}\cr &\ \texttt{return } E(k, m_b)\cr \cr &\underline{\mathtt{Encrypt}(m : \mathcal{M}) : \mathcal{C}}\cr &\ \texttt{return } E(k, m) \end{aligned} }

The assert\texttt{assert} will make sure that only one query can be made, returning \bot if further queries are attempted.

A natural question is how much being able to make multiple queries helps. An adversary for IND-CPA-1\text{IND-CPA-1} can obviously break IND-CPA\text{IND-CPA}, since the latter allows them to make the one query they need. In the other direction, the question is more subtle. It turns out that if we make QQ queries in the IND-CPA\text{IND-CPA} game, then our advantage is only larger by a factor of QQ compared to the IND-CPA-1\text{IND-CPA-1} game.

The intuition behind the proof is that to bridge the gap between IND-CPA0\text{IND-CPA}_0 and IND-CPA1\text{IND-CPA}_1 we create a series of hybrid games H0,,HQH_0, \ldots, H_Q. The first hybrid starts at IND-CPA0\text{IND-CPA}_0, and the last ends at IND-CPA1\text{IND-CPA}_1. At each step, instead of moving from m0m_0 to m1m_1 in all of the queries, we only change to m1m_1 in a single query:

1.png

Then, the idea is that distinguishing between two successive games is like distinguishing between IND-CPA-10\text{IND-CPA-1}_0 and IND-CPA-11\text{IND-CPA-1}_1:

2.png

Since there are QQ hops, we end up with a factor of QQ in our advantage.

The General Hybrid Argument

We can formalize this argument in a general way, which will be usable for a wide variety of games.

The basic setup is that we have two different game pairs G0G_0 and G1G_1, with out(G0)=out(G1)\text{out}(G_0) = \text{out}(G_1), which represent our “single query” games. We have another pair of games, M0M_0 and M1M_1, with out(M0)=out(M1)\text{out}(M_0) = \text{out}(M_1), which represent our “multi-query” games. We then have a series of hybrid games H0,,HQH_0, \ldots, H_Q, satisfying out(Hi)=out(Mb)\text{out}(H_i) = \text{out}(M_b). Finally, we have a series of “shims” R0,,RQ1R_0, \ldots, R_{Q - 1}, with in(Ri)=out(Gb)\text{in}(R_i) = \text{out}(G_b) and out(Ri)=out(Mb)\text{out}(R_i) = \text{out}(M_b).

If it holds that:

H0=M0HQ=M1 \begin{aligned} H_0 &= M_0\cr H_Q &= M_1 \end{aligned}

and that for all i{0,,Q1}i \in \{0,\ldots, Q - 1\}, we have:

RiG0=HiRiG1=Hi+1 \begin{aligned} R_i \circ G_0 &= H_i\cr R_i \circ G_1 &= H_{i + 1}\cr \end{aligned}

Then for any adversary A\mathcal{A} against MM, there exists an adversary B\mathcal{B} against GG, satisfying:

ϵ(AMb)=Qϵ(BGb) \epsilon(\mathcal{A} \circ M_b) = Q \cdot \epsilon(\mathcal{B} \circ G_b)

Proof:

First, we have:

ϵ(AMb)=P[1AM0]P[1AM1] \epsilon(\mathcal{A} \circ M_b) = |P[1 \gets \mathcal{A} \circ M_0] - P[1 \gets \mathcal{A} \circ M_1]|

Because of the properties of H0H_0 and HQH_Q, we can write this as:

P[1AH0]P[1AH1] |P[1 \gets \mathcal{A} \circ H_0] - P[1 \gets \mathcal{A} \circ H_1]|

We can then write this as a telescoping sum:

i=0Q1P[1AHi]P[1AHi+1] \left|\sum_{i = 0}^{Q - 1} P[1 \gets \mathcal{A} \circ H_i] - P[1 \gets \mathcal{A} \circ H_{i + 1}]\right|

This works because the intermediate terms cancel each other out, giving us the final result.

Next, we apply the properties of RiR_i, giving us:

i=0Q1P[1ARiG0]P[1ARiG1] \left|\sum_{i = 0}^{Q - 1} P[1 \gets \mathcal{A} \circ R_i \circ G_0] - P[1 \gets \mathcal{A} \circ R_i \circ G_1]\right|

At this point, we need to introduce a little gadget. We define a new package RR, which samples a random j{0,,Q1}j \in \{0, \ldots, Q - 1\}, and then behaves like the package RjR_j.

Conditioning on jj, we can write our current value as:

i=0Q1P[1ARG0  j=i]P[1ARiG1  j=i] \left|\sum_{i = 0}^{Q - 1} P[1 \gets \mathcal{A} \circ R \circ G_0 \ |\ j = i] - P[1 \gets \mathcal{A} \circ R_i \circ G_1 \ |\ j = i]\right|

Now, by basic probability, we have that:

1Qi=0Q1P[1ARGb  j=i]=P[1ARGb] \frac{1}{Q} \sum_{i = 0}^{Q - 1} P[1 \gets \mathcal{A} \circ R \circ G_b \ |\ j = i] = P[1 \gets \mathcal{A} \circ R \circ G_b]

since P[j=i]=1QP[j = i] = \frac{1}{Q}.

Multiplying our current telescoping sum by 1Q\frac{1}{Q} inside, and QQ on the outside, we get:

QP[1ARG0]P[1ARG1] Q \cdot \left|P[1 \gets \mathcal{A} \circ R \circ G_0] - P[1 \gets \mathcal{A} \circ R \circ G_1]\right|

Denoting the package AR\mathcal{A} \circ R as an adversary B\mathcal{B}, This is just the value:

Qϵ(BGb) Q \cdot \epsilon(\mathcal{B} \circ G_b)

Tying everything together, we conclude that:

ϵ(AMb)=Qϵ(BGb) \epsilon(\mathcal{A} \circ M_b) = Q \cdot \epsilon(\mathcal{B} \circ G_b)

\square

Our Specific Case

Now, let’s recall our specific case. We have the following game:

IND-CPAb kRKChallenge(m0:M):C m1RM return E(k,mb)Encrypt(m:M):C return E(k,m) \boxed{ \begin{aligned} &\colorbox{#FBCFE8}{\large $\text{IND-CPA}_b$ }\cr \cr &k \xleftarrow{R} \mathcal{K}\cr \cr &\underline{\mathtt{Challenge}(m_0 : \mathcal{M}): \mathcal{C}}\cr &\ m_1 \xleftarrow{R} \mathcal{M}\cr &\ \texttt{return } E(k, m_b)\cr \cr &\underline{\mathtt{Encrypt}(m : \mathcal{M}): \mathcal{C}}\cr &\ \texttt{return } E(k, m)\cr \end{aligned} }

We also have a variant IND-CPA-1b\text{IND-CPA-1}_b, which only allows a single query to Challenge\texttt{Challenge}. IND-CPA-1b\text{IND-CPA-1}_b will play the role of GbG_b, and IND-CPAb\text{IND-CPA}_b the role of MbM_b. We’ll implicitly limit IND-CPAb\text{IND-CPA}_b to QQ queries, for the sake of the argument.

The next step is to define H0,HQH_0, \ldots H_Q:

Hi countQkRKChallenge(m0:M):C m1RM bcounti countcount1 return E(k,mb)Encrypt(m:M):C return E(k,m) \boxed{ \begin{aligned} &\colorbox{#FBCFE8}{\large $\text{H}_i$ }\cr \cr &\text{count} \gets Q\cr &k \xleftarrow{R} \mathcal{K}\cr \cr &\underline{\mathtt{Challenge}(m_0 : \mathcal{M}): \mathcal{C}}\cr &\ m_1 \xleftarrow{R} \mathcal{M}\cr &\ b \gets \text{count} \geq i\cr &\ \text{count} \gets \text{count} - 1\cr &\ \texttt{return } E(k, m_b)\cr \cr &\underline{\mathtt{Encrypt}(m : \mathcal{M}): \mathcal{C}}\cr &\ \texttt{return } E(k, m)\cr \end{aligned} }

For H0H_0, we always use m0m_0, for H1H_1 we start using m1m_1 only for the last query, when count is 00, for H2H_2 we use m1m_1 for the last two, etc., until we reach HQH_Q, which always uses m1m_1. Thus, we clearly have H0=IND0H_0 = \text{IND}_0 and H1=IND1H_1 = \text{IND}_1.

The final step is to construct our shims R0,,RQ1R_0, \ldots, R_{Q - 1}. The idea is that we use the IND-CPA-1\text{IND-CPA-1} game to make the transition between choosing m0m_0 and choosing m1m_1.

Ri countQkRKChallenge(m0:M):C if count>i: return IND-CPA-1.Encrypt(m0) elif count=i: return IND-CPA-1.Challenge(m0) else count<i: m1RM return IND-CPA-1.Encrypt(m1)Encrypt(m:M):C return IND-CPA-1.Encrypt(m) \boxed{ \begin{aligned} &\colorbox{#FBCFE8}{\large $\text{R}_i$ }\cr \cr &\text{count} \gets Q\cr &k \xleftarrow{R} \mathcal{K}\cr \cr &\underline{\mathtt{Challenge}(m_0 : \mathcal{M}): \mathcal{C}}\cr &\ \texttt{if } \text{count} > i:\cr &\ \quad \texttt{return } \text{IND-CPA-1}.\texttt{Encrypt}(m_0)\cr &\ \texttt{elif } \text{count} = i:\cr &\ \quad \texttt{return } \text{IND-CPA-1}.\texttt{Challenge}(m_0)\cr &\ \texttt{else } \text{count} < i:\cr &\ \quad m_1 \xleftarrow{R} \mathcal{M}\cr &\ \quad \texttt{return } \text{IND-CPA-1}.\texttt{Encrypt}(m_1)\cr \cr &\underline{\mathtt{Encrypt}(m : \mathcal{M}): \mathcal{C}}\cr &\ \texttt{return } \text{IND-CPA-1}.\texttt{Encrypt}(m)\cr \end{aligned} }

For example, in the game R0R_0, we always encrypt m0m_0 until we reach the QQth query. At this point, if we’re against IND-CPA-10\text{IND-CPA-1}_0, then we’ll encrypt m0m_0, otherwise, we end up encrypting m1m_1. And the linking works fine, since we only make a single call to Challenge\texttt{Challenge}.

Thus, we have that:

RiIND-CPA-10=HiRiIND-CPA-11=Hi+1 \begin{aligned} R_i \circ \text{IND-CPA-1}_0 &= H_i\cr R_i \circ \text{IND-CPA-1}_1 &= H_{i+1}\cr \end{aligned}

At this point, all of the conditions are satisfied for us to apply the hybrid argument lemma we proved earlier. This lets us conclude that for every adversary A\mathcal{A} against IND-CPA\text{IND-CPA}, making QQ challenge queries, there exists an adversary B\mathcal{B} against IND-CPA-1\text{IND-CPA-1} satisfying:

ϵ(AIND-CPAb)Qϵ(BIND-CPA-1b) \epsilon(\mathcal{A} \circ \text{IND-CPA}_b) \leq Q \cdot \epsilon(\mathcal{B} \circ \text{IND-CPA-1}_b)

This means that whether or not you allow multiple challenge queries doesn’t fundamentally change the security of the scheme. You just get a linear increase in advantage by being able to make more queries. Thus, you can safely allow multiple challenge queries if that’s more convenient.

Hybrid Encryption

As a final example, I’d like to explore hybrid encryption, where we combine a key exchange with symmetric encryption, in order to create a public key encryption scheme. This will let us explore a few interesting techniques in proofs, including random oracles, and modelling games where the adversary is expected to return a complex answer, like a group element.

First, let’s recall how a this encryption scheme works. The basic idea is to combine a Diffie-Hellman key exchange with an encryption scheme. First, we need some Cryptographic group G\mathbb{G}, generated by GG, and with order qq. We also need an underlying symmetric encryption scheme Σ\Sigma, defined by:

Σ.E:K×MCΣ.D:K×CM \begin{aligned} \Sigma.E &: \mathcal{K} \times \mathcal{M} \to \mathcal{C}\cr \Sigma.D &: \mathcal{K} \times \mathcal{C} \to \mathcal{M}\cr \end{aligned}

Finally, we need a hash function H:GKH : \mathbb{G} \to \mathcal{K}.

With this, we can define our encryption scheme:

Gen(): aRZ/(q) AaG return (a,A)E(A,m): bRZ/(q) kH(bA) return (bG,Σ.E(k,m))D(a,(B,c)): kH(aB) return Σ.D(k,m) \begin{aligned} &\underline{\text{Gen}():}\cr &\ a \xleftarrow{R} \mathbb{Z}/(q)\cr &\ A \gets a \cdot G\cr &\ \texttt{return } (a, A)\cr \cr &\underline{E(A, m):}\cr &\ b \xleftarrow{R} \mathbb{Z}/(q)\cr &\ k \gets H(b \cdot A)\cr &\ \texttt{return } (b \cdot G, \Sigma.E(k, m))\cr \cr &\underline{D(a, (B, c)):}\cr &\ k \gets H(a \cdot B)\cr &\ \texttt{return } \Sigma.D(k, m)\cr \end{aligned}

Encryption works by generating a new key pair, and then performing a key exchange with the receiver. This ephemeral key is included with the ciphertext, to allow the receiver to perform their end of the key exchange, deriving the symmetric key needed to decrypt the ciphertext.

The CDH Problem

When trying to break this scheme, you see the public key A=aGA = a \cdot G, and the ephemeral key B=bG{B = b \cdot G}. If you could derive abGab \cdot G from these values, you would be able to decrypt ciphertexts. The problem of deriving abGab \cdot G from AA and BB is referred to as the Computational Diffie-Hellman (CDH) problem.

In traditional game-based security, the way you’d model the security of the CDH problem is with a simple game, wherein an adversary tries to guess abGab \cdot G from AA and BB:

a,bRZ/(q)A,B,CaG,bG,abG(A,B)CwinC=C \boxed{ \begin{aligned} &a, b \xleftarrow{R} \mathbb{Z}/(q)&\cr &A, B, C \gets a \cdot G, b \cdot G, ab \cdot G&\cr &&\xrightarrow{(A, B)}\cr &&\xleftarrow{C'}\cr &\text{win} \gets C = C'\cr \end{aligned} }

The advantage in this game would be defined as the probability that win\text{win} gets set to 11; in other words, the probability that the adversary succeeds in finding CC.

For this to be useful in a state-separable proof setting, we’d need to make this into a pair of games, modelling a “real vs ideal” situation. It’s not immediately clear how to do that.

We can emulate the first message sent to the adversary with a method returning that information, so that’s not the issue. In order to emulate the winning aspect, we can have a method which lets the adversary make an attempt, and tells them whether or not they’ve guessed correctly. If, in the ideal case, we make it impossible for the adversary to win, then their distinguishing ability is related to whether or not they can guess correctly. Putting this into a package, we have:

CDHb a,bRZ/(q)A,BaG,bGPair(): return (A,B)Challenge(C): return b=0C=abG \boxed{ \begin{aligned} &\colorbox{#FBCFE8}{\large $\text{CDH}_b$ }\cr \cr &a, b \xleftarrow{R} \mathbb{Z}/(q)\cr &A, B \gets a \cdot G, b \cdot G \cr \cr &\underline{\mathtt{Pair}():}\cr &\ \texttt{return } (A, B)\cr \cr &\underline{\mathtt{Challenge}(C):}\cr &\ \texttt{return } b = 0 \land C = ab \cdot G\cr \end{aligned} }

In the case that b=1b = 1, Challenge\texttt{Challenge} always returns 00, otherwise, it directly tells us whether or not the guess was correct. This means that the difference between the two games is bounded by the probability that an adversary makes a correct guess. If they can’t make a correct guess, then they won’t be able to distinguish the two games.

One subtle difference we’ve introduced with this game is that the adversary is allowed to make multiple different guesses for CC, whereas in the traditional game, the adversary can only make a single guess. We can fix this by modifying Challenge\texttt{Challenge} to return \bot after the first guess, giving us a CDH-1\text{CDH-1} game. It turns out that allowing QQ queries instead of a single one only increases the advantage by a factor of QQ, so we can use the multi-query CDH\text{CDH} variant without hesitation. Proving this, on the other hand, relies on a somewhat technical argument, so the next subsection can safely be skipped.

Single vs Multiple Guesses

We can generalize this proof to many situations beyond just the CDH problem. In general, we define a guessing scheme as a set of types P,S,G\mathcal{P}, \mathcal{S}, \mathcal{G}, along with functions:

Setup:RP×SCorrect:P×S×G{0,1} \begin{aligned} \text{Setup} &: \bullet \xrightarrow{R} \mathcal{P} \times \mathcal{S}\cr \text{Correct} &: \mathcal{P} \times \mathcal{S} \times \mathcal{G} \to \{0, 1\} \end{aligned}

We have some public information P\mathcal{P}, some secret information S\mathcal{S}, as well as a type of guesses G\mathcal{G}. Given all of the information, and a guess, we can determine if that guess is correct. In the case of the CDH problem, the public information is A,BA, B, the secret information a,ba, b, and the guess CC is correct when C=abGC = ab \cdot G.

Given a guessing scheme, we have the associated guessing game:

Guessb (p,s)RSetup()Public(): return pGuess(g): return b=0Correct(p,s,g) \boxed{ \begin{aligned} &\colorbox{#FBCFE8}{\large $\text{Guess}_b$ }\cr \cr &(p, s) \xleftarrow{R} \text{Setup}()\cr \cr &\underline{\mathtt{Public}():}\cr &\ \texttt{return } p\cr \cr &\underline{\mathtt{Guess}(g):}\cr &\ \texttt{return } b = 0 \land \text{Correct}(p, s, g)\cr \end{aligned} }

We also have a restricted version of the game, Guess-1b\text{Guess-1}_b, where only a single guess is allowed; subsequent guesses return \bot.

We prove that GuessbQGuess-1b\text{Guess}_b \leq Q \cdot \text{Guess-1}_b with QQ the number of guesses made.

Proof:

We start with an adversary A\mathcal{A} against Guessb\text{Guess}_b. Our adversary B\mathcal{B} will not be constructed using a wrapper package around Guess-1b\text{Guess-1}_b, like we’ve done with everything else so far. Instead, B\mathcal{B} will itself run A\mathcal{A}, with a simulated version of Guessb\text{Guess}_b, and use its behavior to make a single guess. More formally, B\mathcal{B} plays against Guess-1b\text{Guess-1}_b, with run\texttt{run} behaving as follows:

  1. B\mathcal{B} calls Public\texttt{Public}, receiving pp.
  2. B\mathcal{B} runs A\mathcal{A} with a simulated version of Guess-11\text{Guess-1}_1. In this simulation, Public\texttt{Public} returns the pp that B\mathcal{B} saw earlier, and Guess\texttt{Guess} always returns 00. The guesses {g0,,gQ}\{g_0, \ldots, g_Q\} made by A\mathcal{A} are recorded in a list.
  3. B\mathcal{B} picks one of the QQ guesses at random, and calls Guess\texttt{Guess} with that guess, and then returns 11 if Guess\texttt{Guess} does.

Since B\mathcal{B} will never return 11 against Guess-11\text{Guess-1}_1, we have:

ϵ(BGuess-1b)=P[1BGuess-10] \epsilon(\mathcal{B} \circ \text{Guess-1}_b) = P[1 \gets \mathcal{B} \circ \text{Guess-1}_0]

Denoting the guess B\mathcal{B} picks as gjg_j, this probability satisfies:

P[1BGuess-1b]P[i. gi is correct, j=i]=1QP[i. gi is correct] P[1 \gets \mathcal{B} \circ \text{Guess-1}_b] \geq P[\exists i.\ g_i \text{ is correct},\ j = i] = \frac{1}{Q}P[\exists i.\ g_i \text{ is correct}]

this means that:

Qϵ(BGuess-1b)P[i. gi is correct] Q\cdot \epsilon(\mathcal{B} \circ \text{Guess-1}_b) \geq P[\exists i.\ g_i \text{ is correct}]

But, unless that last even happens, then A\mathcal{A} won’t be able to distinguish between Guess0\text{Guess}_0 and Guess1\text{Guess}_1. Thus we have:

ϵ(AGuessb)P[i. gi is correct] \epsilon(\mathcal{A} \circ \text{Guess}_b) \leq P[\exists i.\ g_i \text{ is correct}]

Putting this all together, we have:

ϵ(AGuessb)Qϵ(BGuess-1b) \epsilon(\mathcal{A} \circ \text{Guess}_b) \leq Q \cdot \epsilon(\mathcal{B} \circ \text{Guess-1}_b)

\square

The IND-CPA\text{IND-CPA} Game, with Random Oracles

After that little detour, let’s get back to the main course. We want to investigate the IND-CPA\text{IND-CPA} security of our hybrid encryption scheme. In this model of security, the adversary tries to distinguish between encryptions of a real message, and encryptions of a random message. In principle, they’re also allowed to make queries for encryptions of messages on their choice, hence the CPA\text{CPA}. One neat aspect of public-key encryption is that we don’t need to allow for these, since the adversary can use the public key to do encryption themself. We also need to model the hash function used inside of our encryption scheme. We model this as a random oracle, making HH a perfectly random function, which we allow the adversary to query. Putting this all together, our game pair looks like this:

IND-CPAb aZ/(q)AaGPk(): return AChallenge(m0): bZ/(q) kH(bA) m1RM return (bG,E(k,mb))h[]H(P): if Ph: h[P]RK return h[P] \boxed{ \begin{aligned} &\colorbox{#FBCFE8}{\large $\text{IND-CPA}_b$ }\cr \cr &a \gets \mathbb{Z}/(q)\cr &A \gets a \cdot G\cr \cr &\underline{\mathtt{Pk}():}\cr &\ \texttt{return } A\cr \cr &\underline{\mathtt{Challenge}(m_0):}\cr &\ b \gets \mathbb{Z}/(q)\cr &\ k \gets \texttt{H}(b \cdot A)\cr &\ m_1 \xleftarrow{R} \mathcal{M}\cr &\ \texttt{return } (b \cdot G, E(k, m_b))\cr \cr &h[\cdot] \gets \bot\cr \cr &\underline{\mathtt{H}(P):}\cr &\ \texttt{if } P \notin h:\cr &\ \quad h[P] \xleftarrow{R} \mathcal{K}\cr &\ \texttt{return } h[P]\cr \end{aligned} }

Instead of actually querying a hash function, instead we treat it as a completely random function, which we also allow the adversary to query. This is the essence of proofs in the “random oracle model”.

Reducing to CDH

We’ll show that the IND-CPA\text{IND-CPA} security of this scheme reduces to the CDH\text{CDH} security of the underlying group, as well as the IND\text{IND} security of the underlying symmetric encryption scheme. In particular, we show that IND-CPA-1b2CDHb+INDb \text{IND-CPA-1}_b \leq 2 \cdot \text{CDH}_b + \text{IND}_b

It’s easier to work with IND-CPA-1\text{IND-CPA-1}. With a hybrid argument, we can show that this only decreases the advantage by a factor of QQ, the number of challenge queries made.

First, let’s separate out the CDH\text{CDH} game:

IND-CPA-1b=Γb0 h[]kRKcount0Pk(): (A,)Pair() return AChallenge(m0): assert count++=0 (,B)Pair() m1RM return (B,E(k,mb))H(P): if CDH.Challenge(P): h[P]k if Ph: h[P]RK return h[P]CDH0 \text{IND-CPA-1}_b = \boxed{ \begin{aligned} &\colorbox{#FBCFE8}{\large $\Gamma^0_b$ }\cr \cr &h[\cdot] \gets \bot\cr &k \xleftarrow{R} \mathcal{K}\cr &\text{count} \gets 0\cr \cr &\underline{\mathtt{Pk}():}\cr &\ (A, \cdot) \gets \texttt{Pair}()\cr &\ \texttt{return } A\cr \cr &\underline{\mathtt{Challenge}(m_0):}\cr &\ \texttt{assert } \text{count++} = 0\cr &\ (\cdot, B) \gets \texttt{Pair}()\cr &\ m_1 \xleftarrow{R} \mathcal{M}\cr &\ \texttt{return } (B, E(k, m_b))\cr \cr &\underline{\mathtt{H}(P):}\cr &\ \texttt{if } \text{CDH}.\texttt{Challenge}(P):\cr &\ \quad h[P] \gets k\cr &\ \texttt{if } P \notin h:\cr &\ \quad h[P] \xleftarrow{R} \mathcal{K}\cr &\ \texttt{return } h[P]\cr \end{aligned} } \circ \text{CDH}_0

The basic idea here is that since the adversary only makes a single challenge query, we can use a hardcoded ephemeral key BB. We can also generate the single symmetric key we need in advance. We need to make sure that when the hash function is evaluated at abGab \cdot G, that this symmetric key is what comes out. We do this by deferring to CDH0\text{CDH}_0, which will give us that information.

Now, when we’re playing against CDH1\text{CDH}_1 instead, then this check will never hold, and thus kk is completely unliked from H\texttt{H}. We can simplify this situation as follows:

Γb0CDH1=Γb1 h[]A,BRGcount0Pk(): return AChallenge(m0): assert count++=0 m1RM kRK return (B,E(k,mb))H(P): if Ph: h[P]RK return h[P] \Gamma^0_b \circ \text{CDH}_1 = \boxed{ \begin{aligned} &\colorbox{#FBCFE8}{\large $\Gamma^1_b$ }\cr \cr &h[\cdot] \gets \bot\cr &A, B \xleftarrow{R} \mathbb{G}\cr &\text{count} \gets 0\cr \cr &\underline{\mathtt{Pk}():}\cr &\ \texttt{return } A\cr \cr &\underline{\mathtt{Challenge}(m_0):}\cr &\ \texttt{assert } \text{count++} = 0\cr &\ m_1 \xleftarrow{R} \mathcal{M}\cr &\ k \xleftarrow{R} \mathcal{K}\cr &\ \texttt{return } (B, E(k, m_b))\cr \cr &\underline{\mathtt{H}(P):}\cr &\ \texttt{if } P \notin h:\cr &\ \quad h[P] \xleftarrow{R} \mathcal{K}\cr &\ \texttt{return } h[P]\cr \end{aligned} }

Since CDH1\text{CDH}_1 doesn’t give us any useful information about AA and BB, we might as well just generate them ourselves. Because our key kk has no relation with the rest of our package, we can now unlink the symmetric encryption aspect, deferring to the IND\text{IND} game for symmetric encryption:

Γb1=Γ2 h[]A,BRGcount0Pk(): return AChallenge(m0): assert count++=0 return (B,IND.Challenge(m0))H(P): if Ph: h[P]RK return h[P]INDb \Gamma^1_b = \boxed{ \begin{aligned} &\colorbox{#FBCFE8}{\large $\Gamma^2$ }\cr \cr &h[\cdot] \gets \bot\cr &A, B \xleftarrow{R} \mathbb{G}\cr &\text{count} \gets 0\cr \cr &\underline{\mathtt{Pk}():}\cr &\ \texttt{return } A\cr \cr &\underline{\mathtt{Challenge}(m_0):}\cr &\ \texttt{assert } \text{count++} = 0\cr &\ \texttt{return } (B, \text{IND}.\texttt{Challenge}(m_0))\cr \cr &\underline{\mathtt{H}(P):}\cr &\ \texttt{if } P \notin h:\cr &\ \quad h[P] \xleftarrow{R} \mathcal{K}\cr &\ \texttt{return } h[P]\cr \end{aligned} } \circ \text{IND}_b

And now all we have to do this is tie all of these games together, to bridge IND-CPA-10\text{IND-CPA-1}_0 and IND-CPA-11\text{IND-CPA-1}_1:

IND-CPA-10=Γ00CDH0ϵ1Γ00CDH1=Γ01=Γ2IND0ϵ2Γ2IND1=Γ11=Γ10CDH1ϵ1Γ10CDH0=IND-CPA-11 \begin{aligned} \text{IND-CPA-1}_0 &= \Gamma^0_0 \circ \text{CDH}_0\cr &\stackrel{\epsilon_1}{\approx} \Gamma^0_0 \circ \text{CDH}_1\cr &= \Gamma^1_0\cr &= \Gamma^2 \circ \text{IND}_0\cr &\stackrel{\epsilon_2}{\approx} \Gamma^2 \circ \text{IND}_1\cr &= \Gamma^1_1\cr &= \Gamma^0_1 \circ \text{CDH}_1\cr &\stackrel{\epsilon_1}{\approx} \Gamma^0_1 \circ \text{CDH}_0\cr &= \text{IND-CPA-1}_1 \end{aligned}

This gives us:

ϵ(AIND-CPA-1b)2ϵ(BCDHb)+ϵ(CINDb) \epsilon(\mathcal{A} \circ \text{IND-CPA-1}_b) \leq 2 \cdot \epsilon(\mathcal{B} \circ \text{CDH}_b) + \epsilon(\mathcal{C} \circ \text{IND}_b)

for some adversaries B\mathcal{B} and C\mathcal{C}, which is what we set out to prove.

\square

Conclusion

Hopefully these examples have helped you get a feel for how state-separable proofs work. A lot of the understanding comes from wrestling with proofs yourself, so hopefully you’ll get a chance to try out the technique with an example that matters to you.

I’ve found working with state-separable proofs to be much easier, and quite a bit of fun. This blog post wasn’t something I would’ve thought to write about, but I felt compelled to after playing around with this technique for a while. I didn’t expect this post to be so long either; hopefully its length was warranted.

I’m also planning to write a more beginner-friendly introduction to provable security. This would be relevant to a larger audience, but I think there’s enough people who could benefit from a more advanced post like this one. I would have liked to have a resource like this when first wrapping my head around state-separable proofs a month ago.

Resources

Mike Rosulek’s The Joy of Cryptography is a neat book which uses this technique pervasively, so this can be an interesting read if you’re looking for more examples.

State Separation for Code-Based Game-Playing Proofs is the original paper introducing the technique, and might be an interesting read if you want to know more details about the formalism.