├── en
├── deprecated
│ ├── theory
│ │ ├── glossary.md
│ │ ├── transcript.md
│ │ ├── polycommit.md
│ │ ├── plonk.md
│ │ └── arithmetic.md
│ ├── rust-syntax
│ │ ├── crate.md
│ │ └── macros.md
│ ├── appendix
│ │ ├── graph
│ │ │ ├── affine-form.png
│ │ │ ├── arithmetic-curve.png
│ │ │ ├── arithmetic-macro.png
│ │ │ ├── doubling-elliptic-curve.png
│ │ │ ├── overall-architecture-flowchart.png
│ │ │ ├── mapping-integers-to-field-elements.png
│ │ │ └── conversion-from-projective-to-affine.png
│ │ └── code
│ │ │ ├── mermaid.md
│ │ │ └── python.md
│ └── implementation
│ │ └── 1. overall architecture.md
├── final implementation
│ ├── 2. halo2_gadgets
│ │ └── halo2_gadgets.md
│ ├── 3. halo2_poseidon
│ │ ├── Fill bytes.png
│ │ ├── sponge-function.png
│ │ └── halo2_poseidon.md
│ └── 1. halo2_proofs
│ │ └── module
│ │ └── circuit
│ │ └── circuit.png
└── README.md
├── .gitignore
├── LICENSE
├── zh
└── README.md
└── README.md
/en/deprecated/theory/glossary.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/en/deprecated/rust-syntax/crate.md:
--------------------------------------------------------------------------------
1 | # subtle
2 |
--------------------------------------------------------------------------------
/en/final implementation/2. halo2_gadgets/halo2_gadgets.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/en/deprecated/appendix/graph/affine-form.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sure2web3/build-your-own-zkp/HEAD/en/deprecated/appendix/graph/affine-form.png
--------------------------------------------------------------------------------
/en/deprecated/appendix/graph/arithmetic-curve.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sure2web3/build-your-own-zkp/HEAD/en/deprecated/appendix/graph/arithmetic-curve.png
--------------------------------------------------------------------------------
/en/deprecated/appendix/graph/arithmetic-macro.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sure2web3/build-your-own-zkp/HEAD/en/deprecated/appendix/graph/arithmetic-macro.png
--------------------------------------------------------------------------------
/en/deprecated/appendix/graph/doubling-elliptic-curve.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sure2web3/build-your-own-zkp/HEAD/en/deprecated/appendix/graph/doubling-elliptic-curve.png
--------------------------------------------------------------------------------
/en/final implementation/3. halo2_poseidon/Fill bytes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sure2web3/build-your-own-zkp/HEAD/en/final implementation/3. halo2_poseidon/Fill bytes.png
--------------------------------------------------------------------------------
/en/final implementation/3. halo2_poseidon/sponge-function.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sure2web3/build-your-own-zkp/HEAD/en/final implementation/3. halo2_poseidon/sponge-function.png
--------------------------------------------------------------------------------
/en/deprecated/appendix/graph/overall-architecture-flowchart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sure2web3/build-your-own-zkp/HEAD/en/deprecated/appendix/graph/overall-architecture-flowchart.png
--------------------------------------------------------------------------------
/en/deprecated/appendix/graph/mapping-integers-to-field-elements.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sure2web3/build-your-own-zkp/HEAD/en/deprecated/appendix/graph/mapping-integers-to-field-elements.png
--------------------------------------------------------------------------------
/en/final implementation/1. halo2_proofs/module/circuit/circuit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sure2web3/build-your-own-zkp/HEAD/en/final implementation/1. halo2_proofs/module/circuit/circuit.png
--------------------------------------------------------------------------------
/en/deprecated/appendix/graph/conversion-from-projective-to-affine.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sure2web3/build-your-own-zkp/HEAD/en/deprecated/appendix/graph/conversion-from-projective-to-affine.png
--------------------------------------------------------------------------------
/en/deprecated/theory/transcript.md:
--------------------------------------------------------------------------------
1 | # Transcript Module
2 | * Fiat-Shamir Challenges: The transcript module provides a way to generate Fiat-Shamir challenges, which are used to convert an interactive zero-knowledge proof into a non-interactive one. The challenges are generated by hashing the prover's commitments and other relevant information, ensuring the soundness of the non-interactive proof.
3 |
4 | ## Fiat-Shamir Challenges
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | debug/
4 | target/
5 |
6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
8 | Cargo.lock
9 |
10 | # These are backup files generated by rustfmt
11 | **/*.rs.bk
12 |
13 | # MSVC Windows builds of rustc generate these, which store debugging information
14 | *.pdb
15 |
16 | # RustRover
17 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
18 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
19 | # and can be added to the global gitignore or merged into this file. For a more nuclear
20 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
21 | #.idea/
22 |
23 | .DS_Store
--------------------------------------------------------------------------------
/en/deprecated/theory/polycommit.md:
--------------------------------------------------------------------------------
1 | # Polycommit Module
2 | * Polynomial Commitment Scheme: A polynomial commitment scheme allows a prover to commit to a polynomial and later prove evaluations of the polynomial at specific points. The commitment hides the polynomial coefficients, while the proof allows the verifier to check the correctness of the evaluation without learning the coefficients. The [Halo] paper describes a specific polynomial commitment scheme that is used in this implementation.
3 | * Fiat-Shamir Transformation: The Fiat-Shamir transformation is a technique for converting an interactive zero-knowledge proof into a non-interactive one. It uses a hash function (represented by the sponge function in this code) to generate challenges based on the prover's commitments. The challenges are used to ensure the soundness of the non-interactive proof.
4 |
5 | ## Polynomial Commitment Scheme
6 |
7 | ## Fiat-Shamir Transformation
--------------------------------------------------------------------------------
/en/deprecated/theory/plonk.md:
--------------------------------------------------------------------------------
1 | # Plonk Module
2 | * TurboPLONK Protocol: TurboPLONK is a variant of the PLONK protocol that is optimized for efficiency. It allows a prover to prove the correct execution of a computation without revealing the details of the computation. The protocol involves creating a circuit, generating a structured reference string (SRS), creating a proof, and verifying the proof.
3 | * Constraint System: In the TurboPLONK protocol, a constraint system is used to define the relationships between the inputs and outputs of a computation. The prover must satisfy the constraints in the constraint system to prove the correctness of the computation.
4 | * Vanishing Polynomial: The vanishing polynomial is a polynomial that evaluates to zero at a specific set of points. In the TurboPLONK protocol, the vanishing polynomial is used to ensure that the prover's computation satisfies the constraints in the constraint system.
5 |
6 | ## TurboPLONK Protocol
7 |
8 | ## Constraint System
9 |
10 | ## Vanishing Polynomial
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 sure2web3
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/zh/README.md:
--------------------------------------------------------------------------------
1 | # 🔐 build-your-own-zkp:释放零知识证明的强大力量!
2 |
3 | 从0到1构建零知识证明系统。
4 |
5 | ## 🌟 简介
6 | 这个仓库 [build-your-own-zkp](https://github.com/sure2web3/build-your-own-zkp) 就像一个神奇的传送门 🚪,它将引导你开启从零基础掌握零知识证明(ZKP)的迷人之旅。零知识证明在密码学领域就如同超能力一般。它允许一方(证明者)向另一方(验证者)证明某个陈述是真实的,而除了该陈述确实为真这一事实之外,不透露任何额外信息。这就好比你证明自己知道一个秘密,却不用真的把它说出来!🤫
7 |
8 | ## 🎯 项目目标
9 | 本项目的主要目标是成为你实现完整 ZKP 系统的终极指南 🗺️,我们将以出色的 Halo 2 零知识证明系统为榜样 🌟。在此过程中,我们将深入探究所有相关的密码学原理和知识,还会配有详细的图示,让一切都清晰明了,就像用放大镜审视每一个细节!🔍
10 |
11 | ### 关键目标
12 | - **📚 教育性**:一头扎进 ZKP 的世界,揭开其背后所有的密码学秘密。
13 | - **💻 实用性**:卷起袖子,亲身体验构建一个真正的 ZKP 系统。
14 | - **👐 易上手性**:通过清晰的解释和精彩的图示,让学习过程顺畅无比。
15 |
16 | ## 📂 仓库结构
17 | 这个仓库就像一个组织有序的图书馆 📖。它被划分为各个章节和部分,每个部分都专注于 ZKP 系统实现的特定方面。每个部分都配有详细的解释、炫酷的代码示例和引人注目的图示,帮助你理解概念并像专业人士一样跟进实现过程。这就像在 ZKP 的世界里有一位私人导游!🚶♂️
18 |
19 | ## 🚀 如何使用本仓库
20 | - **📝 彻底阅读文档**:开启你的冒险之旅,先仔细研读 README 文件以及每个部分的详细文档。这将帮助你全面掌握基本概念,深入理解底层原理,并洞察复杂的代码实现。这就像为摩天大楼打下坚实的基础!🏗️
21 | - **👀 查看图示**:花时间认真查看每个部分提供的图示。这些可视化工具就像魔法眼镜 👓,帮助你直观地理解抽象概念,明白不同组件是如何协同工作的。它们就像通往隐藏宝藏的地图! 🗺️
22 | - **✨ 积极贡献**:我们非常高兴你能加入!如果你有任何新颖的想法、实用的改进建议或必要的修正,请不要犹豫!打开一个 issue 分享你的想法,或者提交一个拉取请求直接为项目的发展做出贡献。你的贡献就像火箭的燃料 🚀,我们迫不及待地想看看你能带来什么!
23 |
24 | ## 🎥 视频教程
25 | 除了本仓库中的书面文档和代码示例,我们致力于为你提供深入的视频教程,将你的学习体验提升到新的高度。我们会像勤劳的小蜜蜂 🐝 一样,不断根据本文档的内容录制视频,并上传到 YouTube 和 B 站。
26 | - YouTube:[build-your-own-zkp](https://www.youtube.com/@build-your-own-zkp)
27 | - B 站:[零知识证明我有一朋友](https://space.bilibili.com/3546387138480997)
28 |
29 | ## 📋 前提条件
30 | - 你应该对密码学和编程概念有基本的了解。这就像为这次旅程准备好工具包! 🧰
31 | - 由于 Halo 2 的实现使用的是 Rust 编程语言,所以熟悉 Rust 语言是个不错的选择。这就像在异国他乡会说当地语言一样! 🌍
32 |
33 | ## 📄 许可证
34 | 本项目采用 **MIT 许可证** 授权。查看 [LICENSE](../LICENSE) 文件以获取详细信息。
35 |
36 | **重要通知**:
37 | 本项目的部分内容受到由 Electric Coin Company (ECC) 开发的 **Halo 2** 库的启发,该库同样采用 [MIT 许可证](https://opensource.org/licenses/MIT) 授权。在使用或分发本软件时,你需要同时遵守这两个许可证。这就像遵守两个不同俱乐部的规则一样! 🏌️♂️
38 |
39 | ## 🙏 致谢
40 | 我们要向 **Electric Coin Company (ECC)** 以及 **Halo 2** 零知识证明系统的开发者们致以最热烈的欢呼和诚挚的感谢 🙌。他们的开创性工作就像这个教育项目的基石,我们站在他们的肩膀上,向着星辰前进! 🌟
41 |
42 | ### Halo 2 许可证声明
43 | Halo 2 由 Electric Coin Company (ECC) 开发,并根据 **MIT 许可证** 发布(原始仓库:[https://github.com/zcash/halo2](https://github.com/zcash/halo2))。本项目出于教育目的使用 Halo 2,并遵循以下许可条款:
44 |
45 | 1. **📜 版权保留**:
46 | 所有源自或受 Halo 2 启发的源文件都保留其原始的版权声明和许可通知(通常位于每个文件的开头)。这就像保留家族传家宝一样! 👨👩👧👦
47 | 2. **📄 许可证继承**:
48 | 本项目根据 **MIT 许可证** 发布,但任何来自 Halo 2 的代码仍需遵循其原始许可证。在使用、修改或分发本项目时,你需要同时遵守这两个许可证。这就像同时戴两顶帽子一样! 🎩
49 | 3. **❌ 无官方背书**:
50 | 本仓库并未得到 Zcash 基金会、Electric Coin Company 或 Halo 2 团队的官方关联或背书。它仅用于教育目的,不应被视为一个可直接用于生产环境的实现。这就像大赛前的训练场! ⚽
--------------------------------------------------------------------------------
/en/deprecated/appendix/code/mermaid.md:
--------------------------------------------------------------------------------
1 | # Introduction
2 | You can copy any mermaid code from this document and paste it into the [mermaid live editor](https://mermaid-js.github.io/mermaid-live-editor/) to visualize the flowchart.
3 |
4 | # overall-architecture-flowchart
5 | ```mermaid
6 | graph LR
7 | classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
8 |
9 | A(arithmetic):::process --> B(arithmetic/macros.rs):::process
10 | A --> C(arithmetic/curves.rs):::process
11 | A --> D(arithmetic/fields.rs):::process
12 | E(plonk):::process --> F(plonk/circuit.rs):::process
13 | E --> G(plonk/domain.rs):::process
14 | E --> H(plonk/prover.rs):::process
15 | E --> I(plonk/srs.rs):::process
16 | E --> J(plonk/verifier.rs):::process
17 | K(polycommit):::process --> L(polycommit.rs):::process
18 | M(transcript):::process --> N(transcript.rs):::process
19 | C --> A(arithmetic):::process
20 | D --> A(arithmetic):::process
21 | F --> E(plonk):::process
22 | G --> E(plonk):::process
23 | H --> E(plonk):::process
24 | I --> E(plonk):::process
25 | J --> E(plonk):::process
26 | L --> K(polycommit):::process
27 | N --> M(transcript):::process
28 | A --> E(plonk):::process
29 | A --> K(polycommit):::process
30 | A --> M(transcript):::process
31 | E --> K(polycommit):::process
32 | E --> M(transcript):::process
33 |
34 | style A fill:#FFEBEB,stroke:#E68994,stroke-width:2px;
35 | style E fill:#FFEBEB,stroke:#E68994,stroke-width:2px;
36 |
37 | subgraph Arithmetic Module
38 | B
39 | C
40 | D
41 | end
42 | subgraph Plonk Module
43 | F
44 | G
45 | H
46 | I
47 | J
48 | end
49 | subgraph Polycommit Module
50 | L
51 | end
52 | subgraph Transcript Module
53 | N
54 | end
55 | ```
56 | # macro
57 | ```mermaid
58 | ---
59 | config:
60 | layout: fixed
61 | ---
62 | flowchart TD
63 | A["impl_add_binop_specify_output"] -- impl --> B["Add Trait"]
64 | C["impl_sub_binop_specify_output"] -- impl --> D["Sub Trait"]
65 | E["impl_binops_additive_specify_output"] -- calls --> A & C
66 | F["impl_binops_multiplicative_mixed"] -- impl --> G["Mul Trait"]
67 | H["impl_binops_additive"] -- calls --> E
68 | H -- impl --> I["AddAssign Trait"] & J["SubAssign Trait"]
69 | K["impl_binops_multiplicative"] -- calls --> F
70 | K -- impl --> L["MulAssign Trait"]
71 | A -. Params: $lhs, $rhs, $output .-> A
72 | C -. Params: $lhs, $rhs, $output .-> C
73 | F -. Params: $lhs, $rhs, $output .-> F
74 | E -. Params: $lhs, $rhs, $output .-> E
75 | H -. Params: $lhs, $rhs .-> H
76 | K -. Params: $lhs, $rhs .-> K
77 | B -- &self + rhs, self + &rhs, self + rhs --- M1["Add Methods"]
78 | D -- "&self - rhs, self - &rhs, self - rhs" --- M2["Sub Methods"]
79 | G -- &self * rhs, self * &rhs, self * rhs --- M3["Mul Methods"]
80 | I -- add_assign --- M4["AddAssign Method"]
81 | J -- sub_assign --- M5["SubAssign Method"]
82 | L -- mul_assign --- M6["MulAssign Method"]
83 | A:::macro
84 | B:::trait
85 | C:::macro
86 | D:::trait
87 | E:::macro
88 | F:::macro
89 | G:::trait
90 | H:::macro
91 | I:::trait
92 | J:::trait
93 | K:::macro
94 | L:::trait
95 | M1:::method
96 | M2:::method
97 | M3:::method
98 | M4:::method
99 | M5:::method
100 | M6:::method
101 | classDef macro fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
102 | classDef trait fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px
103 | classDef method fill:#fff,stroke:#bbb,stroke-dasharray: 5 5
104 |
105 | ```
106 |
107 | # arithmetic-curve
108 | ```mermaid
109 | graph LR
110 | classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px
111 |
112 | A(Curve Trait):::process --> B(CurveAffine Trait):::process
113 | A --> C(Curve Implementations):::process
114 | B --> C
115 | C --> D(Projective Point Struct):::process
116 | C --> E(Affine Point Struct):::process
117 | D --> F(Arithmetic Operations):::process
118 | E --> F
119 | F --> G(Serialization and Deserialization):::process
120 | A --> H(Group Operations):::process
121 | H --> F
122 |
123 | I(Group Trait):::process --> J(group_zero):::process
124 | I --> K(group_add):::process
125 | I --> L(group_sub):::process
126 | I --> M(group_scale):::process
127 | H --> I
128 |
129 | N(get_challenge_scalar):::process --> O(best_multiexp):::process
130 | P(multiexp_serial):::process --> O
131 | O --> F
132 |
133 | Q(best_fft):::process --> R(serial_fft):::process
134 | Q --> S(parallel_fft):::process
135 | R --> F
136 | S --> F
137 |
138 | T(eval_polynomial):::process --> F
139 | U(compute_inner_product):::process --> F
140 | V(parallelize):::process --> F
141 | W(log2_floor):::process --> S
142 | ```
--------------------------------------------------------------------------------
/en/deprecated/appendix/code/python.md:
--------------------------------------------------------------------------------
1 | # conversion-from-projective-to-affine
2 | ```python
3 | import numpy as np
4 | import matplotlib.pyplot as plt
5 | from mpl_toolkits.mplot3d import Axes3D
6 |
7 | # Example projective point (X, Y, Z)
8 | X, Y, Z = 2, 3, 1
9 |
10 | # Convert to affine coordinates if Z != 0
11 | if Z != 0:
12 | x_affine = X / Z
13 | y_affine = Y / Z
14 | else:
15 | raise ValueError("Z cannot be zero for affine conversion.")
16 |
17 | fig = plt.figure(figsize=(12, 6))
18 |
19 | # 3D plot for projective coordinates
20 | ax1 = fig.add_subplot(121, projection='3d')
21 | ax1.scatter(X, Y, Z, color='blue', s=100, label='Projective Point (X:Y:Z)')
22 | ax1.set_xlabel('X')
23 | ax1.set_ylabel('Y')
24 | ax1.set_zlabel('Z')
25 | ax1.set_title('Projective Point in 3D')
26 | ax1.legend()
27 | ax1.grid(True)
28 |
29 | # 2D plot for affine coordinates
30 | ax2 = fig.add_subplot(122)
31 | ax2.scatter(x_affine, y_affine, color='red', s=100, label='Affine Point (x, y)')
32 | ax2.set_xlabel('x')
33 | ax2.set_ylabel('y')
34 | ax2.set_title('Affine Point in 2D')
35 | ax2.legend()
36 | ax2.grid(True)
37 |
38 | plt.suptitle('Conversion from Projective (X:Y:Z) to Affine (x, y) Coordinates')
39 | plt.tight_layout()
40 | plt.show()
41 | ```
42 | # doubling-elliptic-curve
43 | ```python
44 | import numpy as np
45 | import matplotlib.pyplot as plt
46 |
47 | # Elliptic curve parameters
48 | a = 0
49 | b = 5
50 |
51 | # Define the elliptic curve equation
52 | def elliptic_curve(x):
53 | return np.sqrt(x**3 + a * x + b), -np.sqrt(x**3 + a * x + b)
54 |
55 | # Define the function to double a point
56 | def double_point(x1, y1, a):
57 | m = (3 * x1**2 + a) / (2 * y1)
58 | x3 = m**2 - 2 * x1
59 | y3 = m * (x1 - x3) - y1
60 | return x3, y3
61 |
62 | # Define the function to find the intersection of the tangent line with the curve
63 | def find_intersection(x1, y1, a, b):
64 | m = (3 * x1**2 + a) / (2 * y1)
65 | c = y1 - m * x1
66 |
67 | # Solve the equation m*x + c = sqrt(x^3 + a*x + b)
68 | # This leads to the cubic equation x^3 - m^2*x^2 + (a - 2*m*c)*x + (b - c^2) = 0
69 | # We know x1 is a double root, so we can factor it out
70 | # The third root x2 can be found from the sum of roots: x1 + x1 + x2 = m^2
71 | x2 = m**2 - 2*x1
72 | y2 = m * x2 + c
73 |
74 | return x2, y2
75 |
76 | # Generate x values for the elliptic curve
77 | x = np.linspace(-3, 3, 400)
78 | y_pos, y_neg = elliptic_curve(x)
79 |
80 | # Select a point P
81 | x1 = 1
82 | y1 = np.sqrt(x1**3 + a * x1 + b)
83 |
84 | # Calculate 2P
85 | x3, y3 = double_point(x1, y1, a)
86 |
87 | # Find the intersection of the tangent line with the curve
88 | x2, y2 = find_intersection(x1, y1, a, b)
89 |
90 | # Calculate the slope of the tangent line
91 | m = (3 * x1**2 + a) / (2 * y1)
92 |
93 | # Calculate the intercept of the tangent line
94 | c = y1 - m * x1
95 |
96 | # Generate x values for the tangent line
97 | x_tangent = np.linspace(x1 - 2, x1 + 2, 200)
98 | y_tangent = m * x_tangent + c
99 |
100 | # Create the plot
101 | plt.figure(figsize=(10, 8))
102 |
103 | # Plot the elliptic curve
104 | plt.plot(x, y_pos, 'b', label=r'$y = \sqrt{x^3 + ax + b}$')
105 | plt.plot(x, y_neg, 'b')
106 |
107 | # Plot point P
108 | plt.scatter(x1, y1, color='red', s=80, label='Point P')
109 |
110 | # Plot the tangent line
111 | plt.plot(x_tangent, y_tangent, 'g--', label='Tangent line at P')
112 |
113 | # Plot the intersection point
114 | plt.scatter(x2, y2, color='purple', s=80, label='Intersection point')
115 |
116 | # Plot point 2P
117 | plt.scatter(x3, y3, color='orange', s=80, label='Point 2P')
118 |
119 | # Draw a dashed line between intersection point and 2P
120 | plt.plot([x2, x3], [y2, -y2], 'r--', label='Reflection line')
121 |
122 | # Set plot properties
123 | plt.title('Elliptic Curve Point Doubling Illustration', fontsize=16)
124 | plt.xlabel('x', fontsize=14)
125 | plt.ylabel('y', fontsize=14)
126 | plt.grid(True, linestyle='--', alpha=0.7)
127 | plt.legend(fontsize=12)
128 | plt.axis('equal') # Make axes equal for better visualization
129 | plt.ylim(-5, 5) # Adjust y-axis limits
130 |
131 | # Show the plot
132 | plt.tight_layout()
133 | plt.show()
134 | ```
135 | # mapping-integers-to-field-elements
136 | ```python
137 | import numpy as np
138 | import matplotlib.pyplot as plt
139 |
140 | # Set modulus p for the finite field
141 | p = 7
142 |
143 | # Select a set of integers n, including values not divisible by p
144 | n_values = np.array([0, 1, 2, 3, 5, 6, 7, 8, 10, 13, 14, 15, 20])
145 | field_elements = n_values % p
146 |
147 | fig = plt.figure(figsize=(10, 6))
148 | ax = fig.add_subplot(111, projection='3d')
149 |
150 | # Plot each integer as a point in (n, p, r) space
151 | sc = ax.scatter(n_values, [p]*len(n_values), field_elements, c=field_elements, cmap='viridis', s=80)
152 |
153 | # Annotate each point
154 | for n, r in zip(n_values, field_elements):
155 | ax.text(n, p, r, f"n={n}\nr={r}", fontsize=9, ha='center', va='bottom')
156 |
157 | ax.set_xlabel('Integer n')
158 | ax.set_ylabel('Modulus p')
159 | ax.set_zlabel('Field Element r = n mod p')
160 | ax.set_title(f'Mapping Integers to Field Elements in Finite Field $\\mathbb{{Z}}_p$ (p={p})')
161 |
162 | plt.colorbar(sc, label='Field Element Value')
163 | plt.tight_layout()
164 | plt.show()
165 | ```
--------------------------------------------------------------------------------
/en/README.md:
--------------------------------------------------------------------------------
1 | # 🔐 build-your-own-zkp: Unleash the Power of Zero-Knowledge Proofs!
2 |
3 | Master Zero-Knowledge Proof (ZKP) by recreating ZKP system from scratch.
4 |
5 | ## 🌟 Introduction
6 | This repository, [build-your-own-zkp](https://github.com/sure2web3/build-your-own-zkp), is like a magical portal 🚪 that guides you through the fascinating journey of mastering Zero-Knowledge Proof (ZKP) from the ground up. Zero-Knowledge Proofs are like a superpower in the cryptographic world. They allow one party (the prover) to prove to another party (the verifier) that a statement is true, without revealing any extra information other than the fact that the statement is indeed true. It's like proving you know a secret without actually sharing it! 🤫
7 |
8 | ## 🎯 Project Goals
9 | The main goal of this project is to be your ultimate guide 🗺️ in implementing a complete ZKP system, using the amazing Halo 2 zero-knowledge proof system as our shining star 🌟. Along the way, we'll dive deep into all the cool cryptographic principles and knowledge, and we'll have detailed illustrations to make everything crystal clear, just like using a magnifying glass to examine every little detail! 🔍
10 |
11 | ### Key Objectives
12 | - **📚 Educational**: Dive headfirst into the world of ZKP and uncover all its underlying cryptographic secrets.
13 | - **💻 Practical**: Roll up your sleeves and get hands-on experience in building a real ZKP system.
14 | - **👐 Accessible**: Make the learning process as smooth as silk with clear explanations and awesome illustrations.
15 |
16 | ## 📂 Repository Structure
17 | The repository is like a well-organized library 📖. It's divided into chapters and sections, each focusing on a specific part of the ZKP system implementation. Every section comes with detailed explanations, cool code examples, and eye-catching illustrations to help you understand the concepts and follow along like a pro. It's like having a personal tour guide through the world of ZKP! 🚶♂️
18 |
19 | ## 🚀 How to Use This Repository
20 | - **📝 Read the Documentation Thoroughly**: Start your adventure by getting cozy with the README file and all the detailed documentation in each section. This will help you grasp the fundamental concepts, understand the underlying principles, and peek into the intricate code implementation. It's like laying a solid foundation for a skyscraper! 🏗️
21 | - **👀 Examine the Illustrations**: Take your time to really look at the illustrations provided in each section. These visual aids are like magic glasses 👓 that help you see abstract concepts and understand how different components work together. They're like a map to a hidden treasure! 🗺️
22 | - **✨ Contribute Actively**: We're super excited to have you on board! If you have any brilliant ideas, practical improvements, or necessary corrections, don't be shy! Open an issue to share your thoughts or submit a pull request to directly contribute to the project's growth. Your input is like fuel for our rocket 🚀, and we can't wait to see what you bring to the table!
23 |
24 | ## 🎥 Video Tutorials
25 | In addition to the written documentation and code examples in this repository, we're on a mission to provide you with in-depth video tutorials to take your learning experience to the next level. We'll be like busy little bees 🐝, continuously recording videos based on the content of this documentation and uploading them to both YouTube and Bilibili.
26 | - YouTube: [build-your-own-zkp](https://www.youtube.com/@build-your-own-zkp)
27 | - Bilibili: [零知识证明我有一朋友](https://space.bilibili.com/3546387138480997)
28 |
29 | ## 📋 Prerequisites
30 | - You should have a basic understanding of cryptography and programming concepts. It's like having a toolkit for this journey! 🧰
31 | - Since the Halo 2 implementation is in Rust, it's a good idea to be familiar with the Rust programming language. It's like speaking the local language in a foreign land! 🌍
32 |
33 | ## 📄 License
34 | This project is licensed under the **MIT License**. Check out the [LICENSE](../LICENSE) file for all the details.
35 |
36 | **Important Notice**:
37 | Some parts of this project are inspired by the **Halo 2** library developed by the Electric Coin Company (ECC), which is also licensed under the [MIT License](https://opensource.org/licenses/MIT). When you use or distribute this software, you need to follow both licenses. It's like following the rules of two different clubs! 🏌️♂️
38 |
39 | ## 🙏 Acknowledgments
40 | We want to give a huge shoutout and a big thank you 🙌 to the **Electric Coin Company (ECC)** and the developers of the **Halo 2** zero-knowledge proof system. Their groundbreaking work is like the foundation stone of this educational project, and we're standing on their shoulders to reach for the stars! 🌟
41 |
42 | ### Halo 2 License Notice
43 | Halo 2 is developed by the Electric Coin Company (ECC) and released under the **MIT License** (original repository: [https://github.com/zcash/halo2](https://github.com/zcash/halo2)). This project uses Halo 2 for educational purposes and follows these licensing terms:
44 |
45 | 1. **📜 Copyright Preservation**:
46 | All source files that are derived from or inspired by Halo 2 keep their original copyright headers and license notices (usually at the beginning of each file). It's like keeping a family heirloom! 👨👩👧👦
47 | 2. **📄 License Inheritance**:
48 | This project is released under the **MIT License**, but any code from Halo 2 still follows its original license. You need to follow both licenses when using, modifying, or distributing this project. It's like wearing two hats at the same time! 🎩
49 | 3. **❌ No Endorsement**:
50 | This repository isn't officially connected to or endorsed by the Zcash Foundation, Electric Coin Company, or the Halo 2 team. It's just for educational fun and shouldn't be seen as a ready-to-use production implementation. It's like a practice field before the big game! ⚽
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 🔐 build-your-own-zkp: Unleash the Power of Zero-Knowledge Proofs!
2 |
3 | Master Zero-Knowledge Proof (ZKP) by recreating ZKP system from scratch.
4 |
5 | 从0到1构建零知识证明系统.
6 |
7 | ## Language Selection
8 | - [English](en/README.md)
9 | - [中文](zh/README.md)
10 |
11 | ## 🌟 Introduction
12 | This repository, [build-your-own-zkp](https://github.com/sure2web3/build-your-own-zkp), is like a magical portal 🚪 that guides you through the fascinating journey of mastering Zero-Knowledge Proof (ZKP) from the ground up. Zero-Knowledge Proofs are like a superpower in the cryptographic world. They allow one party (the prover) to prove to another party (the verifier) that a statement is true, without revealing any extra information other than the fact that the statement is indeed true. It's like proving you know a secret without actually sharing it! 🤫
13 |
14 | ## 🎯 Project Goals
15 | The main goal of this project is to be your ultimate guide 🗺️ in implementing a complete ZKP system, using the amazing Halo 2 zero-knowledge proof system as our shining star 🌟. Along the way, we'll dive deep into all the cool cryptographic principles and knowledge, and we'll have detailed illustrations to make everything crystal clear, just like using a magnifying glass to examine every little detail! 🔍
16 |
17 | ### Key Objectives
18 | - **📚 Educational**: Dive headfirst into the world of ZKP and uncover all its underlying cryptographic secrets.
19 | - **💻 Practical**: Roll up your sleeves and get hands-on experience in building a real ZKP system.
20 | - **👐 Accessible**: Make the learning process as smooth as silk with clear explanations and awesome illustrations.
21 |
22 | ## 📂 Repository Structure
23 | The repository is like a well-organized library 📖. It's divided into chapters and sections, each focusing on a specific part of the ZKP system implementation. Every section comes with detailed explanations, cool code examples, and eye-catching illustrations to help you understand the concepts and follow along like a pro. It's like having a personal tour guide through the world of ZKP! 🚶♂️
24 |
25 | ## 🚀 How to Use This Repository
26 | - **📝 Read the Documentation Thoroughly**: Start your adventure by getting cozy with the README file and all the detailed documentation in each section. This will help you grasp the fundamental concepts, understand the underlying principles, and peek into the intricate code implementation. It's like laying a solid foundation for a skyscraper! 🏗️
27 | - **👀 Examine the Illustrations**: Take your time to really look at the illustrations provided in each section. These visual aids are like magic glasses 👓 that help you see abstract concepts and understand how different components work together. They're like a map to a hidden treasure! 🗺️
28 | - **✨ Contribute Actively**: We're super excited to have you on board! If you have any brilliant ideas, practical improvements, or necessary corrections, don't be shy! Open an issue to share your thoughts or submit a pull request to directly contribute to the project's growth. Your input is like fuel for our rocket 🚀, and we can't wait to see what you bring to the table!
29 |
30 | ## 🎥 Video Tutorials
31 | In addition to the written documentation and code examples in this repository, we're on a mission to provide you with in-depth video tutorials to take your learning experience to the next level. We'll be like busy little bees 🐝, continuously recording videos based on the content of this documentation and uploading them to both YouTube and Bilibili.
32 | - YouTube: [build-your-own-zkp](https://www.youtube.com/@build-your-own-zkp)
33 | - Bilibili: [零知识证明我有一朋友](https://space.bilibili.com/3546387138480997)
34 |
35 | ## 📋 Prerequisites
36 | - You should have a basic understanding of cryptography and programming concepts. It's like having a toolkit for this journey! 🧰
37 | - Since the Halo 2 implementation is in Rust, it's a good idea to be familiar with the Rust programming language. It's like speaking the local language in a foreign land! 🌍
38 |
39 | ## 📄 License
40 | This project is licensed under the **MIT License**. Check out the [LICENSE](LICENSE) file for all the details.
41 |
42 | **Important Notice**:
43 | Some parts of this project are inspired by the **Halo 2** library developed by the Electric Coin Company (ECC), which is also licensed under the [MIT License](https://opensource.org/licenses/MIT). When you use or distribute this software, you need to follow both licenses. It's like following the rules of two different clubs! 🏌️♂️
44 |
45 | ## 🙏 Acknowledgments
46 | We want to give a huge shoutout and a big thank you 🙌 to the **Electric Coin Company (ECC)** and the developers of the **Halo 2** zero-knowledge proof system. Their groundbreaking work is like the foundation stone of this educational project, and we're standing on their shoulders to reach for the stars! 🌟
47 |
48 | ### Halo 2 License Notice
49 | Halo 2 is developed by the Electric Coin Company (ECC) and released under the **MIT License** (original repository: [https://github.com/zcash/halo2](https://github.com/zcash/halo2)). This project uses Halo 2 for educational purposes and follows these licensing terms:
50 |
51 | 1. **📜 Copyright Preservation**:
52 | All source files that are derived from or inspired by Halo 2 keep their original copyright headers and license notices (usually at the beginning of each file). It's like keeping a family heirloom! 👨👩👧👦
53 | 2. **📄 License Inheritance**:
54 | This project is released under the **MIT License**, but any code from Halo 2 still follows its original license. You need to follow both licenses when using, modifying, or distributing this project. It's like wearing two hats at the same time! 🎩
55 | 3. **❌ No Endorsement**:
56 | This repository isn't officially connected to or endorsed by the Zcash Foundation, Electric Coin Company, or the Halo 2 team. It's just for educational fun and shouldn't be seen as a ready-to-use production implementation. It's like a practice field before the big game! ⚽
--------------------------------------------------------------------------------
/en/deprecated/rust-syntax/macros.md:
--------------------------------------------------------------------------------
1 | # Rust Crate Configuration: Allowed Lints and Strict Quality Standards
2 | ```rust
3 | #![allow(
4 | clippy::op_ref,
5 | clippy::assign_op_pattern,
6 | clippy::too_many_arguments,
7 | clippy::suspicious_arithmetic_impl,
8 | clippy::many_single_char_names,
9 | clippy::same_item_push
10 | )]
11 | #![deny(intra_doc_link_resolution_failure)]
12 | #![deny(missing_debug_implementations)]
13 | #![deny(missing_docs)]
14 | #![deny(unsafe_code)]
15 | ```
16 | ## #![allow(...)]
17 | This attribute allows certain lints (warnings) that are normally enabled by the Rust compiler or the Clippy tool. The specific lints being allowed here are:
18 | * `clippy::op_ref`: Allows operations on references instead of values (e.g., &a + &b instead of a + b).
19 | * `clippy::assign_op_pattern`: Suppresses warnings about using assignment operators in patterns.
20 | * `clippy::too_many_arguments`: Disables warnings about functions with a large number of parameters.
21 | * `clippy::suspicious_arithmetic_impl`: Ignores potentially problematic arithmetic implementations.
22 | * `clippy::many_single_char_names`: Allows the use of single-character variable names.
23 | * `clippy::same_item_push`: Suppresses warnings about pushing the same item into a collection multiple times.
24 |
25 | ## #![deny(...)]
26 | This attribute enforces strict rules by turning certain lints into compile errors. The denied lints are:
27 | * `intra_doc_link_resolution_failure`: Ensures all intra-document links (e.g., [Foo] references) resolve correctly.
28 | * `missing_debug_implementations`: Requires all public types to implement the Debug trait.
29 | * `missing_docs`: Mandates that all public items (modules, functions, types, etc.) have documentation comments.
30 | * `unsafe_code`: Prohibits the use of unsafe code in the crate, ensuring it is 100% safe Rust.
31 |
32 | # Macros in Arithmetic Module
33 | ### **Macro Syntax Breakdown**
34 |
35 | #### **1. Macro Definition**
36 | ```rust
37 | macro_rules! impl_add_binop_specify_output {
38 | ($lhs:ident, $rhs:ident, $output:ident) => { ... };
39 | }
40 | ```
41 | - `macro_rules!`: Keyword to define a declarative macro.
42 | - `impl_add_binop_specify_output`: Macro name (convention: `impl__`).
43 | - `($lhs:ident, $rhs:ident, $output:ident)`: Input parameters (3 identifiers).
44 | - `$lhs`: Left-hand side type (e.g., `MyType`).
45 | - `$rhs`: Right-hand side type (e.g., `MyType`).
46 | - `$output`: Result type (e.g., `MyType`).
47 | - In Rust macros, ident is a macro fragment specifier that matches an identifier, such as a type name, variable name, or trait name.Explanation:
48 | * ident stands for "identifier".
49 | * In the macro pattern, $lhs:ident means "match an identifier and bind it to the variable $lhs".
50 | * For example, if you invoke the macro as impl_add_binop_specify_output!(Foo, Bar, Baz);, then:
51 | * $lhs will be Foo
52 | * $rhs will be Bar
53 | * $output will be Baz
54 | * These identifiers can then be used inside the macro body to generate code.
55 | - `=>`: Separates the pattern from the expansion.
56 | - The left side of => is the pattern that the macro matches when it is invoked.
57 | - The right side of => is the code that will be generated (expanded) when the macro is used.
58 |
59 | #### **2. Trait Implementations**
60 | ```rust
61 | impl<'b> Add<&'b $rhs> for $lhs { ... }
62 | ```
63 | - `impl<'b>`: Generic lifetime parameter `'b` for the reference.
64 | - `Add<&'b $rhs> for $lhs`: Implements `Add` trait for `$lhs + &$rhs`.
65 | - `type Output = $output`: Specifies the result type.
66 |
67 | #### **3. Method Implementations**
68 | ```rust
69 | fn add(self, rhs: &'b $rhs) -> $output {
70 | &self + rhs
71 | }
72 | ```
73 | - `self`: Takes ownership of `$lhs` (value).
74 | - `rhs: &'b $rhs`: Reference to `$rhs` with lifetime `'b`.
75 | - `&self + rhs`: Delegate to reference-based operation (avoids copying).
76 |
77 | ### **Key Symbols and Their Meanings**
78 |
79 | | Symbol | Meaning |
80 | |--------------|-------------------------------------------------------------------------|
81 | | `$lhs`, `$rhs`, `$output` | Placeholders for types (resolved when the macro is invoked). |
82 | | `:ident` | Matches an identifier (e.g., type names like `u32`, `MyType`). |
83 | | `impl` | Keyword to implement a trait for a type. |
84 | | `'a`, `'b` | Lifetime parameters (ensure references outlive their borrows). |
85 | | `&` | Reference type (e.g., `&T` is a reference to `T`). |
86 | | `#[inline]` | Hint to the compiler to inline the function for performance. |
87 | | `self` | Represents the instance of the type (value or reference). |
88 |
89 |
90 | ### **Usage Examples**
91 |
92 | #### **1. Basic Addition Implementation**
93 | ```rust
94 | // Implement Add for MyType + MyType -> MyType
95 | impl_add_binop_specify_output!(MyType, MyType, MyType);
96 |
97 | // Usage:
98 | let a = MyType::new(5);
99 | let b = MyType::new(3);
100 | let c = a + b; // Calls the generated Add implementation
101 | ```
102 |
103 | #### **2. Mixed Types**
104 | ```rust
105 | // Implement Add for MyType + u32 -> MyType
106 | impl_add_binop_specify_output!(MyType, u32, MyType);
107 |
108 | // Usage:
109 | let result = my_type_instance + 42;
110 | ```
111 |
112 | #### **3. Combined Macros**
113 | ```rust
114 | // Implement both Add and Sub for MyType
115 | impl_binops_additive!(MyType, MyType);
116 |
117 | // Usage:
118 | let a = MyType::new(5);
119 | let b = MyType::new(3);
120 | let sum = a + b; // Add
121 | let diff = a - b; // Sub
122 | a += b; // AddAssign
123 | a -= b; // SubAssign
124 | ```
125 |
126 |
127 | ### **Generated Code Example**
128 | For `impl_add_binop_specify_output!(MyType, MyType, MyType)`, the macro expands to:
129 | ```rust
130 | impl<'b> Add<&'b MyType> for MyType {
131 | type Output = MyType;
132 | fn add(self, rhs: &'b MyType) -> MyType { &self + rhs }
133 | }
134 |
135 | impl<'a> Add for &'a MyType {
136 | type Output = MyType;
137 | fn add(self, rhs: MyType) -> MyType { self + &rhs }
138 | }
139 |
140 | impl Add for MyType {
141 | type Output = MyType;
142 | fn add(self, rhs: MyType) -> MyType { &self + &rhs }
143 | }
144 | ```
145 |
146 |
147 | ### **Key Features**
148 |
149 | 1. **Reference Handling**:
150 | - Automatically handles all combinations of values and references (`T`, `&T`).
151 | - Delegates to reference-based operations to avoid unnecessary copies.
152 |
153 | 2. **Custom Output Type**:
154 | - `$output` allows specifying a different result type (e.g., `BigInt` for `u32 + u32`).
155 |
156 | 3. **Consistency**:
157 | - Ensures all trait implementations follow the same pattern.
158 |
159 | 4. **Performance**:
160 | - `#[inline]` optimizes method calls.
161 |
162 |
163 | ### **Conclusion**
164 | These macros leverage Rust's macro system to automate boilerplate code for arithmetic operations, making it easier to implement traits for custom types while maintaining type safety and performance.
--------------------------------------------------------------------------------
/en/deprecated/implementation/1. overall architecture.md:
--------------------------------------------------------------------------------
1 | # Introduction
2 | This Rust codebase provides an implementation of a variant of (Turbo)[PLONK] designed specifically for the [polynomial commitment scheme](../theory/arithmetic.md#polynomial-commitment-scheme) described in the [Halo] paper. PLONK (Permutations over Lagrange-bases for Oecumenical Noninteractive arguments of Knowledge) is a zero-knowledge proof system that allows a prover to convince a verifier that a certain computation has been carried out correctly without revealing the details of the computation. The polynomial commitment scheme is a fundamental building block in many cryptographic protocols, enabling the prover to commit to a [polynomial](../theory/arithmetic.md#polynomials-in-cryptography-definition-and-examples) and later prove evaluations of the polynomial at specific points.
3 |
4 | # Overall Architecture
5 | ## Flowchart
6 | [mermaid source code](../appendix/code/mermaid.md#overall-architecture-flowchart)
7 | 
8 | ## Explanation:
9 | * The `arithmetic` module is a core module that provides common utilities, traits, and structures for [group](../theory/arithmetic.md#group), [field](../theory/arithmetic.md#field-theory), and [polynomial](../theory/arithmetic.md#polynomials-in-cryptography-definition-and-examples) arithmetic. It includes sub-modules such as `macros.rs`, `curves.rs`, and `fields.rs`.
10 | * The `plonk` module implements a variant of (Turbo)`plonk`. It depends on the `arithmetic` module and has sub-modules for [circuit synthesis](../theory/arithmetic.md#circuit-synthesis), [domain operations](../theory/arithmetic.md#domain-operations), prover, [structured reference string (SRS)](../theory/arithmetic.md#structured-reference-string-srs) generation, and verifier.
11 | * The `polycommit` module is related to [polynomial commitment schemes](../theory/arithmetic.md#polynomial-commitment-scheme) and depends on the `arithmetic` module.
12 | * The `transcript` module is used for hashing and absorbing values and also depends on the `arithmetic` module. The `plonk` module also uses both the `polycommit` and `transcript` modules.
13 |
14 | ## Detailed Module Relationships
15 | ### arithmetic
16 | #### **1. macros.rs**
17 | Defines Rust macros to implement binary operations for different types, reducing code duplication.
18 | 
19 | The flowchart above visualizes the hierarchical structure of Rust macros that automate the implementation of arithmetic traits.
20 | * `impl_binops_additive`: Extends impl_binops_additive_specify_output to include assignment operations (+=, -=).
21 | * `impl_binops_multiplicative`: Extends impl_binops_multiplicative_mixed to include multiplication assignment (*=).
22 | * [rust syntx for these macros](../rust-syntax/macros.md#macros-in-arithmetic-module)
23 |
24 | #### **2. curves.rs**
25 | Defines trait methods for curve elements, such as converting to [affine form](../theory/arithmetic.md#affine-form), [doubling](../theory/arithmetic.md#doubling), and [getting coordinates](../theory/arithmetic.md#getting-coordinates).
26 |
27 | 
28 | * [mermaid source code](../appendix/code/mermaid.md#arithmetic-curve)
29 |
30 | The `src/arithmetic/curves.rs` file provides implementations for the [Tweedledum and Tweedledee elliptic curve groups](../theory/arithmetic.md#tweedledum-and-tweedledee-elliptic-curve-groups). The `Curve` and `CurveAffine` abstractions allow the code to generalize over these two groups, enabling efficient and generic arithmetic operations on elliptic curve points.
31 |
32 | ##### 2.1 `Curve` Trait
33 | - **Function**: Defines a common interface for dealing with elements of an elliptic curve group in the "projective" form, where arithmetic operations are usually more efficient.
34 | - **Implementation Principle**:
35 | - **Associated Types**:
36 | - `type Affine`: Represents the [affine form](../theory/arithmetic.md#affine-form) of a point on the curve.
37 | - `type Scalar`: Represents the [scalar field of the elliptic curve](../theory/arithmetic.md#scalar-field-in-elliptic-curve-cryptography).
38 | - `type Base`: Represents the [base field over which the elliptic curve is constructed](../theory/arithmetic.md#base-field).
39 | - **Methods**:
40 | - `fn zero() -> Self;`: Returns the additive identity of the curve group.
41 | - `fn one() -> Self;`: Returns the base point of the curve.
42 | - `fn double(&self) -> Self;`: Doubles the current curve point.
43 | - `fn is_zero(&self) -> Choice;`: Checks if the current point is the identity element.
44 | - `fn endo(&self) -> Self;`: Applies the curve endomorphism by multiplying the x - coordinate by an element of multiplicative order 3.
45 | - `fn to_affine(&self) -> Self::Affine;`: Converts the projective point to its affine form.
46 | - `fn is_on_curve(&self) -> Choice;`: Checks if the point is on the curve.
47 | - `fn batch_to_affine(v: &[Self], target: &mut [Self::Affine]);`: Converts multiple projective points to their affine forms in batch.
48 | - `fn b() -> Self::Base;`: Returns the curve constant `b`.
49 | - **Cryptographic Knowledge**: Elliptic curve groups are fundamental in modern cryptography, especially in public - key cryptography. The base point and scalar multiplication operations are used in key generation, digital signatures, and encryption algorithms. Endomorphisms can be used to speed up scalar multiplication operations.
50 |
51 | ##### 2.2 `CurveAffine` Trait
52 | - **Function**: Serves as the affine counterpart to the `Curve` trait. It is used for serialization, storage in memory, and inspection of the $(x, y)$ coordinates of a point on the curve.
53 | - **Implementation Principle**:
54 | - **Associated Types**:
55 | - `type Projective`: Represents the projective form of a point on the curve.
56 | - `type Scalar`: Represents the scalar field of the elliptic curve.
57 | - `type Base`: Represents the base field over which the elliptic curve is constructed.
58 | - **Methods**:
59 | - `fn zero() -> Self;`: Returns the additive identity of the curve group in affine form.
60 | - `fn one() -> Self;`: Returns the base point of the curve in affine form.
61 | - `fn is_zero(&self) -> Choice;`: Checks if the current point is the identity element.
62 | - `fn to_projective(&self) -> Self::Projective;`: Converts the affine point to its projective form.
63 | - `fn get_xy(&self) -> CtOption<(Self::Base, Self::Base)>;`: Gets the $(x, y)$ coordinates of the point.
64 | - `fn from_xy(x: Self::Base, y: Self::Base) -> CtOption;`: Creates a point from given $(x, y)$ coordinates, failing if the point is not on the curve.
65 | - `fn is_on_curve(&self) -> Choice;`: Checks if the point is on the curve.
66 | - `fn from_bytes(bytes: &[u8; 32]) -> CtOption;`: Attempts to obtain a group element from its compressed 32 - byte little - endian representation.
67 | - `fn to_bytes(&self) -> [u8; 32];`: Obtains the compressed, 32 - byte little - endian representation of the element.
68 | - `fn from_bytes_wide(bytes: &[u8; 64]) -> CtOption;`: Attempts to obtain a group element from its uncompressed 64 - byte little - endian representation.
69 | - `fn to_bytes_wide(&self) -> [u8; 64];`: Obtains the uncompressed, 64 - byte little - endian representation of the element.
70 | - `fn b() -> Self::Base;`: Returns the curve constant `b`.
71 | - **Cryptographic Knowledge**: Affine points are more suitable for serialization and storage because they have a simpler representation. Compressed and uncompressed byte representations are used for efficient transmission and storage of elliptic curve points in cryptographic protocols.
72 |
73 | ##### 2.3 `new_curve_impl` Macro
74 | - **Function**: Implements the `Curve` and related traits for a given curve type and its affine counterpart.
75 | - **Implementation Principle**:
76 | - **Struct Definitions**:
77 | - Defines a `Projective` struct representing a point in the projective coordinate space and an `Affine` struct representing a point in the affine coordinate space.
78 | - **Trait Implementations**:
79 | - Implements the `Curve` trait for the `Projective` struct, including methods for obtaining the zero and one points, doubling a point, applying the endomorphism, converting to affine form, and checking if a point is on the curve.
80 | - Implements the `From` trait to convert between `Affine` and `Projective` forms.
81 | - Implements the `Default` and `ConstantTimeEq` traits for the `Projective` struct.
82 | - **Cryptographic Knowledge**: This macro simplifies the implementation of elliptic curve groups, making it easier to work with different curves in a generic way. The use of projective coordinates can significantly improve the efficiency of arithmetic operations on elliptic curves.
83 |
84 | #### **3. fields.rs**
85 | Defines trait methods for field elements, including finding the [multiplicative inverse](../theory/arithmetic.md#multiplicative-inverse), obtaining the [square root](../theory/arithmetic.md#square-root), and [converting integers to field elements](../theory/arithmetic.md#converting-integers-to-field-elements).
86 |
87 | ### polycommit
88 | * `OpeningProof`: Represents a proof object for the polynomial commitment scheme opening.
89 | * `Params`: Contains the public parameters for the polynomial commitment scheme. It provides methods for initializing parameters, committing to polynomials, and creating opening proofs.
90 | ### plonk
91 | * `circuit.rs`: Defines the interface for creating gates and circuits in the TurboPLONK protocol.
92 | * `domain.rs`: Defines the EvaluationDomain struct, which contains precomputed constants for performing operations on an evaluation domain in the context of PLONK.
93 | * `prover.rs`: Implements the prover side of the TurboPLONK protocol, including creating proofs.
94 | * `srs.rs`: Defines the structured reference string (SRS), which is computed from a specific circuit and parameters for the polynomial commitment scheme.
95 | * `verifier.rs`: Implements the verifier side of the TurboPLONK protocol, including verifying proofs.
96 | ### transcript
97 | * Provides a transcript implementation for obtaining Fiat-Shamir challenges, which are used to convert an interactive zero-knowledge proof into a non-interactive one.
--------------------------------------------------------------------------------
/en/deprecated/theory/arithmetic.md:
--------------------------------------------------------------------------------
1 | # Arithmetic Module
2 | * Field Theory: Fields are a fundamental concept in cryptography. In a finite field, operations such as addition, subtraction, multiplication, and division (finding the multiplicative inverse) are well-defined. These operations are used in many cryptographic algorithms, such as elliptic curve cryptography and polynomial commitment schemes.
3 | * Elliptic Curve Cryptography: Elliptic curves are widely used in modern cryptography due to their security and efficiency. Curve operations such as point addition, point doubling, and coordinate extraction are fundamental to elliptic curve cryptography. For example, the double-and-add algorithm, which uses the double operation, is commonly used to perform scalar multiplication on elliptic curves.
4 | * Fast Fourier Transform (FFT): The FFT is an efficient algorithm for computing the discrete Fourier transform (DFT) of a sequence. In cryptography, the FFT is used in polynomial commitment schemes and other cryptographic protocols to perform polynomial evaluation and interpolation efficiently.
5 | * Multi-Exponentiation: Multi-exponentiation is the operation of computing a linear combination of multiple exponentiations. In elliptic curve cryptography, multi-exponentiation is used to compute a point on the curve given a set of coefficients and bases. Parallelizing multi-exponentiation can significantly improve the performance of cryptographic operations.
6 |
7 | ## Field Theory
8 |
9 | In the realm of cryptography, a field is a crucial algebraic structure that provides a set of elements along with two binary operations: addition and multiplication. These operations follow specific rules that endow the field with certain desirable properties.
10 |
11 | ### Definition and General Properties
12 | A field $(F, +, \cdot)$ consists of a set $F$ and two operations:
13 | - **Addition**: The set $F$ forms an abelian (commutative) group under addition. This means it satisfies the following:
14 | - **Closure**: For any $a, b \in F$, $a + b \in F$.
15 | - **Associativity**: $(a + b)+c = a+(b + c)$ for all $a, b, c \in F$.
16 | - **Identity Element**: There exists an element $0 \in F$ such that $a + 0 = 0 + a = a$ for all $a \in F$.
17 | - **Inverse Element**: For every $a \in F$, there exists an element $-a \in F$ such that $a+(-a)=(-a)+a = 0$.
18 | - **Multiplication**: The non-zero elements of $F$, denoted as $F^*=F\setminus\{0\}$, form an abelian group under multiplication. So:
19 | - **Closure**: For any $a, b\in F^*$, $a\cdot b\in F^*$.
20 | - **Associativity**: $(a\cdot b)\cdot c = a\cdot(b\cdot c)$ for all $a, b, c\in F^*$.
21 | - **Identity Element**: There exists an element $1\in F^*$ such that $a\cdot 1 = 1\cdot a = a$ for all $a\in F^*$.
22 | - **Inverse Element**: For every $a\in F^*$, there exists an element $a^{-1}\in F^*$ such that $a\cdot a^{-1}=a^{-1}\cdot a = 1$.
23 | - **Distributivity**: For all $a, b, c\in F$, $a\cdot(b + c)=a\cdot b + a\cdot c$. This property links the addition and multiplication operations.
24 |
25 | ### Finite Fields
26 | A finite field, also known as a Galois field, is a field that contains a finite number of elements. The most common types are prime finite fields $\mathbb{Z}_p$, where $p$ is a prime number.
27 |
28 | - **Elements and Operations in $\mathbb{Z}_p$**:
29 | - The set $\mathbb{Z}_p=\{0, 1,\cdots, p - 1\}$. Operations such as addition, subtraction, multiplication, and division (by finding the multiplicative inverse) are well-defined modulo $p$. For example, in $\mathbb{Z}_7$:
30 | - **Addition**: $3 + 5 = 8\equiv1\pmod{7}$.
31 | - **Subtraction**: $5-3 = 2\pmod{7}$ (since $5+( - 3)\equiv5 + 4\equiv2\pmod{7}$, where $4$ is the additive inverse of $3$ in $\mathbb{Z}_7$).
32 | - **Multiplication**: $3\times 5 = 15\equiv1\pmod{7}$.
33 | - **Division (Finding the Multiplicative Inverse)**: To find the multiplicative inverse of $3$ in $\mathbb{Z}_7$, we need to find an element $x$ such that $3x\equiv1\pmod{7}$. Through trial and error, we find that $x = 5$ since $3\times5 = 15\equiv1\pmod{7}$.
34 |
35 | ### Importance in Cryptography
36 | - **Elliptic Curve Cryptography (ECC)**: Finite fields play a central role in ECC. Elliptic curves are defined over finite fields, and operations on points of the elliptic curve (such as point addition and scalar multiplication) rely on the arithmetic operations of the underlying finite field. For example, the security of ECC is based on the difficulty of solving the elliptic curve discrete logarithm problem over a finite field.
37 | - **Polynomial Commitment Schemes**: These schemes are fundamental in many cryptographic protocols like zero - knowledge proofs. Polynomial operations (such as evaluation, interpolation) are performed over finite fields. The ability to commit to a polynomial and prove its properties at specific points depends on the well - defined operations in the finite field.
38 | - **Symmetric-Key Cryptography**: In algorithms like the Advanced Encryption Standard (AES), operations on bytes (which can be considered as elements of a finite field) are used to perform substitution, permutation, and mixing operations, which contribute to the security of the encryption.
39 |
40 | ### Other Types of Fields in Cryptography
41 | - **Extension Fields**: These are fields that extend a base field. They are constructed by adjoining elements (usually roots of irreducible polynomials) to the base field. Extension fields are used in cryptography when more complex algebraic structures are needed, such as in some elliptic curve cryptography implementations over binary fields (which are extension fields of $\mathbb{Z}_2$). This allows for more efficient computations and can enhance the security of cryptographic protocols.
42 |
43 | In summary, fields, especially finite fields, are essential building blocks in modern cryptography, enabling secure and efficient cryptographic operations across a wide range of applications.
44 |
45 | ## Elliptic Curve Cryptography
46 |
47 | ## Fast Fourier Transform (FFT)
48 |
49 | ## Multi-Exponentiation
50 |
51 |
52 | # Explanation of Terms
53 |
54 | ### Polynomials in Cryptography: Definition and Examples
55 |
56 | In cryptography, **polynomials** are mathematical expressions used to encode computational statements, enable secure protocols, and form the basis of many cryptographic primitives (e.g., zero-knowledge proofs, secret sharing, and polynomial commitments). Here's a detailed explanation:
57 |
58 |
59 | ### **1. Definition of a Polynomial**
60 | A polynomial over a **finite field** $\mathbb{F}$ (e.g., integers modulo a prime $p$) is an expression of the form:
61 | $P(x) = a_0 + a_1 x + a_2 x^2 + \cdots + a_d x^d$
62 | where:
63 | - $a_0, a_1, \ldots, a_d \in \mathbb{F}$ are **coefficients**,
64 | - $x$ is a **variable**,
65 | - $d$ is the **degree** of the polynomial (the highest power of $x$ with a non-zero coefficient).
66 |
67 | **Example in $\mathbb{Z}_7$ (integers modulo 7)**:
68 | $
69 | P(x) = 3 + 2x + 5x^2 \quad (\text{degree } 2)
70 | $
71 | Here, $a_0 = 3$, $a_1 = 2$, $a_2 = 5$, and operations are performed modulo 7.
72 |
73 |
74 | ### **2. Key Properties in Cryptography**
75 | #### **a. Polynomial Evaluation**
76 | For a given value $\alpha \in \mathbb{F}$, evaluating $P(\alpha)$ means computing the result by substituting $x = \alpha$.
77 | **Example**:
78 | Evaluate $P(x) = 3 + 2x + 5x^2$ at $x = 4$ in $\mathbb{Z}_7$:
79 | $$
80 | \begin{align*}
81 | P(4) &= 3 + 2 \cdot 4 + 5 \cdot 4^2 \\
82 | &= 3 + 8 + 5 \cdot 16 \\
83 | &= 3 + 1 + 5 \cdot 2 \quad (\text{since } 8 \bmod 7 = 1,\ 16 \bmod 7 = 2) \\
84 | &= 3 + 1 + 10 \\
85 | &= 14 \bmod 7 = 0.
86 | \end{align*}
87 | $$
88 | Thus, $P(4) = 0$ in $\mathbb{Z}_7$.
89 |
90 | #### **b. Polynomial Interpolation**
91 | Given $d + 1$ points $(x_0, y_0), (x_1, y_1), \ldots, (x_d, y_d)$ where all $x_i$ are distinct, there exists a **unique polynomial** $P(x)$ of degree $d$ such that $P(x_i) = y_i$ for all $i$.
92 | **Example**:
93 | Find $P(x)$ of degree 1 (a line) passing through $(1, 3)$ and $(2, 5)$ in $\mathbb{Z}_7$:
94 | Using Lagrange interpolation:
95 | $$
96 | P(x) = y_0 \cdot \frac{x - x_1}{x_0 - x_1} + y_1 \cdot \frac{x - x_0}{x_1 - x_0}
97 | $$
98 | Substituting $x_0 = 1$, $y_0 = 3$, $x_1 = 2$, $y_1 = 5$:
99 |
100 | $$
101 | \begin{align*}
102 | P(x) &= 3 \cdot \frac{x - 2}{1 - 2} + 5 \cdot \frac{x - 1}{2 - 1} \\
103 | &= 3 \cdot \frac{x - 2}{-1} + 5 \cdot (x - 1) \\
104 | &= 3 \cdot (2 - x) + 5 \cdot (x - 1) \quad \\
105 | &= 6 - 3x + 5x - 5 \\
106 | &= 1 + 2x \quad (\text{in } \mathbb{Z}_7).
107 | \end{align*}
108 | $$
109 | Verification:
110 | - $P(1) = 1 + 2 \cdot 1 = 3$,
111 | - $P(2) = 1 + 2 \cdot 2 = 5$.
112 |
113 |
114 | ### **3. Cryptographic Applications of Polynomials**
115 | #### **a. Secret Sharing (Shamir’s Scheme)**
116 | A secret $s$ is split into $n$ shares such that any $k$ shares can reconstruct $s$, but fewer than $k$ shares reveal nothing.
117 | - **How it works**:
118 | Define $P(x)$ with $a_0 = s$ (the secret) and random coefficients $a_1, \ldots, a_{k - 1}$.
119 | Share $i$ is $P(i)$ for $i = 1, 2, \ldots, n$.
120 | Any $k$ shares can interpolate $P(x)$ to recover $s = P(0)$.
121 |
122 | **Example** ($k = 2$, $n = 3$):
123 | - Secret $s = 5$ in $\mathbb{Z}_7$.
124 | - Choose $P(x) = 5 + 3x$ (random $a_1 = 3$).
125 | - Shares: $P(1) = 1$, $P(2) = 4$, $P(3) = 7 \equiv 0 \mod 7$.
126 | - Given shares $P(1)$ and $P(2)$, interpolate $P(x)$ to find $P(0) = 5$.
127 |
128 | #### **b. Zero-Knowledge Proofs (ZK-SNARKs)**
129 | Complex computations are encoded as polynomials. For example, proving $a \cdot b = c$ involves checking if:
130 | $
131 | P(x) = (x - a)(x - b) - (x^2 - c)
132 | $
133 | evaluates to $0$ at some point $x$.
134 |
135 | * Information Known by the Prover
136 | The prover knows the actual values of $a$, $b$, and $c$. They are aware of the specific operands in the equation $a\cdot b = c$ because they are the ones who possess the knowledge related to this computation. Additionally, the prover knows the construction and properties of the polynomial $P(x)=(x - a)(x - b)-(x^{2}-c)$. They understand how the polynomial is derived from the equation $a\cdot b = c$ and can use this knowledge to find a value of $x$ (such as $x = \frac{2ab}{a + b}$ when $a + b\neq0$) for which $P(x)=0$. However, in the context of zero - knowledge proofs, the prover's goal is to convince the verifier without revealing these actual values.
137 |
138 | * Information Known by the Verifier
139 | The verifier knows the form of the polynomial $P(x)$ and the general concept that the prover is trying to convince them that $P(x)$ evaluates to $0$ at some point $x$. They are aware of the zero - knowledge proof protocol and the rules for verification. But the verifier does not know the actual values of $a$, $b$, and $c$. The verifier only needs to check whether the prover can provide valid evidence (related to the evaluation of $P(x)$) that satisfies the verification conditions of the zero - knowledge proof system, without learning the specific details of the operands in the equation $a\cdot b = c$.
140 |
141 | #### **c. Polynomial Commitments**
142 | Commit to $P(x)$ using a short string $C$. Later, prove $P(\alpha) = \beta$ without revealing $P(x)$.
143 | - **Example**:
144 | Using KZG commitments, $C = g^{P(s)}$ where $g$ is a group generator and $s$ is a trusted setup parameter.
145 |
146 | ### **4. Why Polynomials?**
147 | - **Efficiency**: Polynomial operations (evaluation, interpolation) are computationally fast.
148 | - **Structural Properties**: Polynomials enable powerful cryptographic proofs (e.g., checking if two parties computed the same function).
149 | - **Hiding Information**: Coefficients and evaluations can be hidden behind commitments, ensuring privacy.
150 |
151 | In summary, polynomials are the "workhorses" of modern cryptography, enabling secure and efficient protocols for privacy-preserving computations.
152 |
153 | ## polynomial commitment scheme
154 |
155 | A **polynomial commitment scheme** is a cryptographic primitive that allows a **prover** to commit to a polynomial $ P(x) $ and later convince a **verifier** of specific properties of $ P(x) $ (e.g., its evaluation at a point $ x = \alpha $) without revealing the polynomial itself. Here’s a breakdown of its core principles:
156 |
157 |
158 | ### **1. Commitment Phase**
159 | The prover first generates a **commitment** $ C $ to the polynomial $ P(x) $. This commitment is a short, fixed-size value (often a group element in elliptic curve cryptography) that "binds" the prover to $ P(x) $.
160 |
161 | - **Key Properties**:
162 | - **Hiding**: The commitment $ C $ reveals no information about the coefficients or form of $ P(x) $.
163 | - **Binding**: The prover cannot later "cheat" by claiming $ C $ corresponds to a different polynomial $ Q(x) \neq P(x) $.
164 |
165 | *Analogy*: Think of the commitment as a sealed envelope containing $ P(x) $. The envelope’s seal (the commitment value) proves the contents were fixed at the time of sealing, but the envelope remains unopened until needed.
166 |
167 |
168 | ### **2. Opening Phase (Proving Evaluations)**
169 | After committing to $ P(x) $, the prover can respond to a **challenge** from the verifier, such as proving that $ P(\alpha) = \beta $ for a specific point $ \alpha $.
170 |
171 | #### **Standard Approach (Using Polynomial Interpolation)**:
172 | Suppose the polynomial $ P(x) $ is of degree $ d $. To prove $ P(\alpha) = \beta $:
173 | 1. The prover constructs a **remainder polynomial** $ R(x) = \frac{P(x) - \beta}{x - \alpha} $, which is a degree $ d - 1 $ polynomial (since $ x - \alpha $ is a factor of $ P(x) - \beta $ if $ P(\alpha) = \beta $).
174 | 2. The prover sends the evaluation $ R(\gamma) $ at a random point $ \gamma $ (chosen by the verifier) along with a **proof** that $ R(x) $ is correctly related to $ P(x) $.
175 |
176 | #### **Verification via Polynomial Identities**:
177 | The verifier checks if the following identity holds using the commitment $ C $, the challenge $ \alpha $, and the prover’s response:
178 | $
179 | C = (x - \alpha) \cdot \text{Commit}(R(x)) + \beta \cdot \text{Commit}(1)
180 | $
181 | If true, this guarantees $ P(\alpha) = \beta $ under the assumptions of the commitment scheme’s security (e.g., the discrete logarithm problem or knowledge of exponent assumption in elliptic curve groups).
182 |
183 |
184 | ### **3. Security Assumptions**
185 | Polynomial commitment schemes rely on **computational hardness assumptions** to ensure binding and hiding properties. Common assumptions include:
186 | - **Discrete Logarithm Assumption**: Hardness of solving $ g^x = h $ in a cyclic group.
187 | - **Knowledge of Exponent Assumption**: The prover must know the "exponent" (e.g., polynomial coefficients) corresponding to the commitment.
188 | - **Structural Properties of Polynomials**: E.g., the difficulty of forging evaluations without knowing the polynomial.
189 |
190 |
191 | ### **4. Common Schemes and Their Variants**
192 | - **KZG Commitments (Kate - Zaverucha - Goldberg)**:
193 | Uses elliptic curve pairings and commits to $ P(x) $ as $ g^{P(x)} $ in a group $ G $. Efficient for single evaluations but requires a trusted setup.
194 | - **Halo/PLONK - Style Commitments**:
195 | Rely on **universal and updateable commitments** (e.g., using a trusted setup or structured reference polynomials) to support multiple evaluations and recursive proofs.
196 | - **Hybrid Schemes**:
197 | Combine polynomial commitments with other primitives (e.g., Merkle trees) for batch evaluations or dynamic updates.
198 |
199 |
200 | ### **5. Applications in Cryptography**
201 | - **Zero - Knowledge Proofs (ZK-SNARKs/ZK-STARKs)**:
202 | Prove that a computation (encoded as a polynomial) was executed correctly without revealing inputs/outputs.
203 | - **Verifiable Computation**:
204 | Clients verify that a server correctly computed a function $ f(x) $ represented as a polynomial.
205 | - **Blockchain**:
206 | Commit to large datasets (e.g., transaction histories) and prove membership/updates efficiently.
207 | - **Multi-Party Computation (MPC)**:
208 | Parties commit to private polynomials and securely compute joint operations without revealing inputs.
209 |
210 |
211 | ### **Key Insight**
212 | Polynomial commitments act as a "cryptographic handshake" between the prover and verifier: they enable trustless verification of polynomial properties while preserving privacy. By reducing complex computational statements to polynomial evaluations, these schemes form the backbone of modern privacy-preserving protocols.
213 |
214 | ## Affine Form
215 | In the context of elliptic curves, the **affine form** refers to the representation of a point on the curve using the standard $(x, y)$ coordinates in a two-dimensional plane. For an elliptic curve defined by the equation $y^{2}=x^{3}+2x + 1$ over a field $K$, a point $P=(x,y)$ where $x,y\in K$ is in affine form.
216 |
217 | |  |
218 | | :-------------------------------------------: |
219 | | *$y^{2}=x^{3}+2x + 1$* |
220 |
221 | In contrast, the projective form uses three coordinates $(X:Y:Z)$ where $(X,Y,Z)\neq(0,0,0)$ and two sets of coordinates $(X_1:Y_1:Z_1)$ and $(X_2:Y_2:Z_2)$ represent the same point if there exists a non-zero element $\lambda\in K$ such that $X_2 = \lambda X_1$, $Y_2=\lambda Y_1$, and $Z_2=\lambda Z_1$. The conversion from projective form $(X:Y:Z)$ to affine form $(x,y)$ is done by $x=\frac{X}{Z}$ and $y = \frac{Y}{Z}$ when $Z\neq0$.
222 |
223 | [conversion code](../appendix/code/python.md#conversion-from-projective-to-affine)
224 | |  |
225 | | :-------------------------------------------: |
226 | | Conversion From Projective Form To Affine Form |
227 |
228 | ## Doubling
229 | **Doubling** an element on an elliptic curve means taking a point $P$ on the curve and computing the point $2P = P+P$. Geometrically, to double a point $P=(x,y)$ on an elliptic curve $y^{2}=x^{3}+ax + b$, we find the tangent line to the curve at the point $P$. This tangent line intersects the curve at another point (in most cases). Then, we take the reflection of this intersection point about the $x$-axis.
230 |
231 | Algebraically, if $P=(x_1,y_1)$ is a point on the elliptic curve $y^{2}=x^{3}+ax + b$, the slope of the tangent line $m=\frac{3x_1^{2}+a}{2y_1}$, and the coordinates of $2P=(x_3,y_3)$ are given by $x_3=m^{2}-2x_1$ and $y_3=m(x_1 - x_3)-y_1$.
232 |
233 | [doubling elliptic curve code](../appendix/code/python.md#doubling-elliptic-curve)
234 | |  |
235 | | :-------------------------------------------: |
236 | | Doubling for Elliptic Curve |
237 |
238 | ## Getting Coordinates
239 | **Getting coordinates** means retrieving the values of the $x$ and $y$ components of a point on the elliptic curve. For a point $P$ on an elliptic curve, if $P$ is in affine form, getting coordinates simply means obtaining the values of $x$ and $y$ such that $P=(x,y)$ and $y^{2}=x^{3}+ax + b$. If $P$ is in projective form $(X:Y:Z)$, we can convert it to affine form and then get the $x$ and $y$ coordinates as described above.
240 |
241 | ## Multiplicative Inverse
242 | In the context of finite fields in cryptography, for a given non-zero element $a$ in a finite field $\mathbb{F}$, the multiplicative inverse of $a$, denoted as $a^{-1}$, is an element such that $a\times a^{-1}=1$, where $1$ is the multiplicative identity of the finite field. This operation is crucial in many cryptographic algorithms. For example, in elliptic curve cryptography, operations on points often involve calculations in the underlying finite field. When computing scalar multiplications or solving equations related to point operations, finding the multiplicative inverse is frequently required to perform division-like operations within the finite field. It helps in ensuring the consistency and correctness of arithmetic operations in the finite field, which in turn is fundamental for the security and functionality of cryptographic protocols.
243 |
244 | ## Square Root
245 | In a finite field $\mathbb{F}$, finding the square root of an element $x$ means finding an element $y$ such that $y^{2}=x$. In some cryptographic applications, such as in certain zero-knowledge proof systems and hash function constructions, operations involving square roots in finite fields are utilized. For instance, in the construction of commitment schemes, which are used to "commit" to a value without revealing it initially and then being able to open the commitment later, square root operations in finite fields can play a role in generating and verifying commitments. The ability to compute square roots in a finite field also impacts the design and analysis of cryptographic primitives related to quadratic equations and polynomial evaluations over finite fields.
246 |
247 | ## Converting Integers to Field Elements
248 | Converting integers to field elements is a fundamental operation in cryptographic algorithms that work with finite fields. Given an integer $n$ and a finite field $\mathbb{F}$ with a specific modulus $p$ (for example, in a prime finite field $\mathbb{Z}_p$), the conversion process typically involves reducing $n$ modulo $p$. That is, we find the remainder $r$ when $n$ is divided by $p$, such that $n = kp + r$, where $0\leq r
4 | - [Feedback Function](#feedback-function)
5 | - [Uniform Distribution](#uniform-distribution)
6 | - [Unit Test](#unit-test)
7 | - [Maximum Distance Seprarable Matrices(MDS)](#maximum-distance-seprarable-matricesmds)
8 | - [The inverse of a Cauchy matrix](#the-inverse-of-a-cauchy-matrix)
9 | - [Constants and Matrices](#constants-and-matrices)
10 | - [Galois Field (GF)](#galois-field-gf)
11 | - [Pasta Elliptic Curves](#pasta-elliptic-curves)
12 | - [Prime-order curves](#prime-order-curves)
13 | - [Relative struct or trait](#relative-struct-or-trait)
14 | - [1. Domain](#1-domain)
15 | - [2. Spec](#2-spec)
16 | - [3. Sponge](#3-sponge)
17 | - [4. Hash](#4-hash)
18 | ## Grain LFSR
19 | Some of the most common types of round constants are linear feedback shift registers (LFSRs). Grain LFSR (Linear Feedback Shift Register) is a critical component in the Grain family of stream ciphers, which includes Grain v0, Grain v1, and Grain-128 and has been historically used to generate round constants. This family of ciphers is designed for hardware applications and aims to maintain low hardware cost. The Grain LFSR, along with a Nonlinear Feedback Shift Register (NFSR) and a nonlinear filtering function, forms the basis of these ciphers.
20 |
21 | The LFSR in Grain provides a minimum period for the keystream, ensuring a certain level of security against attacks that exploit short periods. It operates on linear feedback principles, where the output is determined by a linear combination of previous bits in the register. This linear combination is defined by a feedback polynomial. The LFSR's role is to guarantee certain statistical properties and the non-repeating nature of the keystream.
22 |
23 | In contrast, the NFSR, which works alongside the LFSR, introduces nonlinearity into the system. This nonlinearity is crucial for cryptographic strength, as linear systems are generally easier to attack. The NFSR's output, along with the LFSR's output, is processed through a nonlinear filtering function to produce the final keystream bit.
24 |
25 | The specific construction and operation of the LFSR in Grain vary slightly between its different versions (v0, v1, and 128), primarily in terms of the length of the register and the feedback polynomial used. However, the underlying principle of providing a predictable, yet cryptographically secure, sequence of bits remains constant across all versions.
26 |
27 | `FieldType` Enum
28 | * Binary: Represents a binary field ( $\mathbb{F}_{2^n}$ ).
29 | * PrimeOrder: Represents a prime-order field ( $\mathbb{F}_p$ ).
30 | ```rust
31 | #[derive(Debug, Clone, Copy)]
32 | pub(super) enum FieldType {
33 | /// GF(2^n)
34 | #[allow(dead_code)]
35 | Binary,
36 | /// GF(p)
37 | PrimeOrder,
38 | }
39 | /// The tag method returns a numeric identifier for the field type (0 for binary, 1 for prime-order).
40 | impl FieldType {
41 | fn tag(&self) -> u8 {
42 | match self {
43 | FieldType::Binary => 0,
44 | FieldType::PrimeOrder => 1,
45 | }
46 | }
47 | }
48 | ```
49 | `SboxType` Enum
50 | Represents the type of S-box (non-linear transformation) used in Poseidon:
51 | * Pow: Represents the S-box type for raising elements to a power (e.g., $x^5$).
52 | * Inv: Represents the S-box type for inversion (e.g., $x^{-1}$).
53 | ```rust
54 | #[derive(Debug, Clone, Copy)]
55 | pub(super) enum SboxType {
56 | /// x^alpha
57 | Pow,
58 | /// x^(-1)
59 | #[allow(dead_code)]
60 | Inv,
61 | }
62 | /// The tag method returns a numeric identifier for the S-box type (0 for Pow, 1 for Inv).
63 | impl SboxType {
64 | fn tag(&self) -> u8 {
65 | match self {
66 | SboxType::Pow => 0,
67 | SboxType::Inv => 1,
68 | }
69 | }
70 | }
71 | ```
72 | `Grain` Struct
73 | ```rust
74 | /*
75 | (Rust syntax)
76 | Why Use pub(super)?
77 | 1. Encapsulation:
78 | It allows you to expose an item to the parent module while keeping it hidden from other sibling or unrelated modules.
79 | This is useful for maintaining a clean and controlled API surface.
80 |
81 | 2. Controlled Access:
82 | You might want to make certain items accessible to the parent module for internal use but not expose them to the entire crate or other modules.
83 |
84 | */
85 | use bitvec::prelude::*;
86 | pub(super) struct Grain {
87 | /// An 80-bit array representing the internal state of the LFSR.
88 | state: BitArr!(for 80, in u8, Msb0),
89 | /// Tracks the position of the next bit to be output.
90 | next_bit: usize,
91 | /// A marker for the associated finite field type.
92 | _field: PhantomData,
93 | }
94 | ```
95 | ### Feedback Function
96 | A feedback function in the context of a Linear Feedback Shift Register (LFSR) is a mathematical rule or operation that determines how the next bit(s) in the sequence are generated based on the current state of the LFSR. It is a critical component of the LFSR, as it defines how the internal state evolves over time.
97 |
98 | How the Feedback Function Works ?
99 | | Step | Description |
100 | |------|-------------|
101 | | 1. Input | a. The feedback function takes specific bits from the current state of the LFSR as input. b. These bits are selected based on a feedback polynomial, which specifies the positions of the bits to be used. |
102 | | 2. Operation | a. The selected bits are combined using a mathematical operation, typically XOR (exclusive OR). b. XOR is used because it is a simple, efficient, and non-linear operation that ensures the output bit depends on multiple bits of the state. |
103 | | 3. Output | a. The result of the XOR operation is the new bit that will be added to the LFSR's state. b. This new bit is then shifted into the register, replacing the oldest bit, and the process repeats. |
104 |
105 | Feedback Polynomial
106 | The feedback polynomial defines which bits of the LFSR's state are used in the feedback function. For example:
107 | * A feedback polynomial like ( $x^{62} + x^{51} + x^{38} + x^{23} + x^{13} + x^0$ ) means that the feedback function will use the bits at positions `62, 51, 38, 23, 13, and 0` in the state.
108 |
109 | In the code:
110 | ```rust
111 | new_bits |= ((self.state[i + 62]
112 | ^ self.state[i + 51]
113 | ^ self.state[i + 38]
114 | ^ self.state[i + 23]
115 | ^ self.state[i + 13]
116 | ^ self.state[i]) as u8)
117 | << i;
118 | ```
119 | * The feedback function computes the XOR of the bits at positions `i + 62, i + 51, i + 38, i + 23, i + 13, and i` in the state.
120 | * These positions correspond to the terms in the feedback polynomial.
121 |
122 | Purpose of the Feedback Function ?
123 | | Purpose | Description |
124 | |---------|-------------|
125 | | State Evolution | The feedback function ensures that the LFSR's state evolves in a deterministic but pseudo-random manner. |
126 | | Periodicity | A well-designed feedback function ensures that the LFSR produces a sequence with a long period (the number of steps before the sequence repeats).
Example of Periodicity: Suppose an LFSR has a 3-bit state and a feedback polynomial that produces a sequence like this: 101 → 010 → 001 → 100 → 110 → 011 → 111 → 101 (repeats) The period of this LFSR is 7, as it takes 7 steps to return to the initial state (101). |
127 | | Randomness | The feedback function introduces pseudo-randomness into the sequence, making it suitable for cryptographic and other applications. |
128 | | Security | In cryptographic applications, the feedback function ensures that the output sequence is unpredictable and resistant to attacks. |
129 |
130 | 1. What is a Period?
131 | * The period of an LFSR is the number of steps (or clock cycles) it takes for the LFSR to return to its initial state.
132 | * Once the LFSR returns to its initial state, it will start generating the same sequence of bits again.
133 | 2. Why is a Long Period Important?
134 | * A short period means the sequence repeats quickly, making it predictable and insecure for cryptographic purposes.
135 | * A long period ensures that the sequence appears random for a much longer time, making it harder to predict or exploit.
136 | 3. How is the Period Determined?
137 | * The period of an LFSR depends on its feedback polynomial and the initial state.
138 | * A well-designed feedback polynomial ensures that the LFSR achieves its maximum period, which is ( $2^n - 1$ ), where ( $n$ ) is the number of bits in the LFSR.
139 | * For example, an 80-bit LFSR with a well-designed feedback polynomial can produce a sequence of ( $2^{80} - 1$ ) bits before repeating.
140 |
141 | `Grain::load_next_8_bits` Method
142 | * Updates the LFSR state by generating 8 new bits based on a feedback function.
143 | * The feedback function uses XOR operations on specific bits of the state.
144 | ```rust
145 | impl Grain {
146 | fn load_next_8_bits(&mut self) {
147 | /*
148 | new_bits will store the 8 new bits generated by the feedback function.
149 | It is initialized to 0 and updated bit by bit in the loop.
150 | */
151 | let mut new_bits = 0u8;
152 | /*
153 | The new bit is computed as the XOR (^) of specific bits in the current state.
154 | These positions are chosen based on the feedback polynomial of the LFSR, which determines how the state evolves.
155 | The result of the XOR operation is cast to u8 and shifted left by i bits (<< i).
156 | This places the new bit in the correct position within the new_bits byte.
157 | The |= operator accumulates the new bit into new_bits.
158 | */
159 | for i in 0..8 {
160 | new_bits |= ((self.state[i + 62]
161 | ^ self.state[i + 51]
162 | ^ self.state[i + 38]
163 | ^ self.state[i + 23]
164 | ^ self.state[i + 13]
165 | ^ self.state[i]) as u8)
166 | << i;
167 | }
168 | /*
169 | Rotates the LFSR's state to the left by 8 bits.
170 | This effectively shifts the state, making room for the new bits to be added.
171 | The leftmost 8 bits are moved to the rightmost positions, and all other bits are shifted left by 8 positions.
172 |
173 | /// ## Examples
174 | ///
175 | /// let bits = bits![mut 0, 0, 1, 0, 1, 0];
176 | /// // split occurs here ^
177 | /// bits.rotate_left(2);
178 | /// assert_eq!(bits, bits![1, 0, 1, 0, 0, 0]);
179 | */
180 | self.state.rotate_left(8);
181 | /*
182 | Tracks the position of the next bit to be output from the LFSR.
183 | Decreases by 8 because 8 new bits have been generated and added to the state.
184 | */
185 | self.next_bit -= 8;
186 | /*
187 | Purpose:
188 |
189 | Inserts the 8 new bits (new_bits) into the appropriate positions in the state.
190 |
191 | How It Works:
192 |
193 | Extract Each Bit:
194 |
195 | (new_bits >> i) & 1 extracts the i-th bit of new_bits.
196 | This is done by shifting new_bits right by i positions and masking with 1 to isolate the least significant bit.
197 |
198 | Update the State:
199 |
200 | self.state.get_mut(self.next_bit + i).unwrap() accesses the i-th position in the state (starting from next_bit).
201 | The extracted bit is stored in this position.
202 |
203 | Example:
204 |
205 | If new_bits = 0b10101010:
206 | The loop sets the bits at positions next_bit to next_bit + 7 in the state to [0, 1, 0, 1, 0, 1, 0, 1].
207 | */
208 | for i in 0..8 {
209 | *self.state.get_mut(self.next_bit + i).unwrap() = (new_bits >> i) & 1 != 0;
210 | }
211 | }
212 | }
213 | ```
214 |
215 | `Grain::new` Method
216 | | Parameters | Description |
217 | |------------|-------------|
218 | | `sbox: SboxType`| Specifies the type of S-box (non-linear transformation) used in the Poseidon permutation. |
219 | | `t: u16` | Represents the width of the Poseidon state, i.e., the number of field elements in the state array. |
220 | | `r_f: u16` | a. Specifies the number of full rounds in the Poseidon permutation. b. In a full round, the S-box is applied to all elements of the state, followed by the MDS matrix for mixing. |
221 | | `r_p: u16` | a. Specifies the number of partial rounds in the Poseidon permutation. b. In a partial round, the S-box is applied to only one element of the state (usually the first element), followed by the MDS matrix. |
222 | ```rust
223 | use group::ff::{Field, FromUniformBytes, PrimeField};
224 | const STATE: usize = 80;
225 |
226 | impl Grain {
227 | pub(super) fn new(sbox: SboxType, t: u16, r_f: u16, r_p: u16) -> Self {
228 | // Initialize the LFSR state.
229 | /*
230 | 1. LFSR State Initialization
231 |
232 | bitarr!:
233 |
234 | Creates a bit array to represent the internal state of the LFSR.
235 | The state is initialized with all bits set to 1.
236 |
237 | Msb0:
238 |
239 | Specifies that the most significant bit (MSB) is stored first in the bit array.
240 | */
241 | let mut state = bitarr![u8, Msb0; 1; STATE];
242 | /*
243 | 2. Setting Bits in the LFSR State
244 |
245 | Purpose:
246 |
247 | Encodes specific parameters into the LFSR state by setting bits at specific positions.
248 |
249 | How It Works:
250 |
251 | * offset: The starting position in the bit array where the bits will be set.
252 | * len: The number of bits to set.
253 | * value: The value to encode into the bit array.
254 | * The loop iterates over the bits of value and sets them in reverse order (MSB-first) in the bit array.
255 | */
256 | let mut set_bits = |offset: usize, len, value| {
257 | // Poseidon reference impl sets initial state bits in MSB order.
258 | for i in 0..len {
259 | *state.get_mut(offset + len - 1 - i).unwrap() = (value >> i) & 1 != 0;
260 | }
261 | };
262 |
263 | /*
264 | 4. Encoding Parameters into the LFSR State
265 |
266 | FieldType::PrimeOrder.tag():
267 |
268 | Encodes the field type (e.g., prime-order field ( \mathbb{F}_p )) into the first 2 bits.
269 |
270 | sbox.tag():
271 |
272 | Encodes the S-box type (e.g., (x^5) or (x^{-1})) into the next 4 bits.
273 |
274 | F::NUM_BITS:
275 |
276 | Encodes the number of bits in the finite field (F) (e.g., 255 bits for ( \mathbb{F}_p )) into 12 bits.
277 |
278 | t:
279 |
280 | Encodes the width of the Poseidon state into 12 bits.
281 |
282 | r_f and r_p:
283 |
284 | Encode the number of full and partial rounds into 10 bits each.
285 | */
286 | set_bits(0, 2, FieldType::PrimeOrder.tag() as u16);
287 | set_bits(2, 4, sbox.tag() as u16);
288 | /*
289 | /// How many bits are needed to represent an element of this field.
290 | const NUM_BITS: u32;
291 | */
292 | set_bits(6, 12, F::NUM_BITS as u16);
293 | set_bits(18, 12, t);
294 | set_bits(30, 10, r_f);
295 | set_bits(40, 10, r_p);
296 |
297 | /*
298 | 5. Discarding Initial Bits
299 |
300 | Purpose:
301 |
302 | Discards the first 160 bits (20 bytes) of the LFSR output to ensure that the initial state does not leak information about the parameters.
303 |
304 | How It Works:
305 |
306 | The load_next_8_bits method generates 8 bits of output from the LFSR.
307 | This process is repeated 20 times to discard the first 160 bits.
308 | */
309 | let mut grain = Grain {
310 | state,
311 | /// Tracks the position of the next bit to be output.
312 | next_bit: STATE,
313 | _field: PhantomData::default(),
314 | };
315 |
316 | // Discard the first 160 bits.
317 | for _ in 0..20 {
318 | /*
319 | Why Reset next_bit to STATE? (TODO:)
320 |
321 | After calling grain.load_next_8_bits(), the internal state is updated with new bits, but the next_bit field must be reset to indicate that the entire state is available for reading again.
322 | By setting next_bit = STATE, the code ensures that the next call to get_next_bit will correctly start reading from the beginning of the updated state.
323 | */
324 | grain.load_next_8_bits();
325 | grain.next_bit = STATE;
326 | }
327 |
328 | grain
329 | }
330 | }
331 | ```
332 |
333 | `Grain::get_next_bit` Method
334 | * Outputs the next bit from the LFSR.
335 | * If all bits in the current state have been consumed, it generates `8` new bits using `load_next_8_bits`.
336 | ```rust
337 | impl Grain {
338 | fn get_next_bit(&mut self) -> bool {
339 | /// const STATE: usize = 80;
340 | if self.next_bit == STATE {
341 | self.load_next_8_bits();
342 | }
343 | let ret = self.state[self.next_bit];
344 | self.next_bit += 1;
345 | ret
346 | }
347 | }
348 | ```
349 |
350 | `Grain::next_field_element` Method
351 | * Generates the next field element from the LFSR output.
352 | * Uses rejection sampling to ensure the generated value is valid in the field.
353 |
354 | Rejection Sampling
355 | * Rejection sampling is a technique used to generate valid samples from a specific distribution.
356 | * In this case, the method ensures that the generated byte array represents a valid field element by discarding invalid values.
357 |
358 | MSB vs. LSB Order
359 | * MSB Order:
360 | * Bits are interpreted starting from the most significant bit (leftmost).
361 | * This is the convention used in the Poseidon reference implementation.
362 | * LSB Order:
363 | * Bits are interpreted starting from the least significant bit (rightmost).
364 | * The comment in the code explains that the implementation follows the Poseidon reference's MSB order for consistency.
365 |
366 | ```rust
367 | impl Grain {
368 | /// Returns the next field element from this Grain instantiation.
369 | /// TODO: Currently, halo2 only call this function in test.
370 | pub(super) fn next_field_element(&mut self) -> F {
371 | // Loop until we get an element in the field.
372 | loop {
373 | /*
374 | 1. Initialize a Default Representation
375 | Purpose:
376 | F::Repr is the internal representation of a field element in ( F ), typically a byte array.
377 | F::Repr::default() initializes this byte array to all zeros.
378 | */
379 | let mut bytes = F::Repr::default();
380 |
381 | // Poseidon reference impl interprets the bits as a repr in MSB order, because
382 | // it's easy to do that in Python. Meanwhile, our field elements all use LSB
383 | // order. There's little motivation to diverge from the reference impl; these
384 | // are all constants, so we aren't introducing big-endianness into the rest of
385 | // the circuit (assuming unkeyed Poseidon, but we probably wouldn't want to
386 | // implement Grain inside a circuit, so we'd use a different round constant
387 | // derivation function there).
388 |
389 | /*
390 | 2. Interpret Bits in MSB Order
391 |
392 | Purpose:
393 |
394 | This loop fills the bytes array with bits generated by the LFSR, interpreting them in MSB (Most Significant Bit) order.
395 | How It Works:
396 |
397 | self.take(F::NUM_BITS as usize):
398 | This retrieves the next F::NUM_BITS bits from the LFSR, where F::NUM_BITS is the number of bits required to represent a field element in ( F ).
399 |
400 | enumerate():
401 | Iterates over the bits, providing both the index (i) and the bit value
402 | (bit).
403 |
404 | Reverse Indexing:
405 | let i = F::NUM_BITS as usize - 1 - i; reverses the bit order to match the MSB-first convention.
406 |
407 | Set the Bit:
408 | view[i / 8] accesses the byte in the bytes array corresponding to the bit's position.
409 | 1 << (i % 8) shifts 1 to the correct bit position within the byte.
410 | |= sets the bit if bit is true.
411 |
412 | Example:
413 |
414 | If F::NUM_BITS = 8 and the LFSR produces 0b10101010:
415 | The loop sets the bits in bytes as [0b10101010].
416 | */
417 | let view = bytes.as_mut();
418 | /*
419 | /// How many bits are needed to represent an element of this field.
420 | const NUM_BITS: u32;
421 | */
422 | for (i, bit) in self.take(F::NUM_BITS as usize).enumerate() {
423 | // If we diverged from the reference impl and interpreted the bits in LSB
424 | // order, we would remove this line.
425 | let i = F::NUM_BITS as usize - 1 - i;
426 |
427 | view[i / 8] |= if bit { 1 << (i % 8) } else { 0 };
428 | }
429 | /*
430 | 3. Convert Bytes to a Field Element
431 |
432 | Purpose:
433 |
434 | Converts the bytes array into a field element ( f ) in ( F ).
435 | The from_repr_vartime method attempts to interpret the byte array as a valid field element.
436 |
437 | How It Works:
438 |
439 | F::from_repr_vartime(bytes):
440 | Tries to construct a field element from the byte array.
441 | Returns Some(f) if the byte array represents a valid field element.
442 | Returns None if the byte array is invalid (e.g., if it represents a value greater than or equal to the field's modulus).
443 |
444 | Rejection Sampling:
445 |
446 | If the byte array is invalid, the loop continues, and new bits are generated from the LFSR.
447 | This ensures that only valid field elements are returned.
448 | */
449 | if let Some(f) = F::from_repr_vartime(bytes) {
450 | break f;
451 | }
452 | }
453 | }
454 | }
455 | ```
456 | Example Execution
457 |
458 | Setup
459 | * ( F ) is a finite field with ( F::NUM_BITS = 8 ).
460 | * The LFSR produces the sequence: `0b10101010`.
461 |
462 | Steps
463 | 1. Initialize bytes:
464 | * bytes = `[0b00000000]`.
465 | 2. Fill bytes:
466 | * The loop processes the bits in MSB order:
467 | ![alt text]()
468 | 3. Convert to Field Element:
469 | * F::from_repr_vartime([0b10101010]) succeeds and returns a valid field element.
470 | 4. Return the Field Element:
471 | * The method returns the field element.
472 |
473 | `Grain::next_field_element_without_rejection` Method
474 | This method generates the next field element from the Grain LFSR (Linear Feedback Shift Register) output without using rejection sampling. It directly converts the bits generated by the LFSR into a field element, even if the resulting value is not uniformly distributed within the field. This approach is used when uniform randomness is not required for security.
475 | * Generates the next field element without rejection sampling.
476 | * This is used when uniform randomness is not required for security.
477 | ```rust
478 | impl> Grain {
479 | /// Returns the next field element from this Grain instantiation, without using
480 | /// rejection sampling.
481 | pub(super) fn next_field_element_without_rejection(&mut self) -> F {
482 | /*
483 | 1. Initialize a 64-byte Array
484 | Purpose:
485 | Creates a 64-byte array (bytes) to store the bits generated by the LFSR.
486 | This array will later be used to construct a field element.
487 | */
488 | let mut bytes = [0u8; 64];
489 |
490 | // Poseidon reference impl interprets the bits as a repr in MSB order, because
491 | // it's easy to do that in Python. Additionally, it does not use rejection
492 | // sampling in cases where the constants don't specifically need to be uniformly
493 | // random for security. We do not provide APIs that take a field-element-sized
494 | // array and reduce it modulo the field order, because those are unsafe APIs to
495 | // offer generally (accidentally using them can lead to divergence in consensus
496 | // systems due to not rejecting canonical forms).
497 | //
498 | // Given that we don't want to diverge from the reference implementation, we hack
499 | // around this restriction by serializing the bits into a 64-byte array and then
500 | // calling F::from_bytes_wide. PLEASE DO NOT COPY THIS INTO YOUR OWN CODE!
501 | let view = bytes.as_mut();
502 | for (i, bit) in self.take(F::NUM_BITS as usize).enumerate() {
503 | // If we diverged from the reference impl and interpreted the bits in LSB
504 | // order, we would remove this line.
505 | let i = F::NUM_BITS as usize - 1 - i;
506 |
507 | view[i / 8] |= if bit { 1 << (i % 8) } else { 0 };
508 | }
509 | /*
510 | 3. Convert Bytes to a Field Element
511 |
512 | Purpose:
513 |
514 | Converts the 64-byte array (bytes) into a field element ( F ) using the from_uniform_bytes method.
515 |
516 | How It Works:
517 |
518 | F::from_uniform_bytes(&bytes):
519 | Interprets the 64-byte array as a large integer.
520 | Reduces this integer modulo the field's prime modulus ( p ) to produce a valid field element.
521 |
522 | This method does not perform rejection sampling, so the resulting field element may not be uniformly distributed.
523 |
524 | /// Returns a field element that is congruent to the provided little endian unsigned
525 | /// byte representation of an integer.
526 | fn from_uniform_bytes(bytes: &[u8; N]) -> Self;
527 | */
528 | F::from_uniform_bytes(&bytes)
529 | }
530 | }
531 | ```
532 |
533 | The difference between `next_field_element` and `next_field_element_without_rejection`
534 | | Feature | `next_field_element_without_rejection` | `next_field_element` |
535 | |---------|----------------------------------------|----------------------|
536 | | Rejection Sampling | No | Yes |
537 | | Uniform Distribution | Not guaranteed | Guaranteed |
538 | | Performance | Faster (no rejection loop) | Slower (due to rejection loop) |
539 | | Use Case | Non-critical randomness (e.g., constants) | Critical randomness (e.g., cryptography) |
540 | | Method for Conversion | `F::from_uniform_bytes` | `F::from_repr_vartime` |
541 | | Security | Lower (non-uniform values may be insecure) | Higher (uniform values ensure security) |
542 |
543 | `Iterator` for Grain
544 | This code implements the `Iterator` trait for the Grain struct, allowing it to produce a sequence of boolean values (`true` or `false`) based on the internal state of the Grain LFSR (Linear Feedback Shift Register). The next method defines how the iterator generates the next value in the sequence.
545 |
546 | * The Iterator implementation for Grain produces a sequence of pseudo-random bits using a self-shrinking mechanism.
547 | * It ensures that only bits following a 1 are output, improving randomness.
548 | * This is a critical component for cryptographic applications, particularly in the context of the Poseidon hash function.
549 |
550 | What is Self-Shrinking ?
551 | * Self-shrinking is a mechanism used in pseudo-random bit generators, such as the Grain LFSR (Linear Feedback Shift Register), to improve the randomness of the output sequence. It works by evaluating bits in pairs and selectively discarding some bits based on the value of the first bit in each pair.
552 |
553 | How Self-Shrinking Works ?
554 | 1. Evaluate Bits in Pairs:
555 | * The generator produces bits sequentially.
556 | * For every two bits generated (a pair), the first bit determines what happens to the second bit:
557 | * If the first bit is 1, the second bit is output.
558 | * If the first bit is 0, the second bit is discarded.
559 | 2. Repeat the Process:
560 | * The generator continues to evaluate the next pair of bits and applies the same rule.
561 | 3. Effect:
562 | * The output rate is reduced (not all bits are used), but the randomness of the output is improved because the process removes predictable patterns.
563 |
564 | ```rust
565 | impl Iterator for Grain {
566 | /*
567 | Specifies that the iterator will produce items of type bool.
568 | Each item represents a single bit (true for 1, false for 0) generated by the Grain LFSR.
569 | */
570 | type Item = bool;
571 |
572 | fn next(&mut self) -> Option {
573 | // Evaluate bits in pairs:
574 | // - If the first bit is a 1, output the second bit.
575 | // - If the first bit is a 0, discard the second bit.
576 | while !self.get_next_bit() {
577 | self.get_next_bit();
578 | }
579 | Some(self.get_next_bit())
580 | }
581 | }
582 | ```
583 |
584 | #### Uniform Distribution
585 | Uniform distribution refers to the property that all possible values in a given range or set are equally likely to occur. In the context of cryptography and finite fields, a uniformly distributed field element means that every valid element in the field has an equal probability of being generated.
586 |
587 | Why Uniform Distribution Matters ?
588 | 1. Cryptographic Security:
589 | * Uniform distribution ensures that the generated values are unpredictable and do not exhibit patterns that attackers could exploit.
590 | * For example, in cryptographic protocols like zero-knowledge proofs or digital signatures, non-uniform values could leak information about private keys or other sensitive data.
591 | 2. Fairness:
592 | * Uniform distribution guarantees that no specific value is favored over others, which is critical for ensuring fairness in applications like random number generation or lotteries.
593 | 3. Avoiding Bias:
594 | * Non-uniform distributions introduce bias, meaning some values are more likely to occur than others. This can weaken the security of cryptographic systems.
595 |
596 | #### Unit Test
597 | The provided test code is a unit test for the Grain struct, which is part of the Poseidon hash function implementation. The test verifies that the Grain LFSR (Linear Feedback Shift Register) can be initialized and used to generate a field element.
598 |
599 | ```rust
600 | /*
601 | The #[cfg(test)] attribute indicates that this module is only compiled and run during testing.
602 | */
603 | #[cfg(test)]
604 | mod tests {
605 | use pasta_curves::Fp;
606 |
607 | use super::{Grain, SboxType};
608 | /*
609 | Marks this function as a test case.
610 | The test framework will automatically run this function during testing.
611 | */
612 | #[test]
613 | fn grain() {
614 | /*
615 | Initializes a new Grain LFSR instance with the following parameters:
616 | SboxType::Pow: Specifies that the S-box used in Poseidon is ( x^\alpha ) (e.g., ( x^5 )).
617 | 3: The state width of the Poseidon permutation (number of elements in the state).
618 | 8: The number of full rounds in the Poseidon permutation.
619 | 56: The number of partial rounds in the Poseidon permutation.
620 | */
621 | let mut grain = Grain::::new(SboxType::Pow, 3, 8, 56);
622 | let _f = grain.next_field_element();
623 | }
624 | }
625 | ```
626 |
627 | ## Maximum Distance Seprarable Matrices(MDS)
628 | * Maximum Distance Seprarable Matrices(MDS) are commonly used in the linear layer of hash functions. It's used in the linear mixing step of cipher algorithms to ensure a high level of diffusion, meaning that a change in a single input bit will affect many output bits. This contributes to the cipher's security by making it resistant to certain types of cryptanalytic attacks. MDS matrices are designed so that any subset of rows (or columns) forms a linearly independent set, maximizing the spread of input differences across the cipher's state.
629 | * The mathematical notion of linear independence refers to a set of vectors in which no vector can be represented as a linear combination of the others. This means that each vector adds a new dimension to the space spanned by the set. For example, in a three-dimensional space, three vectors are linearly independent if none of them lies in the plane formed by the other two. This concept is crucial in many areas of mathematics and is fundamental in understanding the behavior of systems of linear equations and transformations.
630 |
631 | This code implements the generate_mds function, which generates a Maximum Distance Separable (MDS) matrix and its inverse for use in the Poseidon hash function. The MDS matrix is a key component of the Poseidon permutation, ensuring diffusion (mixing) of the state elements.
632 | ```rust
633 | /*
634 | 1. Function Signature
635 |
636 | F: Represents the finite field type. It must implement:
637 | FromUniformBytes<64>: Allows generating field elements from uniform bytes.
638 | Ord: Ensures field elements can be ordered (used for uniqueness checks).
639 | T: The size of the MDS matrix (a ( T * T ) matrix).
640 | grain: A mutable reference to a Grain instance, which generates pseudo-random field elements.
641 | select: A counter used to skip a fixed number of MDS matrices before selecting one (used for ensuring security). TODO:
642 | Return Value: A tuple containing:
643 | The generated MDS matrix (Mds).
644 | The inverse of the MDS matrix (Mds).
645 | */
646 | pub(super) fn generate_mds + Ord, const T: usize>(
647 | grain: &mut Grain,
648 | mut select: usize,
649 | ) -> (Mds, Mds) {
650 | let (xs, ys, mds) = loop {
651 | /*
652 | 2. Generating Unique Field Elements
653 |
654 | Purpose: Generates two arrays of ( T ) unique field elements (xs and ys).
655 |
656 | Steps:
657 | Generate ( 2T ) field elements using the Grain LFSR.
658 | Check if all elements are unique:
659 | Clone and sort the elements.
660 | Remove duplicates using dedup.
661 | If the number of unique elements matches the original count, split the array into two halves:
662 | xs: The first ( T ) elements.
663 | ys: The second ( T ) elements.
664 | */
665 | // Generate two [F; T] arrays of unique field elements.
666 | let (xs, ys) = loop {
667 | let mut vals: Vec<_> = (0..2 * T)
668 | .map(|_| grain.next_field_element_without_rejection())
669 | .collect();
670 |
671 | // Check that we have unique field elements.
672 | let mut unique = vals.clone();
673 | unique.sort_unstable();
674 | unique.dedup();
675 | if vals.len() == unique.len() {
676 | let rhs = vals.split_off(T);
677 | break (vals, rhs);
678 | }
679 | };
680 |
681 | /*
682 | 3. Ensuring MDS Security
683 |
684 | Purpose: Ensures that the generated MDS matrix satisfies cryptographic security requirements.
685 |
686 | How It Works:
687 | The select counter determines how many MDS matrices to skip before selecting one.
688 | This is based on pre-determined security checks performed out-of-band (e.g., using a Sage reference implementation).
689 | */
690 |
691 | // We need to ensure that the MDS is secure. Instead of checking the MDS against
692 | // the relevant algorithms directly, we witness a fixed number of MDS matrices
693 | // that we need to sample from the given Grain state before obtaining a secure
694 | // matrix. This can be determined out-of-band via the reference implementation in
695 | // Sage. ([pasta-hadeshash](https://github.com/daira/pasta-hadeshash))
696 | if select != 0 {
697 | select -= 1;
698 | continue;
699 | }
700 |
701 | /*
702 | 4. Generating the MDS Matrix
703 |
704 | Purpose: Constructs a ( T \times T ) Cauchy matrix, which serves as the MDS matrix.
705 |
706 | Steps:
707 | Compute each element ( a_{ij} ) of the matrix as: [ a_{ij} = \frac{1}{x_i + y_j}, \quad x_i + y_j \neq 0 ]
708 | Ensure that ( x_i + y_j \neq 0 ) using assert!.
709 | Compute the multiplicative inverse of ( x_i + y_j ) to populate the matrix.
710 | */
711 |
712 | // Generate a Cauchy matrix, with elements a_ij in the form:
713 | // a_ij = 1/(x_i + y_j); x_i + y_j != 0
714 | //
715 | // It would be much easier to use the alternate definition:
716 | // a_ij = 1/(x_i - y_j); x_i - y_j != 0
717 | //
718 | // These are clearly equivalent on `y <- -y`, but it is easier to work with the
719 | // negative formulation, because ensuring that xs ∪ ys is unique implies that
720 | // x_i - y_j != 0 by construction (whereas the positive case does not hold). It
721 | // also makes computation of the matrix inverse simpler below (the theorem used
722 | // was formulated for the negative definition).
723 | //
724 | // However, the Poseidon paper and reference impl use the positive formulation,
725 | // and we want to rely on the reference impl for MDS security, so we use the same
726 | // formulation.
727 | let mut mds = [[F::ZERO; T]; T];
728 | /*
729 | The #[allow(clippy::needless_range_loop)] attribute in Rust is used to suppress a specific Clippy lint warning. In this case, it suppresses the needless_range_loop lint, which is triggered when a for loop iterates over a range (e.g., 0..n) but could be rewritten to iterate directly over an iterator or collection.
730 | */
731 | #[allow(clippy::needless_range_loop)]
732 | for i in 0..T {
733 | for j in 0..T {
734 | let sum = xs[i] + ys[j];
735 | // We leverage the secure MDS selection counter to also check this.
736 | assert!(!sum.is_zero_vartime());
737 | mds[i][j] = sum.invert().unwrap();
738 | }
739 | }
740 |
741 | break (xs, ys, mds);
742 | };
743 |
744 | /*
745 | 5. Computing the Inverse of the MDS Matrix
746 |
747 | This code computes the inverse of an MDS (Maximum Distance Separable) matrix using the mathematical properties of Cauchy matrices. The inverse is calculated based on Lagrange polynomials and the structure of the Cauchy matrix.
748 |
749 | Purpose: Computes the inverse of the MDS matrix using a formula for the inverse of a Cauchy matrix.
750 |
751 | Steps:
752 | Define a helper function l to compute Lagrange polynomials for xs and ys.
753 | Negate the ys array to adapt the formula for the positive Cauchy matrix.
754 | Compute each element ( b_{ij} ) of the inverse matrix as: [ b_{ij} = (x_j - (-y_i)) A_j(-y_i) B_i(x_j) ] where ( A_j(x) ) and ( B_i(x) ) are Lagrange polynomials.
755 | */
756 |
757 | // Compute the inverse. All square Cauchy matrices have a non-zero determinant and
758 | // thus are invertible. The inverse for a Cauchy matrix of the form:
759 | //
760 | // a_ij = 1/(x_i - y_j); x_i - y_j != 0
761 | //
762 | // has elements b_ij given by:
763 | //
764 | // b_ij = (x_j - y_i) A_j(y_i) B_i(x_j) (Schechter 1959, Theorem 1)
765 | //
766 | // where A_i(x) and B_i(x) are the Lagrange polynomials for xs and ys respectively.
767 | //
768 | // We adapt this to the positive Cauchy formulation by negating ys.
769 |
770 | /*
771 | Purpose: Initializes the inverse MDS matrix (mds_inv) as a ( T \times T ) matrix filled with zeros.
772 | Type: Each element of the matrix is a field element of type F.
773 | */
774 | let mut mds_inv = [[F::ZERO; T]; T];
775 | /*
776 | Purpose: Computes the Lagrange polynomial ( A_j(x) ) or ( B_i(x) ) for the given set of points xs.
777 |
778 | Parameters:
779 | xs: The set of points (either xs or ys).
780 | j: The index of the point for which the Lagrange polynomial is being computed.
781 | x: The value at which the polynomial is evaluated.
782 |
783 | Steps:
784 | Exclude the j-th Point:
785 | The Lagrange polynomial excludes the j-th point from the computation.
786 | Compute the Product:
787 | For each other point ( x_m ), compute: [ \frac{x - x_m}{x_j - x_m} ]
788 | Multiply these terms together to compute the Lagrange polynomial.
789 | Invert the Denominator:
790 | The denominator ( x_j - x_m ) is inverted using denominator.invert().unwrap().
791 | This is safe because the elements of xs are guaranteed to be distinct.
792 |
793 | 1. xs
794 | Type: &[F] (a slice of field elements).
795 | Purpose: Represents the set of points (either xs or ys) used to compute the Lagrange polynomial.
796 | Role: The iter() method creates an iterator over the elements of xs.
797 |
798 | 2. .enumerate()
799 | Purpose: Converts the iterator into an iterator of (index, value) pairs.
800 | Role: Allows access to both the index (m) and the value (x_m) of each element in xs.
801 |
802 | 3. fold(F::ONE, |acc, (m, x_m)| { ... })
803 | Purpose: Aggregates a value by iterating over the elements of xs and applying a closure.
804 | Parameters:
805 | F::ONE: The initial value of the accumulator (acc). It represents the multiplicative identity in the field ( \mathbb{F}_p ).
806 | |acc, (m, x_m)| { ... }: The closure that updates the accumulator for each element in xs.
807 |
808 | 4. acc
809 | Type: F (a field element).
810 | Purpose: Represents the accumulated product of terms in the Lagrange polynomial.
811 | Role: Starts as F::ONE and is updated in each iteration by multiplying with the current term.
812 |
813 | 5. (m, x_m)
814 | m: The index of the current element in xs.
815 | x_m: The value of the current element in xs.
816 | Purpose: Used to compute the Lagrange polynomial by iterating over all elements of xs.
817 |
818 | 6. j
819 | Type: usize (an index).
820 | Purpose: Represents the index of the point being excluded from the Lagrange polynomial computation.
821 | Role: Ensures that the j-th point is skipped in the computation.
822 |
823 | 7. x
824 | Type: F (a field element).
825 | Purpose: The value at which the Lagrange polynomial is being evaluated.
826 | Role: Used in the numerator of the Lagrange polynomial.
827 |
828 | How It Works ?
829 |
830 | Iterate Over xs:
831 | The enumerate() method provides both the index (m) and the value (x_m) of each element in xs.
832 |
833 | Skip the j-th Point:
834 | The if m == j condition ensures that the j-th point is excluded from the computation.
835 |
836 | Compute the Term:
837 | For each other point ( x_m ), compute: [ \frac{x - x_m}{x_j - x_m} ]
838 | Numerator: ( x - x_m ).
839 | Denominator: ( x_j - x_m ), which is inverted using denominator.invert().unwrap().
840 |
841 | Update the Accumulator:
842 | Multiply the current accumulator (acc) with the computed term.
843 |
844 | Return the Final Product:
845 | After iterating over all points in xs (except the j-th point), the result is the Lagrange polynomial ( A_j(x) ).
846 | */
847 | let l = |xs: &[F], j, x: F| {
848 | let x_j = xs[j];
849 | xs.iter().enumerate().fold(F::ONE, |acc, (m, x_m)| {
850 | if m == j {
851 | acc
852 | } else {
853 | // We hard-code the type, to avoid spurious "cannot infer type" rustc errors.
854 | let denominator: F = x_j - x_m;
855 |
856 | // We can invert freely; by construction, the elements of xs are distinct.
857 | let denominator_inverted: F = denominator.invert().unwrap();
858 |
859 | acc * (x - x_m) * denominator_inverted
860 | }
861 | })
862 | };
863 | let neg_ys: Vec<_> = ys.iter().map(|y| -*y).collect();
864 | for i in 0..T {
865 | for j in 0..T {
866 | mds_inv[i][j] = (xs[j] - neg_ys[i]) * l(&xs, j, neg_ys[i]) * l(&neg_ys, i, xs[j]);
867 | }
868 | }
869 |
870 | (mds, mds_inv)
871 | }
872 | ```
873 |
874 | ### The inverse of a Cauchy matrix
875 |
876 | [Cauchy matrix](https://en.wikipedia.org/wiki/Cauchy_matrix)
877 |
878 | The reason for multiplying by two Lagrange polynomials (l(&xs, j, $neg_{ys}[i]$) and l(&$neg_{ys}$, i, xs[j])) is due to the mathematical properties of the inverse of a Cauchy matrix. Specifically, the formula for the inverse of a Cauchy matrix involves two Lagrange polynomials to account for the relationships between the rows and columns of the matrix.
879 |
880 | 1. Cauchy Matrix and Its Inverse
881 | A Cauchy matrix is defined as:
882 |
883 | $[ a_{ij} = \frac{1}{x_i - y_j}, \quad x_i - y_j \neq 0 ]$
884 |
885 | The inverse of a Cauchy matrix is given by:
886 |
887 | $[ b_{ij} = (x_j - y_i) \cdot A_j(y_i) \cdot B_i(x_j) ]$
888 |
889 | Where:
890 |
891 | * ($ A_j(y_i)$ ) is the Lagrange polynomial for the ( $j$ )-th column, evaluated at ( $y_i$ ).
892 | * ( $B_i(x_j)$ ) is the Lagrange polynomial for the ( $i$ )-th row, evaluated at ( $x_j$ ).
893 |
894 | 2. Why Two Lagrange Polynomials?
895 | * First Lagrange Polynomial: ( l(&xs, j, $neg_{ys}[i]$) )
896 | * This corresponds to ( $A_j(y_i)$ ), the Lagrange polynomial for the ( $j$ )-th column.
897 | * It ensures that the inverse matrix element ( $b_{ij}$ ) correctly accounts for the relationship between the ( $j$ )-th column of the original matrix and the ( $i$ )-th row of the inverse matrix.
898 | * Second Lagrange Polynomial: ( l(&$neg_{ys}$, i, xs[j]) )
899 | * This corresponds to ( $B_i(x_j)$ ), the Lagrange polynomial for the ( $i$ )-th row.
900 | * It ensures that the inverse matrix element ( $b_{ij}$ ) correctly accounts for the relationship between the ( $i$ )-th row of the original matrix and the ( $j$ )-th column of the inverse matrix.
901 | * Combined Effect
902 | * The product of the two Lagrange polynomials ensures that the inverse matrix element ( $b_{ij}$ ) satisfies the mathematical properties of the inverse of a Cauchy matrix.
903 | * This guarantees that the matrix multiplication of the original matrix and its inverse results in the identity matrix.
904 |
905 | ## Constants and Matrices
906 | There are two kinds of constants for the Poseidon in halo2.
907 | | Type | Description | File |
908 | |------|-------------|-------------|
909 | | pallas::Base | Constants for using Poseidon with the Pallas field. | halo2_poseidon/src/fp.rs |
910 | | vesta::Base | Constants for using Poseidon with the Vesta field. | halo2_poseidon/src/fp.rs |
911 |
912 | The constants and matrices in this code are precomputed and hardcoded for efficiency. Take the pallas::Base type as an example, they include:
913 | | Type | Description | Purpose | Structure in halo2 | Representation in halo2 |
914 | |------|-------------|-------------|-------------|-------------|
915 | | Round Constants (ROUND_CONSTANTS) | Used in each round of the Poseidon permutation to ensure cryptographic security. | a. Round constants are added to the state in each round of the Poseidon permutation. b. They ensure that the permutation is unique for each round, preventing attacks that exploit symmetry or predictability in the permutation. | a 2D array with 64 rows and 3 columns: a. Each row corresponds to a single round of the Poseidon permutation. b. Each column corresponds to one element of the state (the state has 3 elements in this configuration). | Each constant is represented as a `pallas::Base` element, which is an element of the finite field over which the Pallas curve is defined. |
916 | | MDS Matrix (MDS) | A mixing matrix that ensures diffusion across the state. | a. The Maximum Distance Separable (MDS) matrix is a linear transformation applied to the state in each round of the Poseidon permutation. b. It ensures diffusion, meaning that every element of the state influences every other element. This is critical for cryptographic security. | a. a 3x3 matrix, where each element is a `pallas::Base` field element. b. The matrix is applied to the state using matrix multiplication: new_state[i] = $\sum_{j=0}^{2} \text{MDS}[i][j] \cdot \text{state}[j]$ | a. The MDS matrix is carefully chosen to maximize diffusion while being efficient to compute. b. It is invertible, which ensures that the Poseidon permutation is reversible (a requirement for cryptographic permutations). |
917 | | Inverse MDS Matrix (MDS_INV) | The inverse of the MDS matrix, which may be used in certain applications. | a. The inverse MDS matrix is the inverse of the MDS matrix. b. It may be used in certain applications where the inverse transformation is required, such as in cryptographic protocols that involve reversing the Poseidon permutation. | a. a 3x3 matrix, with each element being a `pallas::Base` field element. b. It satisfies the property: $[ MDS_INV \cdot MDS = I ]$ where ( $I$ ) is the identity matrix. |
918 |
919 | How to create these constants and matrices ?
920 | * The constants in this file can be reproduced using a Sage script provided in the [pasta-hadeshash](https://github.com/daira/pasta-hadeshash) repository. The script generates the round constants and MDS matrix based on the Posei don specification.
921 | ```bash
922 | $ sage generate_parameters_grain.sage 1 0 255 3 8 56 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001
923 | ```
924 | Parameters Introduction ([parameters](https://github.com/daira/pasta-hadeshash/blob/master/code/parameters.txt)
925 | | Parameter | Description |
926 | |-----------|-------------|
927 | | 1 - $GF(p)$ | a. GF(p) refers to a Galois Field of prime order p. b. Specifies that the Poseidon hash function will operate over a prime field ( $\mathbb{F}_p$ ). c. ( $\mathbb{F}_p$ ) is a finite field where arithmetic is performed modulo a prime number ( $p$ ). |
928 | | 0 - $x^\alpha$ | a. Specifies the S-box used in the Poseidon permutation. b. $x^\alpha$ is the non-linear transformation applied to elements of the state during the permutation. c. In this case, 0 indicates that the S-box is ( x^5 ), which is efficient in finite fields and provides strong non-linearity. |
929 | | 255 - Field Size in Bits | a. Specifies the size of the field in bits. b. The field size is 255 bits, meaning the prime ( $p$ ) defining the field is a 255-bit number. |
930 | | 3 - Width | a. Specifies the width of the Poseidon state, i.e., the number of elements in the state. b. A width of 3 means the state consists of 3 field elements. |
931 | | 8 - $R_F$ | a. Specifies the number of full rounds in the Poseidon permutation. b. In a full round: The S-box is applied to all elements of the state. The MDS matrix is applied to mix the state. c. Full rounds ensure strong non-linearity across the entire state. |
932 | | 56 - $R_P$ | a. Specifies the number of partial rounds in the Poseidon permutation. b. In a partial round: The S-box is applied to only the first element of the state. The MDS matrix is applied to mix the state. c. Partial rounds reduce computational cost while maintaining sufficient non-linearity and diffusion. |
933 | | 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001 - Field Size | a. Specifies the prime ( $p$ ) that defines the finite field ( $\mathbb{F}_p$ ). b. This is a 255-bit prime number: [ $p = 2^{254} + 0x224698fc094cf91b992d30ed00000001$ ] c. All arithmetic operations (addition, multiplication, etc.) in the Poseidon hash function are performed modulo this prime. |
934 |
935 | ### Galois Field (GF)
936 | A Galois Field (GF), named after the mathematician Évariste Galois, is a finite field that contains a finite number of elements. It is a fundamental concept in mathematics and is widely used in cryptography, error correction codes, and other areas of computer science and engineering.
937 |
938 | Key Properties of Galois Fields
939 | 1. Finite Number of Elements:
940 | * A Galois Field contains a finite number of elements, denoted as `q`.
941 | * The size of the field is always a power of a prime number: $q = p^n$, where:
942 | * `p` is a prime number (the characteristic of the field).
943 | * `n` is a positive integer (the degree of the field).
944 | 2. Field Properties:
945 | * A Galois Field satisfies all the properties of a field:
946 | * Closure: Addition, subtraction, multiplication, and division (except by zero) always produce elements within the field.
947 | * Associativity: Operations are associative.
948 | * Commutativity: Addition and multiplication are commutative.
949 | * Identity Elements: There are additive (0) and multiplicative (1) identities.
950 | * Inverses: Every non-zero element has a multiplicative inverse.
951 | 3. Two Types of Galois Fields:
952 | * GF(p): A Galois Field with `p` elements, where `p` is a prime number.
953 | * Example: GF(7) contains `{0, 1, 2, 3, 4, 5, 6}` with arithmetic modulo `7`.
954 | * GF($p^n$): A Galois Field with $p^n$ elements, where `n > 1`.
955 | * These fields are extensions of GF(p) and are used in more complex applications like cryptography and error correction.
956 |
957 | Examples of Galois Fields
958 | 1. GF(2)
959 | * The simplest Galois Field with two elements: `{0, 1}`.
960 | * Arithmetic is performed modulo `2`:
961 | * Addition: `0 + 0 = 0`, `1 + 1 = 0`, `0 + 1 = 1`, `1 + 0 = 1`.
962 | * Multiplication: `0 * 0 = 0`, `1 * 1 = 1`, `0 * 1 = 0`, `1 * 0 = 0`.
963 | 2. GF(7)
964 | * A prime-order field with seven elements: `{0, 1, 2, 3, 4, 5, 6}`.
965 | * Arithmetic is performed modulo `7`:
966 | * Addition: `(3 + 5) mod 7 = 1`, `4 + 6 = 3`.
967 | * Multiplication: `2 * 3 = 6`, `(4 * 6) mod 7 = 3`.
968 | 3. GF($2^3$)
969 | * A field with `2^3 = 8` elements.
970 | * Elements are represented as polynomials of degree less than `3` over GF(2), such as `{0, 1, x, x+1, x^2, x^2+1, x^2+x, x^2+x+1}`.
971 | * The polynomial `x^2 + x + 1` is valid because the coefficients of `x^2`, `x`, and the constant term are all either `0` or `1`.
972 | * A polynomial like `2x^2 + x + 3` is not valid in GF(2) because coefficients must be reduced modulo `2`, resulting in `0x^2 + x + 1` (or simply `x + 1`).
973 | * Arithmetic is performed modulo an irreducible polynomial, such as `x^3 + x + 1`.
974 | * An irreducible polynomial in the context of finite fields like GF($2^3$) is a polynomial that cannot be factored into smaller polynomials over GF(2). It plays a critical role in defining the field extension GF($2^n$), as it is used to perform modular arithmetic on polynomials to ensure the field has exactly $2^n$ elements.
975 |
976 | How is the irreducible polynomial selected?
977 | 1. Properties of the Polynomial:
978 | * The irreducible polynomial must have a degree equal to `n` (e.g., degree `3` for GF($2^3$)).
979 | * It must be irreducible over GF(2), meaning it cannot be factored into two non-constant polynomials with coefficients in GF(2).
980 | 2. Examples:
981 | * For GF($2^3$), a common irreducible polynomial is `x^3 + x + 1`.
982 | * This polynomial is irreducible because it cannot be factored into smaller polynomials over GF(2).
983 | 3. Selection Process:
984 | * The irreducible polynomial is typically chosen during the design of the cryptographic system or algorithm that uses the finite field.
985 | * The choice is often made to ensure efficiency in arithmetic operations and compatibility with the specific application.
986 |
987 | When is the irreducible polynomial selected?
988 | * The irreducible polynomial is selected once during the definition of the finite field GF($2^n$).
989 | * It is fixed for the specific implementation of the field and does not change during runtime.
990 | * For example, in cryptographic systems like AES or Poseidon, the irreducible polynomial is predefined and hardcoded into the implementation.
991 |
992 | ### Pasta Elliptic Curves
993 | The Pasta elliptic curves are a pair of cryptographic curves designed for use in zero-knowledge proofs (ZKPs) and other cryptographic applications. These curves are specifically optimized for efficiency and security in zk-SNARKs (Zero-Knowledge Succinct Non-Interactive Arguments of Knowledge) and recursive proof systems.
994 |
995 | Key Features of Pasta Curves:
996 | | Feature | Description |
997 | |---------|-------------|
998 | | Cycle-Friendly Curves | a. Pasta curves consist of two elliptic curves: a.1. Pallas: Defined over the scalar field of the Vesta curve. a.2. Vesta: Defined over the scalar field of the Pallas curve. b. These curves form a "curve cycle", meaning the scalar field of one curve is the base field of the other. This property is particularly useful for recursive proof systems, where one proof verifies another proof. |
999 | | Prime-Order Curves | Both Pallas and Vesta are prime-order curves, which ensures strong security properties for cryptographic operations. |
1000 | | Finite Field Arithmetic | The curves are defined over finite fields, enabling efficient arithmetic operations required for cryptographic computations. |
1001 | | Optimized for zk-SNARKs | a. Pasta curves are designed to be efficient in zk-SNARK systems like Halo 2, which is a proving system for zero-knowledge proofs. b. They are optimized for performance in both proof generation and verification. |
1002 | | Security | The curves are designed to provide strong security guarantees against known cryptographic attacks, such as those targeting elliptic curve discrete logarithms. |
1003 |
1004 | #### Prime-order curves
1005 | Prime-order curves ensure strong security properties for cryptographic operations because of the following reasons:
1006 | | Reason | Description | Why is this important? |
1007 | |--------|-------------|-------------|
1008 | | No Small Subgroups | a. A prime-order curve has a group order ( q ), where ( q ) is a prime number. b. This ensures that the only subgroups of the curve are the trivial subgroup (containing just the identity element) and the entire group itself. | a. In cryptographic operations, small subgroups can lead to vulnerabilities such as small subgroup attacks, where an attacker exploits the existence of small subgroups to gain information about private keys. b. Prime-order curves eliminate this risk because there are no small subgroups. |
1009 | | Uniform Distribution of Points | a. On a prime-order curve, every non-identity point generates the entire group when multiplied by integers (i.e., it is a generator of the group). b. This ensures that the points on the curve are uniformly distributed, which is critical for cryptographic protocols like Diffie-Hellman key exchange and Elliptic Curve Digital Signature Algorithm (ECDSA). | Uniform distribution ensures that cryptographic operations are resistant to statistical attacks, where an attacker might exploit non-uniformity to predict private keys. |
1010 | | Hardness of the Discrete Logarithm Problem (DLP) | a. The security of elliptic curve cryptography (ECC) relies on the Elliptic Curve Discrete Logarithm Problem (ECDLP), which is the problem of finding ( $k$ ) given ( $P$ ) and ( $Q = kP$ ), where ( $P$ ) and ( $Q$ ) are points on the curve. b. On a prime-order curve, the ECDLP is maximally hard because the group order ( $q$ ) is prime, and there are no divisors of ( $q$ ) that could simplify the problem. | If the group order is not prime, the ECDLP can sometimes be reduced to smaller subgroups, making it easier to solve and compromising security. |
1011 | | Simplified Security Analysis | Prime-order curves simplify the security analysis of cryptographic protocols because there are no edge cases involving small subgroups or composite group orders. | This reduces the likelihood of implementation errors and ensures that the cryptographic system behaves as expected under all conditions. |
1012 | | Resistance to Invalid Curve Attacks | a. In some cases, an attacker might try to use a point that does not belong to the intended curve (e.g., a point on a curve with a different order) to exploit weaknesses in the protocol. b. Prime-order curves mitigate this risk because the group structure is simple, and invalid points are easier to detect and reject. | |
1013 | | Strong Security for Zero-Knowledge Proofs | a. Prime-order curves like Pallas and Vesta are particularly well-suited for zero-knowledge proofs and SNARKs (Succinct Non-Interactive Arguments of Knowledge). b. These protocols often rely on the uniformity and simplicity of the group structure to ensure soundness and zero-knowledge properties. | |
1014 |
1015 | Prime-order curves ensure security by:
1016 | * Eliminating small subgroups.
1017 | * Providing uniform distribution of points.
1018 | * Maximizing the hardness of the discrete logarithm problem.
1019 | * Simplifying security analysis.
1020 | * Mitigating invalid curve attacks.
1021 |
1022 | ## Relative struct or trait
1023 | ### 1. Domain
1024 | ```rust
1025 | /// A domain which a Poseidon hash function used.
1026 | /// F: Field: The finite field used for computations.
1027 | /// RATE: usize: The rate of the sponge (number of elements absorbed per round).
1028 | pub trait Domain {
1029 | /// Iterator that outputs padding field elements.
1030 | type Padding: IntoIterator;
1031 |
1032 | /// The name of this domain, for debug formatting purposes.
1033 | fn name() -> String;
1034 |
1035 | /// The initial capacity element, encoding this domain.
1036 | /// TODO: Encodes domain-specific information into the initial state of the Poseidon sponge.
1037 | fn initial_capacity_element() -> F;
1038 |
1039 | /// Returns the padding to be appended to the input.
1040 | /// TODO: Generates padding to ensure the input length is compatible with the sponge's rate.
1041 | fn padding(input_len: usize) -> Self::Padding;
1042 | }
1043 | ```
1044 | `ConstantLength` Struct (Domain specified in [ePrint 2019/458 section 4.2](https://eprint.iacr.org/2019/458.pdf))
1045 | The ConstantLength struct is a specific implementation of the Domain trait for constant-length inputs. It is parameterized by:
1046 | * `L: usize`: The fixed length of the input.
1047 |
1048 | Implementation of Domain for ConstantLength:
1049 | * `name`: Returns a string like "ConstantLength<2>" for debugging.
1050 | * `initial_capacity_element`: Encodes the input length L into the sponge's initial state. This ensures that inputs of different lengths result in different hash outputs, even if the input values are the same. Capacity value is $length \cdot 2^{64} + (o-1)$ where o is the output length. We hard-code an output length of 1.
1051 | * `padding`: Generates padding to make the input length a multiple of the sponge's rate (RATE). The padding consists of zeroes (F::ZERO).
1052 |
1053 | for example:
1054 | L = 5, Rate = 3. Step 1: 5 + 3 - 1 = 7, 7 / 3 = 2, k = 2. Step 2: 2 * 3 - 5 = 1, `iter::repeat(F::ZERO).take(1)` generates a padding of one zero element. This ensures that the input length is compatible with the sponge's rate.
1055 | ```rust
1056 | #[derive(Clone, Copy, Debug)]
1057 | pub struct ConstantLength;
1058 |
1059 | impl Domain for ConstantLength {
1060 | type Padding = iter::Take>;
1061 |
1062 | fn name() -> String {
1063 | format!("ConstantLength<{}>", L)
1064 | }
1065 |
1066 | fn initial_capacity_element() -> F {
1067 | // Capacity value is $length \cdot 2^64 + (o-1)$ where o is the output length.
1068 | // We hard-code an output length of 1.
1069 | F::from_u128((L as u128) << 64)
1070 | }
1071 |
1072 | fn padding(input_len: usize) -> Self::Padding {
1073 | assert_eq!(input_len, L);
1074 | // For constant-input-length hashing, we pad the input with zeroes to a multiple
1075 | // of RATE. On its own this would not be sponge-compliant padding, but the
1076 | // Poseidon authors encode the constant length into the capacity element, ensuring
1077 | // that inputs of different lengths do not share the same permutation.
1078 | let k = (L + RATE - 1) / RATE;
1079 | iter::repeat(F::ZERO).take(k * RATE - L)
1080 | }
1081 | }
1082 | ```
1083 |
1084 | ### 2. Spec
1085 | The Hades design strategy, used in the Poseidon hash function, is a cryptographic approach that optimizes the trade-off between security and efficiency. It incorporates a mix of full and partial rounds within the hash function.
1086 | * Full rounds apply a non-linear operation (like an S-box) to all elements of the state, offering high security.
1087 | * Partial rounds, on the other hand, apply this operation to only a subset of the state elements, enhancing efficiency.
1088 |
1089 | This combination allows for maintaining strong cryptographic security while reducing the computational overhead typically associated with full rounds in every step. This design is particularly beneficial in contexts like zero-knowledge proofs, where efficiency is crucial without compromising security. The paper highlights how the choice of the MDS matrix in HADES significantly affects security against differential and linear attacks.
1090 |
1091 | Generics
1092 | | Type | Description |
1093 | |------|-------------|
1094 | | `F: Field` | a. F is the finite field type used for computations in the Poseidon permutation. b. The Field trait ensures that F supports basic arithmetic operations like addition, multiplication, and inversion. |
1095 | | `const T: usize` | a. T is the width of the Poseidon state, i.e., the number of field elements in the state array. b. It determines the size of the internal state used during the permutation. |
1096 | | `const RATE: usize` | a. RATE is the sponge rate, which specifies how many elements can be absorbed (TODO:) or squeezed in each round of the sponge construction. b. It must satisfy RATE < T because some elements in the state are reserved for the sponge's capacity. |
1097 |
1098 | The `Spec` trait provides a blueprint for configuring the Poseidon permutation. It defines:
1099 | * The number of full and partial rounds.
1100 | * The S-box for non-linear transformations.
1101 | * The MDS matrix for mixing state elements.
1102 | * The round constants for each round.
1103 |
1104 | By implementing this trait, you can define a specific Poseidon permutation tailored to your cryptographic requirements.
1105 | ```rust
1106 | /// A specification for a Poseidon permutation.
1107 | pub trait Spec: fmt::Debug {
1108 | /// The number of full rounds for this specification.
1109 | ///
1110 | /// This must be an even number.
1111 | fn full_rounds() -> usize;
1112 |
1113 | /// The number of partial rounds for this specification.
1114 | fn partial_rounds() -> usize;
1115 |
1116 | /// The S-box for this specification.
1117 | fn sbox(val: F) -> F;
1118 |
1119 | /// Side-loaded index of the first correct and secure MDS that will be generated by
1120 | /// the reference implementation.
1121 | ///
1122 | /// This is used by the default implementation of [`Spec::constants`]. If you are
1123 | /// hard-coding the constants, you may leave this unimplemented.
1124 | fn secure_mds() -> usize;
1125 |
1126 | /// Generates `(round_constants, mds, mds^-1)` corresponding to this specification.
1127 | fn constants() -> (Vec<[F; T]>, Mds, Mds);
1128 | }
1129 | ```
1130 |
1131 | Associated Methods
1132 | | Method | Description |
1133 | |--------|-------------|
1134 | | `fn full_rounds() -> usize` | a. Returns the number of full rounds in the Poseidon permutation. b. A full round applies the S-box (non-linear transformation) to all elements in the state, followed by a matrix multiplication (MDS matrix). c. The number of full rounds must be even to ensure symmetry and security. |
1135 | | `fn partial_rounds() -> usize` | a. Returns the number of partial rounds in the Poseidon permutation. b. In a partial round, the S-box is applied to (TODO:) only one element of the state (usually the first element), followed by a matrix multiplication. c. Partial rounds are used to reduce computational cost while maintaining security. |
1136 | | `fn sbox(val: F) -> F` | a. Defines the S-box (substitution box), which is a non-linear transformation applied to elements of the state. b. The S-box is a critical component for ensuring the cryptographic security of the permutation. c. Common choices for the S-box include raising the element to a power (e.g., x^5). |
1137 | | `fn secure_mds() -> usize` | a. Returns the index of the first secure MDS matrix generated by the reference implementation. b. The MDS (Maximum Distance Separable) matrix is used for mixing the state elements during the permutation. c. This method is optional and is primarily used when the MDS matrix is generated dynamically. If the constants are hard-coded, this method can be left unimplemented. |
1138 | | `fn constants() -> (Vec<[F; T]>, Mds, Mds)` | Generates the constants required for the Poseidon permutation: a. `round_constants`: A vector of arrays, where each array contains the round constants for a single round. Round constants are added to the state elements during each round to ensure security. b. `mds`: The MDS matrix, which is used for mixing the state elements. c. `mds^-1`: The inverse of the MDS matrix, which may be used in certain applications.
1139 |
1140 | `generate_constants` Method
1141 | The generate_constants function is responsible for generating the constants required for the Poseidon permutation. These constants include:
1142 | * Round Constants: Used in each round of the Poseidon permutation to ensure cryptographic security.
1143 | * MDS Matrix: A mixing matrix that ensures diffusion across the state.
1144 | * Inverse MDS Matrix: The inverse of the MDS matrix, which may be used in certain applications.
1145 |
1146 | This function is generic over the field type F, the Poseidon specification S, and the parameters T (state width) and RATE (sponge rate).
1147 |
1148 | Generics
1149 | | Type | Description |
1150 | |------|-------------|
1151 | | `F: FromUniformBytes<64> + Ord` | a. F is the finite field type used for computations. b. FromUniformBytes<64> ensures that F can be constructed from 64 bytes of uniform randomness, which is required for generating random field elements. c. Ord ensures that F supports ordering, which may be required for certain operations. |
1152 | | `S: Spec` | S is the Poseidon specification, which defines parameters like the number of rounds, the S-box, and the MDS matrix. |
1153 | | `const T: usize` | T is the width of the Poseidon state, i.e., the number of field elements in the state. |
1154 | | `const RATE: usize` | RATE is the sponge rate, which specifies how many elements can be absorbed or squeezed in each round of the sponge construction. |
1155 |
1156 | Return Value
1157 | | Type | Description |
1158 | |------|-------------|
1159 | | `Vec<[F; T]>` | A vector of round constants, where each element is an array of T field elements. |
1160 | | `Mds` | The MDS matrix, represented as a 2D array of size T x T. |
1161 | | `Mds` | The inverse of the MDS matrix. |
1162 |
1163 | Function Logic
1164 | | Step | Description |
1165 | |------|-------------|
1166 | | 1. Retrieve Round Parameters | a. r_f: The number of full rounds, retrieved from the Poseidon specification S. b. r_p: The number of partial rounds, also retrieved from S. |
1167 | | 2. Initialize the Grain Generator | The Grain generator is used to produce random field elements for the round constants and MDS matrix. |
1168 | | 3. Generate Round Constants | a. A total of `r_f` + `r_p` rows of round constants are generated, where each row contains `T` field elements. b. For each row: An array `rc_row` of size T is initialized with zeros. Each element of `rc_row` is replaced with a random field element generated by `grain.next_field_element()`. c. The rows are collected into a vector round_constants. |
1169 | | 4. Generate MDS Matrix and Its Inverse | a. The `generate_mds` function is called to generate the MDS matrix and its inverse. b. The grain generator is used to produce the random elements required for the MDS matrix. c. `S::secure_mds()` provides the index of the first secure MDS matrix, as defined by the Poseidon specification. |
1170 | | 5. Return the Constants | The function returns the round constants, MDS matrix, and inverse MDS matrix as a tuple. |
1171 |
1172 | ```rust
1173 | /// Generates `(round_constants, mds, mds^-1)` corresponding to this specification.
1174 | pub fn generate_constants<
1175 | F: FromUniformBytes<64> + Ord,
1176 | S: Spec,
1177 | const T: usize,
1178 | const RATE: usize,
1179 | >() -> (Vec<[F; T]>, Mds, Mds) {
1180 | let r_f = S::full_rounds();
1181 | let r_p = S::partial_rounds();
1182 |
1183 | /*
1184 | (in halo2_poseidon/src/grain.rs)
1185 |
1186 | pub(super) enum SboxType {
1187 | /// x^alpha
1188 | Pow,
1189 | /// x^(-1)
1190 | #[allow(dead_code)]
1191 | Inv,
1192 | }
1193 |
1194 | (Rust syntax)
1195 | #[allow(dead_code)] is an attribute that suppresses the compiler warning for unused code. Specifically, it prevents the compiler from issuing a warning when a function, struct, enum variant, or other code element is defined but not used anywhere in the program.
1196 | */
1197 | let mut grain = grain::Grain::new(SboxType::Pow, T as u16, r_f as u16, r_p as u16);
1198 |
1199 | let round_constants = (0..(r_f + r_p))
1200 | .map(|_| {
1201 | let mut rc_row = [F::ZERO; T];
1202 | for (rc, value) in rc_row
1203 | .iter_mut()
1204 | .zip((0..T).map(|_| grain.next_field_element()))
1205 | {
1206 | /*
1207 | 1. rc is a mutable reference to an element in the array rc_row.
1208 | 2. value is a field element generated by grain.next_field_element().
1209 | 3. The line *rc = value; assigns the value of value to the element in rc_row that rc refers to.
1210 | */
1211 | *rc = value;
1212 | }
1213 | rc_row
1214 | })
1215 | .collect();
1216 |
1217 | let (mds, mds_inv) = mds::generate_mds::(&mut grain, S::secure_mds());
1218 |
1219 | (round_constants, mds, mds_inv)
1220 | }
1221 | ```
1222 |
1223 | #### P128Pow5T3
1224 | This code defines the Poseidon-128 hash function using the ( $x^5$ ) S-box, with a state width of 3 field elements and parameters optimized for 128-bit security. The implementation is generic over two finite fields: Pallas field (Fp) and Vesta field (Fq), which are part of the Pasta curve cycle.
1225 | ```rust
1226 | /// Poseidon-128 using the $x^5$ S-box, with a width of 3 field elements, and the
1227 | /// standard number of rounds for 128-bit security "with margin".
1228 | ///
1229 | /// The standard specification for this set of parameters (on either of the Pasta
1230 | /// fields) uses $R_F = 8, R_P = 56$. This is conveniently an even number of
1231 | /// partial rounds, making it easier to construct a Halo 2 circuit.
1232 | #[derive(Debug)]
1233 | /*
1234 | P128Pow5T3:
1235 | Represents the Poseidon-128 hash function with the following parameters:
1236 | S-box: ( x^5 ) (a non-linear transformation).
1237 | Width: 3 field elements (state size).
1238 | Rounds: ( R_F = 8 ) full rounds and ( R_P = 56 ) partial rounds.
1239 | The name P128Pow5T3 indicates:
1240 | P128: 128-bit security.
1241 | Pow5: The ( x^5 ) S-box.
1242 | T3: State width of 3 elements.
1243 | */
1244 | pub struct P128Pow5T3;
1245 |
1246 | /// Implementation for Fp (Pallas Field)
1247 | /*
1248 | Implements the Spec trait for the Pallas field (Fp), with a state width of 3 and a rate of 2.
1249 | */
1250 | impl Spec for P128Pow5T3 {
1251 | fn full_rounds() -> usize {
1252 | 8
1253 | }
1254 |
1255 | fn partial_rounds() -> usize {
1256 | 56
1257 | }
1258 | /*
1259 | Defines the S-box as ( x^5 ), a non-linear transformation applied to state elements during the Poseidon permutation.
1260 | Uses the pow_vartime method to compute ( x^5 ) efficiently.
1261 | */
1262 | fn sbox(val: Fp) -> Fp {
1263 | val.pow_vartime([5])
1264 | }
1265 |
1266 | fn secure_mds() -> usize {
1267 | unimplemented!()
1268 | }
1269 |
1270 | /*
1271 | Returns the precomputed constants for the Poseidon permutation:
1272 | Round Constants: Used to add randomness to the state in each round.
1273 | MDS Matrix: Ensures diffusion (mixing) of the state.
1274 | Inverse MDS Matrix: Used for certain cryptographic operations.
1275 | */
1276 | fn constants() -> (Vec<[Fp; 3]>, Mds, Mds) {
1277 | (
1278 | super::fp::ROUND_CONSTANTS[..].to_vec(),
1279 | super::fp::MDS,
1280 | super::fp::MDS_INV,
1281 | )
1282 | }
1283 | }
1284 |
1285 | /// Implementation for Fq (Vesta Field)
1286 | /// This implementation is identical to the one for Fp, but it operates over the Vesta field (Fq).
1287 | impl Spec for P128Pow5T3 {
1288 | fn full_rounds() -> usize {
1289 | 8
1290 | }
1291 |
1292 | fn partial_rounds() -> usize {
1293 | 56
1294 | }
1295 |
1296 | fn sbox(val: Fq) -> Fq {
1297 | val.pow_vartime([5])
1298 | }
1299 |
1300 | fn secure_mds() -> usize {
1301 | unimplemented!()
1302 | }
1303 |
1304 | fn constants() -> (Vec<[Fq; 3]>, Mds, Mds) {
1305 | (
1306 | super::fq::ROUND_CONSTANTS[..].to_vec(),
1307 | super::fq::MDS,
1308 | super::fq::MDS_INV,
1309 | )
1310 | }
1311 | }
1312 | ```
1313 |
1314 | ### 3. Sponge
1315 | [(Wikipedia)](https://en.wikipedia.org/wiki/Sponge_function)In cryptography, a sponge function or sponge construction is any of a class of algorithms with finite internal state that take an input bit stream of any length and produce an output bit stream of any desired length. Sponge functions have both theoretical and practical uses. They can be used to model or implement many cryptographic primitives, including cryptographic hashes, message authentication codes, mask generation functions, stream ciphers, pseudo-random number generators, and authenticated encryption.[[1]](https://eprint.iacr.org/2019/458)
1316 |
1317 | The sponge construction for hash functions. $P_i$ are blocks of the input string, $Z_i$ are hashed output blocks.
1318 | 
1319 |
1320 | Like other hash algorithms, Poseidon uses the sponge construction. Sponges are a general method for reading in a data stream at a certain rate, allowing for mixing of the data within the capacity of the sponge, and then outputting a fixed-size hash of the data. There are two important parameters used to construct the sponge that impact the security of the hash function:
1321 | * The rate $r$: Determines how many chunks of size $|𝔽_p|$ are "absorbed" into the sponge in each step.
1322 | * The capacity: Determines how many chunks of size $|𝔽_p|$ are stored in the sponge in each step.
1323 |
1324 | TODO: The higher the rate, the faster and cheaper the hash becomes, but this also makes the hash less secure. Intuitively, the larger the capacity, the more random state you allow yourself to store in the sponge in each step.
1325 |
1326 | The `Sponge` struct is the core of the Poseidon sponge construction. It encapsulates the state, mode, and parameters required for the Poseidon permutation.
1327 | | Field | Description |
1328 | |-------|-------------|
1329 | | `mode: M` | a. Represents the current mode of the sponge (either `Absorbing` or `Squeezing`). b. Determines whether the sponge is absorbing input or squeezing output. |
1330 | | `state: State` | a. The internal state of the sponge, represented as an array of `T` field elements. b. This state is updated during the Poseidon permutation. |
1331 | | `mds_matrix: Mds` | a. The Maximum Distance Separable (MDS) matrix used in the Poseidon permutation. b. Ensures diffusion across the state during the permutation. |
1332 | | `round_constants: Vec<[F; T]>` | a. A vector of round constants used in the Poseidon permutation. b. These constants are added to the state during each round of the permutation. |
1333 | | `_marker: PhantomData` | a. A marker for the Poseidon specification type `S`. b. Used to associate the sponge with a specific Poseidon parameterization without storing actual data. |
1334 | ```rust
1335 | pub(crate) struct Sponge<
1336 | F: Field,
1337 | S: Spec,
1338 | M: SpongeMode,
1339 | const T: usize,
1340 | const RATE: usize,
1341 | > {
1342 | mode: M,
1343 | /*
1344 | /// The type used to hold permutation state.
1345 | pub type State = [F; T];
1346 | */
1347 | state: State,
1348 | /*
1349 | /// The type used to hold the MDS matrix and its inverse.
1350 | pub type Mds = [[F; T]; T];
1351 | */
1352 | mds_matrix: Mds,
1353 | round_constants: Vec<[F; T]>,
1354 | _marker: PhantomData,
1355 | }
1356 | ```
1357 | The Sponge (`State`)
1358 | Think of the sponge as having two action: it first absorbs input data like a sponge absorbs liquid, and subsequently this data gets squeezed to produce output data like a sponge releases liquid. In cryptographic terms, this sponge is the internal state of the hash function.
1359 | | Phase | Description |
1360 | |-------|-------------|
1361 | | 1. Absorbing Phase | a. Data such as a message, a file, or a number is divided into blocks, for instance, of a given number of bits or the size of a finite field. b. Each block of your input data is "absorbed" into the sponge where it is then mixed with the residual data (like the residual "liquid") in the sponge. The absorption and mixing process are defined concretely by the rate and capacity of the sponge respectively. This mixing process can be thought of like absorbing some new blue colored water into one chunk of the sponge and some new red in another while there is already some green soaking in the rest of the sponge. Fitting to the analogy, to truly mix these, the sponge would be allowed to sit where diffusion of the colors would occur. In the cryptographic case, we use a permutation to mix the data like diffusion can mix the colors. |
1362 | | 2. Squeezing Phase | a. Once the sponge has absorbed all your data (or all the colored water in the analogy), it's time to get the hash output, similar to squeezing water out of the sponge. b. In the squeezing phase, part of the sponge's state is read out as the hash result. If the desired hash output is longer, the sponge might be "squeezed" multiple times, with additional transformations in between to ensure security. If it is shorter, the sponge is squeezed only once, and the output is truncated to the desired length. By squeezing the sponge, we are essentially wringing out the water, leaving us with a fixed amount of water (the hash) that is a mix of all the colors (the input data). The blue, red, and green water in our analogy now becomes a single murky brownish purplish color, which is the hash output. Could you ever get the original colors back from this let alone the order they were added in? |
1363 |
1364 | `SealedSpongeMode`
1365 | ```rust
1366 | mod private {
1367 | pub trait SealedSpongeMode {}
1368 | impl SealedSpongeMode for super::Absorbing {}
1369 | impl SealedSpongeMode for super::Squeezing {}
1370 | }
1371 | ```
1372 | | Detail | Description |
1373 | |--------|-------------|
1374 | | Purpose | a. `SealedSpongeMode` is a private trait used to seal the `SpongeMode` trait. b. This ensures that only the Absorbing and Squeezing types (defined in this module) can implement SpongeMode. |
1375 | | Why Sealing? | Sealing prevents external types from implementing `SpongeMode`, which ensures that the sponge's state transitions are controlled and predictable. |
1376 |
1377 | `SpongeMode`
1378 | ```rust
1379 | /// The state of the `Sponge`.
1380 | pub trait SpongeMode: private::SealedSpongeMode {}
1381 | ```
1382 | | Detail | Description |
1383 | |--------|-------------|
1384 | | Purpose | a. `SpongeMode` is a public trait that represents the state of the sponge. b. It is implemented by the Absorbing and Squeezing types. |
1385 | | Relationship with `SealedSpongeMode` | `SpongeMode` inherits from `SealedSpongeMode`, so only types that implement `SealedSpongeMode` (i.e., `Absorbing` and `Squeezing`) can implement SpongeMode. |
1386 |
1387 | #### Absorbing
1388 | ```rust
1389 | /// The type used to hold sponge rate.
1390 | pub(crate) type SpongeRate = [Option; RATE];
1391 |
1392 | /// The absorbing state of the `Sponge`.
1393 | #[derive(Debug)]
1394 | pub struct Absorbing(pub(crate) SpongeRate);
1395 |
1396 | /// This allows Absorbing to be treated as a valid sponge state.
1397 | impl SpongeMode for Absorbing {}
1398 | ```
1399 | | Detail | Description |
1400 | |--------|-------------|
1401 | | Purpose | a. Represents the absorbing state of the sponge. b. The sponge is in this state when it is absorbing input values. |
1402 | | Fields | `SpongeRate`: An array of size RATE that holds the absorbed values. Each element is an Option to indicate whether it is occupied. |
1403 |
1404 | Methods
1405 | ```rust
1406 | impl Absorbing {
1407 | /// Initializes an absorbing state with a single value.
1408 | pub(crate) fn init_with(val: F) -> Self {
1409 | /// 1. Calls init_empty to create an empty absorbing state.
1410 | let mut state = Self::init_empty();
1411 | /// 2. Absorbs the provided value val into the state using the absorb method.
1412 | state.absorb(val).expect("state is not full");
1413 | /// 3. If the state is already full (which shouldn't happen here), (TODO:)it panics with the message "state is not full".
1414 | state
1415 | }
1416 |
1417 | /// Initializes an empty sponge in the absorbing state.
1418 | pub fn init_empty() -> Self {
1419 | /*
1420 | Self: Refers to the type for which the function is implemented (e.g., Absorbing).
1421 | The constructor initializes a new instance of the type using the provided arguments.
1422 | */
1423 | Self(
1424 | /*
1425 | 0..RATE: Creates an iterator that generates numbers from 0 (inclusive) to RATE (exclusive).
1426 | This is used to iterate RATE times, where RATE is a constant representing the sponge's rate (number of elements absorbed per round).
1427 | */
1428 | (0..RATE)
1429 | /*
1430 | .map: A method that applies a closure (anonymous function) to each element of the iterator.
1431 | |_| None: The closure takes one argument (represented by _ because the value is unused). For each iteration, it returns None.
1432 | This effectively creates an iterator that produces RATE None values.
1433 | */
1434 | .map(|_| None)
1435 | /*
1436 | .collect: Consumes the iterator and collects its items into a collection.
1437 | ::>: Specifies the type of collection to create (in this case, a Vec). _ is a placeholder for the type of elements in the vector (inferred as Option).
1438 | */
1439 | .collect::>()
1440 | /*
1441 | .try_into(): Attempts to convert the Vec