From 24f9c03a6a380a9fb276e7b6b79f410a7f99bbba Mon Sep 17 00:00:00 2001 From: "Li, Amazing Ang" Date: Mon, 5 Aug 2024 20:56:06 +0800 Subject: [PATCH] update en code, and fix tutorial --- 24_Polynomial/Polynomial.ipynb | 2 +- 27_GaloisField/GaloisField.ipynb | 4 +- 27_GaloisField/readme.md | 4 +- 32_ECC/ECC.ipynb | 1 + 32_ECC/readme.md | 1 + Languages/en/24_Polynomial/Polynomial.ipynb | 115 +++++ Languages/en/25_PolyRing/PolyRing.ipynb | 111 +++++ .../en/26_FieldExtension/FieldExtension.ipynb | 78 ++++ Languages/en/27_GaloisField/GaloisField.ipynb | 105 +++++ Languages/en/27_GaloisField/readme.md | 4 +- Languages/en/28_Quadratic/Quadratic.ipynb | 82 ++++ Languages/en/28_Quadratic/readme.md | 17 +- .../en/29_EllipticCurve/EllipticCurve.ipynb | 366 +++++++++++++++ Languages/en/30_FiniteEC/FiniteEC.ipynb | 425 ++++++++++++++++++ Languages/en/31_ECDLP/ECDLP.ipynb | 184 ++++++++ Languages/en/32_ECC/ECC.ipynb | 225 ++++++++++ Languages/en/32_ECC/readme.md | 1 + .../en/35_TorsionGroup/TorsionGroup.sage | 27 ++ Languages/en/37_MillerAlgo/WeilPairing.sage | 27 ++ Languages/en/38_TatePairing/Ate.ipynb | 94 ++++ .../40_PopularCurves/40_PopularCurves.ipynb | 368 +++++++++++++++ Languages/en/40_PopularCurves/readme.md | 2 +- 22 files changed, 2219 insertions(+), 24 deletions(-) create mode 100644 Languages/en/24_Polynomial/Polynomial.ipynb create mode 100644 Languages/en/25_PolyRing/PolyRing.ipynb create mode 100644 Languages/en/26_FieldExtension/FieldExtension.ipynb create mode 100644 Languages/en/27_GaloisField/GaloisField.ipynb create mode 100644 Languages/en/28_Quadratic/Quadratic.ipynb create mode 100644 Languages/en/29_EllipticCurve/EllipticCurve.ipynb create mode 100644 Languages/en/30_FiniteEC/FiniteEC.ipynb create mode 100644 Languages/en/31_ECDLP/ECDLP.ipynb create mode 100644 Languages/en/32_ECC/ECC.ipynb create mode 100644 Languages/en/35_TorsionGroup/TorsionGroup.sage create mode 100644 Languages/en/37_MillerAlgo/WeilPairing.sage create mode 100644 Languages/en/38_TatePairing/Ate.ipynb create mode 100644 Languages/en/40_PopularCurves/40_PopularCurves.ipynb diff --git a/24_Polynomial/Polynomial.ipynb b/24_Polynomial/Polynomial.ipynb index 2c8e569..5910ca1 100644 --- a/24_Polynomial/Polynomial.ipynb +++ b/24_Polynomial/Polynomial.ipynb @@ -67,7 +67,7 @@ " Y = list(data)\n", "\n", "# 使用 lagrange 函数构造拉格朗日插值多项式\n", - "interpolation_polynomial = interpolating_poly(len(data_points), x, X, Y)\n", + "interpolation_polynomial = interpolating_poly(len(data), x, X, Y)\n", "\n", "# 输出插值多项式\n", "print(\"拉格朗日多项式:\", interpolation_polynomial)\n", diff --git a/27_GaloisField/GaloisField.ipynb b/27_GaloisField/GaloisField.ipynb index 139a8b9..3afe75c 100644 --- a/27_GaloisField/GaloisField.ipynb +++ b/27_GaloisField/GaloisField.ipynb @@ -44,9 +44,9 @@ "print(\"有限域 GF(4) 的元素\", GF4.elements)\n", "\n", "print(\"有限域 GF(4) 的加法表\")\n", - "print(GF7.arithmetic_table(\"+\"))\n", + "print(GF4.arithmetic_table(\"+\"))\n", "print(\"有限域 GF(4) 的乘法表\")\n", - "print(GF7.arithmetic_table(\"*\"))\n", + "print(GF4.arithmetic_table(\"*\"))\n", "\n", "## 输出示例\n", "# 有限域 GF(4) 的性质 Galois Field:\n", diff --git a/27_GaloisField/readme.md b/27_GaloisField/readme.md index a967bf8..e81783b 100644 --- a/27_GaloisField/readme.md +++ b/27_GaloisField/readme.md @@ -122,9 +122,9 @@ print("有限域 GF(4) 的性质", GF4.properties) print("有限域 GF(4) 的元素", GF4.elements) print("有限域 GF(4) 的加法表") -print(GF7.arithmetic_table("+")) +print(GF4.arithmetic_table("+")) print("有限域 GF(4) 的乘法表") -print(GF7.arithmetic_table("*")) +print(GF4.arithmetic_table("*")) ## 输出示例 # 有限域 GF(4) 的性质 Galois Field: diff --git a/32_ECC/ECC.ipynb b/32_ECC/ECC.ipynb index affd831..5f18121 100644 --- a/32_ECC/ECC.ipynb +++ b/32_ECC/ECC.ipynb @@ -78,6 +78,7 @@ "# EC ElGamal\n", "\n", "from py_ecc.secp256k1 import secp256k1\n", + "from random import randint\n", "\n", "def elgamal_encrypt(G, Y, M):\n", " k = randint(1, secp256k1.N - 1)\n", diff --git a/32_ECC/readme.md b/32_ECC/readme.md index 373d604..686f4d7 100644 --- a/32_ECC/readme.md +++ b/32_ECC/readme.md @@ -132,6 +132,7 @@ Bob 收到密文 $(C_1, C_2)$ 后,需要使用私钥 $x$ 进行解密: ```python from py_ecc.secp256k1 import secp256k1 +from random import randint def elgamal_encrypt(G, Y, M): k = randint(1, secp256k1.N - 1) diff --git a/Languages/en/24_Polynomial/Polynomial.ipynb b/Languages/en/24_Polynomial/Polynomial.ipynb new file mode 100644 index 0000000..2053bd2 --- /dev/null +++ b/Languages/en/24_Polynomial/Polynomial.ipynb @@ -0,0 +1,115 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "a20a2420", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original polynomial: x**3 - 10*x**2 + 31*x - 30\n", + "Factorization of polynomial: (x - 5)*(x - 3)*(x - 2)\n" + ] + } + ], + "source": [ + "# Prime Factorization of polynomials\n", + "from sympy import symbols, factor\n", + "from sympy.abc import x\n", + "\n", + "polynomial = x**3 -10*x**2 + 31*x - 30\n", + "\n", + "factored_polynomial = factor(polynomial)\n", + "\n", + "print(\"Original polynomial:\", polynomial)\n", + "print(\"Factorization of polynomial:\", factored_polynomial)\n", + "\n", + "# Output\n", + "# Original polynomial: x**3 - 10*x**2 + 31*x - 30\n", + "# Factorization of polynomial: (x - 5)*(x - 3)*(x - 2)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "89ff714b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Lagrange Polynomial: (x - 3)*(x - 2) - 3*(x - 3)*(x - 1) + 4*(x - 2)*(x - 1)\n", + "Simplified Polynomial: 2*x**2 - 5*x + 5\n", + "Value at x=4: 8\n" + ] + } + ], + "source": [ + "# Lagrange Interpolation Method\n", + "from sympy import symbols\n", + "from sympy.abc import x\n", + "from sympy.polys.polyfuncs import interpolating_poly\n", + "\n", + "# Given interpolation points and their corresponding function values\n", + "data = [(1, 2), (2, 3), (3, 8)]\n", + "\n", + "if isinstance(data, dict):\n", + " X, Y = list(zip(*data.items()))\n", + "else:\n", + " if isinstance(data[0], tuple):\n", + " X, Y = list(zip(*data))\n", + " else:\n", + " X = list(range(1, n + 1))\n", + " Y = list(data)\n", + "\n", + "# Use the interpolating_poly function to construct the Lagrange interpolation polynomial\n", + "interpolation_polynomial = interpolating_poly(len(data), x, X, Y)\n", + "\n", + "# Output the interpolation polynomial\n", + "print(\"Lagrange Polynomial:\", interpolation_polynomial)\n", + "print(\"Simplified Polynomial:\", interpolation_polynomial.expand())\n", + "# Calculate the value at x=4 using the interpolation polynomial\n", + "result = interpolation_polynomial.subs(x, 3)\n", + "print(\"Value at x=4:\", result)\n", + "\n", + "# Sample Output\n", + "# Lagrange Polynomial: (x - 3)*(x - 2) - 3*(x - 3)*(x - 1) + 4*(x - 2)*(x - 1)\n", + "# Simplified Polynomial: 2*x**2 - 5*x + 5\n", + "# Value at x=4: 8\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6f1b05a6", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Languages/en/25_PolyRing/PolyRing.ipynb b/Languages/en/25_PolyRing/PolyRing.ipynb new file mode 100644 index 0000000..183c8f5 --- /dev/null +++ b/Languages/en/25_PolyRing/PolyRing.ipynb @@ -0,0 +1,111 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "a20a2420", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Polynomial p1: Poly(2*x**3 - 2*x**2 + x + 1, x, modulus=5)\n", + "Polynomial p2: Poly(x**2 - x - 1, x, modulus=5)\n", + "Addition result: Poly(2*x**3 - x**2, x, modulus=5)\n", + "Subtraction result: Poly(2*x**3 + 2*x**2 + 2*x + 2, x, modulus=5)\n", + "Multiplication result: Poly(2*x**5 + x**4 + x**3 + 2*x**2 - 2*x - 1, x, modulus=5)\n", + "Modulo operation: Poly(-2*x + 1, x, modulus=5)\n", + "Quotient: Poly(2*x, x, modulus=5)\n", + "Greatest common divisor: Poly(x + 2, x, modulus=5)\n", + "Result of substituting x = 2 into polynomial p1: 1\n" + ] + } + ], + "source": [ + "from sympy import symbols, Poly, GF, gcd\n", + "\n", + "# Define symbolic variables\n", + "x = symbols('x')\n", + "\n", + "# Define polynomials with coefficients in the ring of integers modulo 5\n", + "p1 = Poly(2*x**3 + 3*x**2 + x + 1, x, domain=GF(5))\n", + "p2 = Poly(x**2 + 4*x + 4, x, domain=GF(5))\n", + "\n", + "# Print the polynomials\n", + "print(\"Polynomial p1:\", p1)\n", + "print(\"Polynomial p2:\", p2)\n", + "\n", + "\n", + "# Polynomial addition\n", + "add_result = p1 + p2\n", + "print(\"Addition result:\", add_result)\n", + "\n", + "# Polynomial subtraction\n", + "sub_result = p1 - p2\n", + "print(\"Subtraction result:\", sub_result)\n", + "\n", + "# Polynomial multiplication\n", + "mul_result = p1 * p2\n", + "print(\"Multiplication result:\", mul_result)\n", + "\n", + "# Note: In the ring of integers modulo n, polynomial division is not always possible\n", + "# However, modulo operation can be performed\n", + "mod_result = p1 % p2\n", + "print(\"Modulo operation:\", mod_result)\n", + "\n", + "# Quotient\n", + "quotient_result = p1 // p2\n", + "print(\"Quotient:\", quotient_result)\n", + "\n", + "# Greatest common divisor\n", + "gcd_result = gcd(p1, p2)\n", + "print(\"Greatest common divisor:\", gcd_result)\n", + "\n", + "# Substituting values to solve\n", + "value_result = p1.subs(x, 2)\n", + "print(\"Result of substituting x = 2 into polynomial p1:\", value_result)\n", + "\n", + "## Output Example\n", + "# Polynomial p1: Poly(2*x**3 - 2*x**2 + x + 1, x, modulus=5)\n", + "# Polynomial p2: Poly(x**2 - x - 1, x, modulus=5)\n", + "# Addition result: Poly(2*x**3 - x**2, x, modulus=5)\n", + "# Subtraction result: Poly(2*x**3 + 2*x**2 + 2*x + 2, x, modulus=5)\n", + "# Multiplication result: Poly(2*x**5 + x**4 + x**3 + 2*x**2 - 2*x - 1, x, modulus=5)\n", + "# Modulo operation: Poly(-2*x + 1, x, modulus=5)\n", + "# Quotient: Poly(2*x, x, modulus=5)\n", + "# Greatest common divisor: Poly(x + 2, x, modulus=5)\n", + "# Result of substituting x = 2 into polynomial p1: 1\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6f1b05a6", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Languages/en/26_FieldExtension/FieldExtension.ipynb b/Languages/en/26_FieldExtension/FieldExtension.ipynb new file mode 100644 index 0000000..59bbb19 --- /dev/null +++ b/Languages/en/26_FieldExtension/FieldExtension.ipynb @@ -0,0 +1,78 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "04209408", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Extension Field: QQ\n", + "a = ANP([MPQ(1,2)], [MPQ(1,1), MPQ(0,1), MPQ(-2,1)], QQ)\n", + "b = ANP([MPQ(3,1)], [MPQ(1,1), MPQ(0,1), MPQ(-2,1)], QQ)\n", + "a + b = ANP([MPQ(7,2)], [MPQ(1,1), MPQ(0,1), MPQ(-2,1)], QQ)\n", + "a * b = ANP([MPQ(3,2)], [MPQ(1,1), MPQ(0,1), MPQ(-2,1)], QQ)\n" + ] + } + ], + "source": [ + "from sympy import symbols, QQ, RootOf\n", + "\n", + "# Define symbolic variables\n", + "x = symbols('x')\n", + "\n", + "# Construct the algebraic extension QQ/(sqrt(2)) (which is a root of x^2 - 2 = 0) over the rational number field QQ\n", + "ext_field = QQ.algebraic_field(RootOf(x**2 - 2, 1))\n", + "\n", + "# Output the extension field and algebraic element\n", + "print(\"Extension Field: \", ext_field)\n", + "# QQ\n", + "\n", + "\n", + "\n", + "# Calculation on extention field\n", + "a = ext_field(QQ(1, 2))\n", + "b = ext_field(3)\n", + "c = a + b\n", + "d = a * b\n", + "\n", + "print(\"a =\", a)\n", + "print(\"b =\", b)\n", + "print(\"a + b =\", c)\n", + "print(\"a * b =\", d)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4c678725", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Languages/en/27_GaloisField/GaloisField.ipynb b/Languages/en/27_GaloisField/GaloisField.ipynb new file mode 100644 index 0000000..1665085 --- /dev/null +++ b/Languages/en/27_GaloisField/GaloisField.ipynb @@ -0,0 +1,105 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 3, + "id": "a20a2420", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Properties of the finite field GF(4): Galois Field:\n", + " name: GF(2^2)\n", + " characteristic: 2\n", + " degree: 2\n", + " order: 4\n", + " irreducible_poly: x^2 + x + 1\n", + " is_primitive_poly: True\n", + " primitive_element: x\n", + "Elements of the finite field GF(4): [0 1 2 3]\n", + "Addition table of the finite field GF(4)\n" + ] + }, + { + "ename": "NameError", + "evalue": "name 'GF7' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m/var/folders/xz/sj6gs0f150n67pbx3mg32v3c0000gn/T/ipykernel_31224/1038025911.py\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 6\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 7\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Addition table of the finite field GF(4)\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 8\u001b[0;31m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mGF7\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marithmetic_table\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"+\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 9\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Multiplication table of the finite field GF(4)\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 10\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mGF7\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marithmetic_table\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"*\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mNameError\u001b[0m: name 'GF7' is not defined" + ] + } + ], + "source": [ + "import galois\n", + "\n", + "GF4 = galois.GF(4)\n", + "print(\"Properties of the finite field GF(4):\", GF4.properties)\n", + "print(\"Elements of the finite field GF(4):\", GF4.elements)\n", + "\n", + "print(\"Addition table of the finite field GF(4)\")\n", + "print(GF4.arithmetic_table(\"+\"))\n", + "print(\"Multiplication table of the finite field GF(4)\")\n", + "print(GF4.arithmetic_table(\"*\"))\n", + "\n", + "## Output Example\n", + "# Properties of the finite field GF(4): Galois Field:\n", + "# name: GF(2^2)\n", + "# characteristic: 2\n", + "# degree: 2\n", + "# order: 4\n", + "# irreducible_poly: x^2 + x + 1\n", + "# is_primitive_poly: True\n", + "# primitive_element: x\n", + "# Elements of the finite field GF(4): [0 1 2 3]\n", + "# Addition table of the finite field GF(4)\n", + "# x + y | 0 1 2 3 \n", + "# ------|------------\n", + "# 0 | 0 1 2 3 \n", + "# 1 | 1 0 3 2 \n", + "# 2 | 2 3 0 1 \n", + "# 3 | 3 2 1 0 \n", + "# Multiplication table of the finite field GF(4)\n", + "# x * y | 0 1 2 3 \n", + "# ------|------------\n", + "# 0 | 0 0 0 0 \n", + "# 1 | 0 1 2 3 \n", + "# 2 | 0 2 3 1 \n", + "# 3 | 0 3 1 2 \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e98f60f5", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Languages/en/27_GaloisField/readme.md b/Languages/en/27_GaloisField/readme.md index 762f4f6..2e31c3e 100644 --- a/Languages/en/27_GaloisField/readme.md +++ b/Languages/en/27_GaloisField/readme.md @@ -122,9 +122,9 @@ print("Properties of the finite field GF(4):", GF4.properties) print("Elements of the finite field GF(4):", GF4.elements) print("Addition table of the finite field GF(4)") -print(GF7.arithmetic_table("+")) +print(GF4.arithmetic_table("+")) print("Multiplication table of the finite field GF(4)") -print(GF7.arithmetic_table("*")) +print(GF4.arithmetic_table("*")) ## Output Example # Properties of the finite field GF(4): Galois Field: diff --git a/Languages/en/28_Quadratic/Quadratic.ipynb b/Languages/en/28_Quadratic/Quadratic.ipynb new file mode 100644 index 0000000..822ac70 --- /dev/null +++ b/Languages/en/28_Quadratic/Quadratic.ipynb @@ -0,0 +1,82 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "a20a2420", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 is not a quadratic residue modulo 7\n", + "1 is a quadratic residue modulo 7\n", + "2 is a quadratic residue modulo 7\n", + "3 is not a quadratic residue modulo 7\n", + "4 is a quadratic residue modulo 7\n", + "5 is not a quadratic residue modulo 7\n", + "6 is not a quadratic residue modulo 7\n" + ] + } + ], + "source": [ + "def legendre_symbol(a, p):\n", + " \"\"\"Calculate the Legendre symbol (a/p)\"\"\"\n", + " legendre = pow(a, (p - 1) // 2, p)\n", + " return -1 if legendre == p - 1 else legendre\n", + "\n", + "def is_quadratic_residue(a, p):\n", + " \"\"\"Check if a is a quadratic residue modulo p\"\"\"\n", + " legendre = legendre_symbol(a, p)\n", + " return legendre == 1\n", + "\n", + "# Example\n", + "p = 7\n", + "for a in range(p):\n", + " if is_quadratic_residue(a, p):\n", + " print(f\"{a} is a quadratic residue modulo {p}\")\n", + " else:\n", + " print(f\"{a} is not a quadratic residue modulo {p}\")\n", + "\n", + "## Output Example \n", + "# 0 is not a quadratic residue modulo 7\n", + "# 1 is a quadratic residue modulo 7\n", + "# 2 is a quadratic residue modulo 7\n", + "# 3 is not a quadratic residue modulo 7\n", + "# 4 is a quadratic residue modulo 7\n", + "# 5 is not a quadratic residue modulo 7\n", + "# 6 is not a quadratic residue modulo 7\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a3263e4d", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Languages/en/28_Quadratic/readme.md b/Languages/en/28_Quadratic/readme.md index 2ac40f6..15535b7 100644 --- a/Languages/en/28_Quadratic/readme.md +++ b/Languages/en/28_Quadratic/readme.md @@ -121,19 +121,4 @@ Finding modular square roots becomes as challenging as factoring large numbers w ## 6. Summary -In this chapter, we learned about quadratic residues, Legendre symbols, and Euler's criterion. Quadratic residues are commonly employed in the field of cryptography for constructing encryption algorithms. - - -The direct translation has the following issues: -1. The abbreviation "WTF" at the beginning of the title does not conform to standard English expression habits. -2. The expression "Let's take an example with $n = 7$" could be clearer by using a more direct and standard form of language. -3. The phrase "each occupying half of the group" is slightly obscure and could be improved for better clarity. -4. In the proof section, the statement "Proof completed" could be more informative and clearer. -5. The phrase "Taking $Z_7^*$ as an example" could be rephrased for better clarity. -6. In the proof section, the sentence "Now let's discuss the case where $a$ and $p$ are coprime" is slightly unclear and could be improved. -7. The phrase "根据拉格朗日定理" could be translated more accurately to "According to Lagrange's theorem" for better clarity and adherence to English expression habits. -8. In the code example, the comment "示例" could be translated more accurately to "Example". -9. The phrase "当 $n$ 为大合数时" could be translated more accurately to "When $n$ is a large composite number" for clearer communication. -10. The phrase "这一特点" could be translated more accurately to "This characteristic" for better clarity. -11. The phrase "这一讲" could be translated more accurately to "In this chapter" for better alignment with English expression habits. - \ No newline at end of file +In this chapter, we learned about quadratic residues, Legendre symbols, and Euler's criterion. Quadratic residues are commonly employed in the field of cryptography for constructing encryption algorithms. \ No newline at end of file diff --git a/Languages/en/29_EllipticCurve/EllipticCurve.ipynb b/Languages/en/29_EllipticCurve/EllipticCurve.ipynb new file mode 100644 index 0000000..5fede5a --- /dev/null +++ b/Languages/en/29_EllipticCurve/EllipticCurve.ipynb @@ -0,0 +1,366 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 11, + "id": "a20a2420", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/usr/local/lib/python3.7/site-packages/ipykernel_launcher.py:10: RuntimeWarning: invalid value encountered in sqrt\n", + " # Remove the CWD from sys.path while we load stuff.\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "\n", + "# 定义椭圆曲线 y^2 = x^3 + ax + b,这里使用示例参数\n", + "a = -1\n", + "b = 1\n", + "\n", + "# 生成椭圆曲线上的点\n", + "x = np.linspace(-2, 3, 400)\n", + "y = np.sqrt(x**3 + a*x + b)\n", + "y_neg = -y\n", + "\n", + "# 绘制椭圆曲线\n", + "plt.figure(figsize=(12, 8))\n", + "plt.plot(x, y, 'b')\n", + "plt.plot(x, y_neg, 'b')\n", + "\n", + "# 点 P 和 Q: x_p != x_q\n", + "x_p = 0\n", + "x_q = 1\n", + "P = np.array([x_p, np.sqrt(x_p**3 + a*x_p + b)])\n", + "Q = np.array([x_q, np.sqrt(x_q**3 + a*x_q + b)])\n", + "\n", + "# 计算 R\n", + "if np.array_equal(P, Q):\n", + " # 点加倍情况\n", + " lambda_ = (3 * P[0]**2 + a) / (2 * P[1])\n", + "else:\n", + " # 普通情况\n", + " lambda_ = (Q[1] - P[1]) / (Q[0] - P[0])\n", + "\n", + "x3 = lambda_**2 - P[0] - Q[0]\n", + "y3 = lambda_ * (P[0] - x3) - P[1]\n", + "R = np.array([x3, -y3]) \n", + "R_dot = np.array([x3, y3]) # 取反射点以符合椭圆曲线的加法规则\n", + "\n", + "# 计算经过 P 和 Q 的直线\n", + "line_x = np.linspace(-2, 2, 2)\n", + "line_y = lambda_ * (line_x - P[0]) + P[1]\n", + "plt.plot(line_x, line_y, 'r--', label='Line through P and Q')\n", + "\n", + "# 绘制点\n", + "plt.plot(P[0], P[1], 'go', markersize=10, label='P')\n", + "plt.plot(Q[0], Q[1], 'ro', markersize=10, label='Q')\n", + "plt.plot(R[0], R[1], 'ko', markersize=10, label='R')\n", + "plt.plot(R_dot[0], R_dot[1], 'ko', markersize=10, label='R` (P+Q)')\n", + "\n", + "# 添加注释\n", + "plt.annotate('P', xy=P, xytext=(P[0], P[1] + 10), textcoords='offset points')\n", + "plt.annotate('Q', xy=Q, xytext=(Q[0], Q[1] + 10), textcoords='offset points')\n", + "plt.annotate('R', xy=R, xytext=(R[0], R[1] + 10), textcoords='offset points')\n", + "plt.annotate('R` (P+Q)', xy=R_dot, xytext=(R_dot[0], R_dot[1] + 10), textcoords='offset points')\n", + "\n", + "plt.axhline(0, color='black',linewidth=0.5)\n", + "plt.axvline(0, color='black',linewidth=0.5)\n", + "plt.grid(color = 'gray', linestyle = '--', linewidth = 0.5)\n", + "plt.legend()\n", + "plt.title('Elliptic Curve Addition Examples')\n", + "plt.xlabel('x')\n", + "plt.ylabel('y')\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "a3263e4d", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/usr/local/lib/python3.7/site-packages/ipykernel_launcher.py:3: RuntimeWarning: invalid value encountered in sqrt\n", + " This is separate from the ipykernel package so we can avoid doing imports until\n" + ] + }, + { + "data": { + "image/png": "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y8sJDw9vkvsW3nEch9zcXFatWsX++++/y/U9e/Y0UJXw2t577226BOEBGc/BJxnbwY85N/tpG9u2bSMpKanJGmeAwsLCJrtv4R2lFElJSXW+S5GRkeFxRcKEtWvXmi5BeEDGc/BJxnbwY87NvnkGmrRx9uL+hXd2l2VsbKyHlQhTIiKa/RtuwgUZz8EnGdvBjzkHonluatHR0bu9Pi4ubpfvPffcc0yaNKnRj52Xl8d//vOf7V/PmTOHs846q9H32xBuH/Pyyy9n//33Jz09nR49evDdd9+FvJYxY8bwxBNP1Hrd888/zyGHHMIhhxxCr169mDNnToPu28QGPMJ7iYmJpksQHpDxHHySsR38mLM0zy7sycmI11xzDZdeemmjH3vn5tktUysKPP7442RmZvLII49w9dVXe/a4H3zwAf/3f//H119/zc8//8zzzz/PxRdfzOrVq13fh5wUaoeNGzeaLkF4QMZz8EnGdvBjztI8uxAVFdXgn6l5hLRv377ccccdHHnkkaSmpvLVV18BusG97bbbOOKII+jevTv/93//t8v93HnnnWRnZ5Oens5tt90GQEFBAUOHDuWQQw7hoosuwnEcQP91dscdd9CjRw/+97//8eabb9KtWzfS0tK44447tt9nzSPlU6ZM4fLLLwcgOzubo446im7dunHPPffscLu6HrMuxx9/PCtWrNjl+9OnT6d3794cfvjhnHLKKaxfv377v9cVV1xB3759OeCAA3j66ae3/8xDDz1Eamoqxx57LMuXL6/18R599FEef/zx7bsQ9ejRg+HDhzNhwoTd1lmTH/+6FaEnR57tIOM5+CRjO/gx5+BN/uvbd9fvnXsujBwJhYVwxhm7Xn/55fqycSMMHbrjdXPmhOQobllZGT/++CMfffQR999/PzNnzuTFF1+kVatWzJ07l+LiYvr06UO/fv12WAnikUceYfHixWRmZlaWM4cFCxawZMkSOnbsSJ8+ffjmm2849thjAUhKSmL+/PmsWbOGo446ioyMDFq3bk2/fv2YNm0agwYNqrPGm266iZtuuokLLriA5557bofrdveYtZk+fTrdunXb5fvHHnss33//PUopJk6cyGOPPcbYsWMB+Pnnn5k9ezb5+fkcfPDBXHvttSxcuJC33nqLzMxMysrK6NGjR61n3i5ZsmSX7/fq1YuXX365zhp3lp+f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o95RZepU+OILEioqcNc+JxAe/he9eulzA088US+lJc20tzZt0g1yVZNc82N+jSCjouo+aa9qDeE777yTRx55xMwTEZ5ZuzaPH35I5K239FHpbdv0vPSLLtKXQw4xXaFoLHlttoPJnOXIcyNkZmbKSQnNUU6OPsy03376kOT11+tO6q67uHj5ciZOm7bDKhs7i4yM5IwzLqFbN/j8c7383cMP67vs0kWvRVu1Jm23bvLWcGOUluq333/9VcdWc6ONrCzIza2+bViYXrLsoIP0CWOpqfrz1FS9tFl9J+2tW7euKZ+K8InlyzMZNKgvgwbpP7DefRdefx3+9S+9qmTPnrqJvvDChm+oJPxBXpvt4Mec5cizC8uXL+fggw/2/HHFHsjKqt7lLyNDN8zPPKPPFly+XHe96PWdu3fvTmHhblbbaNGChQsXbl/vuaAAvvlmxzVp//xT3zYmRh/lPPTQHS/77aebPdtt3aqXDly9Wq9oUdUgV31ctUovUVYlLEy/HX/AAboprtkg779/487bvP766/VJoCLQ6vq9vW4dvPWWbqTnzdOrdgwcCFddBaeeKiumNCfy2mwHkznLkWcRfCedpJfTAH04+NFH9aLOoLuxysYZICUlhSlTptS7znPNjVLi4vQKdaedpr92HPjtN91E//gjLFqkj1BX7ZgGejW7Aw/UDd/+++tmsOrzzp31fNvmOgezogLy8vQfEFWX9et1g7xqVXWzvHq1XlpsZx076n+Hqp3kkpOrP3buLEfyRdPo0AFuvllfli3Ty1i+8or+e3vfffXOpMOH68+FEKI20jy7sHbtWvnr1k8cB+bPrz66/PHHugM94wx9CGnwYN191aN///4sXLiQcePG8dprr5Gfn098fDyXXHIJo0aNqn2HwRqU0o1ecjKce2719/Py9OoOS5boS3a2Xl/6s892XAoN9PbLe++tX9A7dNCft20LiYnVl9at9cf4eH37mBh9iY7e88a7vFzv/VJcrOeCFhbqBnfLFn2p+rzm9zZt2rFR/vPPHY8WVwkL089jn330/OOTT9af17zsu2/1HGSvFRQUmHlg4Sk3v7e7dNHTsR56SM+LnjgR7r9fX047TZ+EOGCAPjot/Edem+3gx5xl2oYLclKCT/z8s351mzpVv98fHq737f3f/0KyJXZT5+w4uuGsms+7ejWsXavfRl63Tn++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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# 生成椭圆曲线上的点\n", + "x = np.linspace(-2, 3, 400)\n", + "y = np.sqrt(x**3 + a*x + b)\n", + "y_neg = -y\n", + "\n", + "# 绘制椭圆曲线\n", + "plt.figure(figsize=(12, 8))\n", + "plt.plot(x, y, 'b')\n", + "plt.plot(x, y_neg, 'b')\n", + "\n", + "# 点 P 和 Q: x_p != x_q\n", + "x_p = 1\n", + "x_q = 1\n", + "P = np.array([x_p, np.sqrt(x_p**3 + a*x_p + b)])\n", + "Q = np.array([x_q, np.sqrt(x_q**3 + a*x_q + b)])\n", + "\n", + "# 计算 R\n", + "if np.array_equal(P, Q):\n", + " # 点加倍情况\n", + " lambda_ = (3 * P[0]**2 + a) / (2 * P[1])\n", + "else:\n", + " # 普通情况\n", + " lambda_ = (Q[1] - P[1]) / (Q[0] - P[0])\n", + "\n", + "x3 = lambda_**2 - P[0] - Q[0]\n", + "y3 = lambda_ * (P[0] - x3) - P[1]\n", + "R = np.array([x3, -y3]) \n", + "R_dot = np.array([x3, y3]) # 取反射点以符合椭圆曲线的加法规则\n", + "\n", + "# 计算经过 P 和 Q 的直线\n", + "line_x = np.linspace(-2, 2, 2)\n", + "line_y = lambda_ * (line_x - P[0]) + P[1]\n", + "plt.plot(line_x, line_y, 'r--', label='Line through P and Q')\n", + "\n", + "# 绘制点\n", + "plt.plot(P[0], P[1], 'go', markersize=10, label='P')\n", + "plt.plot(Q[0], Q[1], 'ro', markersize=10, label='Q')\n", + "plt.plot(R[0], R[1], 'ko', markersize=10, label='R')\n", + "plt.plot(R_dot[0], R_dot[1], 'ko', markersize=10, label='R` (P+Q)')\n", + "\n", + "# 添加注释\n", + "plt.annotate('P', xy=P, xytext=(P[0], P[1] + 10), textcoords='offset points')\n", + "plt.annotate('Q', xy=Q, xytext=(Q[0], Q[1] + 10), textcoords='offset points')\n", + "plt.annotate('R', xy=R, xytext=(R[0], R[1] + 10), textcoords='offset points')\n", + "plt.annotate('R` (P+Q)', xy=R_dot, xytext=(R_dot[0], R_dot[1] + 10), textcoords='offset points')\n", + "\n", + "plt.axhline(0, color='black',linewidth=0.5)\n", + "plt.axvline(0, color='black',linewidth=0.5)\n", + "plt.grid(color = 'gray', linestyle = '--', linewidth = 0.5)\n", + "plt.legend()\n", + "plt.title('Elliptic Curve Addition Examples')\n", + "plt.xlabel('x')\n", + "plt.ylabel('y')\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "7636152e", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/usr/local/lib/python3.7/site-packages/ipykernel_launcher.py:3: RuntimeWarning: invalid value encountered in sqrt\n", + " This is separate from the ipykernel package so we can avoid doing imports until\n", + "/usr/local/lib/python3.7/site-packages/ipykernel_launcher.py:23: RuntimeWarning: divide by zero encountered in double_scalars\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# 生成椭圆曲线上的点\n", + "x = np.linspace(-2, 3, 400)\n", + "y = np.sqrt(x**3 + a*x + b)\n", + "y_neg = -y\n", + "\n", + "# 绘制椭圆曲线\n", + "plt.figure(figsize=(12, 8))\n", + "plt.plot(x, y, 'b')\n", + "plt.plot(x, y_neg, 'b')\n", + "\n", + "# 点 P 和 Q: x_p != x_q\n", + "x_p = 1\n", + "x_q = 1\n", + "P = np.array([x_p, np.sqrt(x_p**3 + a*x_p + b)])\n", + "Q = np.array([x_q, -np.sqrt(x_q**3 + a*x_q + b)])\n", + "\n", + "# 计算 R\n", + "if np.array_equal(P, Q):\n", + " # 点加倍情况\n", + " lambda_ = (3 * P[0]**2 + a) / (2 * P[1])\n", + "else:\n", + " # 普通情况\n", + " lambda_ = (Q[1] - P[1]) / (Q[0] - P[0])\n", + "\n", + "x3 = lambda_**2 - P[0] - Q[0]\n", + "y3 = lambda_ * (P[0] - x3) - P[1]\n", + "R = np.array([x3, -y3]) \n", + "R_dot = np.array([x3, y3]) # 取反射点以符合椭圆曲线的加法规则\n", + "\n", + "# 计算经过 P 和 Q 的直线\n", + "plt.axvline(x_p, color='green',linewidth=0.5)\n", + "\n", + "# 绘制点\n", + "plt.plot(P[0], P[1], 'go', markersize=10, label='P')\n", + "plt.plot(Q[0], Q[1], 'ro', markersize=10, label='Q')\n", + "\n", + "# 添加注释\n", + "plt.annotate('P', xy=P, xytext=(P[0], P[1] + 10), textcoords='offset points')\n", + "plt.annotate('Q', xy=Q, xytext=(Q[0], Q[1] + 10), textcoords='offset points')\n", + "\n", + "plt.axhline(0, color='black',linewidth=0.5)\n", + "plt.axvline(0, color='black',linewidth=0.5)\n", + "plt.grid(color = 'gray', linestyle = '--', linewidth = 0.5)\n", + "plt.legend()\n", + "plt.title('Elliptic Curve Addition Examples')\n", + "plt.xlabel('x')\n", + "plt.ylabel('y')\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "a6a66b03", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/usr/local/lib/python3.7/site-packages/ipykernel_launcher.py:3: RuntimeWarning: invalid value encountered in sqrt\n", + " This is separate from the ipykernel package so we can avoid doing imports until\n", + "/usr/local/lib/python3.7/site-packages/ipykernel_launcher.py:23: RuntimeWarning: divide by zero encountered in double_scalars\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# 生成椭圆曲线上的点\n", + "x = np.linspace(-2, 3, 400)\n", + "y = np.sqrt(x**3 + a*x + b)\n", + "y_neg = -y\n", + "\n", + "# 绘制椭圆曲线\n", + "plt.figure(figsize=(12, 8))\n", + "plt.plot(x, y, 'b')\n", + "plt.plot(x, y_neg, 'b')\n", + "\n", + "# 点 P 和 Q: x_p != x_q\n", + "x_p = -1.3245\n", + "x_q = -1.3245\n", + "P = np.array([x_p, np.sqrt(x_p**3 + a*x_p + b)])\n", + "Q = np.array([x_q, -np.sqrt(x_q**3 + a*x_q + b)])\n", + "\n", + "# 计算 R\n", + "if np.array_equal(P, Q):\n", + " # 点加倍情况\n", + " lambda_ = (3 * P[0]**2 + a) / (2 * P[1])\n", + "else:\n", + " # 普通情况\n", + " lambda_ = (Q[1] - P[1]) / (Q[0] - P[0])\n", + "\n", + "x3 = lambda_**2 - P[0] - Q[0]\n", + "y3 = lambda_ * (P[0] - x3) - P[1]\n", + "R = np.array([x3, -y3]) \n", + "R_dot = np.array([x3, y3]) # 取反射点以符合椭圆曲线的加法规则\n", + "\n", + "# 计算经过 P 和 Q 的直线\n", + "plt.axvline(x_p, color='green',linewidth=0.5)\n", + "\n", + "# 绘制点\n", + "plt.plot(P[0], P[1], 'go', markersize=10, label='P')\n", + "\n", + "# 添加注释\n", + "plt.annotate('P', xy=P, xytext=(P[0], P[1] + 10), textcoords='offset points')\n", + "\n", + "plt.axhline(0, color='black',linewidth=0.5)\n", + "plt.axvline(0, color='black',linewidth=0.5)\n", + "plt.grid(color = 'gray', linestyle = '--', linewidth = 0.5)\n", + "plt.legend()\n", + "plt.title('Elliptic Curve Addition Examples')\n", + "plt.xlabel('x')\n", + "plt.ylabel('y')\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b88e0fb0", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Languages/en/30_FiniteEC/FiniteEC.ipynb b/Languages/en/30_FiniteEC/FiniteEC.ipynb new file mode 100644 index 0000000..5d59845 --- /dev/null +++ b/Languages/en/30_FiniteEC/FiniteEC.ipynb @@ -0,0 +1,425 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "04791372", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(0, 1)\n", + "(0, 12)\n", + "(1, 1)\n", + "(1, 12)\n", + "(3, 5)\n", + "(3, 8)\n", + "(4, 3)\n", + "(4, 10)\n", + "(5, 2)\n", + "(5, 11)\n", + "(6, 4)\n", + "(6, 9)\n", + "(7, 5)\n", + "(7, 8)\n", + "(10, 4)\n", + "(10, 9)\n", + "(12, 1)\n", + "(12, 12)\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "\n", + "# Define the parameters of the elliptic curve over the finite field F_13\n", + "p = 13\n", + "a = -1\n", + "b = 1\n", + "\n", + "# Define all the points over the finite field F_13\n", + "x = np.arange(p)\n", + "y = np.arange(p)\n", + "\n", + "# Enumerate the points on the elliptic curve\n", + "curve_points = []\n", + "for xi in x:\n", + " for yi in y:\n", + " if (yi**2) % p == (xi**3 + a*xi + b) % p:\n", + " print((xi, yi))\n", + " curve_points.append((xi, yi))\n", + "\n", + "# Unpack the coordinates of the points\n", + "curve_points_x, curve_points_y = zip(*curve_points)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "a3a9123c", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "# 绘制椭圆曲线\n", + "plt.figure(figsize=(6, 6))\n", + "plt.scatter(curve_points_x, curve_points_y, color='blue')\n", + "plt.axhline(p/2, color='red',linewidth=0.5)\n", + "plt.title('Elliptic Curve over Finite Field F_13')\n", + "plt.xlabel('x')\n", + "plt.ylabel('y')\n", + "plt.grid(True)\n", + "plt.xticks(np.arange(p))\n", + "plt.yticks(np.arange(p))\n", + "plt.gca().set_aspect('equal', adjustable='box')\n", + "\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "4cd0319a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " (0, 1) (0, 12) (1, 1) (1, 12) (3, 5) \\\n", + "(0, 1) (10, 4) (inf, inf) (12, 12) (3, 5) (6, 4) \n", + "(0, 12) (inf, inf) (10, 9) (3, 8) (12, 1) (1, 12) \n", + "(1, 1) (12, 12) (3, 8) (12, 1) (inf, inf) (0, 1) \n", + "(1, 12) (3, 5) (12, 1) (inf, inf) (12, 12) (5, 2) \n", + "(3, 5) (6, 4) (1, 12) (0, 1) (5, 2) (7, 8) \n", + "(3, 8) (1, 1) (6, 9) (5, 11) (0, 12) (inf, inf) \n", + "(4, 3) (6, 9) (10, 4) (7, 8) (4, 10) (10, 9) \n", + "(4, 10) (10, 9) (6, 4) (4, 3) (7, 5) (5, 11) \n", + "(5, 2) (7, 8) (12, 12) (3, 5) (10, 4) (4, 3) \n", + "(5, 11) (12, 1) (7, 5) (10, 9) (3, 8) (1, 1) \n", + "(6, 4) (4, 10) (3, 5) (10, 4) (7, 8) (7, 5) \n", + "(6, 9) (3, 8) (4, 3) (7, 5) (10, 9) (0, 12) \n", + "(7, 5) (5, 11) (7, 8) (4, 10) (6, 9) (3, 8) \n", + "(7, 8) (7, 5) (5, 2) (6, 4) (4, 3) (6, 9) \n", + "(10, 4) (4, 3) (0, 1) (5, 2) (6, 4) (4, 10) \n", + "(10, 9) (0, 12) (4, 10) (6, 9) (5, 11) (12, 1) \n", + "(12, 1) (1, 12) (5, 11) (0, 12) (1, 1) (12, 12) \n", + "(12, 12) (5, 2) (1, 1) (1, 12) (0, 1) (10, 4) \n", + "(inf, inf) (0, 1) (0, 12) (1, 1) (1, 12) (3, 5) \n", + "\n", + " (3, 8) (4, 3) (4, 10) (5, 2) (5, 11) \\\n", + "(0, 1) (1, 1) (6, 9) (10, 9) (7, 8) (12, 1) \n", + "(0, 12) (6, 9) (10, 4) (6, 4) (12, 12) (7, 5) \n", + "(1, 1) (5, 11) (7, 8) (4, 3) (3, 5) (10, 9) \n", + "(1, 12) (0, 12) (4, 10) (7, 5) (10, 4) (3, 8) \n", + "(3, 5) (inf, inf) (10, 9) (5, 11) (4, 3) (1, 1) \n", + "(3, 8) (7, 5) (5, 2) (10, 4) (1, 12) (4, 10) \n", + "(4, 3) (5, 2) (1, 1) (inf, inf) (5, 11) (3, 5) \n", + "(4, 10) (10, 4) (inf, inf) (1, 12) (3, 8) (5, 2) \n", + "(5, 2) (1, 12) (5, 11) (3, 8) (4, 10) (inf, inf) \n", + "(5, 11) (4, 10) (3, 5) (5, 2) (inf, inf) (4, 3) \n", + "(6, 4) (0, 1) (0, 12) (12, 1) (6, 9) (12, 12) \n", + "(6, 9) (7, 8) (12, 12) (0, 1) (12, 1) (6, 4) \n", + "(7, 5) (6, 4) (1, 12) (12, 12) (0, 12) (10, 4) \n", + "(7, 8) (3, 5) (12, 1) (1, 1) (10, 9) (0, 1) \n", + "(10, 4) (12, 12) (3, 8) (0, 12) (7, 5) (1, 12) \n", + "(10, 9) (4, 3) (0, 1) (3, 5) (1, 1) (7, 8) \n", + "(12, 1) (10, 9) (6, 4) (7, 8) (0, 1) (6, 9) \n", + "(12, 12) (12, 1) (7, 5) (6, 9) (6, 4) (0, 12) \n", + "(inf, inf) (3, 8) (4, 3) (4, 10) (5, 2) (5, 11) \n", + "\n", + " (6, 4) (6, 9) (7, 5) (7, 8) (10, 4) \\\n", + "(0, 1) (4, 10) (3, 8) (5, 11) (7, 5) (4, 3) \n", + "(0, 12) (3, 5) (4, 3) (7, 8) (5, 2) (0, 1) \n", + "(1, 1) (10, 4) (7, 5) (4, 10) (6, 4) (5, 2) \n", + "(1, 12) (7, 8) (10, 9) (6, 9) (4, 3) (6, 4) \n", + "(3, 5) (7, 5) (0, 12) (3, 8) (6, 9) (4, 10) \n", + "(3, 8) (0, 1) (7, 8) (6, 4) (3, 5) (12, 12) \n", + "(4, 3) (0, 12) (12, 12) (1, 12) (12, 1) (3, 8) \n", + "(4, 10) (12, 1) (0, 1) (12, 12) (1, 1) (0, 12) \n", + "(5, 2) (6, 9) (12, 1) (0, 12) (10, 9) (7, 5) \n", + "(5, 11) (12, 12) (6, 4) (10, 4) (0, 1) (1, 12) \n", + "(6, 4) (5, 11) (inf, inf) (1, 1) (3, 8) (10, 9) \n", + "(6, 9) (inf, inf) (5, 2) (3, 5) (1, 12) (1, 1) \n", + "(7, 5) (1, 1) (3, 5) (0, 1) (inf, inf) (12, 1) \n", + "(7, 8) (3, 8) (1, 12) (inf, inf) (0, 12) (5, 11) \n", + "(10, 4) (10, 9) (1, 1) (12, 1) (5, 11) (6, 9) \n", + "(10, 9) (1, 12) (10, 4) (5, 2) (12, 12) (inf, inf) \n", + "(12, 1) (5, 2) (4, 10) (4, 3) (10, 4) (3, 5) \n", + "(12, 12) (4, 3) (5, 11) (10, 9) (4, 10) (7, 8) \n", + "(inf, inf) (6, 4) (6, 9) (7, 5) (7, 8) (10, 4) \n", + "\n", + " (10, 9) (12, 1) (12, 12) (inf, inf) \n", + "(0, 1) (0, 12) (1, 12) (5, 2) (0, 1) \n", + "(0, 12) (4, 10) (5, 11) (1, 1) (0, 12) \n", + "(1, 1) (6, 9) (0, 12) (1, 12) (1, 1) \n", + "(1, 12) (5, 11) (1, 1) (0, 1) (1, 12) \n", + "(3, 5) (12, 1) (12, 12) (10, 4) (3, 5) \n", + "(3, 8) (4, 3) (10, 9) (12, 1) (3, 8) \n", + "(4, 3) (0, 1) (6, 4) (7, 5) (4, 3) \n", + "(4, 10) (3, 5) (7, 8) (6, 9) (4, 10) \n", + "(5, 2) (1, 1) (0, 1) (6, 4) (5, 2) \n", + "(5, 11) (7, 8) (6, 9) (0, 12) (5, 11) \n", + "(6, 4) (1, 12) (5, 2) (4, 3) (6, 4) \n", + "(6, 9) (10, 4) (4, 10) (5, 11) (6, 9) \n", + "(7, 5) (5, 2) (4, 3) (10, 9) (7, 5) \n", + "(7, 8) (12, 12) (10, 4) (4, 10) (7, 8) \n", + "(10, 4) (inf, inf) (3, 5) (7, 8) (10, 4) \n", + "(10, 9) (6, 4) (7, 5) (3, 8) (10, 9) \n", + "(12, 1) (7, 5) (3, 8) (inf, inf) (12, 1) \n", + "(12, 12) (3, 8) (inf, inf) (3, 5) (12, 12) \n", + "(inf, inf) (10, 9) (12, 1) (12, 12) (inf, inf) \n" + ] + } + ], + "source": [ + "from sympy import mod_inverse\n", + "\n", + "# 定义有限域F_13的椭圆曲线参数\n", + "p = 13\n", + "a = -1\n", + "b = 1\n", + "\n", + "# 计算椭圆曲线上的点\n", + "curve_points = []\n", + "for xi in range(p):\n", + " for yi in range(p):\n", + " if (yi**2) % p == (xi**3 + a*xi + b) % p:\n", + " curve_points.append((xi, yi))\n", + "\n", + "# 添加无穷远点\n", + "curve_points.append(('inf', 'inf'))\n", + "\n", + "# 定义加法运算\n", + "def elliptic_curve_addition(P, Q, a, p):\n", + " if P == ('inf', 'inf'):\n", + " return Q\n", + " if Q == ('inf', 'inf'):\n", + " return P\n", + " if P[0] == Q[0] and (P[1] != Q[1] or P[1] == 0):\n", + " # P + Q = O (无穷远点) 如果它们是垂直对称的点或P是切点\n", + " return ('inf', 'inf')\n", + " if P == Q:\n", + " # 点翻倍\n", + " lambda_ = (3 * P[0]**2 + a) * mod_inverse(2 * P[1], p) % p\n", + " else:\n", + " # 点加法\n", + " lambda_ = (Q[1] - P[1]) * mod_inverse(Q[0] - P[0], p) % p\n", + "\n", + " x3 = (lambda_**2 - P[0] - Q[0]) % p\n", + " y3 = (lambda_ * (P[0] - x3) - P[1]) % p\n", + "\n", + " return (x3, y3)\n", + "\n", + "# 创建加法表格\n", + "addition_table = [[None for _ in curve_points] for _ in curve_points]\n", + "for i, P in enumerate(curve_points):\n", + " for j, Q in enumerate(curve_points):\n", + " addition_table[i][j] = elliptic_curve_addition(P, Q, a, p)\n", + "\n", + "# 打印加法表格\n", + "# for row in addition_table:\n", + "# print(row)\n", + "\n", + "import pandas as pd\n", + "\n", + "# 将加法表格转换为Pandas DataFrame以美化输出\n", + "addition_table_df = pd.DataFrame(addition_table, index=curve_points, columns=curve_points)\n", + "\n", + "# 设置显示选项以确保表格能够整齐地打印\n", + "pd.set_option('display.max_rows', None)\n", + "pd.set_option('display.max_columns', None)\n", + "pd.set_option('display.width', None)\n", + "pd.set_option('display.max_colwidth', None)\n", + "\n", + "# 打印加法表格\n", + "print(addition_table_df)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "0538a937", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(0, 0)\n", + "(1, 0)\n", + "(2, 1)\n", + "(2, 4)\n", + "(3, 2)\n", + "(3, 3)\n", + "(4, 0)\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "\n", + "# 定义有限域F_13的椭圆曲线参数\n", + "p = 5\n", + "a = -1\n", + "b = 0\n", + "\n", + "# 定义有限域F_13下的所有点\n", + "x = np.arange(p)\n", + "y = np.arange(p)\n", + "\n", + "# 计算椭圆曲线上的点\n", + "curve_points = []\n", + "for xi in x:\n", + " for yi in y:\n", + " if (yi**2) % p == (xi**3 + a*xi + b) % p:\n", + " print((xi, yi))\n", + " curve_points.append((xi, yi))\n", + "\n", + "# 解压点坐标\n", + "curve_points_x, curve_points_y = zip(*curve_points)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "48665179", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " (0, 0) (1, 0) (2, 1) (2, 4) (3, 2) \\\n", + "(0, 0) (inf, inf) (4, 0) (2, 4) (2, 1) (3, 3) \n", + "(1, 0) (4, 0) (inf, inf) (3, 3) (3, 2) (2, 4) \n", + "(2, 1) (2, 4) (3, 3) (0, 0) (inf, inf) (1, 0) \n", + "(2, 4) (2, 1) (3, 2) (inf, inf) (0, 0) (4, 0) \n", + "(3, 2) (3, 3) (2, 4) (1, 0) (4, 0) (0, 0) \n", + "(3, 3) (3, 2) (2, 1) (4, 0) (1, 0) (inf, inf) \n", + "(4, 0) (1, 0) (0, 0) (3, 2) (3, 3) (2, 1) \n", + "(inf, inf) (0, 0) (1, 0) (2, 1) (2, 4) (3, 2) \n", + "\n", + " (3, 3) (4, 0) (inf, inf) \n", + "(0, 0) (3, 2) (1, 0) (0, 0) \n", + "(1, 0) (2, 1) (0, 0) (1, 0) \n", + "(2, 1) (4, 0) (3, 2) (2, 1) \n", + "(2, 4) (1, 0) (3, 3) (2, 4) \n", + "(3, 2) (inf, inf) (2, 1) (3, 2) \n", + "(3, 3) (0, 0) (2, 4) (3, 3) \n", + "(4, 0) (2, 4) (inf, inf) (4, 0) \n", + "(inf, inf) (3, 3) (4, 0) (inf, inf) \n" + ] + } + ], + "source": [ + "from sympy import mod_inverse\n", + "\n", + "# 定义有限域F_13的椭圆曲线参数\n", + "p = 5\n", + "a = -1\n", + "b = 0\n", + "\n", + "# 计算椭圆曲线上的点\n", + "curve_points = []\n", + "for xi in range(p):\n", + " for yi in range(p):\n", + " if (yi**2) % p == (xi**3 + a*xi + b) % p:\n", + " curve_points.append((xi, yi))\n", + "\n", + "# 添加无穷远点\n", + "curve_points.append(('inf', 'inf'))\n", + "\n", + "# 定义加法运算\n", + "def elliptic_curve_addition(P, Q, a, p):\n", + " if P == ('inf', 'inf'):\n", + " return Q\n", + " if Q == ('inf', 'inf'):\n", + " return P\n", + " if P[0] == Q[0] and (P[1] != Q[1] or P[1] == 0):\n", + " # P + Q = O (无穷远点) 如果它们是垂直对称的点或P是切点\n", + " return ('inf', 'inf')\n", + " if P == Q:\n", + " # 点翻倍\n", + " lambda_ = (3 * P[0]**2 + a) * mod_inverse(2 * P[1], p) % p\n", + " else:\n", + " # 点加法\n", + " lambda_ = (Q[1] - P[1]) * mod_inverse(Q[0] - P[0], p) % p\n", + "\n", + " x3 = (lambda_**2 - P[0] - Q[0]) % p\n", + " y3 = (lambda_ * (P[0] - x3) - P[1]) % p\n", + "\n", + " return (x3, y3)\n", + "\n", + "# 创建加法表格\n", + "addition_table = [[None for _ in curve_points] for _ in curve_points]\n", + "for i, P in enumerate(curve_points):\n", + " for j, Q in enumerate(curve_points):\n", + " addition_table[i][j] = elliptic_curve_addition(P, Q, a, p)\n", + "\n", + "# 打印加法表格\n", + "# for row in addition_table:\n", + "# print(row)\n", + "\n", + "import pandas as pd\n", + "\n", + "# 将加法表格转换为Pandas DataFrame以美化输出\n", + "addition_table_df = pd.DataFrame(addition_table, index=curve_points, columns=curve_points)\n", + "\n", + "# 设置显示选项以确保表格能够整齐地打印\n", + "pd.set_option('display.max_rows', None)\n", + "pd.set_option('display.max_columns', None)\n", + "pd.set_option('display.width', None)\n", + "pd.set_option('display.max_colwidth', None)\n", + "\n", + "# 打印加法表格\n", + "print(addition_table_df)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2aa3b533", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Languages/en/31_ECDLP/ECDLP.ipynb b/Languages/en/31_ECDLP/ECDLP.ipynb new file mode 100644 index 0000000..dd7c94f --- /dev/null +++ b/Languages/en/31_ECDLP/ECDLP.ipynb @@ -0,0 +1,184 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "04791372", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(7, 8)" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from sympy import mod_inverse\n", + "\n", + "# Redefine the parameters of the elliptic curve and the point P\n", + "p = 13\n", + "a = -1\n", + "b = 1\n", + "P = (0, 1)\n", + "\n", + "# Define point addition and point doubling operations on the elliptic curve\n", + "# Define addition operation\n", + "def elliptic_curve_addition(P, Q, a, p):\n", + " if P == ('inf', 'inf'):\n", + " return Q\n", + " if Q == ('inf', 'inf'):\n", + " return P\n", + " if P[0] == Q[0] and (P[1] != Q[1] or P[1] == 0):\n", + " # P + Q = O (infinity) if they are vertically symmetric or P is a tangent point\n", + " return ('inf', 'inf')\n", + " if P == Q:\n", + " # Point doubling\n", + " lambda_ = (3 * P[0]**2 + a) * mod_inverse(2 * P[1], p) % p\n", + " else:\n", + " # Point addition\n", + " lambda_ = (Q[1] - P[1]) * mod_inverse(Q[0] - P[0], p) % p\n", + "\n", + " x3 = (lambda_**2 - P[0] - Q[0]) % p\n", + " y3 = (lambda_ * (P[0] - x3) - P[1]) % p\n", + "\n", + " return (x3, y3)\n", + "\n", + "def ecc_double_and_add(P, k, a, p):\n", + " \"\"\"Double-And-Add algorithm for scalar multiplication on elliptic curves.\"\"\"\n", + " result = ('inf', 'inf') # Infinity\n", + " addend = P\n", + "\n", + " while k:\n", + " if k & 1:\n", + " result = elliptic_curve_addition(result, addend, a, p)\n", + " addend = elliptic_curve_addition(addend, addend, a, p)\n", + " k >>= 1\n", + " return result\n", + "\n", + "# Test the Double-And-Add algorithm\n", + "# Choose a scalar k = 9 for scalar multiplication testing\n", + "k = 9\n", + "result = ecc_double_and_add(P, k, a, p)\n", + "\n", + "result\n", + "# (7, 8)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "a3a9123c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Order of point P: 19\n" + ] + } + ], + "source": [ + "# Order of point\n", + "def find_order_of_point(P, a, p):\n", + " S = P\n", + " n = 1\n", + " while S != ('inf', 'inf'): # ('inf', 'inf') 是无穷远点\n", + " S = elliptic_curve_addition(S, P, a, p)\n", + " n += 1\n", + " return n\n", + "\n", + "# calculate order of point\n", + "order = find_order_of_point(P, a, p)\n", + "print(f\"Order of point P: {order}\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "4cd0319a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Private Key: 123456789\n", + "Public Key: (4051293998585674784991639592782214972820158391371785981004352359465450369227, 88166831356626186178414913298033275054086243781277878360288998796587140930350)\n" + ] + } + ], + "source": [ + "# Elliptic Curve secp256k1 Example\n", + "# Generate public key from private key using scalar multiplication\n", + "from py_ecc.secp256k1 import secp256k1\n", + "\n", + "def generate_public_key(private_key):\n", + " \"\"\"\n", + " Generate the public key using the secp256k1 elliptic curve from the given private key.\n", + " \n", + " Args:\n", + " private_key (int): The private key, a large integer.\n", + " \n", + " Returns:\n", + " (int, int): The public key, a point on the elliptic curve.\n", + " \"\"\"\n", + " # Base point of secp256k1\n", + " G = secp256k1.G\n", + " \n", + " # Calculate the public key\n", + " public_key = secp256k1.multiply(G, private_key)\n", + " \n", + " return public_key\n", + "\n", + "# Example: Using a random private key\n", + "private_key = 123456789 # In practice, the private key should be a randomly generated large integer, e.g., private_key = int(os.urandom(32).hex(), 16)\n", + "\n", + "# Generate the public key\n", + "public_key = generate_public_key(private_key)\n", + "\n", + "# Print the results\n", + "print(f\"Private Key: {private_key}\")\n", + "print(f\"Public Key: {public_key}\")\n", + "\n", + "# Output\n", + "# Private Key: 123456789\n", + "# Public Key: (4051293998585674784991639592782214972820158391371785981004352359465450369227, 88166831356626186178414913298033275054086243781277878360288998796587140930350)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "27df043e", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Languages/en/32_ECC/ECC.ipynb b/Languages/en/32_ECC/ECC.ipynb new file mode 100644 index 0000000..3a0f26d --- /dev/null +++ b/Languages/en/32_ECC/ECC.ipynb @@ -0,0 +1,225 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "04791372", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Shared secret matches.\n", + "Alice's Private Key: 82606499507747440987050479812813078635285952037786416673976012283989331288142\n", + "Alice's Public Key: (62514881804714428231192859056082221811533284700435585343955717198524964794335, 95177630059588049283095802652884455297801361561329699096982207819905288167168)\n", + "Bob's Private Key: 88901982354510689156671390758696246479998240706232250638309895970936046480827\n", + "Bob's Public Key: (12025260431957395985379890515856805874022361988002716563414115736497149099301, 79273637794819036250326671113309215648490064743739747012363645833579807535904)\n", + "Shared Secret: (77451587716775443510009755960062594320580380408009722874722913678825763876550, 2649561184996041046878485472775414139434239070258036874344063326827993327163)\n" + ] + } + ], + "source": [ + "# Elliptic Curve Diffie-Hellman (ECDH) algorithm\n", + "\n", + "from py_ecc.secp256k1 import secp256k1\n", + "import os\n", + "\n", + "def generate_keys():\n", + " private_key = int.from_bytes(os.urandom(32), 'big') % secp256k1.N\n", + " public_key = secp256k1.multiply(secp256k1.G, private_key)\n", + " return private_key, public_key\n", + "\n", + "# Alice and Bob generate their own key pairs\n", + "alice_private, alice_public = generate_keys()\n", + "bob_private, bob_public = generate_keys()\n", + "\n", + "# Calculate the shared secret\n", + "shared_secret_alice = secp256k1.multiply(bob_public, alice_private)\n", + "shared_secret_bob = secp256k1.multiply(alice_public, bob_private)\n", + "\n", + "if shared_secret_alice == shared_secret_bob:\n", + " print(\"Shared secret matches.\")\n", + "else: \n", + " print(\"Shared secret does not match!\")\n", + "\n", + "print(f\"Alice's Private Key: {alice_private}\")\n", + "print(f\"Alice's Public Key: {alice_public}\")\n", + "\n", + "print(f\"Bob's Private Key: {bob_private}\")\n", + "print(f\"Bob's Public Key: {bob_public}\")\n", + "\n", + "print(f\"Shared Secret: {shared_secret_alice}\")\n", + "\n", + "# Output:\n", + "# Shared secret matches.\n", + "# Alice's Private Key: 44226773042722162955098193291492534006186517732096623157459837212766793078584\n", + "# Alice's Public Key: (113906392817926084413632896524344771269472367375880032535005632965062391078788, 49665636540644454541653315656482000530366349019751676160955522917215379042285)\n", + "# Bob's Private Key: 51860882402071446551116109914681284224864199234652843480335793819475548437366\n", + "# Bob's Public Key: (52340819409831460217804635786419806447405367609650964443132838196582132856471, 56429557458241459690871510882159471830396052430769816127197158365607969924309)\n", + "# Shared Secret: (39817116182924354378808003014233470575110979407770339130416639641795260327693, 42970388080766198583159133018251494914868250846130428856587988974064644921855)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "a3a9123c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original Plaintext Message: (55066263022277343669578718895168534326250603453777594175500187360389116729240, 32670510020758816978083085130507043184471273380659243275938904335757337482424)\n", + "Encrypted Message: ((59830309720978449946889995874587870215667310393260257620370408789811812953791, 61955688499586988048604902054852801385524339809529140973272234470367112364645), (29105031096182708747750302737496064946401641390342920957387424407347571278406, 115327997712399318117788727312520711761194913441694122125064926039535571946108))\n", + "Decrypted Message: (55066263022277343669578718895168534326250603453777594175500187360389116729240, 32670510020758816978083085130507043184471273380659243275938904335757337482424)\n", + "Message decrypted successfully!\n" + ] + } + ], + "source": [ + "from py_ecc.secp256k1 import secp256k1\n", + "from random import randint\n", + "\n", + "def elgamal_encrypt(G, Y, M):\n", + " k = randint(1, secp256k1.N - 1)\n", + " C1 = secp256k1.multiply(G, k)\n", + " C2 = secp256k1.add(M, secp256k1.multiply(Y, k))\n", + " return (C1, C2)\n", + "\n", + "def elgamal_decrypt(C1, C2, x):\n", + " # Compute xC1 using the private key x\n", + " xC1 = secp256k1.multiply(C1, x)\n", + " # Compute M = C2 - xC1\n", + " M = secp256k1.add(C2, (xC1[0], -xC1[1]))\n", + " return M\n", + "\n", + "# Example parameters\n", + "p = secp256k1.N\n", + "G = secp256k1.G\n", + "\n", + "# Generate key pairs\n", + "x, Y = generate_keys()\n", + "\n", + "# Assume the message M is a point on the curve, here we simply choose G as an example\n", + "M = G\n", + "print(\"Original Plaintext Message:\", M)\n", + "\n", + "# Encryption\n", + "C1, C2 = elgamal_encrypt(G, Y, M)\n", + "print(\"Encrypted Message:\", (C1, C2))\n", + "\n", + "# Decryption\n", + "M_decrypted = elgamal_decrypt(C1, C2, x)\n", + "print(\"Decrypted Message:\", M_decrypted)\n", + "\n", + "# Verification\n", + "assert M == M_decrypted, \"Decryption failed!\"\n", + "print(\"Message decrypted successfully!\")\n", + "\n", + "# Example output\n", + "# Original Plaintext Message: (55066263022277343669578718895168534326250603453777594175500187360389116729240, 32670510020758816978083085130507043184471273380659243275938904335757337482424)\n", + "# Encrypted Message: ((87298472810248234319752437423707505477343664832890363292431829216099637291919, 39528614830056678009484946030376271359657183017625571564228160252781333158439), (67113196324182438503834247973075313606138491143388276462715763950508942145812, 59499979624168470896804403233074133393632477568643779021536973756576878140912))\n", + "# Decrypted Message: (55066263022277343669578718895168534326250603453777594175500187360389116729240, 32670510020758816978083085130507043184471273380659243275938904335757337482424)\n", + "# Message decrypted successfully!\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "c841e756", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Original Plaintext Message: b'Hello, ECDSA with secp256k1!'\n", + "Signature: (93155020076571581891828391126177039141373114716842694147583265484823968588061, 23390424861672546041880313820955199567418091327609644416166133268827193110519)\n", + "Signature Verification Result: True\n" + ] + } + ], + "source": [ + "# ECDSA\n", + "\n", + "from py_ecc.secp256k1 import secp256k1\n", + "import os\n", + "import hashlib\n", + "\n", + "def generate_keys():\n", + " # Generate private key\n", + " private_key = os.urandom(32)\n", + " private_key_int = int.from_bytes(private_key, 'big') % secp256k1.N\n", + " # Generate public key\n", + " public_key = secp256k1.multiply(secp256k1.G, private_key_int)\n", + " return private_key_int, public_key\n", + "\n", + "def ecdsa_sign(message, private_key):\n", + " # Hash the message\n", + " message_hash = hashlib.sha256(message).digest()\n", + " message_hash_int = int.from_bytes(message_hash, 'big')\n", + " \n", + " k = int.from_bytes(os.urandom(32), 'big') % secp256k1.N\n", + " R = secp256k1.multiply(secp256k1.G, k)\n", + " r = R[0] % secp256k1.N\n", + " s = ((message_hash_int + r * private_key) * secp256k1.inv(k, secp256k1.N)) % secp256k1.N\n", + " \n", + " return (r, s)\n", + "\n", + "def ecdsa_verify(message, signature, public_key):\n", + " r, s = signature\n", + " message_hash = hashlib.sha256(message).digest()\n", + " message_hash_int = int.from_bytes(message_hash, 'big')\n", + " \n", + " w = secp256k1.inv(s, secp256k1.N)\n", + " u1 = (message_hash_int * w) % secp256k1.N\n", + " u2 = (r * w) % secp256k1.N\n", + " \n", + " P = secp256k1.add(secp256k1.multiply(secp256k1.G, u1), secp256k1.multiply(public_key, u2))\n", + " \n", + " return r == P[0] % secp256k1.N\n", + "\n", + "# Example\n", + "x, Y = generate_keys()\n", + "M = b\"Hello, ECDSA with secp256k1!\"\n", + "print(\"Original Plaintext Message:\", M)\n", + "\n", + "signature = ecdsa_sign(M, x)\n", + "print(\"Signature:\", signature)\n", + "\n", + "valid = ecdsa_verify(M, signature, Y)\n", + "print(\"Signature Verification Result:\", valid)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8f12eafb", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Languages/en/32_ECC/readme.md b/Languages/en/32_ECC/readme.md index b03f772..4755436 100644 --- a/Languages/en/32_ECC/readme.md +++ b/Languages/en/32_ECC/readme.md @@ -133,6 +133,7 @@ The following Python code demonstrates the EC Elgamal encryption using the `secp ```python from py_ecc.secp256k1 import secp256k1 +from random import randint def elgamal_encrypt(G, Y, M): k = randint(1, secp256k1.N - 1) diff --git a/Languages/en/35_TorsionGroup/TorsionGroup.sage b/Languages/en/35_TorsionGroup/TorsionGroup.sage new file mode 100644 index 0000000..4eb860f --- /dev/null +++ b/Languages/en/35_TorsionGroup/TorsionGroup.sage @@ -0,0 +1,27 @@ +p = 19 +a = -1 +b = 1 +E = EllipticCurve(GF(p), [a, b]) + +print("椭圆曲线中的元素个数: ", E.cardinality()) + +# 获取11-挠群的点 +INF = E[0] +L_E_11 = INF.division_points(11) # [11]P == INF +E_11 = Set(L_E_11) # $11$-torsion +print("11-torsion points: ", E_11) +print("11-挠群中的元素个数: ", E_11.cardinality()) + +# 初始化绘图对象 +plot = Graphics() + +# 添加椭圆曲线图 +plot += E.plot(size=100) + +# 添加11-挠群的点到图中 +for P in E_11: + if P != E[0]: # 确保P不是无穷远点 + plot += point(P, size=100, color='red') + +# 显示图形,设置标题和坐标轴范围 +plot.show(title="11-torsion points on the Elliptic Curve $y^2 = x^3 - x + 1$ over $\mathbb{F}_{19}$", xmin=-1, xmax=p, ymin=-1, ymax=p) diff --git a/Languages/en/37_MillerAlgo/WeilPairing.sage b/Languages/en/37_MillerAlgo/WeilPairing.sage new file mode 100644 index 0000000..4595c04 --- /dev/null +++ b/Languages/en/37_MillerAlgo/WeilPairing.sage @@ -0,0 +1,27 @@ +p = 631 +a = 30 +b = 34 +E = EllipticCurve(GF(p), [a, b]) +print(E) +print("椭圆曲线中的元素个数: ", E.cardinality()) + +# 获取5-挠群的点 +INF = E[0] +L_E_5 = INF.division_points(5) # [11]P == INF +E_5 = Set(L_E_5) # $5$-torsion +print("5-torsion points: ", E_5) +print("5-挠群中的元素个数: ", E_5.cardinality()) + +P = E([36,60]) +Q = E([121,387]) + +weil_P_Q = P.weil_pairing(Q, 5) +print("5-挠群中点", P, "和", Q, "的Weil配对为", weil_P_Q) + +R = 3 * P +S = 4 * Q + +weil_R_S = R.weil_pairing(S, 5) +print("5-挠群中点", R, "和", S, "的Weil配对为", weil_R_S) + +print("因为 R= 3P, S = 4Q,因此 weil_P_Q ^ 12 = ", weil_P_Q^12 , "和 weil_R_S 相等") \ No newline at end of file diff --git a/Languages/en/38_TatePairing/Ate.ipynb b/Languages/en/38_TatePairing/Ate.ipynb new file mode 100644 index 0000000..07298b3 --- /dev/null +++ b/Languages/en/38_TatePairing/Ate.ipynb @@ -0,0 +1,94 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "81ff1f10", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(1, 2)\n", + "((10857046999023057135944570762232829481370756359578518086990519993285655852781, 11559732032986387107991004021392285783925812861821192530917403151452391805634), (8495653923123431417604973247489272438418190587263600148770280649306958101930, 4082367875863433681332203403145435568316851327593401208105741076214120093531))\n", + "\n", + "\n", + "Pairing of points A and B: (19735667940922659777402175920395455799125563888708961631093487249968872129612, 1976543863057094994989237517814173599120655827589866703826517845909315612857, 19686523416572620016989349096902944934819162198495809257491045534399198954254, 5826646852844954420149583478015267673527445979905768896060072350584178989060, 2064185964405234542610947637037132798744921024553195185441592358018988389207, 8341934863294343910133492936755210611939463215146220944606211376003151106114, 12807669762027938768857302676393862225355612177677457846751491105239425227277, 5741126950795831539169012545403256931813076395529913201048083937620822856065, 11074901068523180915867722424807487877141140784438044188857570704539589417315, 19327019285776193278582429402961044775129507055467003359023290900912857119476, 17306986078986604236447922180440988200852103029519452658980599808670992125088, 13188937242065601189938233945175869194113210620973903647453917247887073581439)\n", + "\n", + "\n", + "Pairing of points G2 and C: (19735667940922659777402175920395455799125563888708961631093487249968872129612, 1976543863057094994989237517814173599120655827589866703826517845909315612857, 19686523416572620016989349096902944934819162198495809257491045534399198954254, 5826646852844954420149583478015267673527445979905768896060072350584178989060, 2064185964405234542610947637037132798744921024553195185441592358018988389207, 8341934863294343910133492936755210611939463215146220944606211376003151106114, 12807669762027938768857302676393862225355612177677457846751491105239425227277, 5741126950795831539169012545403256931813076395529913201048083937620822856065, 11074901068523180915867722424807487877141140784438044188857570704539589417315, 19327019285776193278582429402961044775129507055467003359023290900912857119476, 17306986078986604236447922180440988200852103029519452658980599808670992125088, 13188937242065601189938233945175869194113210620973903647453917247887073581439)\n", + "\n", + "\n", + "Is pair_A_B == pair_G2_C? True\n" + ] + } + ], + "source": [ + "from py_ecc.bn128 import G1, G2, pairing, add, multiply, eq\n", + "\n", + "print(G1)\n", + "print(G2)\n", + "print(\"\\n\")\n", + "\n", + "a = 69\n", + "b = 420\n", + "c = a * b\n", + "A = multiply(G2, a)\n", + "B = multiply(G1, b)\n", + "pair_A_B = pairing(A, B)\n", + "print(\"Pairing of points A and B: \",pair_A_B)\n", + "print(\"\\n\")\n", + "\n", + "C = multiply(G2, c)\n", + "pair_G2_C = pairing(C, G1)\n", + "print(\"Pairing of points G2 and C: \",pair_G2_C)\n", + "print(\"\\n\")\n", + "\n", + "print(\"Is pair_A_B == pair_G2_C? \", pair_A_B == pair_G2_C)\n", + "\n", + "# Output\n", + "# (1, 2)\n", + "# ((10857046999023057135944570762232829481370756359578518086990519993285655852781, 11559732032986387107991004021392285783925812861821192530917403151452391805634), (8495653923123431417604973247489272438418190587263600148770280649306958101930, 4082367875863433681332203403145435568316851327593401208105741076214120093531))\n", + "\n", + "\n", + "# Pairing of points A and B: (19735667940922659777402175920395455799125563888708961631093487249968872129612, 1976543863057094994989237517814173599120655827589866703826517845909315612857, 19686523416572620016989349096902944934819162198495809257491045534399198954254, 5826646852844954420149583478015267673527445979905768896060072350584178989060, 2064185964405234542610947637037132798744921024553195185441592358018988389207, 8341934863294343910133492936755210611939463215146220944606211376003151106114, 12807669762027938768857302676393862225355612177677457846751491105239425227277, 5741126950795831539169012545403256931813076395529913201048083937620822856065, 11074901068523180915867722424807487877141140784438044188857570704539589417315, 19327019285776193278582429402961044775129507055467003359023290900912857119476, 17306986078986604236447922180440988200852103029519452658980599808670992125088, 13188937242065601189938233945175869194113210620973903647453917247887073581439)\n", + "\n", + "\n", + "# Pairing of points G2 and C: (19735667940922659777402175920395455799125563888708961631093487249968872129612, 1976543863057094994989237517814173599120655827589866703826517845909315612857, 19686523416572620016989349096902944934819162198495809257491045534399198954254, 5826646852844954420149583478015267673527445979905768896060072350584178989060, 2064185964405234542610947637037132798744921024553195185441592358018988389207, 8341934863294343910133492936755210611939463215146220944606211376003151106114, 12807669762027938768857302676393862225355612177677457846751491105239425227277, 5741126950795831539169012545403256931813076395529913201048083937620822856065, 11074901068523180915867722424807487877141140784438044188857570704539589417315, 19327019285776193278582429402961044775129507055467003359023290900912857119476, 17306986078986604236447922180440988200852103029519452658980599808670992125088, 13188937242065601189938233945175869194113210620973903647453917247887073581439)\n", + "\n", + "\n", + "# Is pair_A_B == pair_G2_C? True" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "73e7db22", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Languages/en/40_PopularCurves/40_PopularCurves.ipynb b/Languages/en/40_PopularCurves/40_PopularCurves.ipynb new file mode 100644 index 0000000..e941dc6 --- /dev/null +++ b/Languages/en/40_PopularCurves/40_PopularCurves.ipynb @@ -0,0 +1,368 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "22a0b426", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Private Key: 73051617715026562042570104560193685120347929723312246115443813777177460386469\n", + "Public Key: (61787722175226778530120044571133481337720434991827205094296181775022414126002, 87433419887678536938225895447311875645995575638452204249916623104004114030363)\n" + ] + } + ], + "source": [ + "# secp256k1 elliptic curve example\n", + "# Generate public key from private key using scalar multiplication\n", + "from py_ecc.secp256k1 import secp256k1\n", + "import os\n", + "\n", + "def generate_public_key(private_key):\n", + " \"\"\"\n", + " Generate a public key using the secp256k1 elliptic curve and a given private key.\n", + " \n", + " Parameters:\n", + " private_key (int): A large integer representing the private key.\n", + " \n", + " Returns:\n", + " (int, int): The public key, a point on the elliptic curve.\n", + " \"\"\"\n", + " # Base point of secp256k1\n", + " G = secp256k1.G\n", + " \n", + " # Calculate the public key\n", + " public_key = secp256k1.multiply(G, private_key)\n", + " \n", + " return public_key\n", + "\n", + "# Example: Use a random private key\n", + "private_key = int(os.urandom(32).hex(), 16)\n", + "\n", + "# Generate the public key\n", + "public_key = generate_public_key(private_key)\n", + "\n", + "# Print the result\n", + "print(f\"Private Key: {private_key}\")\n", + "print(f\"Public Key: {public_key}\")\n", + "\n", + "# Example output\n", + "# Private Key: 40871478222817722377012551921323657605236631423958081783403470740144884256441\n", + "# Public Key: (18814187692496112820586797121940816605467606938301853840004393937958984136992, 72833048843328294920821861725991661253504985018641366317346599320677055943891)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "d0f2379f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Private Key: 65168246829430582893396906853633973054075218086240417715709317390998177970136\n", + "Public Key: (55227233490656906450298370950698192384505446118954042518669207453123237490621, 63561674915831474560217496361686642590669471463552929481387445307876807473186)\n", + "Signature valid: True\n" + ] + } + ], + "source": [ + "# secp256k1 digital signature\n", + "from py_ecc.secp256k1 import secp256k1\n", + "import os\n", + "import hashlib\n", + "\n", + "def sign_message(private_key, message):\n", + " \"\"\"\n", + " Sign a message using the private key.\n", + " \"\"\"\n", + " message_hash = hashlib.sha256(message.encode()).digest()\n", + " private_key_bytes = private_key.to_bytes(32, \"big\")\n", + " signature = secp256k1.ecdsa_raw_sign(message_hash, private_key_bytes)\n", + " return signature\n", + "\n", + "def verify_signature(message, signature, public_key):\n", + " \"\"\"\n", + " Verify the signature of a message using the public key.\n", + " \"\"\"\n", + " message_hash = hashlib.sha256(message.encode()).digest()\n", + " recovered_public_key = secp256k1.ecdsa_raw_recover(message_hash, signature)\n", + " return recovered_public_key == public_key\n", + "\n", + "# Example usage\n", + "private_key = int.from_bytes(os.urandom(32), 'big')\n", + "public_key = secp256k1.multiply(secp256k1.G, private_key) # Calculate the public key\n", + "\n", + "print(f\"Private Key: {private_key}\")\n", + "print(f\"Public Key: {public_key}\")\n", + "\n", + "message = \"Hello, blockchain world!\"\n", + "signature = sign_message(private_key, message) # Sign the message\n", + "is_valid = verify_signature(message, signature, public_key) # Try to recover the public key from the signature\n", + "\n", + "# Compare the original public key and the recovered public key\n", + "print(f\"Signature valid: {is_valid}\")\n", + "\n", + "# Example output\n", + "# Private Key: 100160191408028635805410835424804882729758587641862022398559246101084514055515\n", + "# Public Key: (94753041202778772959486517607721465828067708966050249355074253521404789962176, 108824044826657285606250531715029407748756362423778933890891533480553307901806)\n", + "# Signature valid: True\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "5a2c9f7b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Private Key: 37835994257930574673581463485697138053401345876525520769753138183127455092054\n", + "Public Key: (9316941486315569368268374503711001335327057752138137580572954957145015876606, 15239042745417678519287704521419065537588318339499033548198252653315177967559)\n" + ] + } + ], + "source": [ + "# bn128 public key generation\n", + "from py_ecc.bn128 import bn128_curve\n", + "import os\n", + "\n", + "def generate_bn128_public_key(private_key):\n", + " \"\"\"\n", + " Generate a public key using the bn_128 curve and a given private key.\n", + " \n", + " Parameters:\n", + " private_key (int): A large integer representing the private key.\n", + " \n", + " Returns:\n", + " (int, int): The public key, a point on the bn_128 curve.\n", + " \"\"\"\n", + " # BN128_G1 is the base point of bn_128 curve\n", + " public_key = bn128_curve.multiply(bn128_curve.G1, private_key)\n", + " return public_key\n", + "\n", + "# Example: Use a random private key\n", + "private_key = int.from_bytes(os.urandom(32), 'big')\n", + "\n", + "# Generate the public key\n", + "public_key = generate_bn128_public_key(private_key)\n", + "\n", + "# Print the result\n", + "print(f\"Private Key: {private_key}\")\n", + "print(f\"Public Key: {public_key}\")\n", + "\n", + "# Example output\n", + "# Private Key: 98359178994781335595533648854802231427270090895769248482397491373685555850978\n", + "# Public Key: (3661113004864472419130070831996330893639690693841499262022550248113059694488, 16647856845341024716707355890250833951196099189567973816478113518219363325204)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "f775bbf6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(1, 2)\n", + "((10857046999023057135944570762232829481370756359578518086990519993285655852781, 11559732032986387107991004021392285783925812861821192530917403151452391805634), (8495653923123431417604973247489272438418190587263600148770280649306958101930, 4082367875863433681332203403145435568316851327593401208105741076214120093531))\n", + "\n", + "\n", + "Pairing of points A and B: (19735667940922659777402175920395455799125563888708961631093487249968872129612, 1976543863057094994989237517814173599120655827589866703826517845909315612857, 19686523416572620016989349096902944934819162198495809257491045534399198954254, 5826646852844954420149583478015267673527445979905768896060072350584178989060, 2064185964405234542610947637037132798744921024553195185441592358018988389207, 8341934863294343910133492936755210611939463215146220944606211376003151106114, 12807669762027938768857302676393862225355612177677457846751491105239425227277, 5741126950795831539169012545403256931813076395529913201048083937620822856065, 11074901068523180915867722424807487877141140784438044188857570704539589417315, 19327019285776193278582429402961044775129507055467003359023290900912857119476, 17306986078986604236447922180440988200852103029519452658980599808670992125088, 13188937242065601189938233945175869194113210620973903647453917247887073581439)\n", + "\n", + "\n", + "Pairing of points G2 and C: (19735667940922659777402175920395455799125563888708961631093487249968872129612, 1976543863057094994989237517814173599120655827589866703826517845909315612857, 19686523416572620016989349096902944934819162198495809257491045534399198954254, 5826646852844954420149583478015267673527445979905768896060072350584178989060, 2064185964405234542610947637037132798744921024553195185441592358018988389207, 8341934863294343910133492936755210611939463215146220944606211376003151106114, 12807669762027938768857302676393862225355612177677457846751491105239425227277, 5741126950795831539169012545403256931813076395529913201048083937620822856065, 11074901068523180915867722424807487877141140784438044188857570704539589417315, 19327019285776193278582429402961044775129507055467003359023290900912857119476, 17306986078986604236447922180440988200852103029519452658980599808670992125088, 13188937242065601189938233945175869194113210620973903647453917247887073581439)\n", + "\n", + "\n", + "Is pair_A_B == pair_G2_C? True\n" + ] + } + ], + "source": [ + "# bn128 bilinear pairing\n", + "from py_ecc.bn128 import G1, G2, pairing, add, multiply, eq\n", + "\n", + "print(G1)\n", + "print(G2)\n", + "print(\"\\n\")\n", + "\n", + "a = 69\n", + "b = 420\n", + "c = a * b\n", + "A = multiply(G2, a)\n", + "B = multiply(G1, b)\n", + "pair_A_B = pairing(A, B)\n", + "print(\"Pairing of points A and B: \",pair_A_B)\n", + "print(\"\\n\")\n", + "\n", + "C = multiply(G2, c)\n", + "pair_G2_C = pairing(C, G1)\n", + "print(\"Pairing of points G2 and C: \",pair_G2_C)\n", + "print(\"\\n\")\n", + "\n", + "print(\"Is pair_A_B == pair_G2_C? \", pair_A_B == pair_G2_C)\n", + "\n", + "# Output\n", + "# (1, 2)\n", + "# ((10857046999023057135944570762232829481370756359578518086990519993285655852781, 11559732032986387107991004021392285783925812861821192530917403151452391805634), (8495653923123431417604973247489272438418190587263600148770280649306958101930, 4082367875863433681332203403145435568316851327593401208105741076214120093531))\n", + "\n", + "\n", + "# Pairing of points A and B: (19735667940922659777402175920395455799125563888708961631093487249968872129612, 1976543863057094994989237517814173599120655827589866703826517845909315612857, 19686523416572620016989349096902944934819162198495809257491045534399198954254, 5826646852844954420149583478015267673527445979905768896060072350584178989060, 2064185964405234542610947637037132798744921024553195185441592358018988389207, 8341934863294343910133492936755210611939463215146220944606211376003151106114, 12807669762027938768857302676393862225355612177677457846751491105239425227277, 5741126950795831539169012545403256931813076395529913201048083937620822856065, 11074901068523180915867722424807487877141140784438044188857570704539589417315, 19327019285776193278582429402961044775129507055467003359023290900912857119476, 17306986078986604236447922180440988200852103029519452658980599808670992125088, 13188937242065601189938233945175869194113210620973903647453917247887073581439)\n", + "\n", + "\n", + "# Pairing of points G2 and C: (19735667940922659777402175920395455799125563888708961631093487249968872129612, 1976543863057094994989237517814173599120655827589866703826517845909315612857, 19686523416572620016989349096902944934819162198495809257491045534399198954254, 5826646852844954420149583478015267673527445979905768896060072350584178989060, 2064185964405234542610947637037132798744921024553195185441592358018988389207, 8341934863294343910133492936755210611939463215146220944606211376003151106114, 12807669762027938768857302676393862225355612177677457846751491105239425227277, 5741126950795831539169012545403256931813076395529913201048083937620822856065, 11074901068523180915867722424807487877141140784438044188857570704539589417315, 19327019285776193278582429402961044775129507055467003359023290900912857119476, 17306986078986604236447922180440988200852103029519452658980599808670992125088, 13188937242065601189938233945175869194113210620973903647453917247887073581439)\n", + "\n", + "\n", + "# Is pair_A_B == pair_G2_C? True\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "cc26d294", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Private Key: 99234858224997257160360898127564472963368815429424966975079332177989178408909\n", + "Public Key: (1073353491808267230317571441929760255646489776013779548920355354899312257027428909631689035616394284762283439852189, 2079153471328874654855572679919169306907584724306097724854419461829076803663479920074278924268616244147592315976686)\n" + ] + } + ], + "source": [ + "# bls12_381 public key generation\n", + "from py_ecc.bls12_381 import bls12_381_curve\n", + "import os\n", + "\n", + "def generate_bn12_381_public_key(private_key):\n", + " \"\"\"\n", + " Generate a public key using the bls12_381 curve and a given private key.\n", + " \n", + " Parameters:\n", + " private_key (int): A large integer representing the private key.\n", + " \n", + " Returns:\n", + " (int, int): The public key, a point on the bls12_381 curve.\n", + " \"\"\"\n", + " # G1 is the base point of bls12_381 curve\n", + " public_key = bls12_381_curve.multiply(bls12_381_curve.G1, private_key)\n", + " return public_key\n", + "\n", + "# Example: Use a random private key\n", + "private_key = int.from_bytes(os.urandom(32), 'big')\n", + "\n", + "# Generate the public key\n", + "public_key = generate_bn12_381_public_key(private_key)\n", + "\n", + "# Print the result\n", + "print(f\"Private Key: {private_key}\")\n", + "print(f\"Public Key: {public_key}\")\n", + "\n", + "# Example output\n", + "# Private Key: 56832202591799069674370871859151631253659339730808373097707650526306669655451\n", + "# Public Key: (2672943242084458229690202553507767493858110823696659228443909079159465919837314837610879707240986236828893077890320, 1516123302208562362629191397278119903430202415903098718379575356530260147717671392463098304288800407793122740332702)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "b9e402ad", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507, 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569)\n", + "((352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160, 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758), (1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905, 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582))\n", + "\n", + "\n", + "Pairing of points A and B: (559875907953044229256999964908655596470329614681804657067074917348889299656574983763205735263906508658423626306192, 3251387332700024622230711712327939415422447046055926038630362608193945095062996827680637432749053965474170688846803, 1858978301495685148589092187900391308425211794249943939941796868269658435530364461212323465553496690057506398710958, 2821404563389658506867792213698964607416184520400354580766817454079710851304957653749944402097411780097914172680501, 3371394908151563362208243796489511825354821020573987752522791881868987441291678825694507113985550194783443672326288, 2297984649797609740520111469542560058998637859756636710986648527547199594538622957148043186058263511759636104385884, 3480762570907741510541555641785158658308333937734941283300383364697580261225975907843679022209374260264933745191828, 3240728397255321363427476858948130370605249287048803384220203355573990507150386888156524321441752887099266166133108, 2376364160413810038009747479244195484637259359228646277111417852748902100064937901534264975269224589631629799823776, 505616797063036550970852124247959597244795697098930755073121309023649013438395471915981324345265013483679297150493, 283646809217572597374969288842854659843926065997224794730305413934017744924705963601539908633134851567640611976585, 208748941951544416005406635656082534817221797560780462964136211816030039051269393961107822344678318294491041226308)\n", + "\n", + "\n", + "Pairing of points G2 and C: (559875907953044229256999964908655596470329614681804657067074917348889299656574983763205735263906508658423626306192, 3251387332700024622230711712327939415422447046055926038630362608193945095062996827680637432749053965474170688846803, 1858978301495685148589092187900391308425211794249943939941796868269658435530364461212323465553496690057506398710958, 2821404563389658506867792213698964607416184520400354580766817454079710851304957653749944402097411780097914172680501, 3371394908151563362208243796489511825354821020573987752522791881868987441291678825694507113985550194783443672326288, 2297984649797609740520111469542560058998637859756636710986648527547199594538622957148043186058263511759636104385884, 3480762570907741510541555641785158658308333937734941283300383364697580261225975907843679022209374260264933745191828, 3240728397255321363427476858948130370605249287048803384220203355573990507150386888156524321441752887099266166133108, 2376364160413810038009747479244195484637259359228646277111417852748902100064937901534264975269224589631629799823776, 505616797063036550970852124247959597244795697098930755073121309023649013438395471915981324345265013483679297150493, 283646809217572597374969288842854659843926065997224794730305413934017744924705963601539908633134851567640611976585, 208748941951544416005406635656082534817221797560780462964136211816030039051269393961107822344678318294491041226308)\n", + "\n", + "\n", + "Is pair_A_B == pair_G2_C? True\n" + ] + } + ], + "source": [ + "# bls12_381 bilinear pairing\n", + "from py_ecc.bls12_381 import G1, G2, pairing, add, multiply, eq\n", + "\n", + "print(G1)\n", + "print(G2)\n", + "print(\"\\n\")\n", + "\n", + "a = 69\n", + "b = 420\n", + "c = a * b\n", + "A = multiply(G2, a)\n", + "B = multiply(G1, b)\n", + "pair_A_B = pairing(A, B)\n", + "print(\"Pairing of points A and B: \",pair_A_B)\n", + "print(\"\\n\")\n", + "\n", + "C = multiply(G2, c)\n", + "pair_G2_C = pairing(C, G1)\n", + "print(\"Pairing of points G2 and C: \",pair_G2_C)\n", + "print(\"\\n\")\n", + "\n", + "print(\"Is pair_A_B == pair_G2_C? \", pair_A_B == pair_G2_C)\n", + "\n", + "# Output\n", + "# (3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507, 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569)\n", + "# ((352701069587466618187139116011060144890029952792775240219908644239793785735715026873347600343865175952761926303160, 3059144344244213709971259814753781636986470325476647558659373206291635324768958432433509563104347017837885763365758), (1985150602287291935568054521177171638300868978215655730859378665066344726373823718423869104263333984641494340347905, 927553665492332455747201965776037880757740193453592970025027978793976877002675564980949289727957565575433344219582))\n", + "\n", + "\n", + "# Pairing of points A and B: (559875907953044229256999964908655596470329614681804657067074917348889299656574983763205735263906508658423626306192, 3251387332700024622230711712327939415422447046055926038630362608193945095062996827680637432749053965474170688846803, 1858978301495685148589092187900391308425211794249943939941796868269658435530364461212323465553496690057506398710958, 2821404563389658506867792213698964607416184520400354580766817454079710851304957653749944402097411780097914172680501, 3371394908151563362208243796489511825354821020573987752522791881868987441291678825694507113985550194783443672326288, 2297984649797609740520111469542560058998637859756636710986648527547199594538622957148043186058263511759636104385884, 3480762570907741510541555641785158658308333937734941283300383364697580261225975907843679022209374260264933745191828, 3240728397255321363427476858948130370605249287048803384220203355573990507150386888156524321441752887099266166133108, 2376364160413810038009747479244195484637259359228646277111417852748902100064937901534264975269224589631629799823776, 505616797063036550970852124247959597244795697098930755073121309023649013438395471915981324345265013483679297150493, 283646809217572597374969288842854659843926065997224794730305413934017744924705963601539908633134851567640611976585, 208748941951544416005406635656082534817221797560780462964136211816030039051269393961107822344678318294491041226308)\n", + "\n", + "\n", + "# Pairing of points G2 and C: (559875907953044229256999964908655596470329614681804657067074917348889299656574983763205735263906508658423626306192, 3251387332700024622230711712327939415422447046055926038630362608193945095062996827680637432749053965474170688846803, 1858978301495685148589092187900391308425211794249943939941796868269658435530364461212323465553496690057506398710958, 2821404563389658506867792213698964607416184520400354580766817454079710851304957653749944402097411780097914172680501, 3371394908151563362208243796489511825354821020573987752522791881868987441291678825694507113985550194783443672326288, 2297984649797609740520111469542560058998637859756636710986648527547199594538622957148043186058263511759636104385884, 3480762570907741510541555641785158658308333937734941283300383364697580261225975907843679022209374260264933745191828, 3240728397255321363427476858948130370605249287048803384220203355573990507150386888156524321441752887099266166133108, 2376364160413810038009747479244195484637259359228646277111417852748902100064937901534264975269224589631629799823776, 505616797063036550970852124247959597244795697098930755073121309023649013438395471915981324345265013483679297150493, 283646809217572597374969288842854659843926065997224794730305413934017744924705963601539908633134851567640611976585, 208748941951544416005406635656082534817221797560780462964136211816030039051269393961107822344678318294491041226308)\n", + "\n", + "\n", + "# Is pair_A_B == pair_G2_C? True\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b99237ea", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Languages/en/40_PopularCurves/readme.md b/Languages/en/40_PopularCurves/readme.md index 405ae72..9cfca50 100644 --- a/Languages/en/40_PopularCurves/readme.md +++ b/Languages/en/40_PopularCurves/readme.md @@ -1,5 +1,5 @@ --- -title: Common Elliptic Curves +title: 40. Common Elliptic Curves tags: - zk - abstract algebra