 Thanks, Nir. So I'm going to talk about patchable indistinguishability obfuscation, which is called IO for Evolving Software. And this is in joint work with Abhishek and Amit. So every software in its lifetime undergoes numerous changes. And these changes are modeled as patches, which the company regularly publishes on their website. The users can download these patches and apply to their local copies of software. So why does software evolve? So there are many reasons. For instance, the hardware requirements can change over time. There could be changes in the functionality requirements. You want to add more features to the software, change the GUI, and so on. Another reason is to resolve compatibility issues. You want to make sure your software is compatible with the latest version of operating system. And another reason could be that you want to fix some bugs in the software. So in this work, we deal with software evolution. So let's see how to model the notion of evolving software. So we model the software as Turing machines, though there is a machine M. And there is an update algorithm that takes this Turing machine M along with the patch B and produces a new machine M Prime. So the size of M Prime could be unrelated to the size of M. It could be much larger than the size of M, or it could be much smaller, and so on. And the goal of this work is to protect evolving software, meaning that there are two main sub-goals that we have to achieve. One is to protect, one is to achieve privacy of the software. And another is a functionality requirement where we want to ensure that the software can be updated over a period of time. While ensuring privacy of software. So in this work, we define the notion of IO for evolving software, which we call patchable IO. So before I define the notion of patchable IO, let's see why the standard IO for Turing machines doesn't itself suffice to get IO for evolving software. So let's just consider a simple scenario. So Apple wants to send the software to the user. So it wants to make sure that it protects the software from the user, so it obfuscates the software first and then sends it to the user. And later on, it wants to update the software. So it publishes a patch P, and it uses patch P to update its software locally to get a new machine, new software M Prime. And now Apple can essentially obfuscate this new software and send the obfuscated software to the user. So this solution ensures that you can actually change software or a period of time, and at the same time, you protect the privacy of the software from the user. So what is the problem with this naive solution? So during every patch, patching phase, the communication complexity from Apple to the user now depends on the size of the updated software. So this software could be of several gigabytes of size, and so the communication complexity during every patching would be several gigabytes. And this is not very reasonable. So in order to sort of handle this issue, we define the notion of patched will.io. In particular, we want this efficiency requirement that the communication complexity during the patching phase should be proportional to the size of the patch. So let me define this notion as in the case of standard.io for Turing machines, there is the obfuscation algorithm that lets you obfuscate Turing machines. In addition, you have this generation patch algorithm that takes as input a patch P and outputs an encoding of P. So you should think of this encoding of P as protecting the patch, right? So it's some form of encryption of P. And Apple essentially sends this encoding of P to the user who runs an apply patch algorithm on the original obfuscated Turing machine M along with the secure patch P to get an obfuscation of the new machine M prime. So gen patch and the apply patch algorithms are what is different from the standard definition of I.O. for Turing machines, okay? And as I said earlier, size of M prime could be much larger or smaller than M, so it is unrelated to the size of the original machine. Okay, so before I deal with this notion in more detail, let's talk about some differential issues that arise. So the first thing is that what is the patching process? I mean, how do we apply patches, right? As if you attended Aloni's talk in the morning, in the case of updatable cryptography, there too we consider this notion of sequential patching where initially you have a Turing machine M and you're going to apply patches sequentially to this original Turing machine, right? So you have initially P1 that you apply on M0 to get Turing machine M1 and then you get a new patch which you apply on M1 to get a new Turing machine and so on. And the second definition issue is how many patches do we handle? So ideally we want to handle unbounded number of patches. In particular, we want to make sure that the parameters of the system does not depend on the number of patches that we handle, right? And we achieve this sort of efficiency requirement. And the third issue is patching multiple programs. So suppose let's say Apple issues personalized software to all its users. So each user has a software of Apple that is tailor-made to his or her requirements. So now at a later point of time, Apple wants to publish a patch that can patch all the individualized software. So it wants to issue one single patch that can change all the individualized software. And to deal with this, we need to come up with a notion where Apple can actually issue one encoded patch that it can put on the bulletin board. And all the users can look at this encoded patch and apply it to their copies and they should be able to ultimately obtain like an obfuscation of their individualized software. So this notion of providing the ability to patch multiple programs is what we call multi-program patch file, okay? So let me just summarize the requirements that I've stated so far. The first main requirement is that the patch encoding size should only depend on the path size. And the second requirement is that the patches should be... should be able to modify the programs arbitrarily using the patches. And the third requirement is you should be able to handle unbounded number of patches. And the final requirement is that you should be able to patch multiple programs using the same patch. So now let me talk about correctness and security definitions. So this is the formal correctness requirement. So suppose, let's say, you start with the Turing machine M0 and you have a sequence of patches P1 to PL, okay? So let's consider two scenarios. In the first scenario, you patch... you apply the sequence of patches to the Turing machine M0 and you will get all these path machines M1 to ML. And then you upfuscate these machines M1 to ML. So this is the first scenario. In the second scenario, you're going to upfuscate the machine M0 and you're going to encode the patches P1 to PL. And then you're going to run the apply patch algorithm on all these encoded patches and the original Turing machine M0. And in the end, you get a sequence of upfuscated Turing machines M1 to ML. So this is the second scenario. We want that both the scenarios are equivalent. Stating this in a more simple manner, whether you patch the Turing machine and then upfuscate the resulting Turing machines should be the same as upfuscating the Turing machines and then running the apply patch algorithm. Okay. And the security requirement is a generalization of IO. Again, you have two worlds here. There is a world corresponding to challenge bit zero and there is a world corresponding to challenge bit one. Let me call this left and right worlds. So in the left world, you have a machine M0 and you have a sequence of patches that are parameterized by challenge bit zero. And in the right world, you have machine M1 and you have a sequence of patches that are parameterized by bit one. So now, as long as the set of patch machines that you get from the left world and the right world are equivalent, the adversary should not be able to distinguish whether the challenger upfuscated the machine as well as encoded the patches in the left world or from the right world. So this is the security requirement. Okay. And here I was only considering the single program setting where you start with one program and apply a sequence of patches to this one program. But you can also consider the multi-program setting where there are many Turing machines to begin with and you apply a sequence of patches to all these Turing machines. And I'm not going to define these two notions in this talk. Okay, so let me state our results. Assuming sub-exponential IO4 circuits and sub-exponential DDH, we get multi-program patchable IO4 Turing machines. Here I want to mention that the notion of IO4 Turing machines has this caveat that the input length of the Turing machine should be fixed apiary. And the parameters of the system grows with this input length. So in the paper, we also consider others patching process. The one I described was sequential. You can also think about parallel, some tree-based patching processes that I'm not going to talk about in this talk. So this notion of considering cryptographic primitives in the updateability context is not new and this has been studied under the banner of incremental cryptography. There is incremental signatures, encryption and so on. And concurrently, there was also this notion of incremental obfuscation studied by Gurgen Pandey. The results they achieve is different from ours. They, in their case, the size of the Turing machine updated is the same as the size of the Turing machine that you begin with. And on the other hand, they have better efficiency properties unlike ours. So if you want to know about it in more detail, I can talk offline. So let me state the theoretical applications. So we can actually get a very, very simple construction of FE for Turing machines starting from patchable IO. So the construction can be summarized in just one slide. So suppose let's say you want to generate a functional key for a Turing machine M. All you have to do is upfuscate a Turing machine, an input-less Turing machine that has hardwired into it, message X. And this X initially is set to BOT. And this upfuscated Turing machine will be the functional key. And now if you want to encrypt a message Y, you will generate an update, you will run the GenPatch algorithm on a patch that sets X to Y. Okay, so this is the encryption algorithm. So now how do you evaluate the functional key on the ciphertext? You essentially run the apply patch algorithm on the Turing machine as well as the secure encoding of the patch. And now you'll get a Turing machine that has, you'll get an upfuscation of a Turing machine that has Y hardwired inside it. And this is an input-less Turing machine, you just run this upfuscated Turing machine and then you will get M of Y, that's it. Yeah, as I said, this is a simple construction and if you're familiar with the previous construction, this, it was more involved and it directly built from IO4 circuits. Okay, so there are also other simple applications of patchable IO, such as multi-input function encryption for Turing machines. And you can also get IO4 Turing machines with unbounded length input. However, you need a very strong form of patchable IO that has this reusability property and we don't know how to achieve this notion of the strong notion of patchable IO, starting from just IO4 circuits. So let me give the technical details. So let me first lay out a simple template for single-program patchable IO and we will modify this template as we identify the problems associated with it. Okay, so in order to generate an upfuscation for Turing machine M, you're first going to encode M using an encoding scheme. And then you're also going to upfuscate a circuit that runs the decoding of this encoding algorithm and this also runs the evaluation algorithm on the result of the decoding on an input X that is fed to this upfuscated circuit. So this circuit takes as input encode of M and X and it runs the decode algorithm and then it outputs M of X. So the evaluation is simple, so on input X, you're just going to run this upfuscated circuit, get M of X and you're done. So now the question is, what is this encoding scheme? What are the properties it needs to satisfy? We require that the encoding scheme satisfies the following properties. It needs to satisfy correctness, hiding and patching properties. So firstly, we want that if you decode encode of, if you run the decoding algorithm on encode of M, then you should get M. And the second property is the hiding property. Whether I give you encoding of M zero or whether I give you encoding of M one, it should be indistinction from each other. And the final property is the patching property that should be able to combine encoding of M and an encoding of patch P to get encoding of M prime. So we are going to call such an encoding scheme as a patchable encoding scheme. So there is a natural candidate to construct this patchable encoding scheme. And this is fully homomorphic encryption. So the correctness of fully homomorphic encryption implies the correctness of this encoding scheme and the hiding is implied by the semantic security and the patching property is implied by the homomorphic evaluation, correctness of homomorphic evaluation. Okay, so the main problem with this, okay, so now that we have identified this patchable encoding scheme, so let's see the problems with this template. The first problem is that the adversary can apply encoded patches of his choice. So he can change this encoding M to whatever he wants. And this should clearly be a problem. And a fix to this problem is to authenticate the patches. So the evaluator should only be able to apply patches that are authenticated. So let's make a second attempt to this template. So now the encoding of M will be the same as before. The first-coded circuit now runs the following steps. First it's going to verify the signature associated with an encoding of the patch. This encoding of the patch and the signature is input to the first-coded circuit. And then it runs the decode algorithm to get the machine M. And then it runs the update algorithm to get the new machine M prime. And then it's going to output the evaluation of M prime on X. So now in order to generate a secure patch, you're going to encode this patch P and you're going to generate a signature on this encoding. So the main issue with this approach is that now the adversary can apply patches out of order. So the authority says, hey, apply patch P1 first P2 and then P3. Of course he signs all these patches. But the evaluator could be malicious and he could apply patch two first and then patch one, patch three and so on. And there's no mechanism currently to deal with such an issue. And the fix for this is not to authenticate the patches, but to authenticate the patched machine. So what do I mean by that? Note that if you patch P on M, you'll get M prime, right? So the authority now generates a signature of encoding of M prime as against encoding of P. So let's see the solution in more detail. So the initial obfuscation algorithm is the same as before. Apple is going to maintain the state encode of M and the signing key. It is then going to generate the update algorithm to get encode of M prime. And then it's going to sign the encode of M prime and it's going to send encoding of this patch along with the signature. This is the basic template. And the problem with this approach is that the authority has to maintain a large state, it has to maintain the encoding of the software and the signing key. And this is fine. In any case, Apple has to maintain the software. However, it becomes a problem if it has to maintain personalized software for all its users. So then the storage would depend on the number of users it has. And the solution to this problem is sort of reverse delegation. So instead of the client delegating something to the server, the server is going to delegate something to the client. So the Apple is going to maintain the state at the user's end instead of maintaining a state sign. However, there are a couple of issues that we need to handle. The first issue is that the state contains secret information. And the second issue is that if Apple doesn't have the state, how does it even generate the secure patches? So it is already delegated to the client. And in order to solve this, you are going to use a tool of adaptive garbage-turing machines for persistent memory that have been studied by two prior works. And for some simpler updates, you can actually just use one-way functions-based carbon terms. So in order to go from single program to multi-program, there are other issues involved. I'm not going to talk about it in detail because of time constraints. We need additional tools to solve this step as well. So so far, we have just constructed a template for a single-program patchable I.O. And we have seen how to go from single-program to multi-program patchable I.O. So now the question is how to implement the template of single-program patchable I.O. So this template requires some properties that we need to solve. We need to handle, for instance, what is the, how do we get this encoding of the Turing machine to be patchable and so on? And to do this, we are going to use construction from prior work with Abhishek and Amit. And this essentially satisfies this template. And there are additional implementation issues that are tailor-made to this construction involved, which I'm not going to talk about with the second floor.