 for verifiable function secret sharing by Leo D'Castro and Antigone. And Leo will give the talk. Yeah. Great. Hi. Yeah, so we're talking about lightweight, maliciously secure verifiable function secret sharing. So in this talk, we'll be constructing a two-party function secret sharing scheme for distributed point functions that will be verifiable, maliciously secure, unconstrained, which means that it will support any output group in any set of evaluation points. It will be concretely efficient. We'll start with the very efficient scheme of Boyle, Gilboa, and Ishai. And we will use only symmetric key operations to build our verifiable technique. And we'll also extend this to multi-point functions kind of beyond the naive construction. This is in contrast to prior works that are constrained either in their security or in the output space that they support. And prior works also use public key operations and NPC. So everything in this talk will be in the two-server model, which is where the client talks to two servers at once. The two servers are allowed to communicate with one another, but really the main assumption that we have here is that at least one of the servers is always honest. So what does that really mean in terms of malicious security? It means that we can handle the malicious corruption of any one of these three parties. So in particular, for a corrupt client, if the client tries to send a malformed function secret share, the servers will catch the client. And if one of the servers is corrupted, we have the client's privacy is maintained. Just one quick note. This model might seem a bit contrived, but actually because you can build so many efficient protocols in this model, it's actually used quite a bit in practice. Many US states use this like Apple and Google to two-server protocol to collect COVID data. Mozilla uses a multi-server protocol to collect and store private browser data. OK, so a brief introduction on function secret sharing. What is function secret sharing? It's a secret sharing scheme for functions. What does that mean? Well, you can take a function and share it into two shares here, k0 and k1. And these shares have a natural hiding property where if you see only one share, you don't learn anything about the function f. And the shares have a nice property where they can be evaluated. So you can take an element x in the function domain and evaluate the share at x. And what you get out is an additive share of the function output at x. So if server 0 and server 1, you evaluate their shares at the same point x, they will get additive shares of the function output at x. So the function class that we'll be focusing on here is the class of point functions. Point functions are quite simple but quite powerful. They're specified by a single input-output pair, alpha and beta. And really, the function is just if the input is alpha, you output beta, otherwise you just output 0. A FSS for point functions is called a distributed point function scheme. And just a quick note, we're not trying to hide the fact that f is a point function. We're trying to hide which non-zero point f is specified by. So we're going to be building off of the FSS scheme of Boyle, Gilboa, and Ishai. This is a very efficient scheme. I'm not going to go into the details of this scheme. This tree is just here to refresh your memory if you've seen this scheme before. The key idea behind this scheme, though, is that the point functions is very close to the zero function. And sharing the zero function is very easy via PRFs. You just send the two servers the same PRF seed and you just have server 1 negate their output. So if you want to then move to point functions, you just puncture this PRF at some point, which I hide the point that you're puncturing at. But yeah, this is the idea behind their work. And really, the main takeaway here is the efficiency. So for an output space of size 2 to the n, this construction has key sizes of size 1 times n and the evaluation time is also 1 times n. It's a depth n tree and you evaluate a constant number of PRGs at each level. So how do we verify that a point function share is well formed? Well, we start with the servers having shares of some output vector y. And this y is the set of evaluations on whatever points the servers have chosen to evaluate their shares at, whereas assuming that the servers are evaluating on the same set. And really, all the servers are trying to check is that y is only non-zero in at most one location. So y is the truth table of some point function. So prior works for this are based off of linear sketching and NPC. And they really all rely on this Schwartz-Zippelema, which really limits the technique because the Schwartz-Zippelema relies on having a very large output field. So you can really only hope to get an output field of size 2 to the lambda. And also prior works only achieve semi-honest security for a general output space. The work of Bonnet, Boyle, Corgan Gibbs, Gilboa, and Ishai achieves malicious security when the valid betas are 0 or 1. But crucially, you still have to share these binary vet values over a large field. And then all of these works require public key operations and NPC. So there's definitely room for improvement. OK, so let's move into this work. We start with a similar observation that the point function is very close to the zero function. And the zero function is actually very easy to verify because if the only valid value for y is all zeros, that means that the share held by server 1 is the negation of the share held by server 0. You can just have server 1 negate their share. And now the servers have the same vector, which means that the verification check is just an equality check, which can be done by just hashing down these vectors and checking that the hashes are equal. So one key observation here is that because all honest shares will pass this test, this is a very nice defense against a malicious server because a malicious server, we assume, has only honest shares. So this malicious server will not receive any new information from this check. It already has the hash that it's going to receive from the other server. So now we're going to sketch the idea to take this technique from zero points to one point. We're going to really just do the toy version of this idea. This is a central idea to the Boyle-Goboa-Ishai construction if you are familiar with it. This will look familiar. But yeah, so let's begin. So the servers have shares of a vector y that is zero in every location, except for some point where it's the shares of beta. We want to somehow have the client correct that share of beta to be a share of zero or to perform some correction operation such that the shares that the servers have become the same. So we want to define this correction operation. That's a function of the share and some correction word that the client has sent to both servers. And the correction operation needs to have the following properties. First it needs to map all of the shares of zero to be the same, to be equal. So we're not going to like destroy our equality on all of these other points. And it also needs to map the share of beta to equal values too so that the whole check just becomes an equality check. To make this not trivial though, we need to have the client, we need to rather prevent this correction operation from mapping really any other value to be equal. So we're going to define this correction operation as a conditional XOR, where for each share we're going to have a control bit t. And t will tell you whether or not to XOR omega to your share. So if t is zero, you just output your share. Otherwise, you output your share XOR with omega. And so when the shares are zero, we really want these t bits to be the same. That way that the operation applied on the two shares will be the same when we maintain the equality for these shares. On the other hand, when the shares are shares of beta, we want the control bits to be different so that the correction operation is applied asymmetrically so that the outputs will be equal. So we want to apply this correction operation on every share. And so we need the shares to be equal for all of these control bits, rather, to be equal for all of the zero shares and differ on the shares of beta. So the way that we're going to do this is observe that the shares of zero are equal, or they map easily to equal values, whereas the shares of beta are not equal. And so we're just going to define some deterministic predicate of the shares to generate our control bits. And least significant bit is just a convenient choice. You can really pick anything you would want here. And so this is what our correction operation will be. We're going to take the shares and take the least significant bit. If it's zero, just output the share. Otherwise, XOR with omega. OK, so let's go over this again with a little bit closer to the real correction operation. So instead of having omega be the XORs of just the beta shares, we're actually going to have omega be the XORs of the hashes of the beta shares. This is for security. And the correction operation is as follows. It takes in a share U. And if you're server one, you negate your share. This way, if the two servers have shares of zero, now their two shares are the same. Then the correction operation hashes the share. And then if the least significant bit of the hash is one, output the hash XOR with omega. Otherwise, just output the hash. So this has all of the properties that we want. If the input shares are zero, then the outputs are equal. And now all the client has to do is sample the function shares such that the least significant bits of the hashes are not equal. That this way, the correction operation will be applied symmetrically. And because there's really only one set of shares the client needs to check, and this happens with good probability, this doesn't incur too much overhead in the share generation. In order to defend against a malicious client, we need to then argue that the client can really only correct one difference with this correction word. And this is not too hard to show that if the client is able to do this, and they found this funny collision in the hash function. This is like this XOR collision resistance. This is given in more detail in the paper. But basically, we are assuming that this XOR collision resistance is hard for that. The hash function, we have this large hash function output to make this hard. And given this hardness, it's not hard to show that the client can only correct one error in their vector. So now this gives us the full verification operation where we have server 0 and server 1 beginning with shares of Y and their correction word omega. They apply the correct operation to each element in their output vector. And then they hash down this vector to be some value pi. They exchange their pies and accept if they're equal. And this is secure against a malicious client because of the XOR collision resistance of the hash function. And it's secure against a malicious server because the malicious servers don't receive any new information about the shares when the shares are honestly generated. Just a quick note about performance. The performance overhead for this verification method is very low. The share generation has a roughly 2x overhead because the client has to resample when the predicates at the shares of beta are equal because you need them to be unequal in order for the correction operation to work correctly. And the share evaluation overhead is very low because if you remember that GGM tree, this is basically the same complexity as just performing one extra level in that tree. OK, so in the last few minutes, we're going to extend this to a multipoint functions. So first, what is a multipoint function? Well, it's a function with a small number of non-zero points. In particular, the number of non-zero points is not something that we're trying to hide. So the function is specified by a set of points alpha i and beta i. And these are the only non-zero points that are going to be in the function. So if x is one of the alpha i's, then the functional output, beta i, otherwise it will output zero. You can view this as the sum of t-point functions. Because you can do this as the sum of t-point functions, this is kind of natural, naive approach to building distributed multipoint functions where you just concatenate t-point functions. This works reasonably well until you go to evaluate. And if you have a lot of non-zero points, then you have to evaluate every single point function share in order to evaluate the multipoint function share. This is a waste of work because you know that only one will evaluate to a non-zero value. So we want to try to reduce this extra work. So the trick that we're going to use is cuckoo hashing. We're not going to go into the cuckoo hashing technique. But basically what it does is it packs t elements into a table of size m. And the guarantee that it gives you is that if an element is in the table, it can only be in a constant number of locations. And these locations are specified by these kappa hash functions. So the concrete parameters for this cuckoo hashing scheme are very good. For kappa equals 3, we can get an overhead of less than a factor of 2 for this very large range of non-zero points. So how do we use cuckoo hashing to build our multipoint function shares? Well, the client takes the alpha points in the multipoint function and inserts them into a cuckoo hash table with m buckets. So there's t points where the function is non-zero and they get mapped to a table with m buckets. And then for each bucket, the client creates a distributed point function. So if the bucket has some element alpha j, then the client creates a point function corresponding to the point alpha i beta i. And if the alpha j beta j, rather, and if the bucket is empty, then it just shares the zero function. And so the multipoint function share is just the concatenation of these m point function keys along with the hash functions. And this is all sent to the servers. Now when the servers go to evaluate, they only need to check, instead of evaluating all t point functions, they only need to check the point functions at the kappa locations specified by the hash function. So they hash their input x with each of the hash functions. This gives them kappa indices. And now they only need to check these particular kappa point functions to see if x evaluates to some non-zero point. Yeah, so this is a huge savings when t becomes large because the client performance really only doubles. And the server performance is kind of independent of the non-zero points. Actually, in the paper, we show a neat trick where you can actually have the server evaluation time shrink as the number of non-zero points grows. But we don't have time to get into that. So the only thing we really give up here is the verification tightness. You could hope that the best you can really hope for here is that if you have t non-zero points in your multi-point function that you check that it really is just t non-zero points in your function share. But because we have m buckets in our cuckoo hash table, we can really only check that there are, at most, m non-zero points in the point function. But for PSI applications and things like that, we believe this is acceptable. You can say, OK, the honest client will submit a set of size t, and I'm OK with a malicious client submitting a set at most size 2t. And the other nice thing is that the verification communication is always two lambda bits. It's basically always going to be the same E quality check from the distributed point functions. Just adding on more point functions here is not going to change the E quality check. The performance is, as you would expect, the factor of two overhead here is, again, in the share generation, is really just from the resampling necessary when the shares at beta are equal for their control bits. The sharing of zero has very low overhead. And the share evaluation is like a very dramatic benefit. These dashed lines here, these two graphs in the share E valuation plot the same data, and these solid lines here are just so much lower than the naive construction. OK, so just to quickly wrap up, this has lots of immediate applications in the two server model for a malicious peer and malicious PSI. For the next steps, we want to try to add back the constrained outputs that are necessary for some applications. If there's only one valid beta, this is pretty trivial because you know that the shares are non-zero at only one location, so you can just shrink them all down and check that they are shares of the right value. Otherwise, you need to do something more clever to defend against additive attacks from a malicious server. Thanks. Questions? OK, so I have one question. You talked about DPFs. Can you do something for related functions such as comparison functions like an interval? That's a great question. The short answer is no. The interval function looks similar in the underlying construction, but this single correction can't really transfer over. So you need to do something a little bit different. Yeah, yeah. Yeah. OK. Thanks. Thanks a lot. Thanks. OK. For the final talk of the session.