Skip to content

Commit

Permalink
Edit intro stuff into the file
Browse files Browse the repository at this point in the history
  • Loading branch information
tideofwords committed Jun 10, 2024
1 parent b167fad commit ba7a02e
Show file tree
Hide file tree
Showing 9 changed files with 305 additions and 48 deletions.
3 changes: 3 additions & 0 deletions easy/main.typ
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#import "src/preamble.typ":*

#let chapter(filename) = {
include filename
pagebreak(weak: true)
Expand All @@ -23,6 +24,8 @@
#toc
#pagebreak()

#chapter("src/intro.typ")

#part[Oblivious transfer, garbled circuits, and multiparty computation]
#chapter("src/mpc.typ")
#chapter("src/ot.typ")
Expand Down
15 changes: 12 additions & 3 deletions easy/src/fhe0.typ
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,18 @@
= Introduction to fully homomorphic encryption
<fhe-intro>

Fully homomorphic encryption (FHE) lets you encrypt a message, and then
other people can perform arbitrary operations on the encrypted message
without being able to read the message.
Alice has a secret $x$, and Bob has a function $f$.
They want to compute $f(x)$.
Actually, Alice wants Bob to compute $f(x)$ -- but
she doesn't want to tell him $x$.

Alice wants to encrypt $x$ and send Bob $Enc (x)$.
Then Bob is going to "apply $f$ to the cyphertext",
to turn $Enc (x)$ into $Enc (f(x))$.
Finally, Bob sends $Enc (f(x))$ back,
and Alice decrypts it to learn $f(x)$.

This is fully homomorphic encryption (FHE).

Levelled FHE is a sort of weaker version of FHE. Like FHE, levelled FHE
lets you perform operations on encrypted data. But unlike FHE, there
Expand Down
33 changes: 24 additions & 9 deletions easy/src/fhe2.typ
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ The
learning with errors
problem (@lwe) is one of those "hard problems that you can build cryptography
on." The problem is to solve for constants
$ a_1, dots, a_n in ZZ / (q ZZ), $ given a bunch of
$ a_1, dots, a_n in ZZ \/ q ZZ, $ given a bunch of
#emph[approximate] equations of the form
$ a_1 x_1 + dots.h + a_n x_n = y + epsilon.alt , $ where each
$epsilon.alt$ is a "small" error (in the linked example, $epsilon.alt$
Expand Down Expand Up @@ -111,12 +111,28 @@ Let’s say you randomly choose the first 4 rows:
, kind: table
)

Now you add them up to get the following. | $upright(bold(x)) : y_0$ | |
\- | | (7, 5, 1, 6) : 6 |
Now you add them up to get the following.
#figure(
align(center)[#table(
columns: 1,
align: (auto,),
table.header([$upright(bold(x)) : y_0$],),
[(7, 5, 1, 6) : 6],
)],
kind: table
)

Finally, let’s say your message is $m = 5$. So you set
$y = y_0 - m = 6 - 5 = 1$, and send the cyphertext: |
$upright(bold(x)) : y_0$ | | - | | (7, 5, 1, 6) : 1. |
$y = y_0 - m = 6 - 5 = 1$, and send the cyphertext:
#figure(
align(center)[#table(
columns: 1,
align: (auto,),
table.header([$upright(bold(x)) : y_0$],),
[(7, 5, 1, 6) : 1],
)],
kind: table
)

== Decryption
<decryption>
Expand Down Expand Up @@ -166,7 +182,6 @@ rather than just a single bit.

When we do FHE, we’re going to apply many operations to a cyphertext,
and each is going to cause the error to grow. We’re going to have to put
some effort into keeping the error under control – and then, when the
error inevitably grows beyond the permissible bound, we’ll need a
special technique ("bootstrapping") to refresh the cyphertext and start
anew.
some effort into keeping the error under control –
and the size of $q\/ r$ will determine how many operations
we can do before the error grows too big.
81 changes: 51 additions & 30 deletions easy/src/fhe3.typ
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,55 @@
<fhe>

== The main idea: Approximate eigenvalues
<the-main-idea-approximate-eigenvalues>
If you haven’t already, this might be a good time to go back and read
about the
#link("https://notes.0xparc.org/notes/learning-with-errors-exercise")[learning with errors]
problem and how you can use it to do
#link("https://hackmd.io/mQB8_nWPTm-Kyua7QgNLNw")[public-key cryptography];.

You should at least understand the vague idea: We’re going pick some

