317 |
318 | **Other Links:**
319 | N/A
320 |
--------------------------------------------------------------------------------
/2023/MODULE_2.md:
--------------------------------------------------------------------------------
1 | # Module 2 - Crypto Primitives: Encryption, Hash Functions, and Beyond
2 |
3 | Welcome to Module 2! As we delve deeper into the world of cryptography and zero-knowledge proofs, this module will introduce you to encryption, hash functions, and various advanced cryptographic concepts. By mastering these core building blocks, we will better understand the practical implementation of these technologies in areas like blockchain and beyond.
4 |
5 | # Detailed Self Study
6 |
7 | The following is a list of topics for you to research and learn about. At the bottom of each section is a set of questions for you to check your understanding. Try to make sure that you can confidently answer the questions before moving on. Feel free to reach out on Discord if you are struggling with a certain topic.
8 |
9 | ## Symmetric vs. Asymmetric Encryption
10 |
11 | Encryption is a technique used to encode data, making it readable only to those who possess the correct decryption key. There are two main types of encryption - symmetric and asymmetric, each serving different purposes and with their own strengths and weaknesses.
12 |
13 | **Symmetric Encryption** - Also known as single-key encryption, this method involves using the same key for both encryption and decryption. The most widely used symmetric encryption algorithm is Advanced Encryption Standard (AES).
14 |
15 | **Asymmetric Encryption** - Also known as public-key encryption, this method uses a pair of keys: one for encryption and another for decryption. The RSA algorithm is one of the best-known public-key algorithms.
16 |
17 | The key difference between the two is the number of keys used: symmetric encryption uses one key for both encrypting and decrypting, while asymmetric encryption uses a different key for each (one public, one private). For a deeper understanding of symmetric and asymmetric encryption, please explore these resources:
18 | - [Symmetric vs. Asymmetric Encryption – What are differences?](https://www.ssl2buy.com/wiki/symmetric-vs-asymmetric-encryption-what-are-differences)
19 | - [AES Explained (Advanced Encryption Standard) - Computerphile [14:13]](https://www.youtube.com/watch?v=O4xNJsjtN6E)
20 | - [Prime Numbers & RSA Encryption Algorithm - Computerphile [15:06]](https://www.youtube.com/watch?v=JD72Ry60eP4)
21 | - [What Is AES Encryption and How Does It Work?](https://www.simplilearn.com/tutorials/cryptography-tutorial/aes-encryption)
22 | - [What is RSA encryption and how does it work?](https://www.comparitech.com/blog/information-security/rsa-encryption/)
23 |
24 |
25 | :::info
26 | **🤔 Consider the following:**
27 | 1. What is the primary difference between symmetric and asymmetric encryption?
28 | 2. Can you briefly explain how AES (Advanced Encryption Standard) works?
29 | 3. What makes RSA a popular choice for public-key encryption?
30 | :::
31 |
32 | ## DLP-based Public-Key Cryptography
33 | While RSA encryption is based on the hardness of the factoring problem, there is another public key cryptography system based on hardness of Discrete Logarithm Problem (DLP).
34 |
35 | 1. **Discrete Log Problem (DLP):** This is a cornerstone problem in public-key cryptography and underlies many key exchange and encryption algorithms. Understanding DLP provides a foundation for the remaining topics.
36 | - [The Discrete Logarithm Problem - Khan Academy [1:55]](https://youtu.be/SL7J8hPKEWY)
37 | - [Public key cryptography using discrete logarithms](https://www.di-mgt.com.au/public-key-crypto-discrete-logs-0.html)
38 |
39 | 2. **Diffie-Hellman Key Exchange:** This is one of the earliest practical implementations of key exchange protocols based on the Discrete Log Problem. It's critical to understand how secure communication can be established over insecure channels.
40 |
41 | This protocol is significant as it enables secure communication over insecure channels by allowing two parties to generate a shared secret key, which can then be used for encryption and decryption of messages
42 | - [Secret Key Exchange (Diffie-Hellman) - Computerphile [8:39]](https://www.youtube.com/watch?v=NmM9HA2MQGI)
43 | - [Diffie Hellman -the Mathematics bit- Computerphile [7:04]](https://youtu.be/Yjrfm_oRO0w)
44 | - [Implementation of Diffie-Hellman Algorithm
45 | ](https://www.geeksforgeeks.org/implementation-diffie-hellman-algorithm/)
46 |
47 | 3. **ElGamal Encryption:** This is a public-key encryption method that utilizes the principles of DLP. This will allow you to see a practical application of these abstract concepts in a real-world encryption scheme.
48 | - [Intro to the ElGamal Cryptosystem [8:20]](https://www.youtube.com/watch?v=oQqr8d5s3Uk)
49 | - [The ElGamal Algorithm: a simple example [6:38]](https://www.youtube.com/watch?v=4xVCrTb_1II)
50 | - [ElGamal Encryption Algorithm](https://www.geeksforgeeks.org/elgamal-encryption-algorithm/)
51 |
52 | :::info
53 | **🤔 Consider the following:**
54 | 1. What is the Discrete Logarithm Problem (DLP)?
55 | 2. How does the Diffie-Hellman protocol work?
56 | 3. What is the main idea behind ElGamal encryption?
57 | 4. Can you name a drawback of using DLP-based systems?
58 | :::
59 |
60 | ## Hash Functions
61 |
62 | A hash function takes an input and returns a fixed-size string of bytes. **SHA-256** and **Poseidon** are popular cryptographic hash functions in our context, with Poseidon specifically designed for arithmetic-friendly operations, benefiting certain applications in blockchains.
63 |
64 | The primary characteristics of a good hash function are [preimage resistance](https://en.wikipedia.org/wiki/Preimage_attack), second preimage resistance, and [collision resistance](https://en.wikipedia.org/wiki/Collision_resistance), ensuring data security and integrity. In blockchain technology, hash functions create an unalterable, unique representation of each block's content, contributing to the immutability and transparency of the system.
65 |
66 | Explore these resources to further your understanding:
67 | - [What Is SHA-256 Algorithm & How It Works](https://www.ssldragon.com/blog/sha-256-algorithm/)
68 | - [How is SHA-256 used in blockchain, and why?](https://www.educative.io/answers/how-is-sha-256-used-in-blockchain-and-why)
69 | - [Poseidon: A new hash function for zero-knowledge proof systems](https://eprint.iacr.org/2019/458.pdf)
70 | - [USENIX Security '21 - Poseidon: A New Hash Function for Zero-Knowledge Proof Systems [10:45]](https://www.youtube.com/watch?v=hUx3WpDV_l0)
71 | - This video is quite technical, but the first few minutes provide a good explanation for the motivation behind the Poseidon hash function.
72 |
73 | :::info
74 | **🤔 Consider the following:**
75 | 1. What is a hash function and what are its primary uses in cryptography?
76 | 2. How does the SHA-256 hashing algorithm function, in simple terms?
77 | 3. What is the Poseidon hash function and why is it particularly useful in ZKPs?
78 | :::
79 |
80 | ## Merkle Trees
81 |
82 | A Merkle tree is a core component of blockchain and cryptography. It's a binary tree filled with cryptographic hashes. This structure enables efficient and secure verification of the contents of large data structures. To understand more about Merkle trees, read the following:
83 | - [How Merkle Trees Enable the Decentralized Web! [10:05]](https://www.youtube.com/watch?v=3giNelTfeAk)
84 | - [Merkle Trees and Merkle Roots Explained](https://academy.binance.com/en/articles/merkle-trees-and-merkle-roots-explained)
85 | - [Visualizing Efficient Merkle Trees for Zero-Knowledge Proofs](https://kndrck.co/posts/efficient-merkletrees-zk-proofs/)
86 | - [The Ultimate Merkle Tree Guide in Solidity](https://soliditydeveloper.com/merkle-tree)
87 |
88 |
89 | :::info
90 | **🤔 Consider the following:**
91 | 1. Can you describe the structure of a Merkle tree?
92 | 2. How are Merkle trees used within the blockchain context?
93 | 3. Why are Merkle trees useful for efficient and secure verification of large data structures?
94 | :::
95 |
96 | ## Cryptographic Commitments
97 |
98 | Cryptographic commitments are essential in cryptography and blockchain technology as they allow for selective hiding and revealing of information. This feature ensures data privacy while still enabling verification processes.
99 |
100 | It helps achieve secure and efficient verification of transactions in blockchain protocols. In such contexts, sensitive information, such as transaction details or user identities, is hidden while revealing others for the verifier to authenticate the transactions.
101 |
102 | You can break down the concept of commitment into two parts: commit and open (reveal).
103 |
104 | 
105 | [Source](https://zecrey.medium.com/commmitment-schemes-in-zecrey-e6c446e2da97)
106 |
107 | There is always a commit phase and a reveal phase, in other words you first encrypt a secret and then reveal it later.
108 |
109 | ### Pedersen Commitments (Optional)
110 | Pedersen Commitments are a type of cryptographic primitive that allows you to commit to a certain value while keeping it hidden, with the ability to disclose the committed value later. They're often used to achieve privacy-preserving properties in cryptographic protocols. For more on Pedersen Commitments, review these materials:
111 |
112 | - [Pedersen Commitments](https://asecuritysite.com/encryption/ped)
113 | - [What is a Pedersen Commitment?](https://crypto.stackexchange.com/questions/64437/what-is-a-pedersen-commitment)
114 |
115 | As a final touch on this section, [this video [10:32]](https://www.youtube.com/watch?v=IkNZWJFcfcU) provides a brief yet comprehensive summary of the fundamental concepts discussed in this section.
116 |
117 | :::info
118 | **🤔 Consider the following:**
119 | 1. What is the main purpose of Cryptographic Commitments?
120 | 2. What type of information does a Polynomial Commitment hide and reveal?
121 | 3. How do Pedersen Commitments contribute to privacy in cryptography?
122 | 4. Why are Cryptographic Commitments important in blockchain technology?
123 | :::
124 |
125 | ## Digital Signatures
126 |
127 | Digital signatures ensure the integrity and authenticity of digital messages or documents. By providing a means to verify the origin and confirm that the content has not been altered, digital signatures play a pivotal role in maintaining trust in digital communications.
128 |
129 | In PKC, anyone can encrypt their message with the receiver's public key, and only the receiver can decrypt the message with their private key. In digital signatures, on the other hand, if a signer generates a signature for a message using their private key, anyone can validate it using the signer's public key. Therefore, the message of the signature is made public, which distinguishes it from cryptographic commitments.
130 |
131 | Start your exploration of digital signatures with this intuitive video:
132 | - [What are Digital Signatures? - Computerphile [10:16]](https://www.youtube.com/watch?v=s22eJ1eVLTU)
133 |
134 | **Schnorr Signature**: Schnorr signatures are a digital signature scheme known for their simplicity and efficiency.
135 | - [Schnorr Digital Signature (by GeeksForGeeks)](https://www.geeksforgeeks.org/schnorr-digital-signature/)
136 | - [Schnorr Digital Signature [4:58]](https://www.youtube.com/watch?v=mV9hXEFUB6A)
137 |
138 | **Exploring DSA**: Get a deep dive into the Digital Signature Algorithm (DSA) and its significance in bolstering internet security. Through the listed resources, understand the mechanics of DSA and its use cases.
139 | - [Digital Signature Algorithm (DSA) in Cryptography](https://www.simplilearn.com/tutorials/cryptography-tutorial/digital-signature-algorithm)
140 | - [Digital Signature Algorithm (DSA) - Cryptography [5:46]](https://www.youtube.com/watch?v=iS1nK4G6EtA)
141 | - [Digital Signature Algorithm (DSA) explained with example [24:32]](https://www.youtube.com/watch?v=MtT3NBfpV5Q)
142 |
143 | :::info
144 | **🤔 Consider the following:**
145 | 1. Can you describe what digital signatures are and why they are essential in digital communications?
146 | 2. Explain the workings of the Digital Signature Algorithm (DSA).
147 | :::
148 |
149 | # 💪 Exercises
150 |
151 | ## Written Questions
152 |
153 | 1. **Symmetric vs. Asymmetric Encryption**: What are the key differences between symmetric and asymmetric encryption? Provide a practical use case for each.
154 | 2. **Public-Key Cryptography and Key Exchange Protocols**: How can the Diffie-Hellman protocol enhance security in a messaging application?
155 | 3. **Hash Functions**: What features make SHA-256 and Poseidon good hash functions for ensuring data integrity? Mention one unique advantage of Poseidon.
156 | 4. **Merkle Trees**: Explain how Merkle trees can help verify data in a large database efficiently.
157 | 5. **Cryptographic Commitments**: How can Pedersen Commitments be used in a blockchain protocol to maintain transaction privacy?
158 | 6. **Digital Signatures**: How can you verify the authenticity of a digitally signed document?
159 |
160 | ## Programming Challenges
161 |
162 | In these challenges, you'll implement cryptographic methods in a Node.js environment. You will need the following packages, which you can install using NPM:
163 |
164 | ```bash
165 | npm install merkletreejs poseidon-encryption ffjavascript
166 | ```
167 |
168 | ### Challenge 1: Asymmetric Encryption and Digital Signature
169 |
170 | In this challenge, you will use the `crypto` built-in library in Node.js to implement asymmetric encryption. Your task is to encrypt and decrypt some sample text, generate a digital signature for the encrypted message, and then verify it. This simulates a secure message exchange where you want to ensure the confidentiality and authenticity of the messages.
171 |
172 | ```javascript
173 | const crypto = require('crypto');
174 |
175 | // Asymmetric encryption
176 | const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', { modulusLength: 2048 });
177 |
178 | // Encrypt
179 | const plaintext = 'This is a secret message.';
180 | // TODO: Use the publicKey to encrypt the plaintext message. Remember that RSA encryption is public key encryption.
181 |
182 | // Decrypt
183 | // TODO: Use the privateKey to decrypt the encrypted message. The result should be the original plaintext.
184 |
185 | // Create a digital signature
186 | const sign = crypto.createSign('SHA256');
187 | sign.update(plaintext);
188 | sign.end();
189 | // TODO: Use the privateKey to sign the plaintext message. This will generate a digital signature.
190 |
191 | // Verify a digital signature
192 | const verify = crypto.createVerify('SHA256');
193 | verify.update(plaintext);
194 | verify.end();
195 | // TODO: Use the publicKey to verify the signature. It should return true if the signature is valid.
196 | ```
197 |
198 | Tip: The `crypto` library has specific functions for encryption, decryption, signing, and verifying. Look up the library documentation for examples and usage [here](https://nodejs.org/api/crypto.html).
199 |
200 | ### Challenge 2: Hashing with SHA-256 and Poseidon
201 |
202 | For this challenge, your task is to compute the SHA-256 and Poseidon hashes of some input data. You will then observe how the hash value changes drastically even with a small change in the input data. This is an important property of cryptographic hash functions called ["avalanche effect"](https://en.wikipedia.org/wiki/Avalanche_effect).
203 |
204 | ```javascript
205 | const crypto = require("crypto");
206 | const poseidon = require("poseidon-encryption");
207 |
208 | // SHA-256
209 | const data = "This is some data X.";
210 | // TODO: Compute the SHA-256 hash of the data and print it. Try changing the data slightly and observe the changes in the hash.
211 |
212 | // Poseidon
213 | const inputs = [1, 2, 3, 4];
214 | // TODO: Compute the Poseidon hash of the inputs and print it. Remember that Poseidon accepts an array of integers as input.
215 | ```
216 |
217 | Tip: Use the `.digest('hex')` method of the `hash` object to print the hash in a human-readable format. As for the `poseidon-encryption` package, there lacks good documentation but have a look at the source code [here](https://github.com/weijiekoh/circomlib/tree/feat/poseidon-encryption) for some hints (pay special attention to the tests for example usage).
218 |
219 | ### Challenge 3: Using a Simple Merkle Tree
220 |
221 | In this challenge, you will use the 'merkletreejs' library to construct a simple Merkle Tree from some input data. You will then generate a proof for a leaf node and verify it. This task is analogous to verifying a transaction in a block in a blockchain.
222 |
223 | ```javascript
224 | const MerkleTree = require('merkletreejs');
225 | const crypto = require('crypto');
226 |
227 | function hashFunction(data) {
228 | const hash = crypto.createHash('sha256');
229 | hash.update(data);
230 | return hash.digest();
231 | }
232 |
233 | // Create tree
234 | const leaves = ['a', 'b', 'c', 'd'].map(x => hashFunction(x));
235 | // TODO: Build the Merkle tree using the leaves and hashFunction. Compute the root of the tree and print it.
236 |
237 | // Generate and verify proof
238 | const leaf = hashFunction('b');
239 | // TODO: Generate a proof for the leaf 'b' and verify it against the root of the tree. It should return true if the leaf is part of the tree.
240 | ```
241 |
242 | Tip: Refer to the `merkletreejs` library [documentation](https://github.com/merkletreejs/merkletreejs) for the functions needed to build the tree, generate the proof, and verify it.
243 |
244 | ### Challenge 4: Implementing Pedersen Commitments
245 |
246 | This challenge is a little more involved, but should be more rewarding. Here, you will be creating a Javascript object capable of Pedersen Commitments. This template should get you started:
247 |
248 | ```javascript
249 | class PedersenCommitment {
250 | constructor() {
251 | // Set prime number (p) and generator (g)
252 | this.p = BigInt(23); // use a large prime in a real-world scenario
253 | this.g = BigInt(4); // use a large number in a real-world scenario
254 | this.h = null;
255 | this.r = null;
256 | this.s = null;
257 | }
258 |
259 | // Generate 'h' with a random number 'r' (h = g^r mod p)
260 | generateH() {
261 | // TODO: Generate a random number r (and save it to this.r)
262 | // TODO: Calculate h using g, r and p (and save it to this.h)
263 | }
264 |
265 | // Generate the commitment (g^s * h^r mod p)
266 | generateCommitment(s) {
267 | // TODO: Convert s to BigInt (and save it to this.s)
268 | // TODO: Calculate and return the commitment using g, s, h, r and p
269 | }
270 |
271 | // Reveal the secret number and random number (s, r)
272 | reveal() {
273 | // TODO: Return the secret and random number
274 | }
275 |
276 | // Verify the commitment (g^s * h^r mod p)
277 | verify(s, r, C) {
278 | // TODO: Verify the commitment by recalculating it and comparing with C
279 | }
280 | }
281 |
282 | // Test the PedersenCommitment
283 | const pc = new PedersenCommitment();
284 | pc.generateH();
285 |
286 | // Party A: Generate a commitment
287 | let secretNumber = 7;
288 | let commitment = pc.generateCommitment(secretNumber);
289 | console.log("Commitment: ", commitment);
290 |
291 | // Party A: Reveal the secret and random number
292 | let reveal = pc.reveal();
293 | console.log("Revealed: ", reveal);
294 |
295 | // Party B: Verify the commitment
296 | let verification = pc.verify(reveal.s, reveal.r, commitment);
297 | console.log("Verification: ", verification);
298 | ```
299 |
300 | If everything worked properly, the final output should read:
301 |
302 | ```
303 | Verification: true
304 | ```
305 |
306 | Congrats on reaching the end of this module!
307 |
308 | ## Conclusion
309 |
310 | In summary, we've ventured through some very important cryptographic primitives, gaining insights into encryption, hash functions, Merkle trees, and more. These components underpin blockchain technologies and zero-knowledge proofs. Moving forward, our next module will navigate the fascinating field of elliptic curve cryptography.
311 |
--------------------------------------------------------------------------------
/2023/answers/MODULE_6_ANSWERS.md:
--------------------------------------------------------------------------------
1 | # Module 6 Answers
2 |
3 | ### 2.1 Trusted Setup
4 |
5 | 1. What does the circuit in `HelloWorld.circom` do?
6 | - *Answer*: It multiplies two input signals to produce an output signal.
7 | 2. Lines 7-12 of `compile-HelloWorld.sh` download a file called `powersOfTau28_hez_final_10.ptau` for Phase 1 trusted setup. What is a Powers of Tau ceremony? Explain why this is important in the setup of zk-SNARK applications.
8 | - *Answer*: The trusted setup ceremony MPC schemes are interactive protocols involving multiple parties who contribute randomness to iteratively construct the common reference string (CRS). The key to this technique is that all parties need to keep their inputs (their sampled randomness) hidden. As long as the answer talks about the setup ceremony it’s ok, no need to be Powers of Tau specific.
9 | 3. Line 24 of `compile-HelloWorld.sh` makes a random entropy contribution as a Phase 2 trusted setup. How are Phase 1 and Phase 2 trusted setup ceremonies different from each other?
10 | - *Answer*: Phase 1 is universal, Phase 2 is circuit-specific.
11 |
12 | ### 2.2 Non-Quadratic Constraints
13 |
14 | Here, you will learn about an important restriction on Circom circuits.
15 |
16 | 1. In the empty `scripts/compile-Multiplier3-groth16.sh`, create a script to compile `contracts/circuits/Multiplier3.circom` and create a verifier contract modeled after `compile-HelloWorld.sh`.
17 | ```bash
18 | #!/bin/bash
19 |
20 | # [assignment] create your own bash script to compile Multiplier3.circom modeling after compile-HelloWorld.sh below
21 |
22 | cd contracts/circuits
23 |
24 | mkdir Multiplier3
25 |
26 | if [ -f ./powersOfTau28_hez_final_10.ptau ]; then
27 | echo "powersOfTau28_hez_final_10.ptau already exists. Skipping."
28 | else
29 | echo 'Downloading powersOfTau28_hez_final_10.ptau'
30 | wget https://hermez.s3-eu-west-1.amazonaws.com/powersOfTau28_hez_final_10.ptau
31 | fi
32 |
33 | echo "Compiling Multiplier3.circom..."
34 |
35 | # compile circuit
36 |
37 | circom Multiplier3.circom --r1cs --wasm --sym -o Multiplier3
38 | snarkjs r1cs info Multiplier3/Multiplier3.r1cs # print the number of constraints in Multiplier3.r1cs
39 |
40 | # Start a new zkey and make a contribution
41 |
42 | snarkjs groth16 setup Multiplier3/Multiplier3.r1cs powersOfTau28_hez_final_10.ptau Multiplier3/circuit_0000.zkey
43 | snarkjs zkey contribute Multiplier3/circuit_0000.zkey Multiplier3/circuit_final.zkey --name="1st Contributor Name" -v -e="random text"
44 | snarkjs zkey export verificationkey Multiplier3/circuit_final.zkey Multiplier3/verification_key.json
45 |
46 | # generate solidity contract
47 | snarkjs zkey export solidityverifier Multiplier3/circuit_final.zkey ../Multiplier3Verifier.sol
48 |
49 | cd ../..
50 | ```
51 | 2. Try to run `compile-Multiplier3-groth16.sh`. You should encounter an `error[T3001]` with the circuit as-is. Explain what the error means and how it arises.
52 | - *Answer*: The error is a non-quadratic constraint. You should use an intermediate signal to do the multiplication of two input signals first, and then the third.
53 | 3. Modify `Multiplier3.circom` to perform a multiplication of three input signals under the restrictions of circom.
54 | ```circom
55 | pragma circom 2.0.0;
56 |
57 | // [assignment] Modify the circuit below to perform a multiplication of three signals
58 |
59 | template Multiplier3 () {
60 |
61 | // Declaration of signals.
62 | signal input a;
63 | signal input b;
64 | signal input c;
65 | signal temp;
66 | signal output d;
67 |
68 | // Constraints.
69 | temp <== a * b;
70 | d <== temp * c;
71 | }
72 |
73 | component main = Multiplier3();
74 | ```
75 |
76 | ### 2.3 Groth16 and PLONK
77 |
78 | In the empty `scripts/compile-Multiplier3-plonk.sh`, create a script to compile `circuit/Multiplier3.circom` using PLONK in SnarkJS. Add a `_plonk` suffix to the build folder and the output contract to distinguish the two sets of output.
79 |
80 | 1. You will encounter an error `zkey file is not groth16` if you just change `snarkjs groth16 setup` to `snarkjs plonk setup`. Resolve this error and answer the following question: *How is the process of compiling with PLONK different from compiling with Groth16?*
81 | - *Answer*: Phase 2 (circuit-specific) contribution not required.
82 | 3. What are the practical differences between Groth16 and PLONK? Hint: compare and contrast the resulting contracts and running time of unit tests (see the next question below) from the two protocols.
83 | - *Answer*: PLONK contract size is larger, gas cost is higher, and verification is time longer.
84 |
85 | ### 2.4 Verify and Test
86 |
87 | So far we have not tested our circuit yet. While you can verify your circuit in the terminal using `snarkjs groth16 fullprove`, you can also do so directly in a Node.js script. We will practice doing so by creating some unit tests to try out our verifier contract(s):
88 |
89 | 1. Running `npx hardhat test` will prompt an `error HH606`. Before we can test our verifier contracts with hardhat, we must modify the Solidity version. In `scripts/bump-solidity.js`, we have already written the regular expressions to modify `HelloWorldVerifier.sol`. Add some code to `bump-solidity.js` to do the same for your new contract for `Multiplier3`.
90 |
91 | ```bash
92 | const fs = require("fs");
93 | const solidityRegex = /pragma solidity \^\d+\.\d+\.\d+/
94 |
95 | const groth16VerifierRegex = /contract Groth16Verifier/
96 |
97 | let content = fs.readFileSync("./contracts/HelloWorldVerifier.sol", { encoding: 'utf-8' });
98 | let bumped = content.replace(solidityRegex, 'pragma solidity ^0.8.0');
99 | bumped = bumped.replace(groth16VerifierRegex, 'contract HelloWorldVerifier');
100 |
101 | fs.writeFileSync("./contracts/HelloWorldVerifier.sol", bumped);
102 |
103 | // [assignment] add your own scripts below to modify the other verifier contracts you will build during the assignment
104 |
105 | // Multiplier2 (with Groth16)
106 | let content2 = fs.readFileSync("./contracts/Multiplier3Verifier.sol", { encoding: 'utf-8' });
107 | let bumped2 = content2.replace(solidityRegex, 'pragma solidity ^0.8.0');
108 | bumped2 = bumped2.replace(groth16VerifierRegex, 'contract Multiplier3Verifier');
109 |
110 | fs.writeFileSync("./contracts/Multiplier3Verifier.sol", bumped2);
111 |
112 | // Multiplier3 (with Plonk)
113 | const plonkVerifierRegex = /contract PlonkVerifier/
114 |
115 | let content3 = fs.readFileSync("./contracts/Multiplier3Verifier_plonk.sol", { encoding: 'utf-8' });
116 | let bumped3 = content3.replace(solidityRegex, 'pragma solidity ^0.8.0');
117 | bumped3 = bumped3.replace(plonkVerifierRegex, 'contract Multiplier3Verifier_plonk');
118 |
119 | fs.writeFileSync("./contracts/Multiplier3Verifier_plonk.sol", bumped3);
120 | ```
121 |
122 | 2. You can now perform the unit tests for `HelloWorldVerifier` by running `npm run test`. Add comments to explain what each line in `test/test.js` is doing.
123 | 3. In `test/test.js`, add the unit tests for `Multiplier3` for both the Groth16 and PLONK versions. Ensure all tests pass (for `HelloWorld`, `Multiplier3 with Groth16`, and `Multiplier3 with PLONK`).
124 |
125 | `test/test.js`:
126 |
127 | ```javascript
128 | const { expect, assert } = require("chai");
129 | const { ethers } = require("hardhat");
130 | const { groth16, plonk } = require("snarkjs");
131 |
132 | const wasm_tester = require("circom_tester").wasm;
133 |
134 | const F1Field = require("ffjavascript").F1Field;
135 | const Scalar = require("ffjavascript").Scalar;
136 | exports.p = Scalar.fromString(
137 | "21888242871839275222246405745257275088548364400416034343698204186575808495617"
138 | );
139 | const Fr = new F1Field(exports.p);
140 |
141 | describe("HelloWorld", function () {
142 | this.timeout(100000000);
143 | let Verifier;
144 | let verifier;
145 |
146 | beforeEach(async function () {
147 | Verifier = await ethers.getContractFactory("HelloWorldVerifier");
148 | verifier = await Verifier.deploy();
149 | await verifier.deployed();
150 | });
151 |
152 | it("Circuit should multiply two numbers correctly", async function () {
153 | const circuit = await wasm_tester("contracts/circuits/HelloWorld.circom");
154 |
155 | const INPUT = {
156 | a: 2,
157 | b: 3,
158 | };
159 |
160 | const witness = await circuit.calculateWitness(INPUT, true);
161 |
162 | //console.log(witness);
163 |
164 | assert(Fr.eq(Fr.e(witness[0]), Fr.e(1)));
165 | assert(Fr.eq(Fr.e(witness[1]), Fr.e(6)));
166 | });
167 |
168 | it("Should return true for correct proof", async function () {
169 | //[assignment] Add comments to explain what each line is doing
170 |
171 | // create a proof and public signals from the circuit and some input
172 | const { proof, publicSignals } = await groth16.fullProve(
173 | { a: "2", b: "3" },
174 | "contracts/circuits/HelloWorld/HelloWorld_js/HelloWorld.wasm",
175 | "contracts/circuits/HelloWorld/circuit_final.zkey"
176 | );
177 |
178 | // print out "c" in the circuit (i.e. the result of the private inputs)
179 | console.log("2x3 =", publicSignals[0]);
180 |
181 | // create a string of the calldata for the verifier contract
182 | const calldata = await groth16.exportSolidityCallData(proof, publicSignals);
183 |
184 | // convert the calldata string into an array of BigInts
185 | const argv = calldata
186 | .replace(/["[\]\s]/g, "")
187 | .split(",")
188 | .map((x) => BigInt(x).toString());
189 |
190 | const a = [argv[0], argv[1]];
191 | const b = [
192 | [argv[2], argv[3]],
193 | [argv[4], argv[5]],
194 | ];
195 | const c = [argv[6], argv[7]];
196 | const Input = argv.slice(8);
197 |
198 | expect(await verifier.verifyProof(a, b, c, Input)).to.be.true;
199 | });
200 | it("Should return false for invalid proof", async function () {
201 | let a = [0, 0];
202 | let b = [
203 | [0, 0],
204 | [0, 0],
205 | ];
206 | let c = [0, 0];
207 | let d = [0];
208 | expect(await verifier.verifyProof(a, b, c, d)).to.be.false;
209 | });
210 | });
211 |
212 | describe("Multiplier3 with Groth16", function () {
213 | beforeEach(async function () {
214 | //[assignment] insert your script here
215 | Verifier = await ethers.getContractFactory("Multiplier3Verifier");
216 | verifier = await Verifier.deploy();
217 | await verifier.deployed();
218 | });
219 |
220 | it("Circuit should multiply three numbers correctly", async function () {
221 | //[assignment] insert your script here
222 | const circuit = await wasm_tester("contracts/circuits/Multiplier3.circom");
223 |
224 | const INPUT = {
225 | a: 2,
226 | b: 3,
227 | c: 4,
228 | };
229 |
230 | const witness = await circuit.calculateWitness(INPUT, true);
231 |
232 | assert(Fr.eq(Fr.e(witness[0]), Fr.e(1)));
233 | assert(Fr.eq(Fr.e(witness[1]), Fr.e(24)));
234 | });
235 |
236 | it("Should return true for correct proof", async function () {
237 | //[assignment] insert your script here
238 |
239 | const { proof, publicSignals } = await groth16.fullProve(
240 | { a: "2", b: "3", c: "4" },
241 | "contracts/circuits/Multiplier3/Multiplier3_js/Multiplier3.wasm",
242 | "contracts/circuits/Multiplier3/circuit_final.zkey"
243 | );
244 |
245 | console.log("2x3x4 =", publicSignals[0]);
246 |
247 | const calldata = await groth16.exportSolidityCallData(proof, publicSignals);
248 |
249 | const argv = calldata
250 | .replace(/["[\]\s]/g, "")
251 | .split(",")
252 | .map((x) => BigInt(x).toString());
253 |
254 | const a = [argv[0], argv[1]];
255 | const b = [
256 | [argv[2], argv[3]],
257 | [argv[4], argv[5]],
258 | ];
259 | const c = [argv[6], argv[7]];
260 |
261 | const Input = argv.slice(8);
262 |
263 | expect(await verifier.verifyProof(a, b, c, Input)).to.be.true;
264 | });
265 |
266 | it("Should return false for invalid proof", async function () {
267 | //[assignment] insert your script here
268 | let a = [0, 0];
269 | let b = [
270 | [0, 0],
271 | [0, 0],
272 | ];
273 | let c = [0, 0];
274 | let d = [0];
275 | expect(await verifier.verifyProof(a, b, c, d)).to.be.false;
276 | });
277 | });
278 |
279 | describe("Multiplier3 with PLONK", function () {
280 | beforeEach(async function () {
281 | //[assignment] insert your script here
282 | Verifier = await ethers.getContractFactory("Multiplier3Verifier_plonk");
283 | verifier = await Verifier.deploy();
284 | await verifier.deployed();
285 | });
286 |
287 | it("Should return true for correct proof", async function () {
288 | //[assignment] insert your script here
289 | const { proof, publicSignals } = await plonk.fullProve(
290 | { a: "2", b: "3", c: "4" },
291 | "contracts/circuits/Multiplier3_plonk/Multiplier3_js/Multiplier3.wasm",
292 | "contracts/circuits/Multiplier3_plonk/circuit_final.zkey"
293 | );
294 |
295 | console.log("2x3x4 =", publicSignals[0]);
296 |
297 | let rawCalldata = await plonk.exportSolidityCallData(proof, publicSignals);
298 |
299 | // fix string by replacing "][" with ", "
300 | const fixedStr = rawCalldata.replace(/\]\[/g, ", ");
301 |
302 | // convert the calldata string into an array of BigInts
303 | const fixedArray = fixedStr
304 | .replace(/["[\]\s]/g, "")
305 | .split(",")
306 | .map((x) => BigInt(x).toString());
307 |
308 | // drop the last element of the array (the inputs)
309 | const calldata = [...fixedArray.slice(0, -1)];
310 |
311 | expect(await verifier.verifyProof(calldata, publicSignals)).to.be.true;
312 | });
313 |
314 | it("Should return false for invalid proof", async function () {
315 | //[assignment] insert your script here
316 | let calldata = new Array(24).fill(0);
317 | let publicSignals = [0];
318 | expect(await verifier.verifyProof(calldata, publicSignals)).to.be.false;
319 | });
320 | });
321 | ```
322 |
323 | ### 2.5 Circuit Libraries
324 |
325 | In this section, you will be learning about libraries that you can import to create more complicated circuits. The original exercises are found in the Q3 directory, however we will present the code here for your convenience.
326 |
327 | #### Tip: For this section, use [**zkREPL**](https://zkrepl.dev/) for quick compiling and testing of circuits
328 |
329 | ### 2.5.1 circomlib
330 |
331 | - `LessThan10.circom` implements a circuit that verifies an input is less than 10 using the [LessThan](https://github.com/iden3/circomlib/blob/master/circuits/comparators.circom#L89) template. Study how the template is used in this circuit. What does the 32 in Line 9 stand for?
332 | - *Answer*: Maximum number of bits of the input.
333 | - What are the possible outputs for the `LessThan` template and what do they mean respectively? If you cannot figure this out by reading the code alone, feel free to compile the circuit and test with different input values.
334 | - *Answer*: 1 if true, 0 if false
335 | - Proving a number is within a range without revealing the actual number could be useful in applications like proving our income when applying for a credit card. In the following code `RangeProof.circom`, create a template that uses `GreaterEqThan` and `LessEqThan` to perform a range proof.
336 |
337 | Answer for `RangeProof.circom`:
338 |
339 | ```circom
340 | pragma circom 2.1.4;
341 |
342 | include "circomlib/comparators.circom";
343 |
344 | template RangeProof(n) {
345 | assert(n <= 252);
346 | signal input in; // this is the number to be proved inside the range
347 | signal input range[2]; // the two elements should be the range, i.e. [lower bound, upper bound]
348 | signal output out;
349 | signal result;
350 |
351 | component lt = LessEqThan(n);
352 | component gt = GreaterEqThan(n);
353 |
354 | // Check if in is greater or equal to the lower bound
355 | gt.in[0] <== in;
356 | gt.in[1] <== range[0];
357 |
358 | // Check if in is less or equal to the upper bound
359 | lt.in[0] <== in;
360 | lt.in[1] <== range[1];
361 |
362 | // The result is 1 if both conditions also return 1
363 | out <== gt.out * lt.out;
364 | }
365 |
366 | component main { public [ range ] } = RangeProof(32);
367 |
368 | /* INPUT = {
369 | "in": "5",
370 | "range": ["1", "10"]
371 | } */
372 | ```
373 |
374 | ### 2.5.2 circomlib-matrix and Sudoku
375 |
376 | ```circom
377 | pragma circom 2.1.4;
378 |
379 | include "https://github.com/socathie/circomlib-matrix/blob/master/circuits/matAdd.circom";
380 | include "https://github.com/socathie/circomlib-matrix/blob/master/circuits/matElemMul.circom";
381 | include "https://github.com/socathie/circomlib-matrix/blob/master/circuits/matElemSum.circom";
382 | include "https://github.com/socathie/circomlib-matrix/blob/master/circuits/matElemPow.circom";
383 | include "circomlib/poseidon.circom";
384 | include "circomlib/comparators.circom";
385 |
386 | template RangeProof(n) {
387 | assert(n <= 252);
388 | signal input in; // this is the number to be proved inside the range
389 | signal input range[2]; // the two elements should be the range, i.e. [lower bound, upper bound]
390 | signal output out;
391 | signal result;
392 |
393 | component lt = LessEqThan(n);
394 | component gt = GreaterEqThan(n);
395 |
396 | // Check if in is greater or equal to the lower bound
397 | gt.in[0] <== in;
398 | gt.in[1] <== range[0];
399 |
400 | // Check if in is less or equal to the upper bound
401 | lt.in[0] <== in;
402 | lt.in[1] <== range[1];
403 |
404 | // The result is 1 if both conditions also return 1
405 | out <== gt.out * lt.out;
406 | }
407 |
408 | template sudoku() {
409 | signal input puzzle[9][9]; // 0 where blank
410 | signal input solution[9][9]; // 0 where original puzzle is not blank
411 | signal output out;
412 |
413 | // check whether the solution is zero everywhere the puzzle has values (to avoid trick solution)
414 |
415 | component mul = matElemMul(9,9);
416 |
417 | //[assignment] hint: you will need to initialize your RangeProof components here
418 | component puzRange[9][9];
419 | component solRange[9][9];
420 | for (var i=0; i<9; i++) {
421 | for (var j=0; j<9; j++) {
422 | puzRange[i][j] = RangeProof(32);
423 | puzRange[i][j].range[0] <== 0;
424 | puzRange[i][j].range[1] <== 9;
425 | solRange[i][j] = RangeProof(32);
426 | solRange[i][j].range[0] <== 0;
427 | solRange[i][j].range[1] <== 9;
428 | }
429 | }
430 |
431 | for (var i=0; i<9; i++) {
432 | for (var j=0; j<9; j++) {
433 | puzRange[i][j].in <== puzzle[i][j];
434 | solRange[i][j].in <== puzzle[i][j];
435 | assert(puzRange[i][j].out == 1);
436 | assert(solRange[i][j].out == 1);
437 | mul.a[i][j] <== puzzle[i][j];
438 | mul.b[i][j] <== solution[i][j];
439 | }
440 | }
441 | for (var i=0; i<9; i++) {
442 | for (var j=0; j<9; j++) {
443 | mul.out[i][j] === 0;
444 | }
445 | }
446 |
447 | // sum up the two inputs to get the full solution and square the full solution
448 |
449 | component add = matAdd(9,9);
450 |
451 | for (var i=0; i<9; i++) {
452 | for (var j=0; j<9; j++) {
453 | add.a[i][j] <== puzzle[i][j];
454 | add.b[i][j] <== solution[i][j];
455 | }
456 | }
457 |
458 | component square = matElemPow(9,9,2);
459 |
460 | for (var i=0; i<9; i++) {
461 | for (var j=0; j<9; j++) {
462 | square.a[i][j] <== add.out[i][j];
463 | }
464 | }
465 |
466 | // check all rows and columns and blocks sum to 45 and sum of squares = 285
467 |
468 | component row[9];
469 | component col[9];
470 | component block[9];
471 | component rowSq[9];
472 | component colSq[9];
473 | component blockSq[9];
474 |
475 |
476 | for (var k=0; k<9; k++) {
477 | row[k] = matElemSum(1,9);
478 | col[k] = matElemSum(1,9);
479 | block[k] = matElemSum(3,3);
480 |
481 | rowSq[k] = matElemSum(1,9);
482 | colSq[k] = matElemSum(1,9);
483 | blockSq[k] = matElemSum(3,3);
484 |
485 | for (var i=0; i<9; i++) {
486 | row[k].a[0][i] <== add.out[k][i];
487 | col[k].a[0][i] <== add.out[i][k];
488 |
489 | rowSq[k].a[0][i] <== square.out[k][i];
490 | colSq[k].a[0][i] <== square.out[i][k];
491 | }
492 | var x = 3*(k%3);
493 | var y = 3*(k\3);
494 | for (var i=0; i<3; i++) {
495 | for (var j=0; j<3; j++) {
496 | block[k].a[i][j] <== add.out[x+i][y+j];
497 | blockSq[k].a[i][j] <== square.out[x+i][y+j];
498 | }
499 | }
500 | row[k].out === 45;
501 | col[k].out === 45;
502 | block[k].out === 45;
503 |
504 | rowSq[k].out === 285;
505 | colSq[k].out === 285;
506 | blockSq[k].out === 285;
507 | }
508 |
509 | // hash the original puzzle and emit so that the dapp can listen for puzzle solved events
510 |
511 | component poseidon[9];
512 | component hash;
513 |
514 | hash = Poseidon(9);
515 |
516 | for (var i=0; i<9; i++) {
517 | poseidon[i] = Poseidon(9);
518 | for (var j=0; j<9; j++) {
519 | poseidon[i].inputs[j] <== puzzle[i][j];
520 | }
521 | hash.inputs[i] <== poseidon[i].out;
522 | }
523 |
524 | out <== hash.out;
525 | }
526 |
527 | component main = sudoku();
528 |
529 | /* INPUT = {
530 | "puzzle": [
531 | ["1", "0", "0", "0", "0", "0", "0", "0", "0"],
532 | ["0", "8", "0", "0", "0", "0", "0", "0", "0"],
533 | ["0", "0", "6", "0", "0", "0", "0", "0", "0"],
534 | ["0", "0", "0", "5", "0", "0", "0", "0", "0"],
535 | ["0", "0", "0", "0", "3", "0", "0", "0", "0"],
536 | ["0", "0", "0", "0", "0", "1", "0", "0", "0"],
537 | ["0", "0", "0", "0", "0", "0", "9", "0", "0"],
538 | ["0", "0", "0", "0", "0", "0", "0", "7", "0"],
539 | ["0", "0", "0", "0", "0", "0", "0", "0", "5"]
540 | ],
541 | "solution": [
542 | ["0", "7", "4", "2", "8", "5", "3", "9", "6"],
543 | ["2", "0", "5", "3", "9", "6", "4", "1", "7"],
544 | ["3", "9", "0", "4", "1", "7", "5", "2", "8"],
545 | ["4", "1", "7", "0", "2", "8", "6", "3", "9"],
546 | ["5", "2", "8", "6", "0", "9", "7", "4", "1"],
547 | ["6", "3", "9", "7", "4", "0", "8", "5", "2"],
548 | ["7", "4", "1", "8", "5", "2", "0", "6", "3"],
549 | ["8", "5", "2", "9", "6", "3", "1", "0", "4"],
550 | ["9", "6", "3", "1", "7", "4", "2", "8", "0"]
551 | ]
552 | } */
553 | ```
554 |
--------------------------------------------------------------------------------
/2024/week1_cryptographic_basics.md:
--------------------------------------------------------------------------------
1 | # Week 1 - Cryptographic Basics
2 |
3 | ## Practical
4 |
5 | In each of our modules, we will have a practical component so that you can get some hands-on experience as soon as possible. In this module, we will first start with some basics about circuit writing with Circom.
6 |
7 | ### Getting Started with Circom
8 |
9 | #### Understanding how a Circom circuit integrates to a system or application
10 | Let's see the workflow to have an overview of how a Circom circuit integrates into an App project:
11 | ```mermaid
12 | sequenceDiagram
13 | participant Developer
14 | participant Circom
15 | participant User
16 | participant App
17 | participant ProverModule
18 | participant Verifier
19 |
20 | Developer->>Circom: Writes circuit definition
21 | Developer->>Circom: Compiles circuit
22 | Developer->>Circom: Runs Trusted Setup
23 | Circom->>Developer: Return the ZK artifacts
24 | Developer->>ProverModule: Installs Witness calculator & Proving key
25 | Developer->>Verifier: Deploys contract or service with .vkey.json
26 | User->>App: Inputs data for proof generation
27 | App->>ProverModule: Sends inputs & proving key
28 | ProverModule->>ProverModule: Generates cryptographic proof
29 | ProverModule->>App: Returns proof
30 | App->>Verifier: Sends proof for verification
31 | Verifier->>App: Verifies proof and returns result
32 | App->>User: Displays verification result
33 | ```
34 | ##### Artifacts (generated by the Circom compiler)
35 | - **Witness calculator:** This is typically used to generate the witness from the input data based on the compiled circuit. This witness is a representation of the intermediate values of the circuit computation, it can be generated (depending in the project needs) for Webassembly (.wasm), Javascript (.js), binary for snarkjs (.wtns + .r1cs), etc.
36 | - **Proving/Verification keys:** The key files necessary for generating and verifying proofs. It is produced during the trusted setup phase of the circuit using Groth16, PLONK, etc. The files are **.zkey** for the proving key file and **.vkey.json** for verifying.
37 | - **Contract:** This is the smart contract to be deployed to a blockchain or DLT to use it to verify the proofs generated from the App using the PowerModule, normally generated in Solidity (.sol) format.
38 |
39 | Now we are able to understand the general idea of how to use build and integrate Circom circuits, we can start with the technical application.
40 |
41 | #### Syntax & zkrepl
42 |
43 | In order to get used to writing circuits, we will start with [zkREPL](https://zkrepl.dev/), which is an online REPL that allows you to write and compile Circom circuits in the browser (with no setup required). There are many good videos on Circom, such as this one:
44 |
45 | - [Circom Workshop 1 by 0xParc [1:31:06]](https://learn.0xparc.org/materials/circom/learning-group-1/circom-1/) - A very good introduction to Circom circuits.
46 |
47 | However, we recommend you work through a few of the following example scenarios with zkREPL first. This way you can gain some familiarity with the syntax without having to setup the environment and dig into too much detail. Afterwards, you can review the video and fill in any gaps.
48 |
49 | #### Hello World - Addition
50 |
51 | This is one of the simplest examples of a ZK circuit. This circuit checks that the `sum` is the addition of two numbers `a` and `b`. Copy-and-paste the following into zkREPL, and compile it in the browser.
52 |
53 | ```circom
54 | pragma circom 2.1.6;
55 |
56 | template AdditionProof() {
57 | // declaration of signals
58 | signal input a;
59 | signal input b;
60 | signal output sum;
61 |
62 | // constraint
63 | sum <== a + b;
64 | }
65 |
66 | component main = AdditionProof();
67 |
68 | /* INPUT = {
69 | "a": 3,
70 | "b": 5
71 | } */
72 | ```
73 |
74 | Note that the `a` and `b` are the private inputs.
75 |
76 | ### Write a circuit to prove multiplication
77 |
78 | From there, let's try to write a circuit to prove the multiplication of two numbers!
79 |
80 | Once that works, try to make a circuit to prove the multiplication of three numbers.
81 |
82 | Make sure to save these circuits and present them at the end of the week.
83 |
84 | ### Write a circuit to prove input to hash
85 |
86 | Now let's take it to the next level, we will write a circuit to prove the input to a hash. In order to do this, we will use the Poseidon hashing algorithm. You will need to include the following line at the top:
87 |
88 | ```circom
89 | include "circomlib/poseidon.circom";
90 | ```
91 |
92 | Here's how you'd use the Poseidon hashing function:
93 |
94 | ```circom
95 | component hasher = Poseidon(1);
96 | hasher.inputs[0] <== preimage;
97 | hashOutput <== hasher.out;
98 | ```
99 |
100 | You might want to research what a `component` is in the Circom syntax.
101 |
102 | Use this as the input:
103 |
104 | ```circom
105 | /* INPUT = {
106 | "preimage": "12345",
107 | "hash": "4267533774488295900887461483015112262021273608761099826938271132511348470966"
108 | } */
109 | ```
110 |
111 | Save the full answer to be submitted at the end of this module!
112 |
113 | ### Write a circuit to add two numbers on an elliptic curve
114 |
115 | We don't dive in the details of elliptic curves in this module, but we can still write a simple circuit to add two numbers on an elliptic curve by importing the `babyjub.circom` file and add the points together.
116 | Link to this code is [here](https://gist.github.com/NOOMA-42/1a9040d49a0a828075971cb50acc4adb)
117 |
118 | ```circom
119 | // Code modified from circomlib test
120 | pragma circom 2.1.6;
121 |
122 | include "circomlib/circuits/babyjub.circom";
123 |
124 | template AddNumOnEllipticCurve() {
125 | signal input x1;
126 | signal input y1;
127 | signal input x2;
128 | signal input y2;
129 | // To check xout yout
130 | signal input xout;
131 | signal input yout;
132 | component babyAdd = BabyAdd();
133 | babyAdd.x1 <== x1;
134 | babyAdd.y1 <== y1;
135 | babyAdd.x2 <== x2;
136 | babyAdd.y2 <== y2;
137 | xout === babyAdd.xout;
138 | yout === babyAdd.yout;
139 | }
140 |
141 | component main = AddNumOnEllipticCurve();
142 |
143 | /* INPUT = {
144 | "x1": "17777552123799933955779906779655732241715742912184938656739573121738514868268",
145 | "y1": "2626589144620713026669568689430873010625803728049924121243784502389097019475",
146 | "x2": "17777552123799933955779906779655732241715742912184938656739573121738514868268",
147 | "y2": "2626589144620713026669568689430873010625803728049924121243784502389097019475",
148 | "xout": "6890855772600357754907169075114257697580319025794532037257385534741338397365",
149 | "yout": "4338620300185947561074059802482547481416142213883829469920100239455078257889"
150 | } */
151 | ```
152 |
153 | ### Write a circuit to prove inclusion in Merkle tree
154 |
155 | TheBC01 has a good example of a Merkle tree circuit [here](https://github.com/TtheBC01/zkSNARK-playground/blob/main/examples/merkle-tree/tree.circom)
156 |
157 | The main component is Poseidon hash. Poseidon hash maps sequences of elements to a fixed-length sequence of elements. You can use it to hash messages of arbitrary length or fixed length (such as in a Merkle tree, where typically two elements are hashed). Here you initialize the Poseidon hash with n = 2
158 |
159 | You may also need a Mux. Mux is a component that takes two inputs and outputs one of them based on a selector. In this case, the selector is the index of the element in the Merkle tree.
160 | ```circom
161 | poseidons[i] = Poseidon(2);
162 | mux[i] = MultiMux1(2);
163 | ```
164 |
165 | ### Tips
166 |
167 | You can always reference circomlib for examples of how to use existing circuits. You can find the circomlib [here](https://github.com/iden3/circomlib). Writing a simple circuit is not too difficult, but it can be a bit tricky to get the constraints right and optimize the circuit. circomlib has many examples that you can use as a reference.
168 |
169 | ## Study
170 |
171 | ### Symmetric vs Asymmetric Encryption (AES, RSA)
172 |
173 | Encryption is a technique used to encode data, making it readable only to those who possess the correct decryption key. There are two main types of encryption - symmetric and asymmetric, each serving different purposes and with their own strengths and weaknesses.
174 |
175 | **Symmetric Encryption** - Also known as single-key encryption, this method involves using the same key for both encryption and decryption. The most widely used symmetric encryption algorithm is Advanced Encryption Standard (AES).
176 |
177 | **Asymmetric Encryption** - Also known as public-key encryption, this method uses a pair of keys: one for encryption and another for decryption. The RSA algorithm is one of the best known public-key algorithms.
178 |
179 | ```mermaid
180 | ---
181 | title: Symmetric vs Asymmetric Encryption
182 | ---
183 | graph TD
184 | subgraph Asymmetric Encryption
185 | A2[Plaintext] -->|Encrypt with Public Key| B2[Ciphertext]
186 | B2 -->|Decrypt with Private Key| C2[Plaintext]
187 | D2[Public Key]
188 | E2[Private Key]
189 | A2 -- Public Key --> D2
190 | D2 -.-> B2
191 | B2 -.-> E2
192 | E2 -.-> C2
193 | end
194 | subgraph Symmetric Encryption
195 | A1[Plaintext] -->|Encrypt with Secret Key| B1[Ciphertext]
196 | B1 -->|Decrypt with Secret Key| C1[Plaintext]
197 | D1[Secret Key]
198 | A1 -- Shared Secret Key --> D1
199 | D1 -.-> C1
200 | end
201 |
202 | classDef symmetric fill:#FFDDDD,stroke:#FF0000,stroke-width:2px;
203 | classDef asymmetric fill:#DDFFDD,stroke:#00FF00,stroke-width:2px;
204 |
205 | class SymmetricEncryption symmetric;
206 | class AsymmetricEncryption asymmetric;
207 |
208 | ```
209 |
210 | The key difference between the two is the number of keys used: symmetric encryption uses one key for both encrypting and decrypting, while asymmetric encryption uses a different key for each (one public, one private). For a deeper understanding of symmetric and asymmetric encryption, please explore these resources:
211 | - [Symmetric vs. Asymmetric Encryption – What are differences?](https://www.ssl2buy.com/wiki/symmetric-vs-asymmetric-encryption-what-are-differences)
212 | - [AES Explained (Advanced Encryption Standard) - Computerphile [14:13]](https://www.youtube.com/watch?v=O4xNJsjtN6E)
213 | - [Prime Numbers & RSA Encryption Algorithm - Computerphile [15:06]](https://www.youtube.com/watch?v=JD72Ry60eP4)
214 | - [What Is AES Encryption and How Does It Work?](https://www.simplilearn.com/tutorials/cryptography-tutorial/aes-encryption)
215 | - [What is RSA encryption and how does it work?](https://www.comparitech.com/blog/information-security/rsa-encryption/)
216 |
217 |
218 | :::info
219 | **🤔 Consider the following:**
220 | 1. What is the primary difference between symmetric and asymmetric encryption?
221 | 2. Can you briefly explain how AES (Advanced Encryption Standard) works?
222 | 3. What makes RSA a popular choice for public-key encryption?
223 | :::
224 |
225 | ### Hash Functions, Merkle Trees
226 |
227 | #### Hash Functions
228 |
229 | A hash function takes an input and returns a fixed-size string of bytes. **SHA-256** and **Poseidon** are popular cryptographic hash functions in our context, with Poseidon specifically designed for arithmetic-friendly operations, benefiting certain applications in blockchains.
230 |
231 | The primary characteristics of a good hash function are [preimage resistance](https://en.wikipedia.org/wiki/Preimage_attack), second preimage resistance, and [collision resistance](https://en.wikipedia.org/wiki/Collision_resistance), ensuring data security and integrity. In blockchain technology, hash functions create an unalterable, unique representation of each block's content, contributing to the immutability and transparency of the system.
232 |
233 | ```mermaid
234 | flowchart TB
235 | subgraph Inputs
236 | A1["Input 1: 'Hello World'"]
237 | A2["Input 2: 'Hello'"]
238 | A3["Input 3: '123456'"]
239 | A4["Input 4: 'Blockchain'"]
240 | end
241 |
242 | subgraph Poseidon Hash Function
243 | Poseidon["Poseidon Hash"]
244 | end
245 |
246 | subgraph Outputs
247 | B1["Hash Output 1: 9f86d081884c7d659a2feaa0c55ad015..."]
248 | B2["Hash Output 2: e0ac3600c813991f82cdbb3f707d7898..."]
249 | B3["Hash Output 3: a83e50450ac94b2c8fd1bc73f540dbdb..."]
250 | B4["Hash Output 4: 0fb2e6a2b3470e2a69d0b13e4b75d28e..."]
251 | end
252 |
253 | A1 -->|Input 1| Poseidon
254 | A2 -->|Input 2| Poseidon
255 | A3 -->|Input 3| Poseidon
256 | A4 -->|Input 4| Poseidon
257 |
258 | Poseidon -->|Hash 1| B1
259 | Poseidon -->|Hash 2| B2
260 | Poseidon -->|Hash 3| B3
261 | Poseidon -->|Hash 4| B4
262 |
263 | ```
264 |
265 | Explore these resources to further your understanding:
266 | - [What Is SHA-256 Algorithm & How It Works](https://www.ssldragon.com/blog/sha-256-algorithm/)
267 | - [How is SHA-256 used in blockchain, and why?](https://www.educative.io/answers/how-is-sha-256-used-in-blockchain-and-why)
268 | - [Poseidon: A new hash function for zero-knowledge proof systems](https://eprint.iacr.org/2019/458.pdf)
269 | - [USENIX Security '21 - Poseidon: A New Hash Function for Zero-Knowledge Proof Systems [10:45]](https://www.youtube.com/watch?v=hUx3WpDV_l0)
270 | - This video is quite technical, but the first few minutes provide a good explanation for the motivation behind the Poseidon hash function.
271 |
272 | :::info
273 | **🤔 Consider the following:**
274 | 1. What is a hash function and what are its primary uses in cryptography?
275 | 2. How does the SHA-256 hashing algorithm function, in simple terms?
276 | 3. What is the Poseidon hash function and why is it particularly useful in ZKPs?
277 | :::
278 |
279 | #### Merkle Trees
280 |
281 | A Merkle tree is a core component of blockchain and cryptography. It's a binary tree filled with cryptographic hashes. This structure enables efficient and secure verification of the contents of large data structures.
282 | ```mermaid
283 | flowchart TB
284 | subgraph "Merkle Tree"
285 | A0["Leaf 1: Tx1 Hash"]
286 | A1["Leaf 2: Tx2 Hash"]
287 | A2["Leaf 3: Tx3 Hash"]
288 | A3["Leaf 4: Tx4 Hash"]
289 |
290 | B0["Hash of (Leaf 1 + Leaf 2)"]
291 | B1["Hash of (Leaf 3 + Leaf 4)"]
292 |
293 | C0["Merkle Root: Hash of (B0 + B1)"]
294 |
295 | A0 -->|Hash| B0
296 | A1 -->|Hash| B0
297 | A2 -->|Hash| B1
298 | A3 -->|Hash| B1
299 |
300 | B0 -->|Hash| C0
301 | B1 -->|Hash| C0
302 | end
303 |
304 | subgraph "Ethereum"
305 | StateRoot["State Root"]
306 | TxRoot["Transaction Root"]
307 | ReceiptRoot["Receipt Root"]
308 | end
309 |
310 | C0 -->|Stored in| TxRoot
311 | TxRoot -->|Verification| EthereumApp["Smart Contract or App"]
312 | EthereumApp -->|Prove Validity| Verifier
313 |
314 | ```
315 |
316 | To understand more about Merkle trees, read the following:
317 | - [How Merkle Trees Enable the Decentralized Web! [10:05]](https://www.youtube.com/watch?v=3giNelTfeAk)
318 | - [Merkle Trees and Merkle Roots Explained](https://academy.binance.com/en/articles/merkle-trees-and-merkle-roots-explained)
319 | - [Visualizing Efficient Merkle Trees for Zero-Knowledge Proofs](https://kndrck.co/posts/efficient-merkletrees-zk-proofs/)
320 | - [The Ultimate Merkle Tree Guide in Solidity](https://soliditydeveloper.com/merkle-tree)
321 |
322 |
323 | :::info
324 | **🤔 Consider the following:**
325 | 1. Can you describe the structure of a Merkle tree?
326 | 2. How are Merkle trees used within the blockchain context?
327 | 3. Why are Merkle trees useful for efficient and secure verification of large data structures?
328 | :::
329 |
330 | ### Digital Signatures (Schnorr)
331 |
332 | Digital signatures ensure the integrity and authenticity of digital messages or documents. By providing a means to verify the origin and confirm that the content has not been altered, digital signatures play a pivotal role in maintaining trust in digital communications.
333 |
334 | In PKC (Public Key Cryptography), anyone can encrypt their message with the receiver's public key, and only the receiver can decrypt the message with their private key. In digital signatures, on the other hand, if a signer generates a signature for a message using their private key, anyone can validate it using the signer's public key. Therefore, the message of the signature is made public, which distinguishes it from cryptographic commitments.
335 |
336 | ```mermaid
337 | sequenceDiagram
338 | participant Sender as Sender
339 | participant PrivateKey as Sender's Private Key (sk)
340 | participant Message as Message
341 | participant PublicKey as Receiver's Public Key (pk)
342 | participant Receiver as Receiver
343 | participant Verifier as Verifier (Any Third-Party)
344 |
345 | Note over Sender, Receiver: Public Key Cryptography (PKC)
346 | Sender->>PublicKey: Gets Receiver's Public Key (pk)
347 | Sender->>Message: Encrypts Message with Receiver's Public Key
348 | Sender->>Receiver: Sends Encrypted Message
349 | Receiver->>PrivateKey: Decrypts Message using Private Key (sk)
350 |
351 | Note over Sender, Verifier: Digital Signatures
352 | Sender->>Message: Signs Message using Sender's Private Key (sk)
353 | Sender->>Verifier: Sends Signed Message and Sender's Public Key
354 | Verifier->>Sender: Validates Signature using Sender's Public Key
355 | ```
356 |
357 | Start your exploration of digital signatures with this intuitive video:
358 | - [What are Digital Signatures? - Computerphile [10:16]](https://www.youtube.com/watch?v=s22eJ1eVLTU)
359 |
360 | **Schnorr Signature**: Schnorr signatures are a digital signature scheme known for their simplicity and efficiency.
361 | - [Schnorr Digital Signature (by GeeksForGeeks)](https://www.geeksforgeeks.org/schnorr-digital-signature/)
362 | - [Schnorr Digital Signature [4:58]](https://www.youtube.com/watch?v=mV9hXEFUB6A)
363 |
364 | **Exploring DSA**: Get a deep dive into the Digital Signature Algorithm (DSA) and its significance in bolstering internet security. Through the listed resources, understand the mechanics of DSA and its use cases.
365 | - [Digital Signature Algorithm (DSA) in Cryptography](https://www.simplilearn.com/tutorials/cryptography-tutorial/digital-signature-algorithm)
366 | - [Digital Signature Algorithm (DSA) - Cryptography [5:46]](https://www.youtube.com/watch?v=iS1nK4G6EtA)
367 | - [Digital Signature Algorithm (DSA) explained with example [24:32]](https://www.youtube.com/watch?v=MtT3NBfpV5Q)
368 |
369 | :::info
370 | **🤔 Consider the following:**
371 | 1. Can you describe what digital signatures are and why they are essential in digital communications?
372 | 2. Explain the workings of the Digital Signature Algorithm (DSA).
373 | :::
374 |
375 | ### DLP-based Public-Key Cryptography (DLP, DH, Elgamal)
376 |
377 | While RSA encryption is based on the hardness of factoring problem, there is another public key cryptography system based on hardness of Discrete Logarithm Problem (DLP).
378 |
379 | 1. **Discrete Log Problem (DLP):** This is a cornerstone problem in public-key cryptography and underlies many key exchange and encryption algorithms. Understanding DLP provides a foundation for the remaining topics.
380 | - [The Discrete Logarithm Problem - Khan Academy [1:55]](https://youtu.be/SL7J8hPKEWY)
381 | - [Public key cryptography using discrete logarithms](https://www.di-mgt.com.au/public-key-crypto-discrete-logs-0.html)
382 |
383 | 2. **Diffie-Hellman Key Exchange:** This is one of the earliest practical implementations of key exchange protocols based on the Discrete Log Problem. It's critical to understand how secure communication can be established over insecure channels.
384 |
385 | This protocol is significant as it enables secure communication over insecure channels by allowing two parties to generate a shared secret key, which can then be used for encryption and decryption of messages
386 | - [Secret Key Exchange (Diffie-Hellman) - Computerphile [8:39]](https://www.youtube.com/watch?v=NmM9HA2MQGI)
387 | - [Diffie Hellman -the Mathematics bit- Computerphile [7:04]](https://youtu.be/Yjrfm_oRO0w)
388 | - [Implementation of Diffie-Hellman Algorithm
389 | ](https://www.geeksforgeeks.org/implementation-diffie-hellman-algorithm/)
390 |
391 | 3. **ElGamal Encryption:** This is a public-key encryption method that utilizes the principles of DLP. This will allow you to see a practical application of these abstract concepts in a real-world encryption scheme.
392 | - [Intro to the ElGamal Cryptosystem [8:20]](https://www.youtube.com/watch?v=oQqr8d5s3Uk)
393 | - [The ElGamal Algorithm: a simple example [6:38]](https://www.youtube.com/watch?v=4xVCrTb_1II)
394 | - [ElGamal Encryption Algorithm](https://www.geeksforgeeks.org/elgamal-encryption-algorithm/)
395 |
396 | #### The ElGamal workflow sample
397 | ##### Key Generation (Alice - message receiver)
398 | | Step | Description |
399 | | --- | --- |
400 | | Select **p** | **p** is avery large prime number |
401 | | Find a primitive root of *p*:g | |
402 | | Choose a random intenger **a** as her private key | 1<**a**<**p**-1 |
403 | | Compute **e**: **e**=**g**^**a** mod **p** | |
404 | | **Public key: (p,g,e)** | |
405 |
406 | ##### Encryption (Bob - message sender)
407 | | Step | Description |
408 | | --- | --- |
409 | | Plaintext: m | m>PrivateKey: Generates Private Key (sk)
483 | PrivateKey->>EllipticCurve: Computes Public Key using Elliptic Curve
484 | EllipticCurve->>PublicKey: Returns Public Key (pk)
485 |
486 | Note over User, Message: ECDSA Signing
487 | User->>Message: Chooses a Message to Sign
488 | Message->>EllipticCurve: Calculates Signature (using sk)
489 | EllipticCurve->>User: Returns Signature (r, s)
490 |
491 | User->>Verifier: Sends Signed Message and Public Key
492 | Verifier->>Message: Extracts Signature (r, s) from Message
493 | Verifier->>EllipticCurve: Uses Public Key and Signature to Verify Message
494 | EllipticCurve->>Verifier: Returns Verification Result (Valid/Invalid)
495 | Verifier->>User: Returns Verification Status
496 | ```
497 |
498 | The following StackExchange answer goes over the differences between some of the most popular digital signature protocols. It also leads into our next topic, which is another type of digital signature called the Edwards-curve Digital Signature Algorithm (EdDSA).
499 |
500 | - [What is the difference between the RSA, DSA, and ECDSA keys that ssh uses?](https://askubuntu.com/a/1000928/733503)
501 |
502 | ### BLS Signatures
503 |
504 | BLS (Boneh-Lynn-Shacham) signatures are a type of cryptographic signature scheme that allows for efficient aggregation of individual signatures into a single signature. It also makes use of pairing-based cryptography.
505 |
506 | The intuition of BLS is that it is an aggregation signature. The image below shows how the public key and signature are aggregated. The advantage is that all signatures can be verified at once. That's why it is commonly used in consensus protocols.
507 | 
508 | [Source](https://www.inevitableeth.com/en/home/concepts/bls-signatures)
509 |
510 | ### Pairing-Based Cryptography
511 |
512 | Pairing-based cryptography underly many of the following topics. So it is important to know what it is, even if you might not need to know the specifics.
513 |
514 | You can imagine pairing as the multiplication of elliptic curves. The original elliptic curve operation is homomorphically additive but not homomorphically multiplicative. Pairing is a way to mimic this "multiplication".
515 |
516 | This is largely used in ZKPs, especially during the verification stage.
517 |
518 | 
519 | [Source](https://www.inevitableeth.com/home/concepts/elliptic-curve-pairings)
520 |
521 | If you would like to know more about it, you can check out the following resources:
522 |
523 | - **[Exploring Elliptic Curve Pairings by Vitalik Buterin (optional)](https://medium.com/@VitalikButerin/exploring-elliptic-curve-pairings-c73c1864e627)** - This resource builds upon the knowledge you learned above regarding elliptic curves and sets the stage for the topics discussed below. It is an excellent introduction to the topic.
524 | - **[Pairings or Bilinear Maps by Alin Tomescu](https://alinush.github.io/2022/12/31/pairings-or-bilinear-maps.html)** - This resource begins with an introduction to the three fundamental properties of bilinear maps. Building on this foundation, it further explores applications such as the Tripartite Diffie-Hellman protocol, BLS signatures, and Identity-Based Encryption (IBE).
525 |
526 | Optional Resources (For Deepening Your Understanding)
527 | - **[Pairings for beginners by Craig Costello](https://static1.squarespace.com/static/5fdbb09f31d71c1227082339/t/5ff394720493bd28278889c6/1609798774687/PairingsForBeginners.pdf)** - This resource introduces bilinear pairings and their cryptographic applications in a beginner-friendly manner.
528 |
529 | # 💪 Exercises
530 |
531 | 1. **Symmetric vs. Asymmetric Encryption**: What are the key differences between symmetric and asymmetric encryption? Provide a practical use case for each.
532 | 2. **Public-Key Cryptography and Key Exchange Protocols**: How can the Diffie-Hellman protocol enhance security in a messaging application?
533 | 3. **Hash Functions**: What features make SHA-256 and Poseidon good hash functions for ensuring data integrity? Mention one unique advantage of Poseidon.
534 | 4. **Merkle Trees**: Explain how Merkle trees can help verify data in a large database efficiently.
535 | 5. **Cryptographic Commitments**: How can Pedersen Commitments be used in a blockchain protocol to maintain transaction privacy?
536 | 6. **Digital Signatures**: How can you verify the authenticity of a digitally signed document?
537 |
--------------------------------------------------------------------------------