From 0465d89ac56a2bf7d6f354ea7eed3611b0d8edad Mon Sep 17 00:00:00 2001 From: Daniel Bejarano <58019353+dbejarano820@users.noreply.github.com> Date: Wed, 6 Sep 2023 10:48:28 -0600 Subject: [PATCH 1/3] fix: broken links in readme (#551) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0b46d7e0e..92337543d 100644 --- a/README.md +++ b/README.md @@ -28,8 +28,8 @@ Right now Plonk prover is in this repo, you can find the others here: ## Main crates - [Math](https://github.com/lambdaclass/lambdaworks/tree/main/math) -- [Crypto primitives](https://github.com/lambdaclass/lambdaworks/crypto) -- [Plonk Prover](https://github.com/lambdaclass/lambdaworks/provers/plonk) +- [Crypto primitives](https://github.com/lambdaclass/lambdaworks/tree/main/crypto) +- [Plonk Prover](https://github.com/lambdaclass/lambdaworks/tree/main/provers/plonk) ### Crypto - [Elliptic curves](https://github.com/lambdaclass/lambdaworks/tree/main/math/src/elliptic_curve) From 29af640a8e4e22301e7866981f156281d03ab51a Mon Sep 17 00:00:00 2001 From: Diego K <43053772+diegokingston@users.noreply.github.com> Date: Fri, 8 Sep 2023 10:47:31 -0300 Subject: [PATCH 2/3] added exercises (#553) --- exercises/README.md | 7 + exercises/blind_trust/Cargo.toml | 15 + exercises/blind_trust/README.md | 158 ++ exercises/blind_trust/proof | Bin 0 -> 1620 bytes exercises/blind_trust/src/circuit.rs | 187 ++ exercises/blind_trust/src/lib.rs | 3 + .../blind_trust/src/sith_generate_proof.rs | 106 ++ exercises/blind_trust/src/solution.rs | 165 ++ exercises/blind_trust/srs | Bin 0 -> 2172 bytes exercises/broken_heart/Cargo.toml | 12 + exercises/broken_heart/README.md | 109 ++ exercises/broken_heart/src/circuit.rs | 178 ++ exercises/broken_heart/src/lib.rs | 3 + exercises/broken_heart/src/server.rs | 96 + exercises/broken_heart/src/solution.rs | 119 ++ exercises/challenge_1/Cargo.toml | 10 + exercises/challenge_1/README.md | 6 + exercises/challenge_1/src/cypher.rs | 11 + exercises/challenge_1/src/data.rs | 148 ++ exercises/challenge_1/src/field.rs | 5 + exercises/challenge_1/src/main.rs | 18 + exercises/challenge_1/src/solver.rs | 6 + exercises/challenge_2/Cargo.toml | 10 + exercises/challenge_2/README.md | 16 + exercises/challenge_2/src/main.rs | 90 + exercises/challenge_2/srs.bin | Bin 0 -> 148044 bytes exercises/challenge_3/Cargo.toml | 17 + exercises/challenge_3/README.md | 15 + exercises/challenge_3/src/main.rs | 124 ++ exercises/challenge_3/srs.bin | Bin 0 -> 148044 bytes exercises/message/Cargo.toml | 45 + exercises/message/LICENSE | 201 +++ exercises/message/README.md | 42 + exercises/message/src/cairo/air.rs | 1523 ++++++++++++++++ exercises/message/src/cairo/cairo_layout.rs | 30 + exercises/message/src/cairo/cairo_mem.rs | 132 ++ .../src/cairo/decode/instruction_flags.rs | 709 ++++++++ .../src/cairo/decode/instruction_offsets.rs | 94 + exercises/message/src/cairo/decode/mod.rs | 2 + exercises/message/src/cairo/errors.rs | 29 + .../message/src/cairo/execution_trace.rs | 1312 ++++++++++++++ exercises/message/src/cairo/mod.rs | 8 + .../message/src/cairo/register_states.rs | 252 +++ .../message/src/cairo/runner/file_writer.rs | 35 + exercises/message/src/cairo/runner/mod.rs | 3 + .../message/src/cairo/runner/program.json | 200 +++ .../message/src/cairo/runner/program.memory | Bin 0 -> 320 bytes .../message/src/cairo/runner/program.trace | Bin 0 -> 72 bytes exercises/message/src/cairo/runner/run.rs | 410 +++++ .../message/src/cairo/runner/vec_writer.rs | 58 + exercises/message/src/lib.rs | 13 + exercises/message/src/main.rs | 266 +++ exercises/message/src/starks/config.rs | 20 + .../src/starks/constraints/boundary.rs | 174 ++ .../starks/constraints/evaluation_table.rs | 34 + .../src/starks/constraints/evaluator.rs | 323 ++++ .../message/src/starks/constraints/mod.rs | 3 + exercises/message/src/starks/context.rs | 32 + exercises/message/src/starks/debug.rs | 98 ++ exercises/message/src/starks/domain.rs | 57 + .../message/src/starks/example/dummy_air.rs | 118 ++ .../src/starks/example/fibonacci_2_columns.rs | 130 ++ .../src/starks/example/fibonacci_rap.rs | 260 +++ exercises/message/src/starks/example/mod.rs | 5 + .../src/starks/example/quadratic_air.rs | 125 ++ .../src/starks/example/simple_fibonacci.rs | 128 ++ exercises/message/src/starks/frame.rs | 195 +++ .../message/src/starks/fri/fri_commitment.rs | 48 + .../message/src/starks/fri/fri_decommit.rs | 247 +++ .../message/src/starks/fri/fri_functions.rs | 64 + exercises/message/src/starks/fri/mod.rs | 127 ++ exercises/message/src/starks/grinding.rs | 78 + exercises/message/src/starks/mod.rs | 19 + exercises/message/src/starks/proof/errors.rs | 9 + exercises/message/src/starks/proof/mod.rs | 3 + exercises/message/src/starks/proof/options.rs | 266 +++ exercises/message/src/starks/proof/stark.rs | 701 ++++++++ exercises/message/src/starks/prover.rs | 1549 +++++++++++++++++ exercises/message/src/starks/trace.rs | 206 +++ exercises/message/src/starks/traits.rs | 128 ++ exercises/message/src/starks/transcript.rs | 216 +++ exercises/message/src/starks/utils.rs | 38 + exercises/message/src/starks/verifier.rs | 631 +++++++ 83 files changed, 13030 insertions(+) create mode 100644 exercises/README.md create mode 100644 exercises/blind_trust/Cargo.toml create mode 100644 exercises/blind_trust/README.md create mode 100644 exercises/blind_trust/proof create mode 100644 exercises/blind_trust/src/circuit.rs create mode 100644 exercises/blind_trust/src/lib.rs create mode 100644 exercises/blind_trust/src/sith_generate_proof.rs create mode 100644 exercises/blind_trust/src/solution.rs create mode 100644 exercises/blind_trust/srs create mode 100644 exercises/broken_heart/Cargo.toml create mode 100644 exercises/broken_heart/README.md create mode 100644 exercises/broken_heart/src/circuit.rs create mode 100644 exercises/broken_heart/src/lib.rs create mode 100644 exercises/broken_heart/src/server.rs create mode 100644 exercises/broken_heart/src/solution.rs create mode 100644 exercises/challenge_1/Cargo.toml create mode 100644 exercises/challenge_1/README.md create mode 100644 exercises/challenge_1/src/cypher.rs create mode 100644 exercises/challenge_1/src/data.rs create mode 100644 exercises/challenge_1/src/field.rs create mode 100644 exercises/challenge_1/src/main.rs create mode 100644 exercises/challenge_1/src/solver.rs create mode 100644 exercises/challenge_2/Cargo.toml create mode 100644 exercises/challenge_2/README.md create mode 100644 exercises/challenge_2/src/main.rs create mode 100644 exercises/challenge_2/srs.bin create mode 100644 exercises/challenge_3/Cargo.toml create mode 100644 exercises/challenge_3/README.md create mode 100644 exercises/challenge_3/src/main.rs create mode 100644 exercises/challenge_3/srs.bin create mode 100644 exercises/message/Cargo.toml create mode 100644 exercises/message/LICENSE create mode 100644 exercises/message/README.md create mode 100644 exercises/message/src/cairo/air.rs create mode 100644 exercises/message/src/cairo/cairo_layout.rs create mode 100644 exercises/message/src/cairo/cairo_mem.rs create mode 100644 exercises/message/src/cairo/decode/instruction_flags.rs create mode 100644 exercises/message/src/cairo/decode/instruction_offsets.rs create mode 100644 exercises/message/src/cairo/decode/mod.rs create mode 100644 exercises/message/src/cairo/errors.rs create mode 100644 exercises/message/src/cairo/execution_trace.rs create mode 100644 exercises/message/src/cairo/mod.rs create mode 100644 exercises/message/src/cairo/register_states.rs create mode 100644 exercises/message/src/cairo/runner/file_writer.rs create mode 100644 exercises/message/src/cairo/runner/mod.rs create mode 100644 exercises/message/src/cairo/runner/program.json create mode 100644 exercises/message/src/cairo/runner/program.memory create mode 100644 exercises/message/src/cairo/runner/program.trace create mode 100644 exercises/message/src/cairo/runner/run.rs create mode 100644 exercises/message/src/cairo/runner/vec_writer.rs create mode 100644 exercises/message/src/lib.rs create mode 100644 exercises/message/src/main.rs create mode 100644 exercises/message/src/starks/config.rs create mode 100644 exercises/message/src/starks/constraints/boundary.rs create mode 100644 exercises/message/src/starks/constraints/evaluation_table.rs create mode 100644 exercises/message/src/starks/constraints/evaluator.rs create mode 100644 exercises/message/src/starks/constraints/mod.rs create mode 100644 exercises/message/src/starks/context.rs create mode 100644 exercises/message/src/starks/debug.rs create mode 100644 exercises/message/src/starks/domain.rs create mode 100644 exercises/message/src/starks/example/dummy_air.rs create mode 100644 exercises/message/src/starks/example/fibonacci_2_columns.rs create mode 100644 exercises/message/src/starks/example/fibonacci_rap.rs create mode 100644 exercises/message/src/starks/example/mod.rs create mode 100644 exercises/message/src/starks/example/quadratic_air.rs create mode 100644 exercises/message/src/starks/example/simple_fibonacci.rs create mode 100644 exercises/message/src/starks/frame.rs create mode 100644 exercises/message/src/starks/fri/fri_commitment.rs create mode 100644 exercises/message/src/starks/fri/fri_decommit.rs create mode 100644 exercises/message/src/starks/fri/fri_functions.rs create mode 100644 exercises/message/src/starks/fri/mod.rs create mode 100644 exercises/message/src/starks/grinding.rs create mode 100644 exercises/message/src/starks/mod.rs create mode 100644 exercises/message/src/starks/proof/errors.rs create mode 100644 exercises/message/src/starks/proof/mod.rs create mode 100644 exercises/message/src/starks/proof/options.rs create mode 100644 exercises/message/src/starks/proof/stark.rs create mode 100644 exercises/message/src/starks/prover.rs create mode 100644 exercises/message/src/starks/trace.rs create mode 100644 exercises/message/src/starks/traits.rs create mode 100644 exercises/message/src/starks/transcript.rs create mode 100644 exercises/message/src/starks/utils.rs create mode 100644 exercises/message/src/starks/verifier.rs diff --git a/exercises/README.md b/exercises/README.md new file mode 100644 index 000000000..c9cc91089 --- /dev/null +++ b/exercises/README.md @@ -0,0 +1,7 @@ +# Lambdaworks Exercises & Challenges + +Contains several examples and challenges to use Lambdaworks. + +Challenges 1, 2 and 3 appeared in [Ingonyama's CTF event](https://ingonyama.ctfd.io/) + +Challenges message, blind_trust and broken heart appeared in the first LambdaIngo ZK CTF diff --git a/exercises/blind_trust/Cargo.toml b/exercises/blind_trust/Cargo.toml new file mode 100644 index 000000000..9b30aca96 --- /dev/null +++ b/exercises/blind_trust/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "blind_trust" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rand = "0.8.5" + + +lambdaworks-math = { git = "https://github.com/lambdaclass/lambdaworks", rev = "8fcd64f" } +lambdaworks-crypto = { git = "https://github.com/lambdaclass/lambdaworks", rev = "8fcd64f" } +lambdaworks-plonk = { git = "https://github.com/lambdaclass/lambdaworks_plonk_prover", rev = "6e39865"} + diff --git a/exercises/blind_trust/README.md b/exercises/blind_trust/README.md new file mode 100644 index 000000000..36f9c1617 --- /dev/null +++ b/exercises/blind_trust/README.md @@ -0,0 +1,158 @@ +# Obi-Wan's search for the Sith Foundry + +In his quest to stop the Sith’s menace, Obi-Wan Kenobi finds a (Sith) holocron, giving a zero-knowledge proof of the existence of the Sith’s galactic foundry (using galactic Plonk). This place is rumored to contain several artifacts that could aid the Galactic Republic in its war efforts. The position, given by (x,h,y), satisfies the equation y=x*h+b. After some study, Obi-Wan finds the values of y and b (which belong to Sith lore). The only problem is that, even with this knowledge, it may take him quite long to find the mysterious planet, and the situation in the Republic is desperate. He also finds, together with the holocron, a second item containing the SRS used to generate the proof, the prover, and a description of the circuit used. Will he be able to find the position of the foundry before it is too late? The flag consists of the x and h concatenated and written in hex (for example, x=0x123, h=0x789, the FLAG=123789) + +## Description +In this challenge the participants have to exploit a vulnerability in a PLONK implementation that's missing the blindings of the wire polynomials. + +The first round of PLONK reads as follows: + +``` +Compute polynomials a',b',c' as the interpolation polynomials of the columns of T at the domain H. +Sample random b_1, b_2, b_3, b_4, b_5, b_6 +Let + +a := (b_1X + b_2)Z_H + a' + +b := (b_3X + b_4)Z_H + b' + +c := (b_5X + b_6)Z_H + c' + +Compute [a]_1, [b]_1, [c]_1 and add them to the transcript. +``` + +The multiples of $Z_H$ that are added to $a', b', c'$ are the called the blindings. In subsequent rounds the polynomials $a, b, c$ are opened at a point chosen by the verifier. If the blindings are missing, information about the prover's private inputs can be leaked. + +In this challenge the participant is given a single proof of the following simple circuit, along with the corresponding values of $b$ and $y$: + +``` +PRIVATE INPUT: + x + h + +PUBLIC INPUT: + b + y + +OUTPUT: + ASSERT y == h * x + b +``` + +The flag is `x.representative() || h.representative()`. The objective of the challenge is to utilize the provided information in order to retrieve the private inputs. + +## Data provided to participants + +Participants get the following values: + +1. `y: "3610e39ce7acc430c1fa91efcec93722d77bc4e910ccb195fa4294b64ecb0d35"`, +1. `b: "1b0871ce73e72c599426228e37e7469be9f4fa0b7c9dae950bb77539ca9ebb0f"`. + +They also get access to the following files: + +1. `src/sith_generate_proof.rs` (this file has flags and toxic waste replaced by `???`) +1. `src/circuit.rs` +1. `srs` +1. `proof` + +The files `srs` and `proof` can be deserialized using Lambdaworks methods as follows. + +```rust +use std::{fs, io::{BufReader, Read}}; +use lambdaworks_plonk::prover::Proof; +use lambdaworks_crypto::commitments::kzg::StructuredReferenceString; +use lambdaworks_math::traits::{Deserializable, Serializable}; +use crate::sith_generate_proof::{SithProof, SithSRS}; + +fn read_challenge_data_from_files() -> (SithSRS, SithProof) { + // Read proof from file + let f = fs::File::open("./proof").unwrap(); + let mut reader = BufReader::new(f); + let mut buffer = Vec::new(); + reader.read_to_end(&mut buffer).unwrap(); + let proof = Proof::deserialize(&buffer).unwrap(); + + // Read SRS from file + let f = fs::File::open("./srs").unwrap(); + let mut reader = BufReader::new(f); + let mut buffer = Vec::new(); + reader.read_to_end(&mut buffer).unwrap(); + let srs = StructuredReferenceString::deserialize(&buffer).unwrap(); + (srs, proof) +} +``` + +## Solution + +The solution for the coordinates is: + +1. `x: "2194826651b32ca1055614fc6e2f2de86eab941d2c55bd467268e9"`, +1. `h: "432904cca36659420aac29f8dc5e5bd0dd57283a58ab7a8ce4d1ca"`. + +The flag is the concatenation of the two: `FLAG: 2194826651b32ca1055614fc6e2f2de86eab941d2c55bd467268e9432904cca36659420aac29f8dc5e5bd0dd57283a58ab7a8ce4d1ca` + +## Solution description + +We'll use the notation of the `lambdaworks_plonk_prover` docs. + +By checking the code of the challenge the participants can find the following in `circuit.rs` + +```rust +/// Witness generator for the circuit `ASSERT y == x * h + b` +pub fn circuit_witness( + b: &FrElement, + y: &FrElement, + h: &FrElement, + x: &FrElement, +) -> Witness { + let z = x * h; + let w = &z + b; + let empty = b.clone(); + Witness { + a: vec![ + b.clone(), + y.clone(), + x.clone(), + b.clone(), + w.clone(), + empty.clone(), + empty.clone(), + empty.clone(), + ], + ... +``` + +This code reveals that the way prover constructs the $V$ matrix is + +| A | B | C | +| --- | --- | --- | +| b | - | - | +| y | - | - | +| x | h | z | +| b | z | w | +| w | y | - | +| - | - | - | +| - | - | - | +| - | - | - | + +Where `-` are empty values. The PLONK implementation of `lambdaworks-plonk` requires the empty values to be filled in with the first public input. So in this case the values `-` will be replaced by $b$. This can be seen directly from the code of the challenge + +Therefore, the polynomial $a'$, being the interpolation of the column `A` is + +$$a' = b L_1 + y L_2 + x L_3 + b L_4 + w L_5 + b L_6 + b L_7 + b L_8,$$ + +where $L_i$ is the $i$-th polynomial of the Lagrange basis. Also, the value $w$ is equal to $y$. That can be seen from the code and the fact that the last row of the $V$ matrix corresponds to the assertion of the actual output of the circuit being equal to the claimed output $y$. + +During the proof, the verifier sends a challenge $\zeta$ and the prover opens, among other things, the polynomial $a$ at $\zeta$. Since the implementation of the challenge does not include blindings, $a(\zeta) = a'(\zeta)$ and we get + +$$a(\zeta) = b L_1(\zeta) + y L_2(\zeta) + x L_3(\zeta) + b L_4(\zeta) + y L_5(\zeta) + b L_6(\zeta) + b L_7(\zeta) + b L_8(\zeta).$$ + +All the terms in this expression are known to the participants except for $x$, which can be cleared from the equation. To do so the participants need to know how to recover the challenges to get $\zeta$ and how to compute the Lagrange polynomials evaluated at it. The second private input $h$ can be computed as $h = (y - b) / x$. + +## Test + +A test with the above solution is given in `solution.rs`. To make it pass, lines 25 and 26 of `sith_generate_proof.rs` need to be replaced by the following + +```rust +pub const FLAG1: &str = "2194826651b32ca1055614fc6e2f2de86eab941d2c55bd467268e9"; +pub const FLAG2: &str = "432904cca36659420aac29f8dc5e5bd0dd57283a58ab7a8ce4d1ca"; +``` diff --git a/exercises/blind_trust/proof b/exercises/blind_trust/proof new file mode 100644 index 0000000000000000000000000000000000000000..ad5dc4f7e484005491137cb8d584dd2b3746aa84 GIT binary patch literal 1620 zcmV-a2CMl1001BnsZ%Qap+xj%V6;Wa<$xxeXzx@pwPLfXdeqMfe&e42001B(_w5#^ zF;1yx%0#B}kuZUxu%Ji%Fj*DtDYr&|E$;vT001B*4Ylmob&iD*xyPsP&jjv=m<{2- zKz$5T;0tI=q8B&-001Bd+qOuCo-w@(r@Cufg`l$A4!GUw(*r&L z001CYf#MdBZpD*;Dy=x|FN;JHb`Rt005Bs#J+g%9Qya~&AA8Z zx%s#%{?Nd_V-@1-;>Yj&1`%*uE+NqBnHzcjQ=p+{3T_nPw*nUG1AR7%H3wZ=nN-en z9KmvHNr+=oTV$56LITMjBK~BTe{WNF`_MxnE)E2jBESW26vlv>(d<+wRvw;JY=CG% zESP-V{mQ--0W9$Z_4j?<CRxo`hJK!P2+dpBm zJ*fGk=4HoSmV0$Y4*dX7XMfpps>lZa36k-ss!Y`dBX4kq^Y3CmbT(X25Sc*B=!`Fj zu;-EeoayD-WM22Tn7u&=({lm<005BP%SQE9RvUY)p_rYush`;SS?>ltWweY*fhAu_ zzsKE$>jR;?>vF{db5kA2rqc&qxT~|OrO;J^{cLlXeuVX)P;6^@&B!%W%=sSGAYPs& z=+QsDPXEQ_73*(|WmpG!joA}A^p_v*Khw-896f3_4qok&iw(~3f0{P!tu8W<(55<^ z0RQ_k;e8C7_$?0r0058~n9HL;y@Rp@l4jF6`4@f@08#Eq)cV_XKT@Xzr;Y}rV~%|$ z{X%7;TK%6Bt>Fzdj2f8BGupbfF$cNBPyA;X6%y!cy~**i(ujxE9+PQMO}`M6m8d18 z=jRo1_4hWC-*#GTg_|^R0tMT82&up&+K_Cmmfx+Lq%HAD)Gy!5Ve=ldW@g~L$4m*^ z9R!~01{}E@1+tq9MuMY*Phra_U5Psn8r$L6WvKXyG;GSEuwm|{YNdx>%kLEc0059N z%n$JCY3?pF8kFPnp{GBB=Y{71>o2%tznlpGAjSD6w$yWyOW0WBxg7{J9!3VO=pFo` zPmhbaVV9q@P6jy0#>B{ufkR&mBq`?6(ee%@;{IpHQan7hn z6GJ-SBu}UwYE*}21BYr8FggfINex~tTMD}lpE*R;c?%1%kj#n6;uB(#X60{pK);xv zw%KfZ8qG)TM`?T$BbQ93+1W2G9CHD$^BwSNv?l-n0059vy%-Ba97O7Qeix8p_W(Kb z{esqx3u>spKoAIOck!ll&9-M;elys}dF&|RQ6v+ikKR)QOG;A&R{e2iHy6+KHn1DX zSo!`_P^9rU2C!Y|Zpqd}1PCYF7W|5XgToa|m@Qxozg6F{JQ+)-Q*<3mp4X~08%I}; z599-WAh0T*uLP_v`4Yd?T*6Bvmqro*0058})3vO~KoG6@>#x^T?sEXjV0W+RuhIqy z!MsHmc4H^?hz3eMsgiT37=?Vti?%FrG<&9RBK_|iaInU` literal 0 HcmV?d00001 diff --git a/exercises/blind_trust/src/circuit.rs b/exercises/blind_trust/src/circuit.rs new file mode 100644 index 000000000..562059593 --- /dev/null +++ b/exercises/blind_trust/src/circuit.rs @@ -0,0 +1,187 @@ +use lambdaworks_math::{ + elliptic_curve::short_weierstrass::curves::bls12_381::default_types::{FrElement, FrField}, + field::element::FieldElement, + polynomial::Polynomial, +}; +use lambdaworks_plonk::setup::{CommonPreprocessedInput, Witness}; + +use crate::sith_generate_proof::{ORDER_8_ROOT_UNITY, ORDER_R_MINUS_1_ROOT_UNITY}; + +/// Generates a domain to interpolate: 1, omega, omega², ..., omega^size +pub fn generate_domain(omega: &FrElement, size: usize) -> Vec { + (1..size).fold(vec![FieldElement::one()], |mut acc, _| { + acc.push(acc.last().unwrap() * omega); + acc + }) +} + +/// The identity permutation, auxiliary function to generate the copy constraints. +fn identity_permutation(w: &FrElement, n: usize) -> Vec { + let u = ORDER_R_MINUS_1_ROOT_UNITY; + let mut result: Vec = vec![]; + for index_column in 0..=2 { + for index_row in 0..n { + result.push(w.pow(index_row) * u.pow(index_column as u64)); + } + } + result +} + +/// Generates the permutation coefficients for the copy constraints. +/// polynomials S1, S2, S3. +pub fn generate_permutation_coefficients( + omega: &FrElement, + n: usize, + permutation: &[usize], +) -> Vec { + let identity = identity_permutation(omega, n); + let permuted: Vec = (0..n * 3) + .map(|i| identity[permutation[i]].clone()) + .collect(); + permuted +} + +/// Witness generator for the circuit `ASSERT y == x * h + b` +pub fn circuit_witness( + b: &FrElement, + y: &FrElement, + h: &FrElement, + x: &FrElement, +) -> Witness { + let z = x * h; + let w = &z + b; + let empty = b.clone(); + Witness { + a: vec![ + b.clone(), + y.clone(), + x.clone(), + b.clone(), + w.clone(), + empty.clone(), + empty.clone(), + empty.clone(), + ], + b: vec![ + empty.clone(), + empty.clone(), + h.clone(), + z.clone(), + y.clone(), + empty.clone(), + empty.clone(), + empty.clone(), + ], + c: vec![ + empty.clone(), + empty.clone(), + z.clone(), + w.clone(), + empty.clone(), + empty.clone(), + empty.clone(), + empty.clone(), + ], + } +} + +/// Common preprocessed input for the circuit `ASSERT y == x * h + b` +pub fn circuit_common_preprocessed_input() -> CommonPreprocessedInput { + let n: usize = 8; + let omega = ORDER_8_ROOT_UNITY; + let domain = generate_domain(&omega, n); + let permutation = &[ + 23, 12, 2, 0, 19, 3, 5, 6, 7, 8, 10, 18, 1, 9, 13, 14, 15, 16, 11, 4, 17, 20, 21, 22, + ]; + let permuted = generate_permutation_coefficients(&omega, n, permutation); + + let s1_lagrange: Vec = permuted[..8].to_vec(); + let s2_lagrange: Vec = permuted[8..16].to_vec(); + let s3_lagrange: Vec = permuted[16..].to_vec(); + + CommonPreprocessedInput { + n, + omega, + k1: ORDER_R_MINUS_1_ROOT_UNITY, + domain: domain.clone(), + + ql: Polynomial::interpolate( + &domain, + &[ + -FieldElement::one(), + -FieldElement::one(), + FieldElement::zero(), + FieldElement::one(), + FieldElement::one(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + ], + ) + .unwrap(), + qr: Polynomial::interpolate( + &domain, + &[ + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::one(), + -FieldElement::one(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + ], + ) + .unwrap(), + qo: Polynomial::interpolate( + &domain, + &[ + FieldElement::zero(), + FieldElement::zero(), + -FieldElement::one(), + -FieldElement::one(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + ], + ) + .unwrap(), + qm: Polynomial::interpolate( + &domain, + &[ + FieldElement::zero(), + FieldElement::zero(), + FieldElement::one(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + ], + ) + .unwrap(), + qc: Polynomial::interpolate( + &domain, + &[ + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + ], + ) + .unwrap(), + + s1: Polynomial::interpolate(&domain, &s1_lagrange).unwrap(), + s2: Polynomial::interpolate(&domain, &s2_lagrange).unwrap(), + s3: Polynomial::interpolate(&domain, &s3_lagrange).unwrap(), + + s1_lagrange, + s2_lagrange, + s3_lagrange, + } +} diff --git a/exercises/blind_trust/src/lib.rs b/exercises/blind_trust/src/lib.rs new file mode 100644 index 000000000..685512a0a --- /dev/null +++ b/exercises/blind_trust/src/lib.rs @@ -0,0 +1,3 @@ +pub mod circuit; +pub mod sith_generate_proof; +pub mod solution; diff --git a/exercises/blind_trust/src/sith_generate_proof.rs b/exercises/blind_trust/src/sith_generate_proof.rs new file mode 100644 index 000000000..838f25d2a --- /dev/null +++ b/exercises/blind_trust/src/sith_generate_proof.rs @@ -0,0 +1,106 @@ +use lambdaworks_crypto::commitments::{ + kzg::{KateZaveruchaGoldberg, StructuredReferenceString}, + traits::IsCommitmentScheme, +}; +use lambdaworks_math::{ + cyclic_group::IsGroup, + elliptic_curve::{ + short_weierstrass::curves::bls12_381::{ + curve::BLS12381Curve, + default_types::{FrElement, FrField}, + pairing::BLS12381AtePairing, + twist::BLS12381TwistCurve, + }, + traits::{IsEllipticCurve, IsPairing}, + }, + traits::IsRandomFieldElementGenerator, + unsigned_integer::element::U256, +}; +use lambdaworks_plonk::{ + prover::{Proof, Prover}, + setup::{setup, VerificationKey}, +}; + +use crate::circuit::{circuit_common_preprocessed_input, circuit_witness}; +use rand::Rng; + +pub const FLAG1: &str = "??????????????????????????????????????????????????????"; +pub const FLAG2: &str = "??????????????????????????????????????????????????????"; + +pub const X_COORDINATE: FrElement = FrElement::from_hex_unchecked(FLAG1); +pub const H_COORDINATE: FrElement = FrElement::from_hex_unchecked(FLAG2); + +pub type SithSRS = StructuredReferenceString< + ::G1Point, + ::G2Point, +>; + +pub const ORDER_8_ROOT_UNITY: FrElement = FrElement::from_hex_unchecked( + "345766f603fa66e78c0625cd70d77ce2b38b21c28713b7007228fd3397743f7a", +); // order 8 + +pub const ORDER_R_MINUS_1_ROOT_UNITY: FrElement = FrElement::from_hex_unchecked("7"); +pub type SithCS = KateZaveruchaGoldberg; +pub type SithVK = VerificationKey<>::Commitment>; +pub type SithProof = Proof; +pub type Pairing = BLS12381AtePairing; +pub type KZG = KateZaveruchaGoldberg; +type G1Point = ::PointRepresentation; +type G2Point = ::PointRepresentation; + +/// Generates a test SRS for the BLS12381 curve +/// n is the number of constraints in the system. +pub fn generate_srs(n: usize) -> StructuredReferenceString { + let mut rng = rand::thread_rng(); + let s = FrElement::new(U256 { + limbs: [ + rng.gen::(), + rng.gen::(), + rng.gen::(), + rng.gen::(), + ], + }); + let g1 = ::generator(); + let g2 = ::generator(); + + let powers_main_group: Vec = (0..n + 3) + .map(|exp| g1.operate_with_self(s.pow(exp as u64).representative())) + .collect(); + let powers_secondary_group = [g2.clone(), g2.operate_with_self(s.representative())]; + + StructuredReferenceString::new(&powers_main_group, &powers_secondary_group) +} + +pub struct SithRandomFieldGenerator; +impl IsRandomFieldElementGenerator for SithRandomFieldGenerator { + fn generate(&self) -> FrElement { + FrElement::zero() + } +} + +pub fn generate_proof(b: &FrElement) -> (FrElement, Proof, SithSRS) { + let common_preprocessed_input = circuit_common_preprocessed_input(); + let srs = generate_srs(common_preprocessed_input.n); + let kzg = KZG::new(srs.clone()); + let verifying_key = setup(&common_preprocessed_input.clone(), &kzg); + + let x = X_COORDINATE; + let h = H_COORDINATE; + + // Output + let y = &x * &h + b; + + // This is the circuit for y == x * h + b + let witness = circuit_witness(&b, &y, &h, &x); + let public_input = vec![b.clone(), y.clone()]; + + let random_generator = SithRandomFieldGenerator {}; + let prover = Prover::new(kzg.clone(), random_generator); + let proof = prover.prove( + &witness, + &public_input, + &common_preprocessed_input, + &verifying_key, + ); + (y, proof, srs) +} diff --git a/exercises/blind_trust/src/solution.rs b/exercises/blind_trust/src/solution.rs new file mode 100644 index 000000000..7feb52414 --- /dev/null +++ b/exercises/blind_trust/src/solution.rs @@ -0,0 +1,165 @@ +use lambdaworks_crypto::{ + commitments::traits::IsCommitmentScheme, fiat_shamir::transcript::Transcript, +}; +use lambdaworks_math::{ + field::{element::FieldElement, traits::IsField}, + traits::{ByteConversion, Serializable}, +}; +use lambdaworks_plonk::{ + prover::Proof, + setup::{new_strong_fiat_shamir_transcript, CommonPreprocessedInput, VerificationKey}, +}; + +fn compute_private_input( + proof: &Proof, + vk: &VerificationKey, + public_input: &[FieldElement], + common_preprocessed_input: &CommonPreprocessedInput, +) -> (FieldElement, FieldElement) +where + F: IsField, + CS: IsCommitmentScheme, + CS::Commitment: Serializable, + FieldElement: ByteConversion, +{ + // Replay interactions to recover challenges + let mut transcript = new_strong_fiat_shamir_transcript::(vk, public_input); + transcript.append(&proof.a_1.serialize()); + transcript.append(&proof.b_1.serialize()); + transcript.append(&proof.c_1.serialize()); + let _beta = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + let _gamma = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + + transcript.append(&proof.z_1.serialize()); + let _alpha = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + + transcript.append(&proof.t_lo_1.serialize()); + transcript.append(&proof.t_mid_1.serialize()); + transcript.append(&proof.t_hi_1.serialize()); + let zeta = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + + // Compute `x` and `h` + let [b, y] = [&public_input[0], &public_input[1]]; + let n = common_preprocessed_input.n as u64; + let omega = &common_preprocessed_input.omega; + let domain = &common_preprocessed_input.domain; + let l1_zeta = + (zeta.pow(n) - FieldElement::one()) / (&zeta - FieldElement::one()) / FieldElement::from(n); + + let mut li_zeta = l1_zeta; + let mut lagrange_basis_zeta = Vec::new(); + lagrange_basis_zeta.push(li_zeta.clone()); + for i in 1..domain.len() { + li_zeta = omega * &li_zeta * ((&zeta - &domain[i - 1]) / (&zeta - &domain[i])); + lagrange_basis_zeta.push(li_zeta.clone()); + } + + let x = (&proof.a_zeta + - b * &lagrange_basis_zeta[3] + - y * &lagrange_basis_zeta[4] + - b * &lagrange_basis_zeta[0] + - y * &lagrange_basis_zeta[1] + - b * &lagrange_basis_zeta[5] + - b * &lagrange_basis_zeta[6] + - b * &lagrange_basis_zeta[7]) + / &lagrange_basis_zeta[2]; + let h = (y - b) / &x; + (x, h) +} + +#[cfg(test)] +mod tests { + use std::{ + fs, + io::{BufReader, Read}, + }; + + use lambdaworks_crypto::commitments::kzg::StructuredReferenceString; + use lambdaworks_math::{ + elliptic_curve::short_weierstrass::curves::bls12_381::default_types::FrElement, + traits::{Deserializable, Serializable}, + }; + use lambdaworks_plonk::{prover::Proof, setup::setup, verifier::Verifier}; + + use crate::{ + circuit::circuit_common_preprocessed_input, + sith_generate_proof::{ + generate_proof, SithProof, SithSRS, H_COORDINATE, KZG, X_COORDINATE, + }, + solution::compute_private_input, + }; + + fn read_challenge_data_from_files() -> (SithSRS, SithProof) { + // Read proof from file + let f = fs::File::open("./proof").unwrap(); + let mut reader = BufReader::new(f); + let mut buffer = Vec::new(); + reader.read_to_end(&mut buffer).unwrap(); + let proof = Proof::deserialize(&buffer).unwrap(); + + // Read SRS from file + let f = fs::File::open("./srs").unwrap(); + let mut reader = BufReader::new(f); + let mut buffer = Vec::new(); + reader.read_to_end(&mut buffer).unwrap(); + let srs = StructuredReferenceString::deserialize(&buffer).unwrap(); + (srs, proof) + } + + #[test] + fn test_challenge_data() { + let b = + FrElement::from_hex("1b0871ce73e72c599426228e37e7469be9f4fa0b7c9dae950bb77539ca9ebb0f") + .unwrap(); + let y = + FrElement::from_hex("3610e39ce7acc430c1fa91efcec93722d77bc4e910ccb195fa4294b64ecb0d35") + .unwrap(); + let public_input = vec![b, y]; + + let (srs, proof) = read_challenge_data_from_files(); + let common_preprocessed_input = circuit_common_preprocessed_input(); + let kzg = KZG::new(srs.clone()); + let verifier = Verifier::new(kzg.clone()); + let vk = setup(&common_preprocessed_input, &kzg); + + assert!(verifier.verify(&proof, &public_input, &common_preprocessed_input, &vk)) + } + + fn export_challenge_data() { + use std::fs; + use std::io::Write; + + let b = + FrElement::from_hex("1b0871ce73e72c599426228e37e7469be9f4fa0b7c9dae950bb77539ca9ebb0f") + .unwrap(); + let (y, proof, srs) = generate_proof(&b); + + let mut srs_file = fs::File::create("./srs").unwrap(); + srs_file.write_all(&srs.serialize()).unwrap(); + let mut srs_file = fs::File::create("./proof").unwrap(); + srs_file.write_all(&proof.serialize()).unwrap(); + println!("{}", y); + } + + #[test] + fn test_solution() { + let b = + FrElement::from_hex("1b0871ce73e72c599426228e37e7469be9f4fa0b7c9dae950bb77539ca9ebb0f") + .unwrap(); + let y = + FrElement::from_hex("3610e39ce7acc430c1fa91efcec93722d77bc4e910ccb195fa4294b64ecb0d35") + .unwrap(); + let public_input = vec![b, y]; + + let (srs, proof) = read_challenge_data_from_files(); + let common_preprocessed_input = circuit_common_preprocessed_input(); + let kzg = KZG::new(srs.clone()); + + let vk = setup(&common_preprocessed_input, &kzg); + // Extract private input from proof, public input and public keys + let (x, h) = compute_private_input(&proof, &vk, &public_input, &common_preprocessed_input); + + assert_eq!(&X_COORDINATE, &x); + assert_eq!(&H_COORDINATE, &h); + } +} diff --git a/exercises/blind_trust/srs b/exercises/blind_trust/srs new file mode 100644 index 0000000000000000000000000000000000000000..7662fe652adaea7cc5b118a6171ccdb8793a5460 GIT binary patch literal 2172 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

Z literal 0 HcmV?d00001 diff --git a/exercises/broken_heart/Cargo.toml b/exercises/broken_heart/Cargo.toml new file mode 100644 index 000000000..089261596 --- /dev/null +++ b/exercises/broken_heart/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "irreducibull" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +lambdaworks-math = { git = "https://github.com/lambdaclass/lambdaworks", rev = "d8f14cb"} +lambdaworks-crypto = { git = "https://github.com/lambdaclass/lambdaworks", rev = "d8f14cb"} +# lambdaworks-plonk = { git = "https://github.com/lambdaclass/lambdaworks_plonk_prover", rev="07e36bf"} +lambdaworks-plonk = { path = "path/to/local/lambdaworks_plonk_prover"} diff --git a/exercises/broken_heart/README.md b/exercises/broken_heart/README.md new file mode 100644 index 000000000..1ba59911c --- /dev/null +++ b/exercises/broken_heart/README.md @@ -0,0 +1,109 @@ +# Loki’s broken heart + +After successfully breaking into Loki’s vault and getting access to some of his finest treasures and weapons, you spot a small trapdoor under a carpet. The trapdoor is locked and contains a device with a PLONK prover. It says: “Prove that the point $(1,y)$ belongs to the elliptic curve $y^2 = x^3 + 4$”. You see that, in order to prove this, you need that $y^2 - x^3 - 4$ is equal to zero, which corresponds to the circuit for the prover provided by Loki. Can you open the trapdoor? + +# Description + +This challenge is about exploiting a vulnerability in weak Fiat-Shamir implementations. + +The idea is to have a small server with an endpoint accepting proofs of executions of circuits. The plonk backend will have a bug in the initialization of the transcript and won't add the public inputs to the transcript. So as long as one public input is in control of the attacker, he can forge fake proofs. + +At the moment the circuit is: + +``` +PUBLIC INPUT: x +PUBLIC INPUT: y + +ASSERT 0 == y^2 - x^3 - 4 +``` +And it instantiated over the `BLS12 381` scalar field. +If the user achieves to send a proof for `x==1`, then they obtain the flag. Since $5$ is a quadratic non residue in the base field of the circuit, this can only be achieved by forging a fake proof. + +The vulnerability stems from a bug in the implementation of strong Fiat-Shamir. A correct implementation should add, among other things, all the public inputs to the transcript at initialization. If a public input is not added to the transcript and is in control of the attacker, they can forge a fake proof. Here, fixing `x=1` leaves `y` under control of the user. + +The attack is described in Section V of [Weak Fiat-Shamir Attacks on Modern Proof Systems](https://eprint.iacr.org/2023/691.pdf). + +Here is a description of the attack. + +![image](https://github.com/lambdaclass/challenges-ctf/assets/41742639/d2040ccd-17ad-4f0e-b910-a17ceda96ed4) + +Instead of taking random polynomials (steps (1) to (7)), the current solution takes a valid proof for the pair `x=0`, `y=2` and uses it to forge a `y'` for `x=1` that's compatible with the original proof. + +At the moment, the server endpoint is simulated with the following function. + +```rust +pub fn server_endpoint_verify( + srs: ChallengeSRS, + common_preprocessed_input: CommonPreprocessedInput, + vk: &ChallengeVK, + x: &FrElement, + y: &FrElement, + proof: &ChallengeProof, +) -> String { + let public_input = [x.clone(), y.clone()]; + let kzg = KZG::new(srs); + let verifier = Verifier::new(kzg); + let result = verifier.verify(proof, &public_input, &common_preprocessed_input, vk); + if !result { + "Invalid Proof".to_string() + } else if x != &FieldElement::one() { + "Valid Proof. Congrats!".to_string() + } else { + FLAG.to_string() + } +} +``` + +The attack can be found in `src/solution.rs` along with a test that showcases it. + +## Get it to work + +Currently `lambdaworks_plonk_prover` does not expose the weak Fiat-Shamir vulnerability. +So to make the challenge work we need to modify it. + +1. Clone `lambdaworks_plonk_prover` repo: `git clone git@github.com:lambdaclass/lambdaworks_plonk_prover.git` +1. `git checkout 07e36bf` +1. Make the following changes to it: + +```diff +diff --git a/Cargo.toml b/Cargo.toml +index 7f0e324..c36a00d 100644 +--- a/Cargo.toml ++++ b/Cargo.toml +@@ -7,8 +7,8 @@ edition = "2021" + + [dependencies] + serde = { version = "1.0", features = ["derive"]} +-lambdaworks-math = { git = "https://github.com/lambdaclass/lambdaworks", rev = "943963c" } +-lambdaworks-crypto = { git = "https://github.com/lambdaclass/lambdaworks", rev = "943963c" } ++lambdaworks-math = { git = "https://github.com/lambdaclass/lambdaworks", rev = "d8f14cb" } ++lambdaworks-crypto = { git = "https://github.com/lambdaclass/lambdaworks", rev = "d8f14cb" } + + thiserror = "1.0.38" + serde_json = "1.0" +diff --git a/src/setup.rs b/src/setup.rs +index 493278a..437bcc9 100644 +--- a/src/setup.rs ++++ b/src/setup.rs +@@ -69,7 +69,7 @@ pub fn setup>( + + pub fn new_strong_fiat_shamir_transcript( + vk: &VerificationKey, +- public_input: &[FieldElement], ++ _public_input: &[FieldElement], + ) -> DefaultTranscript + where + F: IsField, +@@ -88,9 +88,6 @@ where + transcript.append(&vk.qo_1.serialize()); + transcript.append(&vk.qc_1.serialize()); + +- for value in public_input.iter() { +- transcript.append(&value.to_bytes_be()); +- } + transcript + } +``` + +1. Clone this repo and modify its `Cargo.toml` to point the `lambdaworks-plonk` dependency to your local copy of `lambdaworks_plonk_prover`. +1. Run `cargo test` diff --git a/exercises/broken_heart/src/circuit.rs b/exercises/broken_heart/src/circuit.rs new file mode 100644 index 000000000..80fe94406 --- /dev/null +++ b/exercises/broken_heart/src/circuit.rs @@ -0,0 +1,178 @@ +use lambdaworks_math::{ + elliptic_curve::short_weierstrass::curves::bls12_381::default_types::{FrElement, FrField}, + field::element::FieldElement, + polynomial::Polynomial, +}; +use lambdaworks_plonk::setup::{CommonPreprocessedInput, Witness}; + +use crate::server::{ORDER_8_ROOT_UNITY, ORDER_R_MINUS_1_ROOT_UNITY}; + +/// Generates a domain to interpolate: 1, omega, omega², ..., omega^size +pub fn generate_domain(omega: &FrElement, size: usize) -> Vec { + (1..size).fold(vec![FieldElement::one()], |mut acc, _| { + acc.push(acc.last().unwrap() * omega); + acc + }) +} + +/// The identity permutation, auxiliary function to generate the copy constraints. +fn identity_permutation(w: &FrElement, n: usize) -> Vec { + let u = ORDER_R_MINUS_1_ROOT_UNITY; + let mut result: Vec = vec![]; + for index_column in 0..=2 { + for index_row in 0..n { + result.push(w.pow(index_row) * u.pow(index_column as u64)); + } + } + result +} + +/// Generates the permutation coefficients for the copy constraints. +/// polynomials S1, S2, S3. +pub fn generate_permutation_coefficients( + omega: &FrElement, + n: usize, + permutation: &[usize], +) -> Vec { + let identity = identity_permutation(omega, n); + let permuted: Vec = (0..n * 3) + .map(|i| identity[permutation[i]].clone()) + .collect(); + permuted +} + +/// Witness generator for the circuit `ASSERT 0 == y ** 2 - x ** 3 - 4` +pub fn circuit_witness(x: &FrElement, y: &FrElement) -> Witness { + let b = FieldElement::from(4); + let x2 = x * x; + let x3 = &x2 * x; + let u = &x3 + &b; + let y2 = y * y; + let w = &y2 - &u; + let empty = x.clone(); + Witness { + a: vec![ + x.clone(), + y.clone(), + x.clone(), + x2.clone(), + x3.clone(), + y.clone(), + u.clone(), + w.clone(), + ], + b: vec![ + empty.clone(), + empty.clone(), + x.clone(), + x.clone(), + empty.clone(), + y.clone(), + y2.clone(), + empty.clone(), + ], + c: vec![empty.clone(), empty.clone(), x2, x3, u, y2, w, empty], + } +} + +/// Common preprocessed input for the circuit `ASSERT 0 == y ** 2 - x ** 3 - 4` +pub fn circuit_common_preprocessed_input() -> CommonPreprocessedInput { + let n: usize = 8; + let omega = ORDER_8_ROOT_UNITY; + let domain = generate_domain(&omega, n); + + let permutation = &[ + 23, 13, 0, 18, 19, 1, 20, 22, 2, 8, 9, 10, 11, 5, 21, 12, 15, 16, 3, 4, 6, 14, 7, 17, + ]; + let permuted = generate_permutation_coefficients(&omega, n, permutation); + + let s1_lagrange: Vec = permuted[..8].to_vec(); + let s2_lagrange: Vec = permuted[8..16].to_vec(); + let s3_lagrange: Vec = permuted[16..].to_vec(); + + CommonPreprocessedInput { + n, + omega, + k1: ORDER_R_MINUS_1_ROOT_UNITY, + domain: domain.clone(), + + ql: Polynomial::interpolate( + &domain, + &[ + -FieldElement::one(), + -FieldElement::one(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::one(), + FieldElement::zero(), + -FieldElement::one(), + FieldElement::one(), + ], + ) + .unwrap(), + qr: Polynomial::interpolate( + &domain, + &[ + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::one(), + FieldElement::zero(), + ], + ) + .unwrap(), + qo: Polynomial::interpolate( + &domain, + &[ + FieldElement::zero(), + FieldElement::zero(), + -FieldElement::one(), + -FieldElement::one(), + -FieldElement::one(), + -FieldElement::one(), + -FieldElement::one(), + FieldElement::zero(), + ], + ) + .unwrap(), + qm: Polynomial::interpolate( + &domain, + &[ + FieldElement::zero(), + FieldElement::zero(), + FieldElement::one(), + FieldElement::one(), + FieldElement::zero(), + FieldElement::one(), + FieldElement::zero(), + FieldElement::zero(), + ], + ) + .unwrap(), + qc: Polynomial::interpolate( + &domain, + &[ + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::from(4), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + ], + ) + .unwrap(), + + s1: Polynomial::interpolate(&domain, &s1_lagrange).unwrap(), + s2: Polynomial::interpolate(&domain, &s2_lagrange).unwrap(), + s3: Polynomial::interpolate(&domain, &s3_lagrange).unwrap(), + + s1_lagrange, + s2_lagrange, + s3_lagrange, + } +} diff --git a/exercises/broken_heart/src/lib.rs b/exercises/broken_heart/src/lib.rs new file mode 100644 index 000000000..594e2d936 --- /dev/null +++ b/exercises/broken_heart/src/lib.rs @@ -0,0 +1,3 @@ +pub mod circuit; +pub mod server; +pub mod solution; diff --git a/exercises/broken_heart/src/server.rs b/exercises/broken_heart/src/server.rs new file mode 100644 index 000000000..ca923ac62 --- /dev/null +++ b/exercises/broken_heart/src/server.rs @@ -0,0 +1,96 @@ +// -------- Irreduci-bull challenge --------- + +use lambdaworks_crypto::commitments::{ + kzg::{KateZaveruchaGoldberg, StructuredReferenceString}, + traits::IsCommitmentScheme, +}; +use lambdaworks_math::{ + cyclic_group::IsGroup, + elliptic_curve::{ + short_weierstrass::curves::bls12_381::{ + curve::BLS12381Curve, + default_types::{FrElement, FrField}, + pairing::BLS12381AtePairing, + twist::BLS12381TwistCurve, + }, + traits::{IsEllipticCurve, IsPairing}, + }, + field::element::FieldElement, + traits::IsRandomFieldElementGenerator, +}; +use lambdaworks_plonk::{ + prover::Proof, + setup::{CommonPreprocessedInput, VerificationKey}, + verifier::Verifier, +}; + +pub const FLAG: &str = "ZK{dummy_flag}"; + +type ChallengeSRS = StructuredReferenceString< + ::G1Point, + ::G2Point, +>; + +pub const ORDER_8_ROOT_UNITY: FrElement = FrElement::from_hex_unchecked( + "345766f603fa66e78c0625cd70d77ce2b38b21c28713b7007228fd3397743f7a", +); // order 8 + +pub const ORDER_R_MINUS_1_ROOT_UNITY: FrElement = FrElement::from_hex_unchecked("7"); +pub type ChallengeCS = KateZaveruchaGoldberg; +pub type ChallengeVK = VerificationKey<>::Commitment>; +pub type ChallengeProof = Proof; +pub type Pairing = BLS12381AtePairing; +pub type KZG = KateZaveruchaGoldberg; +pub type CPI = CommonPreprocessedInput; +type G1Point = ::PointRepresentation; +type G2Point = ::PointRepresentation; + +pub fn quadratic_non_residue() -> FrElement { + ORDER_R_MINUS_1_ROOT_UNITY +} + +/// Generates a test SRS for the BLS12381 curve +/// n is the number of constraints in the system. +pub fn generate_srs(n: usize) -> StructuredReferenceString { + let s = FrElement::from(2); + let g1 = ::generator(); + let g2 = ::generator(); + + let powers_main_group: Vec = (0..n + 3) + .map(|exp| g1.operate_with_self(s.pow(exp as u64).representative())) + .collect(); + let powers_secondary_group = [g2.clone(), g2.operate_with_self(s.representative())]; + + StructuredReferenceString::new(&powers_main_group, &powers_secondary_group) +} + +/// A mock of a random number generator, to have deterministic tests. +/// When set to zero, there is no zero knowledge applied, because it is used +/// to get random numbers to blind polynomials. +pub struct TestRandomFieldGenerator; +impl IsRandomFieldElementGenerator for TestRandomFieldGenerator { + fn generate(&self) -> FrElement { + FrElement::zero() + } +} + +pub fn server_endpoint_verify( + srs: ChallengeSRS, + common_preprocessed_input: CommonPreprocessedInput, + vk: &ChallengeVK, + x: &FrElement, + y: &FrElement, + proof: &ChallengeProof, +) -> String { + let public_input = [x.clone(), y.clone()]; + let kzg = KZG::new(srs); + let verifier = Verifier::new(kzg); + let result = verifier.verify(proof, &public_input, &common_preprocessed_input, vk); + if !result { + "Invalid Proof".to_string() + } else if x != &FieldElement::one() { + "Valid Proof. Congrats!".to_string() + } else { + FLAG.to_string() + } +} diff --git a/exercises/broken_heart/src/solution.rs b/exercises/broken_heart/src/solution.rs new file mode 100644 index 000000000..5c9e6d28d --- /dev/null +++ b/exercises/broken_heart/src/solution.rs @@ -0,0 +1,119 @@ +use lambdaworks_crypto::{ + commitments::traits::IsCommitmentScheme, fiat_shamir::transcript::Transcript, +}; +use lambdaworks_math::{ + field::{element::FieldElement, traits::IsField}, + traits::{ByteConversion, Serializable}, +}; +use lambdaworks_plonk::{ + prover::Proof, + setup::{new_strong_fiat_shamir_transcript, CommonPreprocessedInput, VerificationKey}, +}; + +#[allow(unused)] +fn forge_y_for_valid_proof>( + proof: &Proof, + vk: &VerificationKey, + common_preprocessed_input: CommonPreprocessedInput, +) -> FieldElement +where + CS::Commitment: Serializable, + FieldElement: ByteConversion, +{ + // Replay interactions like the verifier + let mut transcript = new_strong_fiat_shamir_transcript::(vk, &[]); + + transcript.append(&proof.a_1.serialize()); + transcript.append(&proof.b_1.serialize()); + transcript.append(&proof.c_1.serialize()); + let beta = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + let gamma = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + + transcript.append(&proof.z_1.serialize()); + let alpha = FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + + transcript.append(&proof.t_lo_1.serialize()); + transcript.append(&proof.t_mid_1.serialize()); + transcript.append(&proof.t_hi_1.serialize()); + let zeta = &FieldElement::from_bytes_be(&transcript.challenge()).unwrap(); + + // Forge public input + let zh_zeta = zeta.pow(common_preprocessed_input.n) - FieldElement::one(); + + let omega = &common_preprocessed_input.omega; + let n = common_preprocessed_input.n as u64; + let one = &FieldElement::one(); + + let l1_zeta = ((zeta.pow(n) - one) / (zeta - one)) / FieldElement::from(n); + + let l2_zeta = omega * &l1_zeta * (zeta - one) / (zeta - omega); + + let mut p_constant_zeta = &alpha + * &proof.z_zeta_omega + * (&proof.c_zeta + &gamma) + * (&proof.a_zeta + &beta * &proof.s1_zeta + &gamma) + * (&proof.b_zeta + &beta * &proof.s2_zeta + &gamma); + p_constant_zeta = p_constant_zeta - &l1_zeta * &alpha * α + + let p_zeta = p_constant_zeta + &proof.p_non_constant_zeta; + -(p_zeta + l1_zeta * one - (&zh_zeta * &proof.t_zeta)) / l2_zeta +} + +#[cfg(test)] +mod tests { + use lambdaworks_math::field::element::FieldElement; + use lambdaworks_plonk::{prover::Prover, setup::setup}; + + use crate::{ + circuit::{circuit_common_preprocessed_input, circuit_witness}, + server::{generate_srs, server_endpoint_verify, TestRandomFieldGenerator, FLAG, KZG}, + solution::forge_y_for_valid_proof, + }; + + #[test] + fn test_challenge() { + // This is the circuit for `ASSERT 0 == y ** 2 - x ** 3 - 4` + let cpi = circuit_common_preprocessed_input(); + let srs = generate_srs(cpi.n); + let kzg = KZG::new(srs.clone()); + let verifying_key = setup(&cpi.clone(), &kzg); + + let x = FieldElement::from(0); + let y = FieldElement::from(2); + + let public_input = vec![x.clone(), y.clone()]; + let witness = circuit_witness(&x, &y); + + let random_generator = TestRandomFieldGenerator {}; + let prover = Prover::new(kzg.clone(), random_generator); + let proof = prover.prove(&witness, &public_input, &cpi, &verifying_key); + + let response_valid = + server_endpoint_verify(srs.clone(), cpi.clone(), &verifying_key, &x, &y, &proof); + assert_eq!("Valid Proof. Congrats!".to_string(), response_valid); + + let response_invalid = server_endpoint_verify( + srs.clone(), + cpi.clone(), + &verifying_key, + &FieldElement::one(), + &y, + &proof, + ); + assert_eq!("Invalid Proof".to_string(), response_invalid); + + // Use the real proof to modify the public input + // and make it pass for `x = 1` + let forged_y = forge_y_for_valid_proof(&proof, &verifying_key, cpi.clone()); + + let response_solution = server_endpoint_verify( + srs.clone(), + cpi.clone(), + &verifying_key, + &FieldElement::one(), + &forged_y, + &proof, + ); + assert_eq!(FLAG.to_string(), response_solution); + } +} diff --git a/exercises/challenge_1/Cargo.toml b/exercises/challenge_1/Cargo.toml new file mode 100644 index 000000000..669c6fec2 --- /dev/null +++ b/exercises/challenge_1/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "block_cypher" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +lambdaworks-math = { git = "https://github.com/lambdaclass/lambdaworks.git" } +rand = "0.8.5" diff --git a/exercises/challenge_1/README.md b/exercises/challenge_1/README.md new file mode 100644 index 000000000..8870b820f --- /dev/null +++ b/exercises/challenge_1/README.md @@ -0,0 +1,6 @@ +# The Lost Relic + +During their quest to find the greatest treasure in the world, the One Piece, Luffy and his friends are wandering inside a subterranean maze. After many hours, they arrive at the door hiding an old relic, which can be instrumental to achieving their goal. The big problem is that it is made of sea stone and Luffy is unable to use his strength to break it. There are some inscriptions on the walls, which Nico Robin is able to translate. +It says: +"If you can find the secret hidden among these texts, the door will open." +There are many input plaintexts and their corresponding ciphertexts, all of them encrypted using a custom MiMC algorithm under the same key. There are also many skeletons around, of all the people who have so far failed this test. Luckily, Usopp brought his computing device and will try to break the secret. What can he do to recover the secret? \ No newline at end of file diff --git a/exercises/challenge_1/src/cypher.rs b/exercises/challenge_1/src/cypher.rs new file mode 100644 index 000000000..4d2e9ffcf --- /dev/null +++ b/exercises/challenge_1/src/cypher.rs @@ -0,0 +1,11 @@ +use crate::field::ChallengeElement; + +const ROUNDS: usize = 2_usize.pow(24); + +pub fn evaluate(x: &ChallengeElement, key: &ChallengeElement) -> ChallengeElement { + (0..ROUNDS).fold(x.clone(), |acc, _| evaluate_round(&acc, key)) +} + +pub fn evaluate_round(x: &ChallengeElement, key: &ChallengeElement) -> ChallengeElement { + (x + key).pow(2_u64) +} diff --git a/exercises/challenge_1/src/data.rs b/exercises/challenge_1/src/data.rs new file mode 100644 index 000000000..0e14341ac --- /dev/null +++ b/exercises/challenge_1/src/data.rs @@ -0,0 +1,148 @@ +use lambdaworks_math::unsigned_integer::element::UnsignedInteger; + +use crate::field::ChallengeElement; + +pub fn pairs() -> [(ChallengeElement, ChallengeElement); 10] { + [ + ( + ChallengeElement::new(UnsignedInteger::from_limbs([ + 183240637262039384, + 10134058328874369520, + 15648036570429036245, + 12373630818091389922, + ])), + ChallengeElement::new(UnsignedInteger::from_limbs([ + 58125280520191997, + 7563295584845025545, + 10505934299123791696, + 7243434167283202274, + ])), + ), + ( + ChallengeElement::new(UnsignedInteger::from_limbs([ + 53793503318333808, + 3207637959751464466, + 14494950274942836597, + 4136788124041118761, + ])), + ChallengeElement::new(UnsignedInteger::from_limbs([ + 311364612610444531, + 15966843845133580520, + 11733134576879619546, + 15728250733723614304, + ])), + ), + ( + ChallengeElement::new(UnsignedInteger::from_limbs([ + 568211605542321808, + 3665906368153986805, + 17170247225323812746, + 15915536692872977361, + ])), + ChallengeElement::new(UnsignedInteger::from_limbs([ + 217852115983473995, + 4166846794769956288, + 6317771942840139764, + 14625875306968385082, + ])), + ), + ( + ChallengeElement::new(UnsignedInteger::from_limbs([ + 207549010898490221, + 4243616016599017025, + 9848596493685366018, + 7918147356679190828, + ])), + ChallengeElement::new(UnsignedInteger::from_limbs([ + 477451920176997361, + 15490207318061191317, + 18290929028207201553, + 12313619366283486653, + ])), + ), + ( + ChallengeElement::new(UnsignedInteger::from_limbs([ + 373838775372616312, + 2673582186589028492, + 7759321698787748238, + 13844063650817543069, + ])), + ChallengeElement::new(UnsignedInteger::from_limbs([ + 501628185314588506, + 360101150955423272, + 9640428090924426801, + 4582961170966616797, + ])), + ), + ( + ChallengeElement::new(UnsignedInteger::from_limbs([ + 87489608385277507, + 7831811476568950391, + 2088690257692214216, + 5066699409404733431, + ])), + ChallengeElement::new(UnsignedInteger::from_limbs([ + 355875529635643805, + 13282387072996050664, + 8345773773609640016, + 16439847079178157230, + ])), + ), + ( + ChallengeElement::new(UnsignedInteger::from_limbs([ + 81907762606414085, + 13685489810001955596, + 1500287613219709574, + 2446883281376595718, + ])), + ChallengeElement::new(UnsignedInteger::from_limbs([ + 285826094074462510, + 2412521888121343816, + 17074562155301273597, + 743918196287760338, + ])), + ), + ( + ChallengeElement::new(UnsignedInteger::from_limbs([ + 539334648243077342, + 1241819602239411681, + 5927184179348544209, + 11640860157447976251, + ])), + ChallengeElement::new(UnsignedInteger::from_limbs([ + 502287359473451983, + 2105484562428168146, + 2070489610481465832, + 9074849931098206603, + ])), + ), + ( + ChallengeElement::new(UnsignedInteger::from_limbs([ + 536884484895507488, + 5347777065114328110, + 18395674441153693721, + 10552627961256589724, + ])), + ChallengeElement::new(UnsignedInteger::from_limbs([ + 55173036628470166, + 605478499650837527, + 3313559020999214570, + 17867174237670907305, + ])), + ), + ( + ChallengeElement::new(UnsignedInteger::from_limbs([ + 420067545220093896, + 2975148993333630768, + 15772310561964876975, + 5736425213799489626, + ])), + ChallengeElement::new(UnsignedInteger::from_limbs([ + 245745081194512045, + 17128497290282674116, + 12860542778099276299, + 11433466637600805681, + ])), + ), + ] +} diff --git a/exercises/challenge_1/src/field.rs b/exercises/challenge_1/src/field.rs new file mode 100644 index 000000000..718328ea2 --- /dev/null +++ b/exercises/challenge_1/src/field.rs @@ -0,0 +1,5 @@ +use lambdaworks_math::field::{ + element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField, +}; + +pub type ChallengeElement = FieldElement; diff --git a/exercises/challenge_1/src/main.rs b/exercises/challenge_1/src/main.rs new file mode 100644 index 000000000..6dd6dda95 --- /dev/null +++ b/exercises/challenge_1/src/main.rs @@ -0,0 +1,18 @@ +use data::pairs; +use solver::solve; + +use crate::cypher::evaluate; + +mod cypher; +mod data; +mod field; +mod solver; + +fn main() { + let key = solve(); + + let (p, c) = pairs()[0].clone(); + assert_eq!(evaluate(&p, &key), c); + + println!("Found Key! {}", &key); +} diff --git a/exercises/challenge_1/src/solver.rs b/exercises/challenge_1/src/solver.rs new file mode 100644 index 000000000..2e3efa67a --- /dev/null +++ b/exercises/challenge_1/src/solver.rs @@ -0,0 +1,6 @@ +use crate::field::ChallengeElement; + +pub fn solve() -> ChallengeElement { + println!("Solving..."); + todo!(); +} diff --git a/exercises/challenge_2/Cargo.toml b/exercises/challenge_2/Cargo.toml new file mode 100644 index 000000000..5d1375d22 --- /dev/null +++ b/exercises/challenge_2/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "template_solution_srs_1" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +lambdaworks-crypto = { git = "https://github.com/lambdaclass/lambdaworks", rev = "366ac95" } +lambdaworks-math= { git = "https://github.com/lambdaclass/lambdaworks", rev = "366ac95" } diff --git a/exercises/challenge_2/README.md b/exercises/challenge_2/README.md new file mode 100644 index 000000000..9b821968d --- /dev/null +++ b/exercises/challenge_2/README.md @@ -0,0 +1,16 @@ +# Breaking into the vault of Loki + +After years of careful investigation, you have reached the gate to Loki's vault in the icy mountains of Norway, where it is said that many great treasures and powerful weapons are hidden. The gate seems unbreakable, but you spot some ancient machinery with inscriptions in old runes. After some help from ChatGPT, you are able to translate the symbols and the whole message into modern English, and it reads: + +If you can prove that the polynomial + +$$ +\begin{aligned} +p(x) &= 69 +78x + 32x^2 + 65x^3 + 82x^4 + 71x^5 + 69x^6 + 78x^7 + 84x^8 + 73x^9 \newline &+78x^{10} + 65x^{11} + 32x^{12} + 78x^{13} + 65x^{14}+ 67x^{15} + 73x^{16} + 32x^{17} \newline +&+ 84x^{18} + 73x^{19} + 69x^{20} + 82x^{21} + 82x^{22} + 65 x^{23} +\end{aligned} +$$ + +is equal to $3$ at $x = 1$ modulo the BLS12-381 $r$ parameter, then the gate will open. + +Below is a long list of bytes representing the SRS that can be used to perform KZG commitments. The machinery, after careful examination, performs the KZG verification using pairings. There is only one open place where you can place a wooden tablet with your answer, comprising 48 bytes. You guess this should be the proof of the KZG scheme, providing the point in compressed form, following the ZCash standard. The other elements contain the commitment to $p(x)$, the desired value $3$, and the point $x=1$. You ask ChatGPT for enlightenment, but it suddenly collapses and only shows the message: fatal error. Is this just an impossible task? Perhaps there is some trick to get by Loki's challenge... \ No newline at end of file diff --git a/exercises/challenge_2/src/main.rs b/exercises/challenge_2/src/main.rs new file mode 100644 index 000000000..4f6d3ba94 --- /dev/null +++ b/exercises/challenge_2/src/main.rs @@ -0,0 +1,90 @@ +use lambdaworks_crypto::commitments::{ + kzg::{KateZaveruchaGoldberg, StructuredReferenceString}, + traits::IsCommitmentScheme, +}; +use lambdaworks_math::{ + elliptic_curve::{ + short_weierstrass::{ + curves::bls12_381::{ + curve::BLS12381Curve, + default_types::{FrConfig, FrElement}, + field_extension::BLS12381PrimeField, + pairing::BLS12381AtePairing, + twist::BLS12381TwistCurve, + }, + point::ShortWeierstrassProjectivePoint, + }, + traits::FromAffine, + }, + field::{ + element::FieldElement, fields::montgomery_backed_prime_fields::MontgomeryBackendPrimeField, + }, + polynomial::Polynomial, + unsigned_integer::element::UnsignedInteger, +}; + +type G1Point = ShortWeierstrassProjectivePoint; +type G2Point = ShortWeierstrassProjectivePoint; + +type KZG = KateZaveruchaGoldberg, BLS12381AtePairing>; +pub type Fq = FieldElement; + +fn challenge_polynomial() -> Polynomial { + Polynomial::::new(&[ + FieldElement::from(69), + FieldElement::from(78), + FieldElement::from(32), + FieldElement::from(65), + FieldElement::from(82), + FieldElement::from(71), + FieldElement::from(69), + FieldElement::from(78), + FieldElement::from(84), + FieldElement::from(73), + FieldElement::from(78), + FieldElement::from(65), + FieldElement::from(32), + FieldElement::from(78), + FieldElement::from(65), + FieldElement::from(67), + FieldElement::from(73), + FieldElement::from(32), + FieldElement::from(84), + FieldElement::from(73), + FieldElement::from(69), + FieldElement::from(82), + FieldElement::from(65), + ]) +} + +fn main() { + let base_dir = env!("CARGO_MANIFEST_DIR"); + let srs_path = base_dir.to_owned() + "/srs.bin"; + let srs = StructuredReferenceString::::from_file(&srs_path).unwrap(); + + let kzg = KZG::new(srs.clone()); + + let p = challenge_polynomial(); + + let p_commitment: G1Point = kzg.commit(&p); + + // If you need to write a bigger number, you can use + // If you are writing the solution in rust you shouldn't need this + let big_number = UnsignedInteger::<6>::from_limbs([0, 0, 0, 0, 0, 2]); + let y = Fq::new(big_number); + + // TO DO: Make your own fake proof + let fake_proof = + ShortWeierstrassProjectivePoint::::from_affine(Fq::from(0), y).unwrap(); + + println!("Fake proof for submission:"); + println!("{:?}", &fake_proof.to_affine().x().to_string()); + println!("{:?}", &fake_proof.to_affine().y().to_string()); + + assert!(kzg.verify( + &FrElement::from(1), + &FrElement::from(3), + &p_commitment, + &fake_proof + )); +} diff --git a/exercises/challenge_2/srs.bin b/exercises/challenge_2/srs.bin new file mode 100644 index 0000000000000000000000000000000000000000..ff79fcbf8f63328ee0a8d231fda83e3814a34832 GIT binary patch literal 148044 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 9000` - then the enemies will surely flee. \ No newline at end of file diff --git a/exercises/challenge_3/src/main.rs b/exercises/challenge_3/src/main.rs new file mode 100644 index 000000000..9b7b67bc0 --- /dev/null +++ b/exercises/challenge_3/src/main.rs @@ -0,0 +1,124 @@ +use std::net::TcpStream; +use std::io::{Read, Write}; + +use lambdaworks_crypto::commitments::{ + kzg::{KateZaveruchaGoldberg, StructuredReferenceString}, + traits::IsCommitmentScheme, +}; +use lambdaworks_math::{ + elliptic_curve::{ + short_weierstrass::{ + curves::bls12_381::{ + curve::BLS12381Curve, + default_types::{FrElement, FrField}, + field_extension::BLS12381PrimeField, + pairing::BLS12381AtePairing, + twist::BLS12381TwistCurve, + }, + point::ShortWeierstrassProjectivePoint, + }, + }, + field::element::FieldElement, + polynomial::Polynomial, +}; +use serde::{Deserialize, Serialize}; + +const X: u64 = 42; +const NUM_POLYS: usize = 4; +#[allow(clippy::upper_case_acronyms)] +type KZG = KateZaveruchaGoldberg; + +pub type Fq = FieldElement; + +#[derive(Debug, Serialize, Deserialize)] +pub struct PowerProof { + pub proof_x_hex: String, + pub proof_y_hex: String, + pub u: String, + pub y: [String; NUM_POLYS], + pub commitments_x: [String; NUM_POLYS], + pub commitments_y: [String; NUM_POLYS], +} + +type G1Point = ShortWeierstrassProjectivePoint; +type G2Point = ShortWeierstrassProjectivePoint; + +fn load_srs() -> StructuredReferenceString:: { + let base_dir = env!("CARGO_MANIFEST_DIR"); + let srs_path = base_dir.to_owned() + "/srs.bin"; + StructuredReferenceString::::from_file(&srs_path).unwrap() +} + +fn upload_solution(proof: &PowerProof) { + let mut stream = TcpStream::connect("52.7.211.188:8000").unwrap(); + let proof_vec = serde_cbor::to_vec(&proof).expect("Failed serialization"); + + stream.write(&(proof_vec.len() as u64).to_be_bytes()).unwrap(); + stream.write(&proof_vec).unwrap(); + + let mut response = String::new(); + stream.read_to_string(&mut response).unwrap(); + println!("Received response: {}", response); +} + +fn main() { + let srs = load_srs(); + let kzg = KZG::new(srs); + let x = FieldElement::from(X); + + let p1_coeffs = [FieldElement::one(), FieldElement::one()]; + let p2_coeffs = [FieldElement::one(), FieldElement::one()]; + let p3_coeffs = [FieldElement::one(), FieldElement::one()]; + // This is Gohan power level, it can't be tampered with + let p4_coeffs = [FieldElement::from(9000)]; + + // Sample random u + let u = FieldElement::from(rand::random::()); + + let commit_and_open_at = |coeffs: &[FieldElement<_>]| -> ( + Polynomial<_>, + G1Point, + FieldElement<_> + ) { + let poly = Polynomial::::new(coeffs); + let commitment = kzg.commit(&poly); + let eval = poly.evaluate(&x); + + (poly, commitment, eval) + }; + + let (p1, + p1_comm, + y1) = commit_and_open_at(&p1_coeffs); + + let (p2, + p2_comm, + y2) = commit_and_open_at(&p2_coeffs); + + let (p3, + p3_comm, + y3) = commit_and_open_at(&p3_coeffs); + + let (p4, + p4_comm, + y4) = commit_and_open_at(&p4_coeffs); + + + let ys = [y1, y2, y3, y4]; + let ps = [p1, p2, p3 ,p4]; + let ps_c = [p1_comm, p2_comm, p3_comm, p4_comm]; + + let proof = kzg.open_batch(&x, &ys, &ps, &u); + assert!(kzg.verify_batch(&x, &ys, &ps_c, &proof, &u)); + + let power_proof = PowerProof { + proof_x_hex: proof.to_affine().x().to_string(), + proof_y_hex: proof.to_affine().y().to_string(), + u: u.to_string(), + y: ys.map(|y| y.to_string() ), + commitments_x: ps_c.clone().map(|c| c.to_affine().x().to_string()), + commitments_y: ps_c.map(|c| c.to_affine().y().to_string()), + }; + + upload_solution(&power_proof); +} \ No newline at end of file diff --git a/exercises/challenge_3/srs.bin b/exercises/challenge_3/srs.bin new file mode 100644 index 0000000000000000000000000000000000000000..22550f08ac97e5302a65b1e43fb3f014cdb9e294 GIT binary patch literal 148044 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