Skip to content

Commit

Permalink
Merge branch 'main' into add-stark-prover
Browse files Browse the repository at this point in the history
  • Loading branch information
MauroToscano committed Sep 18, 2023
2 parents 4c56b8b + b394a6a commit f23e2f9
Show file tree
Hide file tree
Showing 87 changed files with 13,136 additions and 2 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
7 changes: 7 additions & 0 deletions exercises/README.md
Original file line number Diff line number Diff line change
@@ -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
15 changes: 15 additions & 0 deletions exercises/blind_trust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"}

158 changes: 158 additions & 0 deletions exercises/blind_trust/README.md
Original file line number Diff line number Diff line change
@@ -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<FrField> {
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";
```
Binary file added exercises/blind_trust/proof
Binary file not shown.
187 changes: 187 additions & 0 deletions exercises/blind_trust/src/circuit.rs
Original file line number Diff line number Diff line change
@@ -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<FrElement> {
(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<FrElement> {
let u = ORDER_R_MINUS_1_ROOT_UNITY;
let mut result: Vec<FrElement> = 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<FrElement> {
let identity = identity_permutation(omega, n);
let permuted: Vec<FrElement> = (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<FrField> {
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<FrField> {
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<FrElement> = permuted[..8].to_vec();
let s2_lagrange: Vec<FrElement> = permuted[8..16].to_vec();
let s3_lagrange: Vec<FrElement> = 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,
}
}
3 changes: 3 additions & 0 deletions exercises/blind_trust/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod circuit;
pub mod sith_generate_proof;
pub mod solution;
Loading

0 comments on commit f23e2f9

Please sign in to comment.