Now we want to turn the public-key encryption from @lwe-crypto
into a levelled FHE scheme.
In other words:
We want to be able to encrypt bits (0s and 1s)
and operate on them with AND and NOT gates.

It might help you to imagine that, instead of AND and NOT,
the operations we want to encrypt are addition and multiplication.
If $x$ and $y$ are bits, then
NOT $x$ is just $1 - x$, and $x$ AND $y$ is just $x y$.
But it's easier to do algebra with $+$ and $*$.

Recall the setup from @lwe-crypto: We’re going pick some
large integer $q$ (in practice $q$ could be anywhere from a few thousand
to $2^1000$), and do "approximate linear algebra" modulo $q$. In other
words, we’ll do linear algebra, where all our calculations are done
modulo $q$ – but we’ll also allow the calculations to have a small
"error" $epsilon.alt$, which will typically be much, much smaller than
$q$.

Here’s the setup. Our #emph[secret key] will be a vector
\$\\mathbf{v} = (v\_1, \\ldots, v\_n) \\in (\\ZZ / q \\ZZ)^n\$ – a
Here’s the new idea.
Our #emph[secret key] will be a vector
$ upright(bold(v)) = (v_1, dots, v_n) in (ZZ \/ q ZZ)^n $ – a
vector of length $n$, where the entries are integers modulo $q$. Suppose
we want to encode a message $mu$ that’s just a single bit, let’s say
$mu in { 0 , 1 }$. Our cyphertext will be a square $n$-by$n$ matrix $C$
$mu in { 0 , 1 }$. Our cyphertext will be a square $n$-by-$n$ matrix $C$
such that $ C upright(bold(v)) approx mu upright(bold(v)) . $ Now if we
assume that $upright(bold(v))$ has at least one "big" entry (say $v_i$),
then decryption is easy: Just compute the $i$-th entry of
$C upright(bold(v))$, and determine whether it is closer to $0$ or to
$v_i$.

With a bit of effort, it’s possible to make this into a public-key
cryptosystem. The main idea is to release a
#link("https://hackmd.io/mQB8_nWPTm-Kyua7QgNLNw")[table] of vectors
cryptosystem. Just like in @lwe-crypto,
the main idea is to release a
table of vectors
$upright(bold(x))$ such that
$upright(bold(x)) dot.op upright(bold(v)) approx 0$, and use that as a
$ upright(bold(x)) dot.op upright(bold(v)) approx 0, $ and use that as a
public key. Given $mu$ and the public key, you can find a matrix $C_0$
such that $C_0 upright(bold(v)) approx 0$ – then take
$C = C_0 + mu \* upright(I d)$, where $upright(I d)$ is the identity
such that $ C_0 upright(bold(v)) approx 0 $ – then take
$ C = C_0 + mu upright(I d) $, where $upright(I d)$ is the identity
matrix. And $C_0$ can be built row-by-row… but we won’t get into the
details here.

Indeed homomorphic encryption is already interesting without the
public-key feature. If you assume the person encrypting the data knows
$upright(bold(v))$, it’s easy (linear algebra, again) to find $C$ such
that $C upright(bold(v)) approx mu upright(bold(v))$.
that $ C upright(bold(v)) approx mu upright(bold(v)). $

To make homomorphic encryption work, we need to explain how to operate
on $mu$. We’ll describe three operations: addition, NOT, and
Expand Down Expand Up @@ -97,10 +105,11 @@ value of $a_1$.

Now suppose I give you the vector
$ upright(bold(x)) = (9 , 0 , 0 , 0) . $ I ask you for another vector
$ "Flatten" (upright(bold(x))) = upright(bold(x)) prime , $ where
$upright(bold(x)) prime$ has to have the following two properties: \*
$upright(bold(x)) prime dot.op upright(bold(v)) = upright(bold(x)) dot.op upright(bold(v))$,
and \* All the entries of $upright(bold(x)) prime$ are either 0 or 1.
$ "Flatten"(upright(bold(x))) = upright(bold(x)) prime , $ where
$upright(bold(x)) prime$ has to have the following two properties:
- $upright(bold(x)) prime dot.op upright(bold(v)) = upright(bold(x)) dot.op upright(bold(v))$,
and
- All the entries of $upright(bold(x)) prime$ are either 0 or 1.

