A moon-math-free (purely symmetric crypto, understandable to joe-programmer) non-interactive zero-knowledge (NIZK) proof of faithful execution. I want to compute a function with some of its inputs kept secret and (optionally) some inputs which are known to both of us. Then I want to show you the output and convince you via a proof that the output was the result of me faithfully executing the agreed function. And I want to do it without any math fancier than boolean logic and a cryptographic hash function. NIZK proofs have many applications, including many interesting ones in connection with Bitcoin. E.g. Using a ZK proof you can pay someone who gives you the solution to any problem which verifiable by computer in a manner where neither party can cheat the other. (https://en.bitcoin.it/wiki/Zero_Knowledge_Contingent_Payment) Most current research in this space is mostly focused on efficient versions where the proofs whos size and validation complexity are _sub-linear_, or even O(1), in the number of operations the program performs. These systems require very complex math and complex cryptographic assumptions. Even ignoring efficiency, the fact that general program execution can be proven in zero-knowledge is really surprising to people. As a result I think it would be useful to have a description of this which is maximally simple, even if it is less efficient. The simpler cryptographic assumptions make it more likely that the scheme is secure against breakthrough mathematics or quantum computers. These simplifications, however, mean that it has O(N^2) proof size and validation time. [Explain why] Core to this is the idea that you can use a strong cryptographic hash function H() to commit to a value without revealing what that value is. E.g. I tell you the Y in H(X)=Y and then later I tell you X and you're confident that the X I tell you later was the original X. We first assume that you can convert your program to a fixed binary circuit consisting of regular logic gates. There are compilers to translate from C into logic, so this is fully general. Lets defined an logic gate as a truth table, e.g. an AND gate: in0, in1, out -------------- 1, 1, 1 0, 1, 0 1, 0, 0 0, 0, 0 We can create an encrypted version of the gate by picking an encryption key for each wire in/out (e1,e2,e3) and a random permutation It has a truth table of: (^ is xor) in0, in1, out ----------------- e1^0, e2^0, e3^0 e1^1, e2^1, e3^1 e1^1, e2^0, e3^0 e1^0, e2^1, e3^0 (the rows are randomly permuted) In such a scheme you can get a XOR or a NOT gate applied to any wire in or out "for free": you don't need a separate table, you can also get constant shifts and rotations and bit permutations for free... because NOT and XOR commute across the XOR encryption, and all operations are bit-level. The gates AND and NOT alone are enough to construct any circuit. So we'll assume that everything done here is done using our encrypted AND gates and the XORs and NOTs that we get for free. Now lets define an adaptation key for two wires. If I have two encrypted gates, G1 and G2, and I want to re-encrypt the G1's output to make it compatible with G2's second input, I need to xor it with a key which decrypts with one key and re-encrypts with another. Because our encryption is just xor the xor of the two encryption keys (G1.e3 ^ G2.e2) is the key we need to adapt from one encryption to another because (A^X)^Y == A^(X^Y). [add concrete example] Now say we want to prove the execution of a circuit with N gates. H() is some cryptographic hash function, like SHA256. I generate 4 * N encrypted gates, all with unique keys. I generate 4 * N commitments to the encrypted gates: H(H(H(entry_0)||H(entry_1))||H(H(entry_2)||H(entry_3))) e.g. each is a hashtree over the truth table. [A tree structured commitment makes it somewhat more efficient to reveal single entries at a time because I can reveal three values inseat of four: e.g. entry_0,H(entry_1),H(H(entry_2)||H(entry_3))] Then I generate 4 * N commitments to the gate encryption keys as H(H(e1||e2)||e3). Then I generate 32 * N^2 commitments for the 32 * N^2 adaptation keys between all possible output and input wires in the set of gates. H(adapt_Gx.e3_Gy.e[1,2]) I also commit to the encrypted original inputs and the decrypted final answers, as well as adaptation keys to wire any up any of the inputs/outputs to any of the gates. I send the commitments to you. I then compute the hash of all the commitments. I use the resulting super-commitment to select a random permutation of the encrypted gates. E.g. I use that hash to initialize a random shuffle on the gates. Then I reveal the gates and their encryption keys for the first half of the gates in the shuffled list. With this data you can validate that the gates were valid (e.g. were actually AND gates), that they matched their commitments, that the encryption keys match the commitments, and that all the implicitly revealed adaptation keys match their commitments. I take the original circuit and replace every AND gate with two AND gates in parallel, and a check that they produce the same answer. Now I take the other half of the encrypted gates that weren't revealed and drop them into the circuit, in order. I reveal the relevant adaption keys for the resulting topology. I reveal the single truth table entry used for each gate in the execution, and the additional hashes to prove it is the correct entry in the committed gate. I reveal the encryption keys for the final output wires. You can be confident that my execution is faithful because if I inserted a bad gate or adaptation key in the set it would have been likely to be detected in either the half that I revealed, or by a disagreement between duplicated gates in the circuit. Security can be adjusted by increasing the gate duplication or adjusting the number of unused extra gates which are revealed. With a sutiable choice in parameters the probablity of cheating detection can be made overwhemingly large. The N^2 blowup could be eliminated if the gate encryption keys were committed with a strong hash function which was commutative for XOR, but this appears to require fancy crypto or interaction[1]. With this you don't need the N^2 adaption key commitments because you can just compose the encryption key commitments. ([1] https://eprint.iacr.org/2013/155.pdf )