>> Seny Kamara: All right. So it's a... Mariana is a graduate student at Columbia University. She's...

advertisement
>> Seny Kamara: All right. So it's a pleasure to have Mariana Raykova speaking today.
Mariana is a graduate student at Columbia University. She's interned at MSR Redmond already
twice, and I guess this is her third internship. The first few years she was interning with the
crypto group, and this year she's interning with the security and privacy group. And I guess
she'll be talking about multiparty computation today.
>> Mariana Raykova: Thanks, Seny, for the introduction. And I'm very happy to give this talk
here. This is joint work with the people at Columbia, University of Maryland, and Bell Labs.
These are Dov Gordon, Jonathan Katz, Vlad Kolesnikov, Tal Malkin, and Yevgeniy Vahlis.
And today I'm going to present our work that achieves secure computation with sublinear
amortized work.
So let's start with defining what is the problem of secure computation. And let's go to our
scenario where we have a Bob analysis, and they have some function that they want to compute
together, and this function depends on the inputs that both Bob and Alice have.
So they want to run some protocol that allows them to get the output of this function that
depends on both of their inputs.
So and what is the goal in secure multiparty computation, is to be able to do this computation of
the function so that the only thing that is revealed at the end of the protocol is the output of the
function.
And what you don't want to reveal is anything related to the inputs that are entered in this
functionality or any of the intermediate state that's been computed during the execution of the
protocol.
And for the rest of the talk, we can think of these functions as our running example will be the
function that allows Alice to search the database of Bob. So Bob has some database and Alice
wants to find something in the database, so the function that they want to compute is just finding
this value.
And now looking at what we want to be revealed and what we want to be hidden from this
execution of the protocol is that we want to review the matched value at the end. And we want,
for example, to hide what are the positions in the database were accessed during the search
protocol. So maybe the search involved looking at several different positions until you reach
what you are looking for. And what we want to hide is this access position.
So secure multiparty computation have been around for a very, very long time. And its result, its
main result is very impressive one, and it claims that we can securely compute any function.
So now I was talking for this one example function which is the search functionality, but the
secure multiparty computation says that we have a way to perform this evaluation securely for
any function.
So and what is the idea of most of the protocols is that we want to do the computation looking at
the circuit that's representing this functionality. So we start with either a Boolean circuit or an
arithmetic circuit.
In one case we have gates that are Ns and ors, and in the other case we have gates that are
arithmetic operations, times and plus. And then the computation goes through the eval of this
whole circuit where the parties interact to evaluate each of the gates in this whole circuit.
So this is the main underlying idea in almost all of the existing secure multiparty computation
protocols.
So on the one hand we have this very impressive result that we can do secure computation on
any functionality, but the problem with many of these approaches, almost all of them, is that they
are very inefficient. And this inefficiency somehow seems to be inherent in these approaches.
And there are a few main reasons why this inefficiency is inherent.
So the first reason that we can look at is that the definition of privacy requires you that when you
have a huge input, in order to preserve privacy, you have to touch every single data point in your
input.
And why is that a requirement? Because if you have somebody who is holding your data and
you perform some computation that doesn't touch, for example, the first ten records, the guy who
is holding this data already immediately learns that whatever computation you were performing
didn't meet this part of the database. And this already leak something about the computation,
about the user, the data, and depending what is his external knowledge of this data that is stored,
this can reveal some information about your private computation to this party.
So the next reason is that -- comes from the nature of this computation that works on circuits.
And the problem there is that if you want to do a computation on circuits that involves a huge
database, you're required to take this database as input to your circuit.
And once you include this whole database as input to your circuit, this means that the size of
your circuit will be at least linear in the size of your database. So if you have to deal with a huge
database, you know that the circuit that will correspond to the evaluation of any function on this
huge database will have to be already linear in the size of the database.
And the third reason that we can identify when we are working with circuits and computational
circuits is that if you have some problem that has many different branches and they don't -- they
get executed under different conditions than your input, if you're expressing this functionality as
a circuit, then you have inherently to go through the evaluation of all of the branches of your
program.
So this again requires that if you have input of size N, then any computation that follows the
paradigm of circuits as your representation of your function, will have to take time at least linear
in the size of the database.
So this is what we can achieve with existing schematic computation that uses circumstances as
its tool. And now let's try to forget for a minute about secure computation and look what
happens in practice when we try to compute some functionality without caring about security.
So if we go back to our example with the search functionality, if we have sorted data or hashed
data, then in order to execute a search protocol on big database on N records is no longer -doesn't need to take linear time. We have algorithms such as binary search that requires
logarithmic complexity in the size of the database, or if we use something like hash tables, then
we have just a constant number of lookups.
So now that we have this example where we looked at an algorithm that doesn't have to be
secure, we see that we somehow manage to overcome this need to touch everything in the
database.
So what we can conclude is that existing secure multiparty computation with large databases
somehow inherently stop us from taking advantage of these algorithms that without the security
requirement actually allow us to achieve a sublinear computation or complexity.
So basically this means that using existing techniques computation on very large database is to
the most part very inefficient and very far from practical uses.
So in this work, we tried to answer the question can we do better, can we try to fill this gap that
is between existing algorithms for insecure computation and algorithms that compile them
actually in secure protocols.
So in order to be able to do anything in that direction, we really have to look at computational
complexity in the amortized sense. And what does it mean, amortized complexity. This means
that we will look at the average computational complexity over several multiple executions of a
protocol.
So in our -- in the context of what we'll be doing with secure computation, multiple executions
can be viewed as executions of different functions on the same database, computation with the
same database but with different functions, or maybe we can think of execution of the same
function that has two types of inputs. One of the inputs that is provided by one of the parties is a
small one, and this will be changing with each execution, and then we have the big input that will
be provided by the other guy, and this big input will be some database that will be staying
constant.
If we are thinking about our search example, maybe we have one huge database and then the
small inputs for the different execution is that one of the parties will be searching for many
different things in the different executions of the protocol.
So and we want to achieve this sublinear computation or complexity when we looked at the
amortized computation of complexity over several executions.
And why do we have to do this is because of this inherent requirement that for privacy you have
to touch all the records. And the question is do you have to touch them every time you want to
execute your protocol or you can touch them once in the beginning in some preprocessing stage,
and then from that point on you can leverage off and achieve sublinear complexity.
So, and as I pointed out, circuits on their own do prevent us from achieving this improvement
just by the nature of the way the computation happens in circuits.
That's why we look at a different type of computational model, namely the random access
machine, which is basically the computational model that's closest to what happens in the real
CPU and how a real CPU works in its computation.
So a random access machine model allows us basically to achieve running time that's sublinear
in the input size, so which is to say that if your insecure computation is sublinear time in the size
of the input, your RAM machine is reflecting exactly this computational complexity.
And also when we are using a RAM access machine and we have a program that can have
several different branches, basically the execution of the program doesn't really have to go
through all the branches, but it takes exactly the branch that's necessary for the particular type of
input. And this is -- this gives us leverage to achieve better efficiency.
So our -- if I want to summarize in one slide what is exactly that we manage to achieve is that if
we have -- we give a protocol that allows two-party computation protocol, where you have input
of size N, and then you're running some algorithm that takes T instructions on a RAM machine
when it is executed as a RAM machine, and the computational overhead that we achieve in the
amortized sense for the execution of the protocol in a secure construction is basically a cubed
logarithm in the size of the database or the size of the length of your program, depending which
is the largest one.
And in terms of memory storage that's required from the two parties, we have that one party can
be very limited in its memory and requires only constant memory, and then the other party who
will hold the whole database, the big database, is required to have storage that's linear in the size
of the database.
So our contributions can be viewed as two main points. First we give a generic solution for
sublinear two-party computation that basically proposes a compiler that takes any RAM protocol
and any secure two-party computation protocol and provides a new compiled version of the
secured multiparty computation protocol in the RAM model.
And for this the only tools that we need is some generic two-party computation protocol and
some oblivious RAM protocol.
And it seems a little bit like cheating that we'll be achieving our improvement in efficiency by
using generic two-party computation protocol, which will go eventually back to the circuit
evaluation.
But the point will be that we will find a way to reduce the places where we have to resort back to
this generic two-party computation to very small parts of our overall execution.
So this is the generic result. And then we will look at some optimized efficiency construction
where we'll look at specific instantiations of this oblivious RAM protocol, will look at the
construction of Goldreich and Ostrovsky, and then for the generic two-party computation
protocol we will use the Yao protocol for garbled circuits.
So and just as an example, in the case of binary search, we try to do a comparison where we
count how many gates -- basically the gates that we would need if we involve -- if we use a
generic two-party computation protocol as it is for the whole database, and then compare it to
what happens when we instantiate our construction with a RAM protocol for the same size of
database.
So we looked at an example where we have a database with 10 to the 7th records and each of
size 10 to the 5th bits. And in this case our improvement in efficiency is about to give us about
200 times improvement in the size of the garbled circuits that will need to be evaluated.
So why is this interesting? What is this new computational model considering to consider? It's
because it will be applicable in the cases where we have some computation that involves some
big database, on the one hand, and then on the other hand, we have one of the parties involved in
the computation to be very limited in terms of memory storage.
So in the context of cloud computing, we can see as the big powerful party who is storing the
database to be instantiated as the cloud, and then any little client that is interacting with the cloud
provider will be the party that basically will be limited in terms of its memory storage.
And of course cloud computing in this context can have many applications to medical records,
financial data, location-based services. So that's where we see application of the types of
protocol that we are developing.
So to give you some background on the related work that exists in this area, there have been
several, recently several protocols that look at oblivious RAM. And I will define formally what
oblivious RAM is later.
But just to give you an idea, oblivious RAM basically tries to handle the problem of access
pattern leakage. If you have a huge database and you're accessing different parts of that
database, how can you do that without requiring touching of every single record in the database
and at the same time hiding from the server, for example, who is storing your database, hiding
what parts of your database you're really accessing.
So there have been -- the first work on oblivious RAM was by Goldreich and Ostrovsky in '96,
and then recently last year there has been some work by Pinkas and Reinman [inaudible] by
Damgård, Meldgaard, and Nielsen that have further developed the scheme for oblivious RAM,
and recently there have been even more works that haven't appeared yet in conferences but
appear a lot in print if you're looking there.
So in terms of computational models, the majority of secure multiparty computation works rely
on circuits and use circuits as their underlying computation level. There have been a few
different models that have -- that were used in several different papers. One of them is branching
programs. Another one is OBDDs, which is basically a canonical representation for Boolean
formulas. You can view them as these type of computations.
And then there have been works that consider circuits with lookup tables. The work of
Damgård, Meldgaard, and Nielsen actually has a paragraph that mentioned that we can do
computation using oblivious RAM. But their model is different and they do not require constant
memory storage for either of the participants. And they do not address really -- their goal is not
really to achieve sublinear computational complexity.
So bottom line is that neither of these different computational models manages to go underneath
the sublinear computational complexity requirement for the protocol.
So for the rest of the talk I will present to you our two contributions, which is the generic
protocol and the optimized version. And for that purpose I will introduce you to the RAM
computation model, how it's formally defined. I will tell you what is an oblivious RAM scheme.
And then I will show you what is our generic compiler for multiparty computation using RAM
machine computational models. And then I will show you how we use the construction of
Goldreich-Ostrovsky for oblivious RAM to get our optimized protocol.
So let's start by looking at the RAM computational model. So we can view RAM computation
as execution of a sequence of instructions. And you can think again of the CPU where you have
a series of instructions that tell you what memory you want to fetch and then what type of
computation you want to execute.
So each instruction in our case is either read or write data from some particular memory address.
And then after this read or write of memory, you have some little computation which we will call
this get-next instruction, because this computation both transforms your data and does a little
piece of the computation that you need to perform, but it also tells you what should be the next
instruction that you will be executed -- executing, whether you need to do a read or right on a
data, and in which memory location you need to do this read or write.
So just to try to illustrate what happens with this RAM protocol is that we have in the sense of
two-party computation is that we have two parties and then we have the RAM protocol. So one
of the parties, Alice, sends her data. This is the party that has the very small data. And it sends it
to the RAM protocol. And then from that point on, the execution proceeds as -- the RAM
protocol tells what data it needs to receive. The other party sends that piece of data. The RAM
protocol does the computation it needs and it says whether it needs to write any data back and so
on, so forth.
At the end of the protocol, it computes the function, the value of the function, and it sends it back
to the party that's supposed to receive the output of the functionality.
So I hope that it's clear what the RAM computation model is. And if you have any questions,
like if you'd ask them now, because from now -- from here on I will rely on this computation
model to tell you how we'll make our protocol secure.
>>: [inaudible].
>> Mariana Raykova: Sorry?
>>: Two-party [inaudible]?
>> Mariana Raykova: No. So here one party, the first party, Alice, is supposed to have a very
small input, which is a constant memory. And she can just give it to the computation. If it's a
longer input, you can view it as using this ORAM structure, you can store the input of both
parties in the same ORAM structure and just store it at one of the parties from that point on.
Because the ORAM structure gives you protection for the data, whether its's your data or
whether it's the other party's data as well. You'll get this protection.
So if we want to go back to our binary search example and we want to think how we can
perceive banner search as a RAM computation function, the function that we can see as get-next
instruction is the one that tells us which element we have to look in the database for the binary
search.
Binary search works as you have some sorted array, and then you start looking in the middle and
you figure out from that point whether you have to look to the left or you have to look to the
right. So basically the get-next instruction function, which is performing the computation for us,
will be deciding exactly which position you have to look in the next step of the search.
And then the state information that needs to be kept for the execution of the binary search is
basically the boundaries of the subarray that still needs to be checked in your search.
So in this case, you start always by looking in the middle position in the array, and then your
state remembers which half or next half to be searched, and at the next update for the next get
instruction, you will basically learn the next position that you have to look at.
So now if we want to think of RAM machines and how we want to do secure computation on
RAM machines, we first have to consider the get-next instruction, the one that is doing the
secure computation.
So we will need to find a way to implement this computational instruction in such a way that
neither of the parties learn what is the current state that's kept in the RAM machine, neither the
result of this instruction, basically what will be looked in memory, or whether it will be a write
or read instruction, nor what will be the new state and the new instruction that will have to be
executed next. So this should be the security that is provided for the execution of this
computational step.
And then apart from that, neither party that is executing this protocol will have to -- should be
able to learn anything about the inputs that are being accessed. Because at each -- after each of
these get-next instruction execution steps, we'll have to fetch different pieces of data from
memory, either for the read or for the write.
So basically we will need to guarantee that this sequence of memory lookups that we are doing
are still not linking to either of the parties. Like now we have two parties that we are not taking
to either of them information of what piece of the database is being used.
So this is what we will need in order to implement secure computation for RAM models. And
now a main part for our protocols, we will use protocols for oblivious RAM. And I mentioned,
the goal of an oblivious RAM protocol is to basically hide this access pattern in memory. If you
have a huge database and you want to do queries and access different types of -- different points
in your database, how can you hide your access pattern from the party that's story the database.
So an oblivious RAM protocol basically allows you to compile a lookup query. We can think of
a lookup query as a request for the data, the particular virtual address. And then how do you
compile this query into a sequence of accesses of particular physical locations.
So we will have these differentiations of thinking that queries for particular data are done using
virtual memory addresses, and then the data itself, how it's stored in the physical memory and
where exactly you can find the piece of data that corresponds to virtual address 5.
The physical memory, this will be translated through this sequence of accesses of particular
physical locations.
So an oblivious RAM protocol will have to give you these type of algorithms that allow you to
do this translation from virtual addresses into a sequence of physical locations so that the server
who is accessing this particular physical location still have no idea what data you are really
accessing in your database.
So now an ORAM functionality can be viewed as these two algorithms. One of -- the first one is
basically allowing you to initialize the structure where you'll be keeping your data, and which
will be called the ORAM structure. This will tell you how to arrange your data in physical
memory. Maybe if you need to shuffle it, if you need to order it in any special way.
And then the next algorithm will be the algorithm that basically allows you to execute a read or
write instruction. So this algorithm will take an instruction that refers to particular virtual
address that says read or write data to this particular address, and then compiles it into this
sequence of subqueries that will be referring to particular physical addresses in memory.
So this will be the basic kind of functionality that you want to receive from an ORAM protocol.
And the properties that you want to be guaranteed from this ORAM protocol is of course
correctness; that your reads and writes are consistent, you're reading whatever was written.
And then the most important property is this access pattern hiding, which basically tells you that
observing what has been accessed in memory for two sequences of instructions that are of the
same length. So here A1 through AN and B1 through BN are sequences of instructions, each of
them is either read or write at some particular virtual address.
So for these two sequences of instructions, the execution of the ORAM evaluation algorithm that
allows you to access all these addresses in memory will be indistinguishable for anyone who is
executing this algorithm.
And of course you want efficiency. You want to avoid this linear search. So you want to require
that the execution of the ORAM algorithm achieves logarithmic complexity in the size of your
database. Otherwise, if we go to linear, we might as well scan the whole database and just
retrieve the data item that we needed.
So these were basically the definition of ORAM functionality and the types of property that we
would need.
So now we have the tools that we needed. We have the RAM computation model and the RAM
machines. And we have also this ORAM protocol. So now we can go to our generic
construction. And let me remind you again the goal will be to achieve multiparty computation
protocol that allows you to have sublinear amortized work for each of the executions on the
database.
So now for the generic protocol, the way it will provide is that we'll have first this preprocessing
stage, which would allow us to get rid of this requirement for privacy that we have to touch
every single record in the database.
So the way this will be achieved is that we will run a two-party computation protocol for the
initialization of the ORAM structure. If we need to perform anything in the initialization of the
ORAM structure, which only sets up the structure for where you would hold your memory. And
then you would execute a two-party secure computation in order to insert each data point from
the inputs of both parties in your ORAM structure.
So this would be the part or the preprocessing which would touch each point in the database and
in the inputs. And once you have them stored in this particular memory structure, from that
point on, you will be able to deal with any accesses to those without having to incur this linear
overhead.
So now that we have done this preprocessing stage, the execution for the protocols will go
through this RAM model where you will have the get-next instruction which provides you the
next instruction in the protocol, and then you have the execution of the instruction, which allows
you to fetch the necessary data from memory.
So now the two parties, the way they will do this, is they will run a secure two-party protocol.
But note that this protocol is learned -- is run only on this small functionality, which is just the
get-next instruction that allows you to get the next instruction and do a little piece of the
computation. It doesn't need to be on the whole protocol.
So they will execute this little function in a secure -- in any generic secure multiparty
computation protocol. And the inputs and the outputs that they will have for this will be
basically shares of the state of your RAM protocol. So we were saying that in your RAM
protocol you have a state that helps you get the next instruction that you have to execute and also
keeps some little memory that allows you to do the computation to obtain your final result.
So the way the two parties will be executing is that each of them will be holding a share of this
state. And then they will run a secure two-party computation protocol for the execution of the
small get-next instruction.
And in order to fetch the necessary data from memory, they will have to do a two-party
computation for the ORAM eval function. So the ORAM eval function was the one that allows
you to go to memory and retrieve the pieces of information that you would need. From the
properties of the ORAM eval, we know that this access pattern would not leak any information
about what data you're really accessing.
And now in order to hide this from both parties, we will need to put this execution of the ORAM
access in a two-party computation protocol.
So if you have questions about how this works, please ask me now. I hope that the -- yes.
>>: [inaudible] termination condition, how do you check the quality [inaudible]?
>> Mariana Raykova: So the -- it will be again executed inside the secure multiparty
computation, the two-part -- just the output for the parties. In this case, when it's the stop
condition will be something that each of them can interpret. The shares in this case will be
directly -- the two parties can interpret them as this is the stop condition.
In all the other previous steps, each of the shares will be a random thing that doesn't leak any
information about the intermediate state of the protocol. But once you reach the stop condition,
what you will be returning back will be something that each of them can interpret as, okay, we've
finished and we need to stop.
So any other questions?
>>: I guessed I missed the point of [inaudible] so you use two-party computation [inaudible]
evaluation, ORAM profile and [inaudible]?
>> Mariana Raykova: Yes. The one that tells you what will be the next instruction, what next
you need to fetch from memory.
>>: [inaudible] is that you want to hide [inaudible] what data is being accessed from both
parties?
>> Mariana Raykova: Yeah. So this thing will be -- will come from this part. So you -- the
get-next instruction just tells you the next instruction is read memory 10. Once you have this,
neither party will learn that the next instruction is read memory 10. They will have just shares of
this instruction.
And now with these shares, they will execute this protocol, which again the inputs of this
protocol is the shares of the instruction. And the computation that will happen in this protocol
will be the ORAM eval, which ORAM eval on its own gives you the sublinearity of access to the
data. And the access hiding. But the thing is that the ORAM, on its own, the guarantees that it
gives you is only for one of the parties, the party that holds the database.
Now in this context, because we are running it as a secure multiparty computation, you need to
provide these privacy guarantees with respect to both parties. That's why for this generic
construction we put this in a two-party computation protocol.
Next, when we go to the more efficient construction, you will see how exactly this happens in -with respect to one particular ORAM protocol and the functionality of that protocol.
So the point is that -- yeah?
>>: [inaudible]?
>> Mariana Raykova: This is honest but curious. All ORAM protocols or only honest but
curious.
>>: All the instructions are stored [inaudible]?
>> Mariana Raykova: Sorry?
>>: All of the instructions are stored in [inaudible]?
>> Mariana Raykova: So the thing -- so the RAM that this get-next instruction has a property
that it produces the next one only by looking what's immediately the one that's happened
previously immediately before it. You don't need to remember everything before. You have just
the small state that allows you to compute the next instruction. But you don't need to store -- like
your state for the ORAM is small. It's not long.
>>: Where is the state of the ORAM stored?
>> Mariana Raykova: So the state of the ORAM is in the shares of the parties.
>>: What is shares?
>> Mariana Raykova: Oh. Share means that you have some value and you divide it into pieces
in such a way that if you look at one of the piece on its own, it doesn't reveal any information
about this data. But when you have the two pieces together, you can recover the data.
>>: [inaudible] going to separate, annotate that that's their -- that's the computation?
>> Mariana Raykova: No. So here the two-party computation protocol has this property that
you have as inputs these two shares that don't reveal to each party anything. And then they
execute this series of interactions.
>>: [inaudible].
>> Mariana Raykova: The two parties. The two parties execute the series of interactions.
>>: Okay.
>> Mariana Raykova: So there is like different types of things that they communicate, but the
bottom line is that the properties that a two-party computation protocol that it gives you is that
you learn nothing else except the output. And for the purposes of our overall execution here, the
output is just another random share of the updated state.
So what happens is that the two parties run this protocol in order to update their state. But the
result, the output is still a random share. By just looking at what they received, they cannot
understand what will be the next instruction, for example, that you will read memory 5, or what
was the computation that happened. Until the end, they have just these random shares.
And then at the end when the protocol completes, the guy who was supposed to receive the
output will get the two shares and he will learn only the output but nothing else that happened in
between.
So with this generic construction, we already achieve this logarithmic overhead in terms of
amortized computation complexity. So if we have T instructions and then memory of size N, the
overhead compared to the insecure version that was running in time T, is this logarithm to the
3rd in the size of the database or the size of the program.
And I must point out that this logarithm to the third is really -- has to be translated -- has to be
interpreted as the complexity of the ORAM protocol. For logarithm to the 3rd, we really use the
Goldreich-Ostrovsky protocol. We took the complexity from the Goldreich-Ostrovsky. But if
you have an ORAM protocol that achieves better complexity, you can just plug it. Our
construction is just generic in the sense that you can use any ORAM protocol. You can just plug
it and substitute the complexity with the new complexity of your ORAM protocol.
>>: So are you saying that the database [inaudible] and the amortized time doesn't [inaudible]?
>> Mariana Raykova: So the length of the computation, usually what happens is that these
instructions also get into the database in terms of with the reads and the writes. So somehow the
database, the size of the database already reflects the length of your computation. Because at
each computation you will do something new to the database.
So yeah. I mean, when we have the efficient construction, I will have the computational
complexity in much more fine grain in terms of sizes of records, number of records, number of
instructions. Yeah. But now I just wanted to like simplify what's going on.
>>: [inaudible].
>> Mariana Raykova: The communication complexity is, again, logarithmic. But I think it's log
squared, the overhead.
>>: Oh, log squared and [inaudible]?
>> Mariana Raykova: Yes. Yeah. Yep.
Okay. So now let's move to the optimized protocol. And again this was the picture that we had
before. Now what we will do is we'll look more carefully how do we implement this ORAM
eval, basically the algorithm that allows you to access memory in sublinear computational
complexity and without revealing the access pattern how can you achieve this providing privacy
guarantees with respect to both parties.
Again, if we are just having an ORAM protocol on its own, the kind of guarantee that it provides
is that I'm the client, I put my database over there, I want to access things in the database. And I
want to do this with sublinear computational overhead. But I don't want to hide from the client.
You don't want to provide any privacy with respect to the client. The client is the owner and he
can learn everything.
Now that we are in this setting of two-party computation, we want to provide privacy from both
parties, both the guy who is storing the database, which now can include input from both parties,
this database is not only the client's database, but it's the combined input of both parties, and we
want to provide privacy from the two guys participating in the computation, because this would
relate to the security guarantees for the overall protocol that's the execution of a whole function.
So now in the efficient protocol, as I mentioned, we will try to basically look at the construction
of Goldreich-Ostrovsky and what is happening there. And we will try to minimize what is the
size of the garbled circuits that we will need to use for the execution of that protocol.
So in the generic construction, the only thing that we said is like take whatever that protocol is,
put it in a two-party computation, in a generic two-party computation, which is basically the
garbled circuits evaluation, and you will get the privacy guarantees.
However, so this gives us the theoretical guarantees of polylogarithmic overhead. This already
helps us to get this improvement. However, such a circuit will be kind of complicated in the
sense that these protocols for ORAM include many encryptions, decryptions, evaluations of
pseudorandom functions, reshuffles.
So this whole thing, although it's kind of hidden in the computational complexity that we get
through the generic protocol for practical purposes, this will incur substantial overhead.
So we will look more carefully at what happens in the specific construction from
Goldreich-Ostrovsky, and we will try to tailor it to making it more practical. So basically we'll
minimize the garbled circuits to Boolean comparisons so that the garbled circuits we will have to
use will include only comparison, and then XORs, which XORs in practice for implementations
turn out to be free.
XOR can be free. And then you have a single multiplication. And I have this star there because
we're working on something that actually gets rid of the multiplication in the garbled circuit.
And another contribution that we will have is that we have a new construction for a
pseudorandom function. But pseudorandom function that both the input, the output, and the key
are shared between the two parties.
So in order to get into the optimized construction, I will have to tell you what is the ORAM
construction that was presented by Goldreich-Ostrovsky. So an ORAM construction stores your
database or your memory is organized in this leveled structure. Where if you want to store N
items, your database has N records. You want to have a structure that has log N levels. And
then in each level I, you will be storing two I minus 1 real items. And then you have to store
some dummy items and some empty items.
As I described the protocol, you will see why you need these dummy and empty items. But think
of this that each next level will be larger and larger. And it will include all these kind of
different items.
So also remember that this memory structure will be stored at one of the parties, the one that has
enough memory. So all the items will be encrypted. So everything in this database will be
encrypted.
So additionally, each of these levels that store different items or different records from your
database is divided into buckets. And each bucket will contain a number of different records.
So how do you assign a particular record to a database bucket is you'll be using this hash
function which will need to have the properties of a PRF. And this means is that if you have
some record and you want to find where you will put it in a particular level is you find this
position evaluating the PRF function on your item.
So basically an item with virtual -- with this virtual address will be assigned to the bucket with
number -- the hash function evaluated at the virtual address.
So now if you want to look -- if you're searching for some particular address V, what happens is
that you will be scanning everything that is stored in the first level of the ORAM structure.
And you will be checking whether it's present in any of the positions of the top level. So the top
level acts as -- in a sense as your cache where you have to scan everything in that top level.
And from then on you will be checking one bucket in each of the subsequent levels of the
ORAM. How you would be deciding which bucket to check in each of the following level is by
computing this PRF function or this hash function on the virtual address that you are looking for.
So in the second level you will take the H2, which is the hash function that's corresponding to the
second level, you will evaluate it at V. And you will be looking up that bucket in that level. You
will look at all the records. You will retrieve all the records that are stored in this bucket. And
you will check whether each of any of those matched what you're looking for.
So these buckets will contain a few different records, and you will know that if your record is
present in that level, it has to be in that particular bucket.
So you will be doing this check level by level until you find what you're looking for. So imagine
that in this level we have found the element that we are looking for. Since we want to provide
the guarantees that our access pattern is not leaking when we found the item that we were
looking for, from that point on, after we have located the real item, we start looking at random
places for the remaining levels.
So up to the level where we find the data that we are looking for, from that point on we'll be
looking at random places for the remaining part of the ORAM structure.
And the way we'll be choosing these random places is basically by doing lookups for these type
of dummy items. This will be the role of this dummy item, is that they will help us actually
choose a random indices.
And why we have to go through dummy items I will not explain now, but there is a reason why
we need dummy items as opposed just choosing random index.
So now that we have scanned all these levels, we have done the lookup in all -- at one bucket in
each of the levels. What we will do is we will take the item that we found and we will write it up
on top, in the top level. So we will scan again the top level. We will find the first location that's
empty, and we'll put that item there.
So why do we need to move the item to change its place in the ORAM structure is -- the reason
for this is that if we will do two consecutive lookups for the same item, we want to find it in
different places.
If it stayed in the same level, where we find it the first time, this means that during the next query
that's for the same item, we will have to touch exactly the same place in that level, and this
would already reveal information that you are looking that these two queries were exactly for the
same item.
But now that we have placed it on the top level, we also in each query scan the top level
completely. So in the second query that will be for the same virtual address V, we will find it on
the top level. And from that point on for all of the remaining levels, we will do just these
dummy queries, which will result in completely different access pattern from what we started
with.
So now at each lookup we're moving elements to the top level, we run into the problem that these
levels will start filling up. And once we start filling up levels, we will have to free space
somehow. We'll have to start moving elements from some of the top levels down.
So what happens is when a level fills up, you will take the elements in this level and you will
move them to the level below. And the way you will move them below is you will take the
elements of two consecutive levels, you put them together, you want to shuffle them and write
them back into the new level.
So during this shuffle, basically you'll be choosing a new hash function or a new PRF function
that will assign elements into buckets into this new level. And the type of guarantees you want
to do during this shuffle is basically that you are hiding from the server, from the party who is
holding the database, how you're shuffling the elements, how you are reassigning the elements
from the previous two levels that you're merging into the new buckets.
So the blue and the red in the first -- in the top row basically mean that the elements that came
from the two different levels that you're merging together, and the thing that you will do is you
will permute them and then you will assign them to buckets, bucket 1, bucket 2, and bucket 3.
However, this hash function or PRF function, it might happen that assigns different numbers of
elements into the different buckets.
And in order to hide from the server how full each bucket is, you will use these empty elements
that I was mentioning before. So you will fill up all your buckets with empty elements. And in
order to hide from the server, how full these buckets are.
And all of these things have to happen in such a way that the server, who is storing the data and
who has the large memory, is not learning anything about how elements are assigned to buckets
and how full each of these buckets becomes.
So for this -- in order for this to happen, you will have to use things like oblivious sorting, which
basically means that you are able to sort in such a way that the access pattern in which you're
retrieving items doesn't reveal anything about the permutation that you are sorting according to.
I mean, these are some technicalities that I don't want to go into, but this is some of the tools that
I use to realize the shuffle.
So the privacy from the server in this case, why it comes is that between two shuffles, if we think
about the accesses that are made between two shuffles, we have the property, we're maintaining
the property that we never look for the same virtual address more than once in the same level.
And this comes from the fact that each time we've found an item we move it back to the top. So
any subsequent lookup for the same item between two shuffles will be found on the top level.
So the server doesn't learn where this item was found because you keep on doing these dummy
queries after the point that you have located the item.
And of course between -- after reshuffle what happens is that if you look at the queries before
and after a particular reshuffle, they are independent because you have chosen a new
pseudorandom function that assigns elements to buckets in these levels.
So I want to make a point that actually the size of the buckets in this whole scheme is very
important, because of the reason that I was mentioning, that we don't want the server to learn
whether an item was found in a particular level.
And the problem with the size of the budgets is the following. That sometimes you will -- you
don't -- you want to prevent overflow of the buckets. What does overflow mean, is that you get
more queries that end up touching the same bucket than the size of the bucket.
If this happens, what the server can conclude is that in this sequence of queries that touch the
same bucket, and there are more than the size of the bucket, you have at least one that wasn't
found in that level. And that immediately reveals to the server the fact that one of these -- like
some of this query was found in a different level, which is leakage about the access pattern of the
queries.
So in order to achieve this, you have to guarantee that a level that's of size N or you're holding N
elements will have to have buckets that are of size at least logarithm of N.
If you have -- you're required to have this size in order to guarantee that the probability of
overflow of a bucket will be negligible across the number of queries that happen between two
reshuffles.
So -- yeah?
>>: Your bucket size X, can you access it more than X times? I mean, there can only be
[inaudible] items in there. So it's one of those X plus [inaudible]?
>> Mariana Raykova: Yes. This means that at least one -- you know there are only X queries
and you know that each one is for a different item. So you will know that this one didn't match
there and you are trying to prevent the server from being able to recognize in which level you
found something or you didn't find it.
So I had -- we've been asked this question why didn't use -- why did we go and use
Goldreich-Ostrovsky scheme as opposed to this more recent one that is claiming to achieve a
better complexity, which basically the one from Crypto last year, Pinkas and Reinman. And the
reason why we didn't use this one is that there is an issue with that scheme. And I'll try to like
briefly point out what are the issues.
So in that scheme the assignment of elements for -- of elements to positions in a particular level
is done through this specific special hash that's called a Cuckoo hash. So you no longer have
buckets. So forget about the buckets. So you have directly assignment of elements into
positions in a particular level.
And, again, you have real and dummy elements, which are both assigned through this hash
function. So now the thing is that you have some positions in the level that are filled and you
have some that are empty. This comes just from the nature of the hash function, that you cannot
have a contiguous range that is all assigned to items through the hash function.
And the way they have schemed functions is that the server actually learns which of the positions
are full and which are empty.
So now what happens is that when you do a query for an item that's not in that level, that's not
present in that level and wasn't found above, you will have a nonnegligible probability that you
will touch one of the empty squares. Because you're looking for a real item that wasn't allocated
through the Cuckoo hash for that level. So with high probability we'll go to the -- with
nonnegligible probability we'll go to some of the empty squares.
If the item was found above, you're guaranteed that you will make a query for a dummy item.
And this query is guaranteed to touch one of the full positions because the Cuckoo hash was
created with the full -- with the real and the dummy queries.
However, if you're searching for something that will be found later in some of the subsequent
levels, then you will end up touching one of the empty squares. And this will reveal to the server
the information that you actually didn't find this item in this level.
So one of the fixes, you can think of fixing this by just doing another permutation that you go
over the empty and the full squares. However, this doesn't solve completely the problem, and
this problem was independently pointed out both by us in our work and then at the work of
[inaudible] that's in [inaudible] this year.
And basically the problem is that -- relates again to this overflowing part that if you have two
sequences of queries, it's likely that the sequence of queries that are not present in this level and
will be found later will produce lookups that are incompatible with the Cuckoo hash table.
Which means, again, that you will hit basically one square more times than what you are
supposed to hit it if this was an element that was allocated through the -- to the level through the
Cuckoo hash table.
So if you -- probably you understood this if you have looked at the two protocols and you know
what's going on. But I just wanted to point out for those who know the scheme why we didn't
use the Pinkas-Reinman.
So, as I said, now we want to improve the two-party execution for the ORAM access in a better
way than just a generic one that will put everything in the circuit.
So what we will do is, again, we will use the small circuits that will basically include only a
comparison at each level. Then we will use this new PRF that will construct to assign elements
to buckets, hiding what is this assignment from both parties. And then we'll have some efficient
decryption for the garbled -- in the garbled circuit.
So remember that we were looking -- we had to look and compare what we were looking for
with a bunch of elements at each level. So how we would do this is that we will implement in
secure computation just this comparison that we have this item that we are looking for, and then
we have all the records that we retrieved from the bucket. So we need to compare this item to
each of the elements of the bucket.
So this is what we will have in our garbled circuit. And then the state that each of the parties will
keep will be either a share of something random, if we haven't found the element in the above
levels, or at the point when we hit and we find the level, the two shares of the two parties will be
now shares of the item or the data that we found.
So at each level we'll have this secure computation where the two parties give the two shares. If
we have found the items, whatever happens in that circuit doesn't really matter. It's some
comparison that we don't care about. If the item hasn't been found, we are comparing the virtual
address that we are looking, for example, 5 is the element, with virtual address 5 among the
records that we retrieved from this bucket.
And if we find it, we return shares of the data that's retrieved from that, that was found at virtual
address 5.
So the question is how do we -- how do we know which bucket we have to get. In order to tell
the party that's holding your database give me the elements from bucket No. 5, we have to
evaluate a PRF for this HI on the virtual address. And the virtual address is shared between the
two parties. So each party has a share of this virtual address.
So we can imagine doing this in a garbled circuit by implementing AES in a garbled circuit, but
this would result in 30,000 gates for a Yao garbled circuit that's implementing AES.
So what we do is we have a construction of a shared oblivious PRF. And the functionality that
this primitive implement is that you have both the key for the PRF shared between the two
parties, the message on which the PRF will be evaluated. Evaluate is, again, shared between the
two parties. And then the output of the PRF will be also shares that are given to each of the
party.
So in this case, what you would think is that the message that you want to put in the PRF is your
virtual address and then each party will have part of the key. And then at the end each party will
receive a share of the PRF value.
So this sharing the final output of the PRF will be important only in the shuffled part. In the
lookup part just one of the parties will send its share to the other one, the one that has to locate
the proper bucket from the database and retrieve that bucket.
So for our construction, we use the Naor-Reingold PRF and the kind of functionality that we will
achieve is that this PRF, the key again will be shared and the message will be shared, but I don't
have time to go into this construction.
So just to point out is that initially what you get from the PRF is that you have the share that the
final value is the first one exponentiated to the second share. And then we run a special protocol
that allows us to transform this into multiplicative shares. And this is where we have the single
multiplication that we need to perform in the garbled circuit.
But we have now a protocol that allows you to get the multiplicative shares and actually get to
additive shares.
So now we have this way we have located the item. Now we have the secure -- the circuit that
allows us to compare values.
However, if you remember the items that we're retrieving from the buckets are all encrypted. So
we can -- in order to be able to do this check, whether we have found the correct item or not, we
have to do a decryption. And decryption in a garbled circuit is actually very expensive. If you
are using like a regular public encryption scheme, the decryption becomes very expensive.
So the way we will do encryption is in this special way. Here F denotes a PRF function. And
the key for this PRF function will be hold only by the weak parts. It doesn't have memory. So
the weak parts you will have this, the key for the PRF function.
And in order to encrypt the value V, the encryption will be the XOR of this value V with the
PRF function evaluated at some random point R, which will be also part of the encryption.
So now if you want to decrypt some value, what happens is that the guy who has the encryption,
the party that's holding the database, will send the randomness to the guy who has the key for the
PRF. That guy will evaluate the PRF on this randomness. And now the input for the garbled
circuit or the decryption in the garbled circuit becomes the single XOR, which is free of this part.
And then the other guy will input as his input the PRF function evaluated at the randomness.
So now the decryption in the garbled circuit becomes this single XOR, which is basically free for
an implementation of a garbled circuit. So this is the way we want to do efficient decryption in
the circuit.
And -- sorry?
>>: Does that mean that two-party [inaudible] have to jointly encrypt data items in the
initialization phase?
>> Mariana Raykova: So, yeah, the initialization phase, when you do, you do -- yeah, you have
to run the protocol. You write an item into the database. This already takes care of this, the fact
that in the encryption you will ->>: So that means the database can only work for one specific user?
>> Mariana Raykova: So this is one of the open questions, is you can't have -- we don't know to
do an ORAM protocol where you can have multiple users using the database. Yeah. All
existing ORAM protocols can work with one client. And this comes from the fact that the
encryption, this one client knows the key for the encryption.
So and I will really skip what's going -- this is just a repetition again of what is happening in the
lookup.
And then we have to be careful what's going on in the shuffle when we want to merge two levels,
because now we have to be hiding the permutation and how items are shuffled from both parties.
So here we crucially use this property, that the output of the PRF function is actually shared.
And nobody -- none of the two parties learn what is the exact PRF output. So we manage to get
a protocol that allows us to sort and shuffle the items without revealing to either of the parties
what is the new permutation that assigns items to buckets in the new level.
And so now the exact overhead that we are getting is if we have a protocol that's running in time
T on a RAM machine, and then we have a database of N elements, and each of those elements is
of size N bits, then under the DDH assumption, we achieve a protocol, a secure protocol for the
evaluation of this function against honest and curious adversary that basically has this
polylogarithmic amortized computation and communication overhead.
And, again, we have constant memory storage for one of the parties, for the weak party, and the
other party, which is storing the database, we need storage that's linear in size of the database
basically.
And this database, if you are thinking that both of the parties had some huge input, then both of
inputs get written into this ORAM structure and they are stored just on one of the parties, the one
that has enough memory.
And just in summary, so we introduce the first general secure computation protocol that allows
us to do actually computation on large inputs and achieve a computation complexity sublinear in
the size of the database, or polylogarithmic in the size of the database.
So open directions for improvement is definitely improving the overhead of this oblivious
memory access when we have to provide privacy for both parties. It would be nice if we can get
some better, more improved construction for this shared through the random function, because
the one now requires interactions.
So this would be interesting and, the question that you mentioned, support for multiple clients.
We don't even have an oblivious RAM protocol that allows us to deal with multiple clients. So if
you now want to do this database, allow computation for multiple clients, it would be nice to get
something there. But the problem is, again, encryption and who holds the keys for the
encryption.
So I would like to thank you, and, if you have any further questions, take them now.
[applause]
>> Seny Kamara: Let's thank Mariana again.
[applause]
Download