And you have to find this vector $upright(bold(x)) prime$ without
knowing $a_1$.
Expand All @@ -119,9 +128,10 @@ safe to reduce it mod 11.
Similarly, if you know $upright(bold(v))$ has the form
$ upright(bold(v)) = (a_1 , 2 a_1 , 4 a_1 , dots.h , 2^k a_1 , a_2 , 2 a_2 , 4 a_2 , dots.h , 2^k a_2 , dots.h , a_r , 2 a_r , 4 a_r , dots.h , 2^k a_r) , $
and you are given some matrix $C$ with coefficients in
\$\\ZZ / q \\ZZ\$, then you can compute another matrix $"Flatten" (C)$
such that: \* $"Flatten" (C) upright(bold(v)) = C upright(bold(v))$, and
\* All the entries of $"Flatten" (C)$ are either 0 or 1.
$ZZ \/ q ZZ$, then you can compute another matrix $"Flatten"(C)$
such that:
- $"Flatten"(C) upright(bold(v)) = C upright(bold(v))$, and
- All the entries of $"Flatten"(C)$ are either 0 or 1.

The $"Flatten"$ process is essentially the same binary-expansion process
we used above to turn $upright(bold(x))$ into $upright(bold(x)) prime$,
Expand All @@ -131,7 +141,7 @@ So now, using this $"Flatten"$ operation, we can insist that all of our
cyphertexts $C$ are matrices with coefficients in ${ 0 , 1 }$. For
example, to multiply two messages $mu_1$ and $mu_2$, we first multiply
the corresponding cyphertexts, then flatten the resulting product:
$ "Flatten" (C_1 C_2) . $
$ "Flatten"(C_1 C_2) . $

Of course, revealing that the secret key $upright(bold(v))$ has this
special form will degrade security. This cryptosystem is as secure as an
Expand Down Expand Up @@ -170,16 +180,27 @@ bounded by $(n + 1) B$.

In summary: We can start with cyphertexts having a very small error (if
you think carefully about this
#link("https://hackmd.io/mQB8_nWPTm-Kyua7QgNLNw")[protocol];, you will
protocol, you will
see that the error is bounded by approximately $n log q$). Every
addition operation will double the error bound; every multiplication
("and" gate) will multiply it by $(n + 1)$. And you can’t allow the
(AND gate) will multiply it by $(n + 1)$. And you can’t allow the
error to exceed $q \/ 2$ – otherwise the message cannot be decrypted. So
you can perform calculations of up to approximately $log_n q$ steps. (In
fact, it’s a question of #emph[circuit depth];: you can start with many
more than $log_n q$ input bits, but no bit can follow a path of length
greater than $log_n q$ AND gates.)

This gives us a #emph[levelled] fully homomorphic encryption protocol.
Next we’ll see a trick called "bootstrapping," which lets us turn this
into FHE.
This gives us a #emph[levelled] fully homomorphic encryption protocol:
it lets us evaluate abritrary circuits on encrypted data,
as long as those circuits have bounded depth.
If we need to evaluate a bigger circuit, we have two options.
+ Increase the value of $q$.
Of course, the cost of the computations increases with $q$.
+ Use some technique to "reset" the error
and start anew, as if with a freshly encrypted cyphertext.

This approach is called "bootstrapping" and it incurs some hefty
computational costs.
But for very, very large circuits, it's the only viable option.

Bootstrapping is beyond the scope of this book.
29 changes: 29 additions & 0 deletions easy/src/h-zksnark.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#import "preamble.typ":*

// copied from bigger project, incorporate into this one

This part covers two constructions of the zkSNARK,
the *PLONK* and *Groth16* constructions.
Despite being fairly modern constructions,
these are arguably simpler and more informative to learn about than
the PCP construction that preceded them (which is covered in @pcp).

The dependency chart of this chapter goes as follows:

- @ec describes the discrete logarithm problem on an elliptic curve,
which provides a basis for everything afterwards.

- @kzg and @ipa give two different *polynomial commitment schemes*,
which allow a prover Peggy to

- commit to some polynomial $P(X) in FF_q [X]$ ahead of time,
- and then *open the commitment* at any input $z in FF_q$ while not revealing $P$ itself.

The KZG scheme from @kzg is quite simple and elegant but requires a trusted setup.
In contrast, IPA from @ipa has fewer assumptions and is more versatile,
but it's slower and more complicated.

- Regardless of whether KZG/IPA scheme is used,
we then show two constructions of a zkSNARK.
In @plonk we construct PLONK;
in @groth16 we construct Groth16.
Loading

0 comments on commit ba7a02e

Please sign in to comment.