├── README.md ├── LICENSE ├── KZG.py └── algebra_and_kzg.ipynb /README.md: -------------------------------------------------------------------------------- 1 | # A quick insight on Algebra and KZG Commitments 2 | 3 | The contents are available in the form of a [Jupyter notebook](algebra_and_kzg.ipynb). 4 | 5 | ## Requirements 6 | 7 | - Python 3.6+ 8 | - [Jupyter](https://jupyter.org/install) 9 | - [SageMath 9.0+](https://www.sagemath.org/download.html) 10 | 11 | ## References 12 | 13 | The contents of this notebook are based on the following references: 14 | 15 | - [Kate, Zaverucha, and Goldberg. Polynomial Commitments*](https://cacr.uwaterloo.ca/techreports/2010/cacr2010-10.pdf) 16 | - [Least Authority. The Moonmath Manual](https://github.com/LeastAuthority/moonmath-manual) 17 | 18 | ## Did you find an erratum? Do you believe the materials can be improved upon? 19 | 20 | If so, please [open an issue](https://github.com/luksgrin/opensense-algebra-and-kzg/issues/new) so we can discuss and take care of the matter! I'm sure there are mistakes that can be fixed 😃 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /KZG.py: -------------------------------------------------------------------------------- 1 | from sage.all import * 2 | from reedsolo import RSCodec 3 | from functools import reduce 4 | 5 | class KZG(): 6 | 7 | def __init__(self, 8 | p=None, 9 | t=None, 10 | alpha=None, 11 | k=16, 12 | leak=False 13 | ): 14 | 15 | if p is None: 16 | p = self.safe_random_prime(k) 17 | elif not p.is_prime(): 18 | raise ValueError("p must be a prime number") 19 | 20 | self.p = p 21 | self.F_p = GF(p) 22 | self.g = self.F_p.multiplicative_generator() 23 | self.alpha = None 24 | 25 | if (t is None) or (t >= p): 26 | self.t = self.__generate_t() 27 | if alpha is None: 28 | alpha = self.F_p.random_element() 29 | self.PP = self.compute_public_parameters(alpha) 30 | if leak: 31 | self.alpha = alpha 32 | 33 | def __repr__(self): 34 | 35 | repr = f"KZG commitment class with {self.F_p} whose generator is {self.g}" 36 | 37 | if self.alpha is None: 38 | return repr + "\nToxic waste has been deleted" 39 | else: 40 | return repr + "\nToxic waste has NOT been deleted. Proceed with caution" 41 | 42 | @classmethod 43 | def safe_random_prime(cls, k): 44 | """Generate a safe random prime. 45 | Code taken from 46 | https://ask.sagemath.org/question/44765/randomly-generate-a-safe-prime-of-given-length/?answer=44766#post-id-44766 47 | """ 48 | while true: 49 | p = random_prime(2**k - 1, false, 2**(k - 1)) 50 | if ZZ((p - 1)/2).is_prime(): 51 | return p 52 | 53 | def polynomial_ring(self, var="x"): 54 | return PolynomialRing(self.F_p, var).objgen() 55 | 56 | def __polynomial_checks(self, poly): 57 | if poly.degree() > self.t: 58 | raise ValueError(f"The polynomial's degree is {poly.degree()}, which is bigger than {self.t}") 59 | 60 | if poly.base_ring() != self.F_p: 61 | raise ValueError("The polynomial must be defined on the same base ring as F_p") 62 | 63 | def __generate_t(self): 64 | t = 0 65 | while t == 0: 66 | t = self.F_p.random_element() 67 | return t 68 | 69 | def compute_public_parameters(self, alpha): 70 | 71 | PP = [] 72 | 73 | accumulated = 1 74 | for i in range(self.t + 1): 75 | PP.append(self.F_p.prod(( 76 | accumulated, self.g 77 | ))) 78 | accumulated = self.F_p.prod((accumulated, alpha)) 79 | return PP 80 | 81 | def make_commitment(self, poly): 82 | 83 | self.__polynomial_checks(poly) 84 | 85 | # Get the Polynomial coefficients. They are given in ascending order 86 | poly_coefs = poly.coefficients() 87 | 88 | # Only keep the parameters needed 89 | pp_used = self.PP[:len(poly_coefs)] 90 | 91 | return reduce( 92 | lambda a,b: a + (b[0]*b[1]), 93 | zip(poly_coefs, pp_used), 94 | 0 95 | ) 96 | 97 | def make_opening_triplet(self, i, poly): 98 | self.__polynomial_checks(poly) 99 | 100 | i_eval = poly(i) 101 | x = poly.variables()[0] 102 | 103 | proof_poly = ((poly - i_eval)/(x - i)).numerator() 104 | proof_commitment = self.make_commitment(proof_poly) 105 | return (i, i_eval, proof_commitment) 106 | 107 | def verify(self, commitment, opening_triplet): 108 | 109 | i, i_eval, proof_commitment = opening_triplet 110 | 111 | return ( 112 | self.e(commitment, self.g) == (self.e(proof_commitment, self.PP[1] - i*self.g) + (i_eval*self.e(self.g, self.g))) 113 | ) 114 | 115 | def e(self, g1, g2): 116 | return self.F_p.prod((g1, g2)) 117 | 118 | def reed_solomon_encode_string(input_string): 119 | 120 | # Initialize the Reed-Solomon encoder/decoder with a specific error correction level 121 | rs = RSCodec(10) # You can adjust the error correction level as needed 122 | 123 | # Encode the input string 124 | encoded_bytes = rs.encode(input_string.encode()) 125 | 126 | # Convert the encoded bytes to a list of integers 127 | return list(encoded_bytes) 128 | 129 | def message_as_coords(input_string): 130 | y = reed_solomon_encode_string(input_string) 131 | x = range(1, len(y) + 1) 132 | return list(zip(x, y)) 133 | 134 | def make_faux_poly(F_p_X, alpha, phi_alpha, i, phi_i): 135 | xs = [alpha, i] 136 | ys = [phi_alpha, phi_i] 137 | 138 | F_p = F_p_X.base_ring() 139 | 140 | while (len(xs) != 3): 141 | x = F_p.random_element() 142 | if not (x in xs): 143 | xs.append(x) 144 | ys.append(F_p.random_element()) 145 | return F_p_X.lagrange_polynomial(zip(xs,ys)) -------------------------------------------------------------------------------- /algebra_and_kzg.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "3b3a94a3", 6 | "metadata": {}, 7 | "source": [ 8 | "# A quick insight on Algebra and KZG Commitments\n", 9 | "\n", 10 | "_(These contents do not attempt to be exhaustive. The main objective is to give the bare minimum background to understand KZG commitments)_\n", 11 | " \n", 12 | "_**Disclaimer**: lack of formalism ahead!_\n", 13 | "\n", 14 | "## Contents\n", 15 | "\n", 16 | "1. [Preliminaries](#1.-Preliminaries)\n", 17 | " - 1.1 [Number Sets](#1.1-Number-Sets)\n", 18 | " - 1.2 [Prime Numbers](#1.2-Prime-Numbers)\n", 19 | " - 1.3 [Coprime Integers](#1.3-Coprime-Integers)\n", 20 | " - 1.4 [Modular aritmethic](#1.4-Modular-aritmethic)\n", 21 | " - 1.5 [Basic Algebraic Structures](#1.5-Basic-Algebraic-Structures)\n", 22 | " - 1.5.1 [Abelian Groups](#1.5.1-Abelian-Groups)\n", 23 | " - [Finite Groups, Cyclic Groups and Generators](#Finite-Groups,-Cyclic-Groups-and-Generators)\n", 24 | " - [The Exponential Map](#The-Exponential-Map)\n", 25 | " - [Pairings](#Pairings)\n", 26 | " - 1.5.2 [Rings](#1.5.2-Rings)\n", 27 | " - 1.5.3 [Fields & Galois Fields](#1.5.3-Fields-&-Galois-Fields)\n", 28 | " - 1.6 [Polynomials](#1.6-Polynomials)\n", 29 | "2. [KZG Commitments](#2.-KZG-Commitments)\n", 30 | " - 2.1 [The Setup Phase](#2.1-The-Setup-Phase)\n", 31 | " - 2.2 [The Commitment Phase](#2.2-The-Commitment-Phase)\n", 32 | " - 2.3 [The Opening Phase](#2.3-The-Opening-Phase)\n", 33 | " - 2.4 [The Verification Phase](#2.4.-The-Verification-Phase)\n", 34 | " - 2.5 [A `sagemath` Implementation](#2.5-A-sagemath-Implementation)\n", 35 | "3. [Closing remarks](#3.-Closing-remarks)\n", 36 | " - 3.1 [Recovering the message from the polynomial?](#3.1-Recovering-the-message-from-the-polynomial?)\n", 37 | " - 3.2 [Abstraction!](#3.2-Abstraction!)\n", 38 | "\n", 39 | "# 1. Preliminaries\n", 40 | "\n", 41 | "One can define Mathematics as the field that studies patterns, as it contains a vast amount of subfields, each of which ultimately leads to the description and formalization of patterns and behaviors under the formalism of axioms and theorems. In fact, the whole Mathematic universe sustains itself on a series of axioms (statements that are taken as true), being the most widely used the [Zermelo-Fraenkel axioms](https://en.wikipedia.org/wiki/Zermelo%E2%80%93Fraenkel_set_theory) (ZF axioms), which are the basis of modern Set Theory.\n", 42 | "\n", 43 | "It is through set theory that we can define the most basic mathematical object: numbers.\n", 44 | "\n", 45 | "## 1.1 Number Sets\n", 46 | "\n", 47 | "We are going to start with the set of Natural numbers $\\mathbb{N}$, which most authors define as the set of all positive integers: $\\{1,2,3,4,\\dots\\}$ (we denote a set as a pair of $\\{\\}$ containing some/all of the elements of the set). If you're curious about how this set is constructed, check out [Wikipedia's article on the Set theoretic definition of natural numbers](https://en.wikipedia.org/wiki/Set-theoretic_definition_of_natural_numbers). Some mathematicians (like myself) would also include the number $0$ in this set, but for the sake of coherency with Least Authority's MoonMath manual, we'll stick to the former definition. In case one would want to explicitly include $0$ in the set of Natural numbers, some authors adopt the notation $\\mathbb{N}_0$.\n", 48 | "\n", 49 | "The set of Integers $\\mathbb{Z}$ is an extension of $\\mathbb{N}$ and is defined as the set of all positive and negative integers: $\\{\\dots,-3,-2,-1,0,1,2,3,\\dots\\}$. The need of this extension is due to the fact that some operations (like subtraction $^*$) are not closed under $\\mathbb{N}$, meaning that the result of the operation is not necessarily an element of $\\mathbb{N}$. More on that later.\n", 50 | "\n", 51 | "\n", 52 | "The set of Rational numbers $\\mathbb{Q}$ is an extension of $\\mathbb{Z}$ and is defined as the set of all numbers that can be expressed as the quotient of two integers (assuming the divisor is non-zero): $\\{\\frac{a}{b} \\mid a,b\\in\\mathbb{Z}, b\\neq 0\\}$ (this reads like _\"the set composed by the quotients of $a$ and $b$, so that $a$ and $b$ are elements of the set of integers and $b$ is non-zero\"_). This extension also arises from the need of closure under some operations (like division $^*$).\n", 53 | "\n", 54 | "We are going to define the set of Irrational numbers (some authors denote it as $\\mathbb{I}$) and the set of Real numbers $\\mathbb{R}$ together. $\\mathbb{I}$ is defined as the set of numbers that _\"cannot be expressed as the quotient of two integers\"_, and $\\mathbb{R}$ can be easily defined as the union of $\\mathbb{Q}$ and $\\mathbb{I}$: $\\mathbb{R} = \\mathbb{Q}\\cup\\mathbb{I}$. Given that we won't need these number sets for this session, we won't touch on the construction of $\\mathbb{R}$ and $\\mathbb{I}$. If curious, you may check out [Wikipedia's article on the construction of the real numbers](https://en.wikipedia.org/wiki/Construction_of_the_real_numbers) and [Wikipedia's article on irrational numbers](https://en.wikipedia.org/wiki/Irrational_number).\n", 55 | "\n", 56 | "Finally, another set that is of general interest in mathematics is the set of Complex numbers $\\mathbb{C}$, which is defined as the set of numbers that can be expressed as $a+bi$, where $a,b\\in\\mathbb{R}$ and $i$ is the imaginary unit, defined as $i^2=-1$. This set extends the real numbers $\\mathbb{R}$ and arises from the need of closure under some operations (like the square root of a negative number $^*$)\n", 57 | "\n", 58 | "$^*$ _Soon you wil see that \"subtraction\", \"division\" and \"square root\" are actually aliases for something else..._ 😯\n", 59 | "\n", 60 | "From now on, we'll be working with the set of Integers $\\mathbb{Z}$ and the set of Natural numbers $\\mathbb{N}$, so don't worry too much about the other sets.\n", 61 | "\n", 62 | "## 1.2 Prime Numbers\n", 63 | "\n", 64 | "A prime number is a natural Number ($\\mathbb{N}$) that has the following properties:\n", 65 | "\n", 66 | "- is greater than 1\n", 67 | "- is divisible only by itself and $1$\n", 68 | "\n", 69 | "_Remember: $a$ is divisible by $b$ if_ $\\frac{a}{b}\\in\\mathbb{Z}$\n", 70 | "\n", 71 | "We denote the set of prime numbers as $\\mathbb{P}$. Note that there is only one even prime number: $2$. Several authors denote $\\mathbb{P}_{\\geq 3}$ as the set of **odd** prime numbers.\n", 72 | "\n", 73 | "## 1.3 Coprime Integers\n", 74 | "\n", 75 | "It is said that two integers $a$ and $b$ are coprime if their greatest common divisor (GCD) is $1$. We denote this as $gcd(a,b)=1$. This means that $a$ and $b$ do not share any common factors other than $1$.\n", 76 | "\n", 77 | "For example $gcd(4,9)=1$ ($4$ and $9$) are coprime, but $gcd(4,8)=4$ ($4$ and $8$ are not coprime). Usually, this is checked by means of the [Euclidean algorithm](https://en.wikipedia.org/wiki/Euclidean_algorithm). A more brute-force way to check if two numbers are coprime is to check if their prime factorization has any common factors. Let's take the previous examples:\n", 78 | "\n", 79 | "- $4$ and $9$ are coprime: $4=2^2$ and $9=3^2$, so they don't share any common factors\n", 80 | "- $4$ and $8$ are not coprime: $4=2^2$ and $8=2^3$ because they share the common factor $2$ twice! $4=2^2$ and $8=2^3=2\\cdot 2^2$, thus the GCD is $2^2=4$\n", 81 | "\n", 82 | "A fun corollary of this is that if either $a$ or $b$ is prime, then $gcd(a,b)=1$, i.e. the pair is co-prime. This is because a prime number can only be divided by itself and $1$.\n", 83 | "\n", 84 | "## 1.4 Modular aritmethic\n", 85 | "\n", 86 | "Modular arithmetic is a system of arithmetic for integers, where numbers \"wrap around\" upon reaching a certain value—the modulus _(doesn't this sound familiar with computer science's concept of integer overflow/underflow? A Solidity `uint256` type is a positive integer defined on modular arithmetic of modulus $2^{256}$ !_ 🤯 _)_\n", 87 | "\n", 88 | "For example, let's take modulus 5, and add the numbers 4 and 3:\n", 89 | "\n", 90 | "$$4+3=7\\equiv 2\\quad\\text{mod}~5$$\n", 91 | "\n", 92 | "_(Note that the symbol $\\equiv$ means \"equivalent\")_\n", 93 | "\n", 94 | "So basically, $4$ plus $3$ is $7$, but if we \"wrap around\" the result to the modulus $5$, we get $2$. Another way to think about it is that every time we get a number that is greater than the modulus, we subtract the modulus until we get a number that is less than the modulus. i.e. we divide by the modulus and keep the remainder.\n", 95 | "\n", 96 | "The exact same thing happens with multiplication:\n", 97 | "\n", 98 | "$$2\\cdot 4=8\\equiv 3\\quad\\text{mod}~5$$\n", 99 | "\n", 100 | "One can quickly notice that the modulus gives the number of possible results of the operation. In the previous example, we had $5$ possible results: $0,1,2,3,4$. So, if we are in modulus $n$ the set of numbers $\\{0,\\dots,n-1\\}$ are actually all the numbers we will ever get when doing arithmetic operations with modulus $n$. This set is denoted as $\\mathbb{Z_n}$ (other mathematicians, as myself, prefer denoting it as $\\mathbb{Z}/n\\mathbb{Z}$, but for the sake of coherency with Least Authority's MoonMath manual, we'll stick to the former notation).\n", 101 | "\n", 102 | "These concepts give rise to **equivalence classes**. To put it easy, an equivalente class is a set of numbers that _\"are equal\"_ under modulus $n$. From each equivalente class, we take _\"one representative number\"_ to identify each class. Let's go back again to our modulus $5$ example. As mentioned, $\\mathbb{Z}_5=\\{0,1,2,3,4\\}$. So, the equivalence class of $0$ is $\\{\\dots,-5,0,5,10,15,\\dots\\}$, basically $\\{a\\mid a\\in\\mathbb{Z}, \\frac{a}{5}\\in\\mathbb{Z}\\}$. What about the equivalence class of $3$? Well, it's $\\{\\dots,-2,3,8,13,18,\\dots\\}$, basically $\\{a\\mid a\\in\\mathbb{Z}, \\frac{a - 3}{5}\\in\\mathbb{Z}\\}$. It's the set of numbers that _\"fall\"_ on the same number once wrapping around 5 (i.e. the set of numbers that have the same remainder when divided by 5).\n", 103 | "\n", 104 | "_(Note that the symbol $\\in$ means \"is an element of\")_\n", 105 | "\n", 106 | "If the equivalence classes remain unclear, think about a clock: it has $12$ hours, and once it reaches $12$, it goes back to $1$. So, the equivalence class of $1$ is $\\{\\dots,1,13,25,\\dots\\}$, the equivalence class of $2$ is $\\{\\dots,2,14,26,\\dots\\}$, and so on. The equivalence class of $12$<> is $\\{\\dots,0, 12,24,36,\\dots\\}$. That's why when a digital clock reads `13:00`, we say it's `1:00 PM`, because $13$ is equivalent to $1$ under modulus $12$.\n", 107 | "\n", 108 | "These sets will give rise to interesting algebraic structures, as we'll see later.\n", 109 | "\n", 110 | "## 1.5 Basic Algebraic Structures\n", 111 | "\n", 112 | "So far, we've seen some basic number sets and some basic arithmetic operations. Now, we'll see some basic algebraic structures that arise from these sets and operations.\n", 113 | "\n", 114 | "But first, what is Algebra? Many people think of Algebra as the study of equations, but that's not entirely true. Algebra is the study of mathematical structures, their properties and how to manipulate them. To put it simple, it is like having a _\"bag of objects\"_ and some _\"rules\"_ to manipulate those _\"objects\"_.\n", 115 | "\n", 116 | "_(Doesn't this ring a bell for you? It's like having a `class` in Object Oriented Programming, and some `methods` to manipulate the `class` `instance` !_ 🤯 _)_\n", 117 | "\n", 118 | "### 1.5.1 Abelian Groups\n", 119 | "\n", 120 | "Any algebraic structure is defined through a set and an operation. Let's start with the most basic algebraic structure: the Abelian Group. An Abelian Group (also known as commutative group) $\\mathbb{G}$ is a tuple $(G,\\oplus)$ where $G$ is a set and $\\oplus$ is a binary operation (i.e. an operation that takes two elements of the set and returns another element) that satisfies the following properties:\n", 121 | "\n", 122 | "- Closure: $\\forall a,b\\in G, a\\oplus b\\in G$ (the elements returned by the operation are elements of the set).\n", 123 | "- Associativity: $\\forall a,b,c\\in G, (a\\oplus b)\\oplus c=a\\oplus(b\\oplus c)$\n", 124 | "- Identity element: $\\exists e\\in G, \\forall a\\in G, a\\oplus e=e\\oplus a=a$. $e$ is called the identity element. Sometimes it is also denoted by a **bold** \"one\": $\\boldsymbol{1}$\n", 125 | "- Inverse element: $\\forall a\\in G, \\exists a^{-1}\\in G, a\\oplus a^{-1}=a^{-1}\\oplus a=e$. $a^{-1}$ is called the inverse element of $a$.\n", 126 | "- Commutativity: $\\forall a,b\\in G, a\\oplus b=b\\oplus a$\n", 127 | "\n", 128 | "_(Note that the symbol $\\forall$ means \"for all\" and $\\exists$ means \"there exists\")_\n", 129 | "\n", 130 | "If you're familiar with Object Oriented Programming, you can think of an Abelian Group as a `class` with a `method` that takes two `instances` of the `class` and returns another `instance` of the `class`. The `class` is the set $G$, the `method` is the binary operation $\\oplus$, and the `instances` are the elements of the set $G$.\n", 131 | "\n", 132 | "Let's see some examples of Abelian Groups:\n", 133 | "\n", 134 | "- The set of integers $\\mathbb{Z}$ with the binary operation $+$ (addition) is an Abelian Group. The identity element is $0$ and the inverse element of $a$ is $-a$. _(See why [earlier](#1.1-Number-Sets) we mentioned that subtraction is an alias? It is an alias for \"addition with the inverse\")_\n", 135 | "- The set of integers $\\mathbb{Z}$ with the binary operation $\\cdot$ (product; multiplication) is **NOT** an Abelian Group. The identity element is $1$ but the inverse element of $a$ is $\\frac{1}{a}$, which is NOT an element of $\\mathbb{Z}$. _(Again, see why [earlier](#1.1-Number-Sets) we mentioned that division is an alias? It is an alias for \"multiplication with the inverse\")_.\n", 136 | "- The set of rational numbers $\\mathbb{Q}$ with the binary operation $\\cdot$ (product; multiplication) is an Abelian Group. The identity element is $1$ and the inverse element of $a$ is $\\frac{1}{a}$. _(See why [earlier](#1.1-Number-Sets) we mentioned that $\\mathbb{Q}$ is an extension of $\\mathbb{Z}$ ?)_\n", 137 | "\n", 138 | "If the commutativity property is not present in the Abelian Group, then it is just called a Group. _Can you think of an example of a tuple $(G,\\oplus)$ that is a Group but not an Abelian Group?_ 😉\n", 139 | "\n", 140 | "#### Finite Groups, Group Order, Cyclic Groups and Generators\n", 141 | "\n", 142 | "The previous examples of Abelian Groups are infinite (i.e. the set $G$ that composes $\\mathbb{G}$ contains an infinite amount of elements). But what if the set $G$ is finite? Then we call it a Finite Group. The number of elements in the grpup $\\mathbb{G}$ is called the order of the group, and is usually denoted by $\\left|\\mathbb{G}\\right|$.\n", 143 | "\n", 144 | "Let's go back to our example of the set of integers $\\{0,1,2,3,4\\}$ (which is a finite set) and the binary operation $+$. Is this a Finite Group? The answer is **NO**, because we lack **closure**: if we perform $2 + 3 = 5$, and $5$ is not in the set. However, if we change $+$ for $+$ under modulo $5$, then we do get a Finite Group, because this time $2 + 3 = 5\\equiv 0\\quad\\text{mod}~5$. Indeed, all groups $(\\mathbb{Z}_n, +~\\text{mod}~ n)$ are finite (abelian) groups, whose order is $n$.\n", 145 | "\n", 146 | "Let's talk about Cyclic groups. A Cyclic Group is a group that can be generated by a single element, called **generator** by means of the binary operation. For example, $\\mathbb{Z}$ with the binary operation addition $+$ is indeed an infinite cyclic group, which has two different generators: $1$ and $-1$, because every integer can be generated by adding $1$ or $-1$ to itself, or through their inverses.\n", 147 | "\n", 148 | "The same happens with $\\mathbb{Z}_n$ with the binary operation addition $+$ under modulo $n$. For example, $\\mathbb{Z}_5$ has four generators: $1$, $2$, $3$ and $4$, because every element of $\\mathbb{Z}_5$ can be generated by adding $1$ or $4$ to itself. In fact, every group $(\\mathbb{Z}_n, +~\\text{mod}~ n)$ is a cyclic group, and the generators are co-prime with $n$.\n", 149 | "\n", 150 | "For example, let's take the clock ($\\mathbb{Z}_{12}$) with the binary operation addition $+$ under modulo $12$. The generators are $1$, $5$, $7$ and $11$. Try it out! By now you must have noticed that $\\mathbb{Z}_p$ where $p\\in\\mathbb{P}$ (i.e. the set of prime numbers) with the binary operation addition $+$ under modulo $p$ is very interesting because it is a **finite cyclic abelian group** whose generators are all the numbers in the set except $0$ (because trivially all the numbers in the set are coprime with $p$ 😄).\n", 151 | "\n", 152 | "\n", 153 | "#### The Exponential Map\n", 154 | "\n", 155 | "Let's say we have a finite cyclic group $\\mathbb{G}=(G,\\oplus)$ of order $n$ ($\\left|\\mathbb{G}\\right|=n$; it contains $n$ elements) whose generator is $g$. The exponential map is a function that takes an integer $a\\in\\mathbb{Z}_n$ and returns the element of the group that is generated by $g^a$. One can understand $g^a$ as applying $a$ times the $\\oplus$ operation on $g$.\n", 156 | "\n", 157 | "Let's dig into an example for clarity: take the finite cyclic abelian group $\\mathbb{G}:=(\\mathbb{Z}_5, +~\\text{mod}~5)$ of order $5$, and let's choose the generator $2$ (you know it doesn't matter which one we choose because all the elements of $\\mathbb{G}$ except $0$ are generators). The exponential map, which we'll call $\\xi$ is:\n", 158 | "\n", 159 | "\\begin{align*}\n", 160 | "\\xi : \\mathbb{Z}_5 &\\rightarrow \\mathbb{G}\\\\\n", 161 | "a &\\mapsto 2\\cdot a\\quad\\text{mod}~5\n", 162 | "\\end{align*}\n", 163 | "\n", 164 | "Let's test if this is true:\n", 165 | "\n", 166 | "
\n", 167 | "\n", 168 | "| $\\mathbb{Z}_5$ | $\\mathbb{G}$ |\n", 169 | "|:---:|:----------------------------------------------:|\n", 170 | "| $a$ | $\\xi(a)=2\\cdot a\\quad\\text{mod}~5$ |\n", 171 | "| $0$ | $\\xi(0)=2\\cdot 0 = 0$ |\n", 172 | "| $1$ | $\\xi(1)=2\\cdot 1 = 2$ |\n", 173 | "| $2$ | $\\xi(2)=2\\cdot 2 = 4$ |\n", 174 | "| $3$ | $\\xi(3)=2\\cdot 3 = 6\\equiv 1\\quad\\text{mod}~5$ |\n", 175 | "| $4$ | $\\xi(4)=2\\cdot 4 = 8\\equiv 3\\quad\\text{mod}~5$ |\n", 176 | "\n", 177 | "
\n", 178 | "\n", 179 | "See? We have generated all the elements of $\\mathbb{G}$ with the generator $2$ using the exponential map.\n", 180 | "\n", 181 | "Let's dig into another example: take the finite cyclic abelian group $\\mathbb{G}:=(\\{1,2,3,4,5,6\\}, \\cdot~\\text{mod}~7)$ of order $6$, and let's choose the generator $3$. The exponential map, which we'll call $\\xi$ is:\n", 182 | "\n", 183 | "\\begin{align*}\n", 184 | "\\xi : \\mathbb{Z}_6 &\\rightarrow \\mathbb{G}\\\\\n", 185 | "a &\\mapsto 3^a\\quad\\text{mod}~7\n", 186 | "\\end{align*}\n", 187 | "\n", 188 | "Let's test if this is true:\n", 189 | "\n", 190 | "
\n", 191 | "\n", 192 | "| $\\mathbb{Z}_6$ | $\\mathbb{G}$ |\n", 193 | "|:---:|:-----------------------------------------------:|\n", 194 | "| $a$ | $\\xi(a) = 3^a\\quad\\text{mod}~7$ |\n", 195 | "| $0$ | $\\xi(0)= 3^0 = 1$ |\n", 196 | "| $1$ | $\\xi(1)= 3^1 = 3$ |\n", 197 | "| $2$ | $\\xi(2)= 3^2 = 2$ |\n", 198 | "| $3$ | $\\xi(3)=3^3 = 27 \\equiv 6\\quad\\text{mod}~7$ |\n", 199 | "| $4$ | $\\xi(4)=3^4 = 81 \\equiv 4\\quad\\text{mod}~7$ |\n", 200 | "| $5$ | $\\xi(5)=3^5 = 243 = 8\\equiv 5\\quad\\text{mod}~7$ |\n", 201 | "\n", 202 | "
\n", 203 | "\n", 204 | "_(Note that the set $\\{1,2,3,4,5,6\\}$ can also be referred as $\\mathbb{Z}_7^*$, because it's like $\\mathbb{Z}_7$ but without the $0$)_\n", 205 | "\n", 206 | "The exponential map allows to perform so-called _\"calculations in the exponent\"_. For the previous example, let's take that we want to compute $5\\cdot 6\\cdot 3\\quad\\text{mod}~7$. We can do it two ways:\n", 207 | "\n", 208 | "- Directly: $5\\cdot 6\\cdot 3 = 90\\equiv 6\\quad\\text{mod}~7$\n", 209 | "- In the exponent: $5\\cdot 6\\cdot 3 \\equiv 3^5\\cdot 3^3\\cdot 3^1 = 3^{5+3+1~\\text{mod}~6}=3^3 = 27 \\equiv 6$\n", 210 | "\n", 211 | "This property gets more useful when we are dealing with large numbers, because we can significantly reduce the size of the numbers we are working with.\n", 212 | "\n", 213 | "#### Pairings\n", 214 | "\n", 215 | "A pairing map takes elements from a group $\\mathbb{G}_1$ and elements from a group $\\mathbb{G}_2$ and returns an element of a group $\\mathbb{G}_T$. Let's define the pairing $e$, and a pair $(a,b)\\mid a\\in\\mathbb{G_1}, b\\in\\mathbb{G}_2$:\n", 216 | "\n", 217 | "\\begin{align*}\n", 218 | "e : \\mathbb{G}_1\\times\\mathbb{G}_2 &\\rightarrow \\mathbb{G}_T\\\\\n", 219 | "(a,b) &\\mapsto e(a,b)\n", 220 | "\\end{align*}\n", 221 | "\n", 222 | "A pairing is said to be **bilinear** if for all $g_1,g'_1\\in\\mathbb{G}_1$ and $g_2,g'_2\\in\\mathbb{G}_2$, it is satisfied that:\n", 223 | "\n", 224 | "\\begin{align*}\n", 225 | "e(g_1\\oplus g'_1,g_2) &= e(g_1,g_2)\\oplus e(g'_1,g_2)\\\\\n", 226 | "e(g_1,g_2\\oplus g'_2) &= e(g_1,g_2)\\oplus e(g_1,g'_2)\n", 227 | "\\end{align*}\n", 228 | "\n", 229 | "A pairing is said to be **non-degenerate** if for all $g_1\\in\\mathbb{G}_1$ and $g_2\\in\\mathbb{G}_2$, it is satisfied that:\n", 230 | "\n", 231 | "$$e(g_1,g_2)=\\boldsymbol{1}_{\\mathbb{G}_T}\\Rightarrow \\boldsymbol{1}_{\\mathbb{G}_1}\\quad\\text{or}\\quad\\boldsymbol{1}_{\\mathbb{G}_2}$$\n", 232 | "\n", 233 | "In other words, if the pairing of two elements of $\\mathbb{G}_1$ and $\\mathbb{G}_2$ is the neutral element of $\\mathbb{G}_T$, then at least one of the elements of $\\mathbb{G}_1$ or $\\mathbb{G}_2$ must be the neutral element of their respective group.\n", 234 | "\n", 235 | "A simple example for a pairing is taking all the groups as the set of integers $\\mathbb{Z}_p$ with the binary operation addition $+$ and the pairing $e$ as:\n", 236 | "\n", 237 | "\\begin{align*}\n", 238 | "e : \\mathbb{Z}\\times\\mathbb{Z} &\\rightarrow \\mathbb{Z}\\\\\n", 239 | "(a,b) &\\mapsto a\\cdot b\n", 240 | "\\end{align*}\n", 241 | "\n", 242 | "It is easy to see that\n", 243 | "\n", 244 | "\\begin{align*}\n", 245 | "e(a + b, c) &= (a + b)\\cdot c = a\\cdot c + b\\cdot c = e(a, c) + e(b, c)\\\\\n", 246 | "e(a,b) &= 0 \\Rightarrow a = 0 \\quad\\text{or}\\quad b = 0\n", 247 | "\\end{align*}\n", 248 | "\n", 249 | "both hold.\n", 250 | "\n", 251 | "_Can you think of another example of a pairing?_ 😉\n", 252 | "\n", 253 | "### 1.5.2 Rings\n", 254 | "\n", 255 | "The next algebraic structure we'll see are Rings (more specifically, commutative rings). A Ring $R$ is a tuple $(R,\\oplus,\\otimes)$ where $R$ is a set and $\\oplus$ and $\\otimes$ are binary operations that satisfy the following properties:\n", 256 | "\n", 257 | "- $(R,\\oplus)$ is an Abelian Group\n", 258 | "- Commutativity: $\\forall a,b\\in R, a\\otimes b=b\\otimes a$\n", 259 | "- Associativity: $\\forall a,b,c\\in R, (a\\otimes b)\\otimes c=a\\otimes(b\\otimes c)$\n", 260 | "- Distributivity: $\\forall a,b,c\\in R, (a\\oplus b)\\otimes c=(a\\otimes c)\\oplus(b\\otimes c)$\n", 261 | "- Identity element: $\\exists e\\in R, \\forall a\\in R, a\\otimes e=e\\otimes a=a$. $e$ is called the identity element. Sometimes it is also denoted by a **bold** \"one\": $\\boldsymbol{1}$. In this case, the identity element for $\\oplus$ can be denoted by a **bold** \"zero\": $\\boldsymbol{0}$.\n", 262 | "\n", 263 | "For example, the set of integers $\\mathbb{Z}$ with the binary operations $+$ (addition) and $\\cdot$ (product; multiplication) is a Ring. The identity element for $+$ is $0$ and the identity element for $\\cdot$ is $1$.\n", 264 | "\n", 265 | "### 1.5.3 Fields & Galois Fields\n", 266 | "\n", 267 | "The final algebraic structure we'll see are Fields. A Field $\\mathbb{F}$ is a tuple $(F,\\oplus,\\otimes)$ where $F$ is a set and $\\oplus$ and $\\otimes$ are binary operations that satisfy the following properties:\n", 268 | "\n", 269 | "- $(F,\\oplus,\\otimes)$ is a Ring\n", 270 | "- $\\forall a\\in F\\setminus\\{0\\}, \\exists a^{-1}\\in F, a\\otimes a^{-1}=a^{-1}\\otimes a=e$. $a^{-1}$ is called the inverse element of $a$.\n", 271 | "\n", 272 | "Galois Fields (also known as Finite Fields) are a special kind of Field, where the set $F$ is finite. The order of the field is the number of elements in the set $F$, and is usually denoted by $\\left|\\mathbb{F}\\right|$. We are going to use what are known as Prime Fields, which are build by means of $\\mathbb{Z}/p\\mathbb{Z}$ with the binary operations $+$ (addition) and $\\cdot$ (product; multiplication) under modulo $p$, where $p\\in\\mathbb{P}$. These special fields are denoted as $\\mathbb{F}_p$.\n", 273 | "\n", 274 | "_What if $\\mathbb{Z}/n\\mathbb{Z}$, where $n\\notin\\mathbb{P}$?_\n", 275 | "\n", 276 | "In that case, we would have a Ring, but not a Field, because not every arbitrary element of $a\\in\\mathbb{Z}/n\\mathbb{Z}\\setminus\\{0\\}$ has an inverse $a^{-1}$ in the set. For example, let's take $\\mathbb{Z}/6\\mathbb{Z}$ with the binary operation $\\cdot$ (product; multiplication) under modulo $6$. Since $\\mathbb{Z}/6\\mathbb{Z}=\\{0,1,2,3,4,5\\}$, let's construct the multiplication table:\n", 277 | "\n", 278 | "
\n", 279 | "\n", 280 | "| $\\cdot$ | $0$ | $1$ | $2$ | $3$ | $4$ | $5$ |\n", 281 | "|:-------:|:---:|:---:|:---:|:---:|:---:|:---:|\n", 282 | "| $0$ | $0$ | $0$ | $0$ | $0$ | $0$ | $0$ |\n", 283 | "| $1$ | $0$ | $1$ | $2$ | $3$ | $4$ | $5$ |\n", 284 | "| $2$ | $0$ | $2$ | $4$ | $0$ | $2$ | $4$ |\n", 285 | "| $3$ | $0$ | $3$ | $0$ | $3$ | $0$ | $3$ |\n", 286 | "| $4$ | $0$ | $4$ | $2$ | $0$ | $4$ | $2$ |\n", 287 | "| $5$ | $0$ | $5$ | $4$ | $3$ | $2$ | $1$ |\n", 288 | "\n", 289 | "
\n", 290 | "\n", 291 | "Notice that there are rows/columns (besides $0$) that do not have a single entry that equals 1? For example, $2$, $3$ and $4$. This means that only $1$ and $5$ have a multiplicative inverse in $\\mathbb{Z}/6\\mathbb{Z}$, while the rest of the elements do not. Therefore, $\\mathbb{Z}/6\\mathbb{Z}$ is not a Field. _What do the elements that do not have an inverse have in common?_ 😉\n", 292 | "\n", 293 | "## 1.6 Polynomials\n", 294 | "\n", 295 | "A polynomial is an expression consisting of variables (also called indeterminates) and coefficients, that involves only the operations of addition, subtraction, multiplication, and non-negative integer exponents of variables. An example of a polynomial of a single indeterminate $x$ is $x^2 - 4x + 7$. An example in three variables is $x^3 + 2xyz^2 - yz + 1$. Usually, when talking about polynomials, one refers to the set of polynomials with coefficients and values that \"live\" in the field of real numbers $\\mathbb{R}$ (embeded with the binary operations $+$ (addition) and $\\cdot$ (product; multiplication)). This set is denoted as $\\mathbb{R}[x]$. For example, the polynomial $x^2 - 2$ is an element of $\\mathbb{R}[x]$.\n", 296 | "\n", 297 | "As you may have guessed, we can define polynomials over any field $\\mathbb{F}$, and denote the set of polynomials with coefficients and values that \"live\" in $\\mathbb{F}$ as $\\mathbb{F}[x]$. For example, the polynomial $x^2 - 2$ is also an element of $\\mathbb{F}_5[x]$.\n", 298 | "\n", 299 | "The field over which we construct the polynomials greatly affects the properties of the polynomials. For example, let's take the polynomial $x^2 - 2$.\n", 300 | "- If we are working with $\\mathbb{R}[x]$, then this polynomial has two roots: $\\sqrt{2}$ and $-\\sqrt{2}$, because $x^2 - 2 = (x - \\sqrt{2})(x + \\sqrt{2})$.\n", 301 | "- If we are working with $\\mathbb{F}_5[x]$, then this polynomial has no roots, because $\\pm\\sqrt{2}$ are not elements of $\\mathbb{F}_5$.\n", 302 | "\n", 303 | "So, $x^2 - 2$ is an irreducible polynomial in $\\mathbb{F}_5[x]$, but not in $\\mathbb{R}[x]$.\n", 304 | "\n", 305 | "Let's check out another example: the polynomial $x^2 + 1$.\n", 306 | "- If we are working with $\\mathbb{R}[x]$, then this polynomial has no roots, because $x^2 + 1 = (x + i)(x - i)$, where $i$ is the imaginary unit, and $i$ is not an element of $\\mathbb{R}$.\n", 307 | "- If we are working with $\\mathbb{C}[x]$, then this polynomial has two roots: $\\pm i$.\n", 308 | "- If we are working with $\\mathbb{F}_5[x]$, then this polynomial has two roots: $2$ and $3$, because $x^2 + 1 = (x - 2)(x - 3)$.\n", 309 | "\n", 310 | "Let's elaborate a bit on the previous statement. Remember that in $\\mathbb{F}_5[x]$, all operations are done under modulo $5$. So, let's expand $(x - 2)(x - 3)$:\n", 311 | "\n", 312 | "\\begin{align*}\n", 313 | "(x - 2)(x - 3) &= x^2 - 3x - 2x + 6\\\\\n", 314 | "&= x^2 - 5x + 6\\\\\n", 315 | "&\\equiv x^2 + 1\\quad\\text{mod}~5\n", 316 | "\\end{align*}\n", 317 | "\n", 318 | "# 2. KZG Commitments\n", 319 | "\n", 320 | "Now that we have a basic understanding of the algebraic structures we'll be working with, let's dive into the KZG Commitments. The KZG Commitment scheme is a commitment scheme that allows to commit to a polynomial $\\phi(x) = a_0 + a_1x + a_2x^2+\\dots+a_tx^t$, where $\\phi(x)\\in\\mathbb{F}_p[x]$. _\"To commit\"_ means proving that you know the polynomial $\\phi(x)$ without revealing it. Think of it as a sealed envelope.\n", 321 | "\n", 322 | "## 2.1 The Setup Phase\n", 323 | "\n", 324 | "Let's define a Galois Field $\\mathbb{F}_p$ where $p$ is a prime number. We'll also define a maximum degree $t$ for the polynomials we'll be working with, so that $t < p$. _If you want to do this properly, you must choose a prime so that it fulfills the $k$ bits of security that you want to establish for your commitment scheme. For example, if you want $128$ bits of security, you must choose a prime $p$ so that_ $2^{128} < p < 2^{129}$.\n", 325 | "\n", 326 | "Now, we'll define a generator $g$ of $\\mathbb{F}_p$. We will also need to compute two groups $\\mathbb{G}$ and $\\mathbb{G}_T$ that have $g$ as their generator. We'll also need to define a pairing $e:\\mathbb{G}\\times\\mathbb{G}\\to\\mathbb{G}_T$ that is bilinear and non-degenerate.\n", 327 | "\n", 328 | "A trusted party then picks a random $\\alpha\\in\\mathbb{F}_p^*$ and computes the public parameters $PP$:\n", 329 | "\n", 330 | "$$\n", 331 | "PP = \\left\\langle g, g^\\alpha, g^{\\alpha^2}, \\dots, g^{\\alpha^t}\\right\\rangle\n", 332 | "$$\n", 333 | "\n", 334 | "$PP$ will be publicly available **to everyone**. The trusted party will then **HAVE TO DELETE** $\\alpha$. _(More on that later)_\n", 335 | "\n", 336 | "## 2.2 The Commitment Phase\n", 337 | "\n", 338 | "Now, let's say we want to commit to a polynomial $\\phi(x) = a_0 + a_1x + a_2x^2+\\dots+a_tx^t$, where $\\phi(x)\\in\\mathbb{F}_p[x]$. We'll compute the commitment $\\mathcal{C}\\in\\mathbb{F}_p$:\n", 339 | "\n", 340 | "$$\n", 341 | "\\mathcal{C} = g^{\\phi(\\alpha)}\n", 342 | "$$\n", 343 | "\n", 344 | "You may ask youself, how? $\\alpha$ is unknown! Well, remember that we have the public parameters $PP$, which contain $g^\\alpha$, $g^{\\alpha^2}$, $\\dots$, $g^{\\alpha^t}$. So, we can compute the commitment $\\mathcal{C}$ by **evaluating on the exponent**:\n", 345 | "\n", 346 | "\\begin{align*}\n", 347 | "\\mathcal{C} &= g^{\\phi(\\alpha)}\\\\\n", 348 | "&= g^{a_0 + a_1\\alpha + a_2\\alpha^2+\\dots+a_t\\alpha^t}\\\\\n", 349 | "&= g^{a_0}\\cdot g^{a_1\\alpha}\\cdot g^{a_2\\alpha^2}\\cdot\\dots\\cdot g^{a_t\\alpha^t}\\\\\n", 350 | "&= g^{a_0}\\cdot (g^{\\alpha})^{a_1}\\cdot (g^{\\alpha^2})^{a_2}\\cdot\\dots\\cdot (g^{\\alpha^t})^{a_t}\\\\\n", 351 | "\\end{align*}\n", 352 | "\n", 353 | "Clearly, we know $g^\\alpha, g^{\\alpha^2},\\dots$ from the Public Parameters ($PP$)! So, we can compute the commitment $\\mathcal{C}$.\n", 354 | "\n", 355 | "## 2.3 The Opening Phase\n", 356 | "\n", 357 | "In this step, the Verifier will ask the Prover to \"open\" the commitment $\\mathcal{C}$ to a random specific point $s\\in\\mathbb{F}_p$. In other words, the prover will have to evaluate $\\phi(s)$ and commit the result in the form of the opening triplet $OT = \\left(s,\\phi(s),\\mathcal{C}_{\\psi_s}\\right)$. Computing $\\phi(s)$ is trivial, but how do we compute $\\mathcal{C}_{\\psi_s}$?\n", 358 | "\n", 359 | "$\\mathcal{C}_{\\psi_s}$ is defined as $g^{\\psi(\\alpha)}$, where $\\psi(\\alpha)$ is the proof polynomial that is constructed from $\\phi(x)$ and $s$. To contstruct this polynomial, let's imagine the polynomial $\\rho(x)\\in\\mathbb{F}_p\\mid \\rho(x)=\\phi(x)-\\phi(s)$. Trivially, $\\rho(x)$ has $s$ as one of its roots (because $\\rho(s)=\\phi(s)-\\phi(s)=0$). That means it admits a factorization of the form $\\rho(x)=\\psi_s(x)\\cdot(x-s)$, where $\\psi_s(x)$ is a polynomial of degree $t - 1$.\n", 360 | "\n", 361 | "Given that $\\rho(s)=\\phi(s)-\\phi(s)=\\psi_s(x)\\cdot(x - s)$, we have $\\psi_s(x)=\\frac{\\phi(x)-\\phi(s)}{x - s}$. Now, we can compute the commitment $\\mathcal{C}_{\\psi_s}$ the same way as we computed the commitment $\\mathcal{C}$, we just have to replace $\\phi(x)$ with $\\psi_s(x)$.\n", 362 | "\n", 363 | "The opening triplet $OT$ is then sent to the Verifier.\n", 364 | "\n", 365 | "## 2.4. The Verification Phase\n", 366 | "\n", 367 | "The verifier has to check that the opening triplet $OT$ is valid. In other words, that $\\phi(s)$ is indeed evaluating $\\phi(x)$ (which is hidden in $\\mathcal{C}$) on $s$.\n", 368 | "\n", 369 | "The verifier knows $PP$ (public by default), $\\mathcal{C}=g^{\\phi(\\alpha)}$, $s$, $\\phi(s)$ and $\\mathcal{C}_{\\psi_s}=g^{\\psi_s(\\alpha)}$. Also, the verifier will make use of the pairing $e:\\mathbb{G}\\times\\mathbb{G}\\to\\mathbb{G}_T$ that is bilinear and non-degenerate. Why? Imagine we had no pairing $e$, and the verifier attempted to verify the commitment $\\mathcal{C}$:\n", 370 | "\n", 371 | "\\begin{align*}\n", 372 | "\\mathcal{C} &= g^{\\phi(\\alpha)}\\\\\n", 373 | "&= g^{\\psi_s(\\alpha)\\cdot(\\alpha - s) + \\phi(s)}\\\\\n", 374 | "&= g^{\\psi_s(\\alpha)\\cdot(\\alpha - s)}\\cdot g^{\\phi(s)}\\\\\n", 375 | "&= \\mathcal{C}_{\\psi_s}^{(\\alpha - s)}\\cdot g^{\\phi(s)}\n", 376 | "\\end{align*}\n", 377 | "\n", 378 | "This would precise the verifier knowing $\\alpha$. But, since we have the pairing $e$, we can check if the following holds:\n", 379 | "\n", 380 | "$$\n", 381 | "e(\\mathcal{C}, g) = e(\\mathcal{C}_{\\psi_s}, g^{\\alpha - s})\\cdot e(g^{\\phi(s)}, g)\n", 382 | "$$\n", 383 | "\n", 384 | "Why? Let's elaborate:\n", 385 | "\n", 386 | "\\begin{align*}\n", 387 | "e(\\mathcal{C}, g) &= e(g^{\\phi(\\alpha)}, g)\\\\\n", 388 | "&= e(g, g)^{\\phi(\\alpha)}\\\\\n", 389 | "&= e(g, g)^{\\psi_s(\\alpha)\\cdot(\\alpha - s) + \\phi(s)}\\\\\n", 390 | "&= e(g, g)^{\\psi_s(\\alpha)\\cdot(\\alpha - s)}\\cdot e(g, g)^{\\phi(s)}\\\\\n", 391 | "&= e(g^{\\psi_s(\\alpha)}, g^{(\\alpha - s)})\\cdot e(g, g)^{\\phi(s)}\\\\\n", 392 | "&= e(\\mathcal{C}_{\\psi_s}, g^{\\alpha - s})\\cdot e(g, g)^{\\phi(s)}\\\\\n", 393 | "&= e(\\mathcal{C}_{\\psi_s}, g^{\\alpha}\\cdot g^{- s})\\cdot e(g, g)^{\\phi(s)}\n", 394 | "\\end{align*}\n", 395 | "\n", 396 | "The nice property is that the verifier has all the information needed to compute this. In particular, $g^{\\alpha}$ is given in the Public Parameters ($PP$) and $g^{- s}$ is easily computable.\n", 397 | "\n", 398 | "\n", 399 | "## 2.5 A `sagemath` Implementation\n", 400 | "\n", 401 | "Let's replicate (and implement) the previous steps one by one.\n", 402 | "\n", 403 | "> Remember, we want to prove that we know a Polynomial $\\phi(x) = a_0 + a_1x + a_2x^2+\\dots+a_tx^t$, where $\\phi(x)\\in\\mathbb{F}_p[x]$.\n", 404 | "\n", 405 | "### 2.5.1 Setup\n", 406 | "\n", 407 | "First we'll have to define a couple variables:\n", 408 | "- $p$ prime which will aid us to build our Prime Field $\\mathbb{F}_p$\n", 409 | "- $t$ the maximum degree of the polynomials in our Polynomial Field $\\mathbb{F}_p[x]$, so that $t < p$\n", 410 | "- We'll both take $\\mathbb{G}$ and $\\mathbb{G_T}$ as the Prime Field $\\mathbb{F}_p$ (because it is cyclic and has a generator $g$).\n", 411 | "\n", 412 | "Remember that our prime needs $k$ bits of security! So we'll have to define a function to get a safe random prime number" 413 | ] 414 | }, 415 | { 416 | "cell_type": "code", 417 | "execution_count": 1, 418 | "id": "0479ad61", 419 | "metadata": {}, 420 | "outputs": [ 421 | { 422 | "name": "stdout", 423 | "output_type": "stream", 424 | "text": [ 425 | "Our safe random prime is 51347\n" 426 | ] 427 | } 428 | ], 429 | "source": [ 430 | "def safe_random_prime(k):\n", 431 | " \"\"\"Generate a safe random prime.\n", 432 | " Code taken from\n", 433 | " https://ask.sagemath.org/question/44765/randomly-generate-a-safe-prime-of-given-length/?answer=44766#post-id-44766\n", 434 | " \"\"\"\n", 435 | " while true:\n", 436 | " p = random_prime(2^k - 1, false, 2^(k - 1))\n", 437 | " if ZZ((p - 1)/2).is_prime():\n", 438 | " return p\n", 439 | "\n", 440 | "k = 16 # 16 bits of security. My laptop is not beefy and I want my public parameters to be computed in a matter of seconds\n", 441 | "p = safe_random_prime(k)\n", 442 | "print(f\"Our safe random prime is {p}\")" 443 | ] 444 | }, 445 | { 446 | "cell_type": "markdown", 447 | "id": "ae2ca891", 448 | "metadata": {}, 449 | "source": [ 450 | "Let's define the Prime Field $\\mathbb{F}_p$" 451 | ] 452 | }, 453 | { 454 | "cell_type": "code", 455 | "execution_count": 2, 456 | "id": "f275b41f", 457 | "metadata": {}, 458 | "outputs": [ 459 | { 460 | "name": "stdout", 461 | "output_type": "stream", 462 | "text": [ 463 | "Finite Field of size 51347\n" 464 | ] 465 | } 466 | ], 467 | "source": [ 468 | "F_p = GF(p)\n", 469 | "print(F_p)" 470 | ] 471 | }, 472 | { 473 | "cell_type": "markdown", 474 | "id": "2e63d7ce", 475 | "metadata": {}, 476 | "source": [ 477 | "Let's generate a random $t$ so that $t < p$" 478 | ] 479 | }, 480 | { 481 | "cell_type": "code", 482 | "execution_count": 3, 483 | "id": "9d9ec5a3", 484 | "metadata": {}, 485 | "outputs": [ 486 | { 487 | "name": "stdout", 488 | "output_type": "stream", 489 | "text": [ 490 | "The maximum degree is 37433\n" 491 | ] 492 | } 493 | ], 494 | "source": [ 495 | "t = 0\n", 496 | "\n", 497 | "while t == 0:\n", 498 | " t = F_p.random_element()\n", 499 | "print(f\"The maximum degree is {t}\")" 500 | ] 501 | }, 502 | { 503 | "cell_type": "markdown", 504 | "id": "cefb63a2", 505 | "metadata": {}, 506 | "source": [ 507 | "Now, let's get the generator $g$ of the Prime Field $\\mathbb{F}_p$" 508 | ] 509 | }, 510 | { 511 | "cell_type": "code", 512 | "execution_count": 4, 513 | "id": "1a50efb5", 514 | "metadata": {}, 515 | "outputs": [ 516 | { 517 | "name": "stdout", 518 | "output_type": "stream", 519 | "text": [ 520 | "The generator is 2\n" 521 | ] 522 | } 523 | ], 524 | "source": [ 525 | "g = F_p.multiplicative_generator()\n", 526 | "print(f\"The generator is {g}\")" 527 | ] 528 | }, 529 | { 530 | "cell_type": "markdown", 531 | "id": "960073d7", 532 | "metadata": {}, 533 | "source": [ 534 | "Next, we have to define a pairing $e:\\mathbb{G}\\times\\mathbb{G}\\rightarrow\\mathbb{G}_T$. We will define the simple pairing\n", 535 | "$$\n", 536 | "e(g_1,g_2)\\mapsto g_1\\cdot g_2 \\quad\\left(\\text{mod}~p\\right)\n", 537 | "$$" 538 | ] 539 | }, 540 | { 541 | "cell_type": "code", 542 | "execution_count": 5, 543 | "id": "103eee66", 544 | "metadata": {}, 545 | "outputs": [], 546 | "source": [ 547 | "def e(g1, g2):\n", 548 | " return F_p.prod((g1, g2))" 549 | ] 550 | }, 551 | { 552 | "cell_type": "markdown", 553 | "id": "6e6a3093", 554 | "metadata": {}, 555 | "source": [ 556 | "We may also check that this pairing\n", 557 | "- is bilinear:\n", 558 | "$$\n", 559 | "e(g_1^a,g_2^b)=e(g_1^{ab},g_2)=e(g_1,g_2^{ab})=e(g_1,g_2)^{ab}\n", 560 | "$$\n", 561 | " see:\n", 562 | "$$\n", 563 | "ag_1\\cdot bg_2 = abg_1\\cdot g_2 = g_1\\cdot abg_2 = ab(g_1\\cdot g_2) \\quad\\left(\\text{mod}~p\\right)\n", 564 | "$$ \n", 565 | "- non-degenerate\n", 566 | "$$\n", 567 | "e(g,g)\\neq\\boldsymbol{1}\n", 568 | "$$\n", 569 | " see:\n", 570 | "$$\n", 571 | "2\\cdot2 = 4 \\neq 0 \\quad\\left(\\text{mod}~p\\right)\n", 572 | "$$" 573 | ] 574 | }, 575 | { 576 | "cell_type": "markdown", 577 | "id": "b657166c", 578 | "metadata": {}, 579 | "source": [ 580 | "At this point, the trusted party (us, haha) retrieves a random (and secret) $\\alpha\\in\\mathbb{F}_p$" 581 | ] 582 | }, 583 | { 584 | "cell_type": "code", 585 | "execution_count": 6, 586 | "id": "1786adfa", 587 | "metadata": { 588 | "scrolled": true 589 | }, 590 | "outputs": [], 591 | "source": [ 592 | "alpha = F_p.random_element()" 593 | ] 594 | }, 595 | { 596 | "cell_type": "markdown", 597 | "id": "74c349bc", 598 | "metadata": {}, 599 | "source": [ 600 | "This allows computing the Public Parameters `PP`" 601 | ] 602 | }, 603 | { 604 | "cell_type": "code", 605 | "execution_count": 7, 606 | "id": "8230bf1c", 607 | "metadata": {}, 608 | "outputs": [], 609 | "source": [ 610 | "def compute_public_parameters(F_p, alpha, t):\n", 611 | "\n", 612 | " PP = []\n", 613 | "\n", 614 | " accumulated = 1\n", 615 | " for i in range(t + 1):\n", 616 | " PP.append(F_p.prod((\n", 617 | " accumulated, g\n", 618 | " )))\n", 619 | " accumulated = F_p.prod((accumulated, alpha))\n", 620 | " return PP" 621 | ] 622 | }, 623 | { 624 | "cell_type": "code", 625 | "execution_count": 8, 626 | "id": "b35ef501", 627 | "metadata": {}, 628 | "outputs": [ 629 | { 630 | "name": "stdout", 631 | "output_type": "stream", 632 | "text": [ 633 | "The first elements of the public parameters are: [2, 25632, 32953, 47920, 32600, 42408, 44280, 5436, 41244, 17086, 30568, 33225, 42276, 47019, 38459, 10691, 22060, 4378, 37524, 42929, 46306, 40417, 47083, 37131, 38247, 15090, 20638, 8211, 22173, 14870, 25203, 29018, 39714, 23160, 32900, 36183, 6571, 4856, 1932, 11258, 48805, 27073, 15889, 42569, 2429, 13782, 47779, 22689, 4163, 3475, 17751, 29606, 27513, 6759, 955, 18694, 48549, 32385, 8359, 19102, 40083, 28340, 28109, 45739, 13672, 24388, 7419, 38607, 7620, 47273, 7515, 36615, 48954, 36818, 31905, 18319, 17820, 41011, 9084, 16895, 47368, 44054, 35799, 14539, 44908, 43752, 16392, 19295, 48915, 50464, 31159, 8125, 49631, 35607, 18523, 13587, 13315, 18959, 4540, 8489]\n" 634 | ] 635 | } 636 | ], 637 | "source": [ 638 | "PP = compute_public_parameters(F_p, alpha, t)\n", 639 | "print(\"The first elements of the public parameters are: {}\".format(PP[:100]))" 640 | ] 641 | }, 642 | { 643 | "cell_type": "markdown", 644 | "id": "72ceff4b", 645 | "metadata": {}, 646 | "source": [ 647 | "Remember, once we have the public parameters, **we must delete `alpha`**.\n", 648 | "_We are not going to do it though, for illustrative purposes!_" 649 | ] 650 | }, 651 | { 652 | "cell_type": "markdown", 653 | "id": "0a9c05d3", 654 | "metadata": {}, 655 | "source": [ 656 | "### 2.5.2 Commitment\n", 657 | "\n", 658 | "It's time to do our commitment! In order to make it as applied as possible, let's commit a message. To do that though, we will some auxiliary code to help us!\n", 659 | "\n", 660 | "#### Reed Solomon Codes\n", 661 | "\n", 662 | "We're going to use Reed-Solomon encoding to transform our messages into a list of integers" 663 | ] 664 | }, 665 | { 666 | "cell_type": "code", 667 | "execution_count": 9, 668 | "id": "9a8d81a2", 669 | "metadata": {}, 670 | "outputs": [], 671 | "source": [ 672 | "from reedsolo import RSCodec\n", 673 | "\n", 674 | "def reed_solomon_encode_string(input_string):\n", 675 | "\n", 676 | " # Initialize the Reed-Solomon encoder/decoder with a specific error correction level\n", 677 | " rs = RSCodec(10) # You can adjust the error correction level as needed\n", 678 | "\n", 679 | " # Encode the input string\n", 680 | " encoded_bytes = rs.encode(input_string.encode())\n", 681 | "\n", 682 | " # Convert the encoded bytes to a list of integers\n", 683 | " return list(encoded_bytes)" 684 | ] 685 | }, 686 | { 687 | "cell_type": "markdown", 688 | "id": "1c91275f", 689 | "metadata": {}, 690 | "source": [ 691 | "We will also need to transform this into a list of tuples, which will contain coordinates" 692 | ] 693 | }, 694 | { 695 | "cell_type": "code", 696 | "execution_count": 10, 697 | "id": "a19760ec", 698 | "metadata": {}, 699 | "outputs": [], 700 | "source": [ 701 | "def message_as_coords(input_string):\n", 702 | " y = reed_solomon_encode_string(input_string)\n", 703 | " x = range(1, len(y) + 1)\n", 704 | " return list(zip(x, y))" 705 | ] 706 | }, 707 | { 708 | "cell_type": "markdown", 709 | "id": "7d74b186", 710 | "metadata": {}, 711 | "source": [ 712 | "Let's encode a message!" 713 | ] 714 | }, 715 | { 716 | "cell_type": "code", 717 | "execution_count": 11, 718 | "id": "7586ebe0", 719 | "metadata": { 720 | "scrolled": true 721 | }, 722 | "outputs": [ 723 | { 724 | "name": "stdout", 725 | "output_type": "stream", 726 | "text": [ 727 | "[(1, 72), (2, 101), (3, 108), (4, 108), (5, 111), (6, 32), (7, 90), (8, 101), (9, 112), (10, 112), (11, 33), (12, 25), (13, 255), (14, 128), (15, 142), (16, 165), (17, 163), (18, 57), (19, 193), (20, 169), (21, 195)]\n" 728 | ] 729 | } 730 | ], 731 | "source": [ 732 | "msg = message_as_coords(\"Hello Zepp!\")\n", 733 | "print(msg)" 734 | ] 735 | }, 736 | { 737 | "cell_type": "markdown", 738 | "id": "2daa2aae", 739 | "metadata": {}, 740 | "source": [ 741 | "#### Interpolating Polynomial\n", 742 | "Cool! Let's create our Polynomial Ring $\\mathbb{F}_p[x]$" 743 | ] 744 | }, 745 | { 746 | "cell_type": "code", 747 | "execution_count": 12, 748 | "id": "8ef0d862", 749 | "metadata": {}, 750 | "outputs": [ 751 | { 752 | "data": { 753 | "text/plain": [ 754 | "Univariate Polynomial Ring in x over Finite Field of size 51347" 755 | ] 756 | }, 757 | "execution_count": 12, 758 | "metadata": {}, 759 | "output_type": "execute_result" 760 | } 761 | ], 762 | "source": [ 763 | "F_p_X. = PolynomialRing(F_p)\n", 764 | "F_p_X" 765 | ] 766 | }, 767 | { 768 | "cell_type": "markdown", 769 | "id": "6eaab670", 770 | "metadata": {}, 771 | "source": [ 772 | "And get an interpolating polynomial over our coordinates (our message)" 773 | ] 774 | }, 775 | { 776 | "cell_type": "code", 777 | "execution_count": 13, 778 | "id": "421ea1b7", 779 | "metadata": {}, 780 | "outputs": [ 781 | { 782 | "data": { 783 | "text/plain": [ 784 | "38784*x^20 + 29502*x^19 + 27814*x^18 + 15919*x^17 + 4780*x^16 + 35151*x^15 + 16478*x^14 + 40979*x^13 + 24598*x^12 + 21339*x^11 + 50472*x^10 + 6735*x^9 + 8685*x^8 + 9664*x^7 + 13815*x^6 + 14359*x^5 + 8374*x^4 + 37855*x^3 + 11600*x^2 + 18403*x + 26889" 785 | ] 786 | }, 787 | "execution_count": 13, 788 | "metadata": {}, 789 | "output_type": "execute_result" 790 | } 791 | ], 792 | "source": [ 793 | "msg_poly = F_p_X.lagrange_polynomial(msg)\n", 794 | "msg_poly" 795 | ] 796 | }, 797 | { 798 | "cell_type": "markdown", 799 | "id": "e4822baf", 800 | "metadata": {}, 801 | "source": [ 802 | "Since for $n$ points $(x,y)$ there exists one unique polynomial of degree $n-1$ that passes through all of them, we basically converted `\"Hello Zepp\"` into a polynomial!\n", 803 | "\n", 804 | "---\n", 805 | "\n", 806 | "Okay, now we have the polynomial that we want to commit. We will need to evaluate the polynomial at $\\alpha$. We will \"_evaluate it on the exponent_\". We can write a function for that" 807 | ] 808 | }, 809 | { 810 | "cell_type": "code", 811 | "execution_count": 14, 812 | "id": "55c9cd42", 813 | "metadata": {}, 814 | "outputs": [], 815 | "source": [ 816 | "from functools import reduce\n", 817 | "\n", 818 | "def exponent_evaluation(PP, poly, g):\n", 819 | " # Recover the Prime Field\n", 820 | " F_p = poly.base_ring()\n", 821 | " # Get the Polynomial coefficients. They are given in ascending order\n", 822 | " poly_coefs = poly.coefficients()\n", 823 | " # Only keep the parameters needed\n", 824 | " pp_used = PP[:len(poly_coefs)] \n", 825 | " \n", 826 | " return reduce(\n", 827 | " lambda a,b: a + (b[0]*b[1]),\n", 828 | " zip(poly_coefs, pp_used),\n", 829 | " 0\n", 830 | " )" 831 | ] 832 | }, 833 | { 834 | "cell_type": "code", 835 | "execution_count": 15, 836 | "id": "4cd23868", 837 | "metadata": {}, 838 | "outputs": [ 839 | { 840 | "data": { 841 | "text/plain": [ 842 | "29729" 843 | ] 844 | }, 845 | "execution_count": 15, 846 | "metadata": {}, 847 | "output_type": "execute_result" 848 | } 849 | ], 850 | "source": [ 851 | "commitment = exponent_evaluation(PP, msg_poly, g)\n", 852 | "commitment" 853 | ] 854 | }, 855 | { 856 | "cell_type": "markdown", 857 | "id": "3d13749c", 858 | "metadata": {}, 859 | "source": [ 860 | "This is our commitment!\n", 861 | "\n", 862 | "### 2.5.3 Opening\n", 863 | "\n", 864 | "The Verifier asks the Prover to evaluate the Polynomial at a point $s\\in\\mathbb{F}_p$. So, let's choose this point randomly" 865 | ] 866 | }, 867 | { 868 | "cell_type": "code", 869 | "execution_count": 16, 870 | "id": "dbdca0cc", 871 | "metadata": {}, 872 | "outputs": [ 873 | { 874 | "name": "stdout", 875 | "output_type": "stream", 876 | "text": [ 877 | "The point of evaluation `s` is 32493\n" 878 | ] 879 | } 880 | ], 881 | "source": [ 882 | "s = F_p.random_element()\n", 883 | "print(f\"The point of evaluation `s` is {s}\")" 884 | ] 885 | }, 886 | { 887 | "cell_type": "markdown", 888 | "id": "1db4351f", 889 | "metadata": {}, 890 | "source": [ 891 | "Now we can evaluate our polynomial on `s`" 892 | ] 893 | }, 894 | { 895 | "cell_type": "code", 896 | "execution_count": 17, 897 | "id": "9bf32e75", 898 | "metadata": {}, 899 | "outputs": [ 900 | { 901 | "name": "stdout", 902 | "output_type": "stream", 903 | "text": [ 904 | "The evaluation is 50946\n" 905 | ] 906 | } 907 | ], 908 | "source": [ 909 | "poly_eval = msg_poly(s)\n", 910 | "print(f\"The evaluation is {poly_eval}\")" 911 | ] 912 | }, 913 | { 914 | "cell_type": "markdown", 915 | "id": "7ef7d514", 916 | "metadata": {}, 917 | "source": [ 918 | "Next we compute the proof polynomial" 919 | ] 920 | }, 921 | { 922 | "cell_type": "code", 923 | "execution_count": 18, 924 | "id": "cabc209d", 925 | "metadata": {}, 926 | "outputs": [ 927 | { 928 | "data": { 929 | "text/plain": [ 930 | "38784*x^19 + 28593*x^18 + 27545*x^17 + 6047*x^16 + 36329*x^15 + 5818*x^14 + 1098*x^13 + 32128*x^12 + 23845*x^11 + 42041*x^10 + 1750*x^9 + 28356*x^8 + 9625*x^7 + 212*x^6 + 21833*x^5 + 23876*x^4 + 9419*x^3 + 9955*x^2 + 44662*x + 508" 931 | ] 932 | }, 933 | "execution_count": 18, 934 | "metadata": {}, 935 | "output_type": "execute_result" 936 | } 937 | ], 938 | "source": [ 939 | "proof_poly = ((msg_poly - poly_eval)/(x - s)).numerator()\n", 940 | "proof_poly" 941 | ] 942 | }, 943 | { 944 | "cell_type": "markdown", 945 | "id": "5fa10d16", 946 | "metadata": {}, 947 | "source": [ 948 | "And the corresponding opening commit" 949 | ] 950 | }, 951 | { 952 | "cell_type": "code", 953 | "execution_count": 19, 954 | "id": "6164828b", 955 | "metadata": { 956 | "scrolled": false 957 | }, 958 | "outputs": [ 959 | { 960 | "data": { 961 | "text/plain": [ 962 | "11475" 963 | ] 964 | }, 965 | "execution_count": 19, 966 | "metadata": {}, 967 | "output_type": "execute_result" 968 | } 969 | ], 970 | "source": [ 971 | "opening_commit = exponent_evaluation(PP, proof_poly, g)\n", 972 | "opening_commit" 973 | ] 974 | }, 975 | { 976 | "cell_type": "markdown", 977 | "id": "a9e1025b", 978 | "metadata": {}, 979 | "source": [ 980 | "Now we have the opening triplet $\\left(s,\\phi(s),\\mathcal{C}^{\\psi_s(\\alpha)}\\right)$" 981 | ] 982 | }, 983 | { 984 | "cell_type": "code", 985 | "execution_count": 20, 986 | "id": "c5d14614", 987 | "metadata": { 988 | "scrolled": true 989 | }, 990 | "outputs": [ 991 | { 992 | "data": { 993 | "text/plain": [ 994 | "(32493, 50946, 11475)" 995 | ] 996 | }, 997 | "execution_count": 20, 998 | "metadata": {}, 999 | "output_type": "execute_result" 1000 | } 1001 | ], 1002 | "source": [ 1003 | "opening_triplet = (s, poly_eval, opening_commit)\n", 1004 | "opening_triplet" 1005 | ] 1006 | }, 1007 | { 1008 | "cell_type": "markdown", 1009 | "id": "73c534c5", 1010 | "metadata": {}, 1011 | "source": [ 1012 | "### 2.5.4 Verification\n", 1013 | "\n", 1014 | "Now the Verifier has to check if $\\phi(s)$ is indeed evaluating $\\phi(x)$ on $s$" 1015 | ] 1016 | }, 1017 | { 1018 | "cell_type": "code", 1019 | "execution_count": 21, 1020 | "id": "256e982c", 1021 | "metadata": {}, 1022 | "outputs": [ 1023 | { 1024 | "data": { 1025 | "text/plain": [ 1026 | "True" 1027 | ] 1028 | }, 1029 | "execution_count": 21, 1030 | "metadata": {}, 1031 | "output_type": "execute_result" 1032 | } 1033 | ], 1034 | "source": [ 1035 | "e(commitment, g) == (e(opening_commit, PP[1] - s*g) + (poly_eval*e(g,g)))" 1036 | ] 1037 | }, 1038 | { 1039 | "cell_type": "markdown", 1040 | "id": "44ab81b8", 1041 | "metadata": {}, 1042 | "source": [ 1043 | "### 2.5.5 What if $\\alpha$ is leaked?\n", 1044 | "In that case, we could forge a polynomial that passes the proof without knowing the message at all...\n", 1045 | "In fact, we have not deleted `alpha`, so we can actually act as bad actors and forge said polynomial!" 1046 | ] 1047 | }, 1048 | { 1049 | "cell_type": "code", 1050 | "execution_count": 22, 1051 | "id": "7c80a808", 1052 | "metadata": {}, 1053 | "outputs": [ 1054 | { 1055 | "name": "stdout", 1056 | "output_type": "stream", 1057 | "text": [ 1058 | "Oh no, `alpha` leaked: 12816\n" 1059 | ] 1060 | } 1061 | ], 1062 | "source": [ 1063 | "print(f\"Oh no, `alpha` leaked: {alpha}\")" 1064 | ] 1065 | }, 1066 | { 1067 | "cell_type": "markdown", 1068 | "id": "1902514d", 1069 | "metadata": {}, 1070 | "source": [ 1071 | "So we know the original commitment:" 1072 | ] 1073 | }, 1074 | { 1075 | "cell_type": "code", 1076 | "execution_count": 23, 1077 | "id": "bc51b35b", 1078 | "metadata": {}, 1079 | "outputs": [ 1080 | { 1081 | "name": "stdout", 1082 | "output_type": "stream", 1083 | "text": [ 1084 | "Original commitment: 29729\n" 1085 | ] 1086 | } 1087 | ], 1088 | "source": [ 1089 | "print(f\"Original commitment: {commitment}\")" 1090 | ] 1091 | }, 1092 | { 1093 | "cell_type": "markdown", 1094 | "id": "42ede3ed", 1095 | "metadata": {}, 1096 | "source": [ 1097 | "Since the commitment is $g^{\\phi(\\alpha)}$, and $g$ is publicly known, we can also know a value for $\\phi(\\alpha)$" 1098 | ] 1099 | }, 1100 | { 1101 | "cell_type": "code", 1102 | "execution_count": 24, 1103 | "id": "3f186966", 1104 | "metadata": { 1105 | "scrolled": true 1106 | }, 1107 | "outputs": [ 1108 | { 1109 | "data": { 1110 | "text/plain": [ 1111 | "40538" 1112 | ] 1113 | }, 1114 | "execution_count": 24, 1115 | "metadata": {}, 1116 | "output_type": "execute_result" 1117 | } 1118 | ], 1119 | "source": [ 1120 | "commitment/g" 1121 | ] 1122 | }, 1123 | { 1124 | "cell_type": "markdown", 1125 | "id": "24ad812f", 1126 | "metadata": {}, 1127 | "source": [ 1128 | "Now we just need a polynomial that passes through $(\\alpha, \\phi(\\alpha))$... Just to make it nice, let's compute it as a quadratic function" 1129 | ] 1130 | }, 1131 | { 1132 | "cell_type": "code", 1133 | "execution_count": 25, 1134 | "id": "91eb13a9", 1135 | "metadata": {}, 1136 | "outputs": [], 1137 | "source": [ 1138 | "def make_faux_poly(F_p_X, alpha, phi_alpha, s, phi_s):\n", 1139 | " xs = [alpha, s]\n", 1140 | " ys = [phi_alpha, phi_s]\n", 1141 | "\n", 1142 | " F_p = F_p_X.base_ring()\n", 1143 | "\n", 1144 | " while (len(xs) != 3):\n", 1145 | " x = F_p.random_element()\n", 1146 | " if not (x in xs):\n", 1147 | " xs.append(x)\n", 1148 | " ys.append(F_p.random_element())\n", 1149 | " return F_p_X.lagrange_polynomial(zip(xs,ys))" 1150 | ] 1151 | }, 1152 | { 1153 | "cell_type": "code", 1154 | "execution_count": 26, 1155 | "id": "28b4b5bf", 1156 | "metadata": { 1157 | "scrolled": true 1158 | }, 1159 | "outputs": [ 1160 | { 1161 | "data": { 1162 | "text/plain": [ 1163 | "41411*x^2 + 11139*x + 44042" 1164 | ] 1165 | }, 1166 | "execution_count": 26, 1167 | "metadata": {}, 1168 | "output_type": "execute_result" 1169 | } 1170 | ], 1171 | "source": [ 1172 | "faux_poly = make_faux_poly(F_p_X, alpha, commitment/g, s, poly_eval)\n", 1173 | "faux_poly" 1174 | ] 1175 | }, 1176 | { 1177 | "cell_type": "markdown", 1178 | "id": "74fbdf69", 1179 | "metadata": {}, 1180 | "source": [ 1181 | "Let's run the protocol with this faux polynomial that generates an identical commitment" 1182 | ] 1183 | }, 1184 | { 1185 | "cell_type": "code", 1186 | "execution_count": 27, 1187 | "id": "535862ac", 1188 | "metadata": { 1189 | "scrolled": true 1190 | }, 1191 | "outputs": [ 1192 | { 1193 | "name": "stdout", 1194 | "output_type": "stream", 1195 | "text": [ 1196 | "The evaluation of s on the faux polynomial is 50946\n", 1197 | "The faux proof polynomial 41411*x + 30627\n", 1198 | "The faux opening triplet is (32493, 50946, 11475)\n", 1199 | "\n", 1200 | "Did we deceive the system and verified the faux polynomial 41411*x^2 + 11139*x + 44042\n", 1201 | "as if it was the true polynomial 38784*x^20 + 29502*x^19 + 27814*x^18 + 15919*x^17 + 4780*x^16 + 35151*x^15 + 16478*x^14 + 40979*x^13 + 24598*x^12 + 21339*x^11 + 50472*x^10 + 6735*x^9 + 8685*x^8 + 9664*x^7 + 13815*x^6 + 14359*x^5 + 8374*x^4 + 37855*x^3 + 11600*x^2 + 18403*x + 26889?\n", 1202 | "\n", 1203 | "True!!\n", 1204 | "\n" 1205 | ] 1206 | } 1207 | ], 1208 | "source": [ 1209 | "faux_poly_eval = faux_poly(s)\n", 1210 | "print(f\"The evaluation of s on the faux polynomial is {faux_poly_eval}\")\n", 1211 | "\n", 1212 | "faux_proof_poly = ((faux_poly - faux_poly_eval)/(x - s)).numerator()\n", 1213 | "print(f\"The faux proof polynomial {faux_proof_poly}\")\n", 1214 | "\n", 1215 | "faux_opening_commit = exponent_evaluation(PP, proof_poly, g)\n", 1216 | "opening_triplet = (s, faux_poly_eval, faux_opening_commit)\n", 1217 | "print(f\"The faux opening triplet is {opening_triplet}\")\n", 1218 | "\n", 1219 | "faux_verification = e(commitment, g) == (e(faux_opening_commit, PP[1] - s*g) + (faux_poly_eval*e(g,g)))\n", 1220 | "print(\n", 1221 | "f\"\"\"\n", 1222 | "Did we deceive the system and verified the faux polynomial {faux_poly}\n", 1223 | "as if it was the true polynomial {msg_poly}?\\n\\n{faux_verification}!!\n", 1224 | "\"\"\"\n", 1225 | ")" 1226 | ] 1227 | }, 1228 | { 1229 | "cell_type": "markdown", 1230 | "id": "6edd0261", 1231 | "metadata": {}, 1232 | "source": [ 1233 | "# 3. Closing remarks\n", 1234 | "\n", 1235 | "## 3.1 Recovering the message from the polynomial?\n", 1236 | "\n", 1237 | "Did you wonder how can we recover the original message from the polynomial?" 1238 | ] 1239 | }, 1240 | { 1241 | "cell_type": "code", 1242 | "execution_count": 28, 1243 | "id": "8835944a", 1244 | "metadata": {}, 1245 | "outputs": [], 1246 | "source": [ 1247 | "def decode_poly(poly):\n", 1248 | " images = []\n", 1249 | " for i in range(1, poly.degree() + 2):\n", 1250 | " images.append(poly(i))\n", 1251 | "\n", 1252 | " rs = RSCodec(10)\n", 1253 | " \n", 1254 | " return bytes(rs.decode(images)[0]).decode(\"utf-8\")" 1255 | ] 1256 | }, 1257 | { 1258 | "cell_type": "code", 1259 | "execution_count": 29, 1260 | "id": "a66dec00", 1261 | "metadata": {}, 1262 | "outputs": [ 1263 | { 1264 | "data": { 1265 | "text/plain": [ 1266 | "'Hello Zepp!'" 1267 | ] 1268 | }, 1269 | "execution_count": 29, 1270 | "metadata": {}, 1271 | "output_type": "execute_result" 1272 | } 1273 | ], 1274 | "source": [ 1275 | "decode_poly(msg_poly)" 1276 | ] 1277 | }, 1278 | { 1279 | "cell_type": "markdown", 1280 | "id": "4f948732", 1281 | "metadata": {}, 1282 | "source": [ 1283 | "Ever wondered what our faux polynomial was encoding?" 1284 | ] 1285 | }, 1286 | { 1287 | "cell_type": "code", 1288 | "execution_count": 30, 1289 | "id": "b458d728", 1290 | "metadata": {}, 1291 | "outputs": [ 1292 | { 1293 | "name": "stdout", 1294 | "output_type": "stream", 1295 | "text": [ 1296 | "Exception!\n", 1297 | "Encoded data makes no sense as a reed-solomon encoding\n" 1298 | ] 1299 | } 1300 | ], 1301 | "source": [ 1302 | "try:\n", 1303 | " print(decode_poly(faux_poly))\n", 1304 | "except:\n", 1305 | " print(\"Exception!\\nEncoded data makes no sense as a reed-solomon encoding\")" 1306 | ] 1307 | }, 1308 | { 1309 | "cell_type": "markdown", 1310 | "id": "a1fa64d4", 1311 | "metadata": {}, 1312 | "source": [ 1313 | "## 3.2 Abstraction!\n", 1314 | "\n", 1315 | "Let's abstract all of this into a class" 1316 | ] 1317 | }, 1318 | { 1319 | "cell_type": "code", 1320 | "execution_count": 31, 1321 | "id": "bab7a9e4", 1322 | "metadata": {}, 1323 | "outputs": [], 1324 | "source": [ 1325 | "from KZG import KZG" 1326 | ] 1327 | }, 1328 | { 1329 | "cell_type": "markdown", 1330 | "id": "ac221850", 1331 | "metadata": {}, 1332 | "source": [ 1333 | "Cool! Let's do all of the previous work again" 1334 | ] 1335 | }, 1336 | { 1337 | "cell_type": "code", 1338 | "execution_count": 32, 1339 | "id": "2601472e", 1340 | "metadata": { 1341 | "scrolled": true 1342 | }, 1343 | "outputs": [ 1344 | { 1345 | "data": { 1346 | "text/plain": [ 1347 | "KZG commitment class with Finite Field of size 44939 whose generator is 2\n", 1348 | "Toxic waste has NOT been deleted. Proceed with caution" 1349 | ] 1350 | }, 1351 | "execution_count": 32, 1352 | "metadata": {}, 1353 | "output_type": "execute_result" 1354 | } 1355 | ], 1356 | "source": [ 1357 | "# Initialize the helper class\n", 1358 | "kzg = KZG(leak=True)\n", 1359 | "kzg" 1360 | ] 1361 | }, 1362 | { 1363 | "cell_type": "markdown", 1364 | "id": "f7783762", 1365 | "metadata": {}, 1366 | "source": [ 1367 | "Let's create the polynomial encoding our message `\"Hello Zepp\"` and commit it to the KZG proving scheme" 1368 | ] 1369 | }, 1370 | { 1371 | "cell_type": "code", 1372 | "execution_count": 33, 1373 | "id": "06f38f86", 1374 | "metadata": {}, 1375 | "outputs": [ 1376 | { 1377 | "data": { 1378 | "text/plain": [ 1379 | "7170" 1380 | ] 1381 | }, 1382 | "execution_count": 33, 1383 | "metadata": {}, 1384 | "output_type": "execute_result" 1385 | } 1386 | ], 1387 | "source": [ 1388 | "msg = message_as_coords(\"Hello Zepp!\")\n", 1389 | "msg_poly_again = kzg.polynomial_ring()[0].lagrange_polynomial(msg)\n", 1390 | "commitment_again = kzg.make_commitment(msg_poly_again)\n", 1391 | "commitment_again" 1392 | ] 1393 | }, 1394 | { 1395 | "cell_type": "markdown", 1396 | "id": "7db8d9c6", 1397 | "metadata": {}, 1398 | "source": [ 1399 | "Let's create now the opening triplet. We'll choose an arbitrary `s`" 1400 | ] 1401 | }, 1402 | { 1403 | "cell_type": "code", 1404 | "execution_count": 34, 1405 | "id": "78a81e68", 1406 | "metadata": {}, 1407 | "outputs": [ 1408 | { 1409 | "data": { 1410 | "text/plain": [ 1411 | "26764" 1412 | ] 1413 | }, 1414 | "execution_count": 34, 1415 | "metadata": {}, 1416 | "output_type": "execute_result" 1417 | } 1418 | ], 1419 | "source": [ 1420 | "s_again = kzg.F_p.random_element()\n", 1421 | "s_again" 1422 | ] 1423 | }, 1424 | { 1425 | "cell_type": "code", 1426 | "execution_count": 35, 1427 | "id": "69bb6c40", 1428 | "metadata": {}, 1429 | "outputs": [ 1430 | { 1431 | "data": { 1432 | "text/plain": [ 1433 | "(26764, 28067, 36761)" 1434 | ] 1435 | }, 1436 | "execution_count": 35, 1437 | "metadata": {}, 1438 | "output_type": "execute_result" 1439 | } 1440 | ], 1441 | "source": [ 1442 | "opening_triplet_again = kzg.make_opening_triplet(s_again, msg_poly_again)\n", 1443 | "opening_triplet_again" 1444 | ] 1445 | }, 1446 | { 1447 | "cell_type": "markdown", 1448 | "id": "845e3bfc", 1449 | "metadata": {}, 1450 | "source": [ 1451 | "Let's verify it!" 1452 | ] 1453 | }, 1454 | { 1455 | "cell_type": "code", 1456 | "execution_count": 36, 1457 | "id": "930f0055", 1458 | "metadata": { 1459 | "scrolled": true 1460 | }, 1461 | "outputs": [ 1462 | { 1463 | "data": { 1464 | "text/plain": [ 1465 | "True" 1466 | ] 1467 | }, 1468 | "execution_count": 36, 1469 | "metadata": {}, 1470 | "output_type": "execute_result" 1471 | } 1472 | ], 1473 | "source": [ 1474 | "kzg.verify(commitment_again, opening_triplet_again)" 1475 | ] 1476 | }, 1477 | { 1478 | "cell_type": "markdown", 1479 | "id": "668ed424", 1480 | "metadata": {}, 1481 | "source": [ 1482 | "Let's forge a proof again" 1483 | ] 1484 | }, 1485 | { 1486 | "cell_type": "code", 1487 | "execution_count": 37, 1488 | "id": "3cecda42", 1489 | "metadata": {}, 1490 | "outputs": [ 1491 | { 1492 | "data": { 1493 | "text/plain": [ 1494 | "18795*x^2 + 30195*x + 12801" 1495 | ] 1496 | }, 1497 | "execution_count": 37, 1498 | "metadata": {}, 1499 | "output_type": "execute_result" 1500 | } 1501 | ], 1502 | "source": [ 1503 | "faux_poly_again = make_faux_poly(\n", 1504 | " kzg.polynomial_ring()[0],\n", 1505 | " kzg.alpha,\n", 1506 | " commitment_again/kzg.g,\n", 1507 | " *opening_triplet_again[:2]\n", 1508 | ")\n", 1509 | "faux_poly_again" 1510 | ] 1511 | }, 1512 | { 1513 | "cell_type": "code", 1514 | "execution_count": 38, 1515 | "id": "320c85db", 1516 | "metadata": { 1517 | "scrolled": true 1518 | }, 1519 | "outputs": [ 1520 | { 1521 | "data": { 1522 | "text/plain": [ 1523 | "7170" 1524 | ] 1525 | }, 1526 | "execution_count": 38, 1527 | "metadata": {}, 1528 | "output_type": "execute_result" 1529 | } 1530 | ], 1531 | "source": [ 1532 | "faux_commitment_again = kzg.make_commitment(faux_poly_again)\n", 1533 | "faux_commitment_again" 1534 | ] 1535 | }, 1536 | { 1537 | "cell_type": "code", 1538 | "execution_count": 39, 1539 | "id": "a30043a5", 1540 | "metadata": {}, 1541 | "outputs": [ 1542 | { 1543 | "data": { 1544 | "text/plain": [ 1545 | "(26764, 28067, 36761)" 1546 | ] 1547 | }, 1548 | "execution_count": 39, 1549 | "metadata": {}, 1550 | "output_type": "execute_result" 1551 | } 1552 | ], 1553 | "source": [ 1554 | "faux_opening_triplet = kzg.make_opening_triplet(s_again, faux_poly_again)\n", 1555 | "faux_opening_triplet" 1556 | ] 1557 | }, 1558 | { 1559 | "cell_type": "code", 1560 | "execution_count": 40, 1561 | "id": "7fe8d97d", 1562 | "metadata": {}, 1563 | "outputs": [ 1564 | { 1565 | "data": { 1566 | "text/plain": [ 1567 | "True" 1568 | ] 1569 | }, 1570 | "execution_count": 40, 1571 | "metadata": {}, 1572 | "output_type": "execute_result" 1573 | } 1574 | ], 1575 | "source": [ 1576 | "kzg.verify(faux_commitment_again, faux_opening_triplet)" 1577 | ] 1578 | } 1579 | ], 1580 | "metadata": { 1581 | "kernelspec": { 1582 | "display_name": "SageMath 9.5", 1583 | "language": "sage", 1584 | "name": "sagemath" 1585 | }, 1586 | "language_info": { 1587 | "codemirror_mode": { 1588 | "name": "ipython", 1589 | "version": 3 1590 | }, 1591 | "file_extension": ".py", 1592 | "mimetype": "text/x-python", 1593 | "name": "python", 1594 | "nbconvert_exporter": "python", 1595 | "pygments_lexer": "ipython3", 1596 | "version": "3.10.12" 1597 | } 1598 | }, 1599 | "nbformat": 4, 1600 | "nbformat_minor": 5 1601 | } 1602 | --------------------------------------------------------------------------------